diff --git a/composer.json b/composer.json index d955633..1fb70d4 100644 --- a/composer.json +++ b/composer.json @@ -22,8 +22,9 @@ "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^5.1", - "hamcrest/hamcrest-php": "^1.2", + "phpunit/phpunit": "~5.1.0", + "hamcrest/hamcrest-php": "~1.2.0", + "phpunit/php-token-stream": "~1.4.8", "scrutinizer/ocular": "~1.3", "squizlabs/php_codesniffer": "~2.5" }, diff --git a/composer.lock b/composer.lock index a8da027..c277c42 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "ff51f8e1f5db391280674365e25a3f15", - "content-hash": "7ac05cb5230f735300c99bc448bac42f", + "hash": "89ff393a4b4ed3944d9f92c4561f3c94", + "content-hash": "536260a2dedbd7c6e809ef0c8f88c677", "packages": [], "packages-dev": [ { @@ -977,16 +977,16 @@ }, { "name": "phpunit/phpunit", - "version": "5.1.4", + "version": "5.1.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "676c25c4ac563869572c878fdaf3db21587f5f3b" + "reference": "d0f7ae467dcbe7a6ad050540c9d1d39a7aefff26" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/676c25c4ac563869572c878fdaf3db21587f5f3b", - "reference": "676c25c4ac563869572c878fdaf3db21587f5f3b", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d0f7ae467dcbe7a6ad050540c9d1d39a7aefff26", + "reference": "d0f7ae467dcbe7a6ad050540c9d1d39a7aefff26", "shasum": "" }, "require": { @@ -1047,7 +1047,7 @@ "testing", "xunit" ], - "time": "2016-01-11 10:11:10" + "time": "2016-02-02 09:03:29" }, { "name": "phpunit/phpunit-mock-objects", @@ -1916,7 +1916,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "^5.6|^7.0" + "php": "^7.0" }, "platform-dev": [] } diff --git a/src/Irc/Parser.php b/src/Irc/Parser.php new file mode 100644 index 0000000..0deab8f --- /dev/null +++ b/src/Irc/Parser.php @@ -0,0 +1,108 @@ +$servername)" . + "|(?:(?P$nickname)(?:$bang(?P$user))?(?:$at(?P$host))?))"; + + // = [ ":" prefix SPACE ] command [ params ] crlf + $message = "(?P$colon$prefix$space)?(?P$command)(?P$params)?$crlf"; + + // Do the thing + preg_match("/^$message\$/SU", $raw, $matches); + + // Trim whitespace + $matches = array_map('trim', $matches); + + // Return only the named matches we want in the order we want + return [ + 'prefix' => $matches['prefix'] ?? '', + 'nickname' => $matches['nickname'] ?? '', + 'username' => $matches['username'] ?? '', + 'hostname' => $matches['hostname'] ?? '', + 'servername' => $matches['servername'] ?? '', + 'command' => $matches['command'], + 'params' => $matches['params'] ?? '', + ]; + } +} diff --git a/tests/Irc/ParserTest.php b/tests/Irc/ParserTest.php new file mode 100644 index 0000000..034c89e --- /dev/null +++ b/tests/Irc/ParserTest.php @@ -0,0 +1,198 @@ +parse($message); + + assertThat($parse, is(identicalTo($expected))); + } + + public function dataMessages() + { + return [ + 'Command with no params' => [ + "QUIT\r\n", + [ + 'prefix' => '', + 'nickname' => '', + 'username' => '', + 'hostname' => '', + 'servername' => '', + 'command' => 'QUIT', + 'params' => '', + ] + ], + 'Command with only one param' => [ + "NICK dilling\r\n", + [ + 'prefix' => '', + 'nickname' => '', + 'username' => '', + 'hostname' => '', + 'servername' => '', + 'command' => 'NICK', + 'params' => 'dilling', + ] + ], + 'Command with multiple params' => [ + "USER guest 0 * :Ronnie Reagan\r\n", + [ + 'prefix' => '', + 'nickname' => '', + 'username' => '', + 'hostname' => '', + 'servername' => '', + 'command' => 'USER', + 'params' => 'guest 0 * :Ronnie Reagan', + ] + ], + 'Only first part of the prefix present, then it is the servername' => [ + ":asimov.freenode.net QUIT\r\n", + [ + 'prefix' => ':asimov.freenode.net', + 'nickname' => '', + 'username' => '', + 'hostname' => '', + 'servername' => 'asimov.freenode.net', + 'command' => 'QUIT', + 'params' => '', + ] + ], + 'First two parts of the prefix present, then it is the nickname and username' => [ + ":dilling!~dilling JOIN #phpoxford\r\n", + [ + 'prefix' => ':dilling!~dilling', + 'nickname' => 'dilling', + 'username' => '~dilling', + 'hostname' => '', + 'servername' => '', + 'command' => 'JOIN', + 'params' => '#phpoxford', + ] + ], + 'All three parts of the prefix present, then it is the nickname, username and hostname' => [ + ":dilling!~dilling@cable.virginm.net NICK martindilling\r\n", + [ + 'prefix' => ':dilling!~dilling@cable.virginm.net', + 'nickname' => 'dilling', + 'username' => '~dilling', + 'hostname' => 'cable.virginm.net', + 'servername' => '', + 'command' => 'NICK', + 'params' => 'martindilling', + ] + ], + 'The hostname can be a IPv4 address' => [ + ":dilling!~dilling@168.12.8.204 NICK martindilling\r\n", + [ + 'prefix' => ':dilling!~dilling@168.12.8.204', + 'nickname' => 'dilling', + 'username' => '~dilling', + 'hostname' => '168.12.8.204', + 'servername' => '', + 'command' => 'NICK', + 'params' => 'martindilling', + ] + ], + 'The hostname can be a IPv6 address' => [ + ":dilling!~dilling@2001:0db8:0a0b:12f0:0000:0000:0000:0001 NICK martindilling\r\n", + [ + 'prefix' => ':dilling!~dilling@2001:0db8:0a0b:12f0:0000:0000:0000:0001', + 'nickname' => 'dilling', + 'username' => '~dilling', + 'hostname' => '2001:0db8:0a0b:12f0:0000:0000:0000:0001', + 'servername' => '', + 'command' => 'NICK', + 'params' => 'martindilling', + ] + ], + 'Recognises really long messages' => [ + ":dilling!~dilling@cable.virginm.net PRIVMSG ascii-soup :This is a really really long message! " . + "It is longer than most message, so I really hope this works as it should\r\n", + [ + 'prefix' => ':dilling!~dilling@cable.virginm.net', + 'nickname' => 'dilling', + 'username' => '~dilling', + 'hostname' => 'cable.virginm.net', + 'servername' => '', + 'command' => 'PRIVMSG', + 'params' => 'ascii-soup :This is a really really long message! ' . + 'It is longer than most message, so I really hope this works as it should', + ] + ], + 'Safety: PING command' => [ + "PING :wolfe.freenode.net\r\n", + [ + 'prefix' => '', + 'nickname' => '', + 'username' => '', + 'hostname' => '', + 'servername' => '', + 'command' => 'PING', + 'params' => ':wolfe.freenode.net', + ] + ], + 'Safety: NICK command' => [ + ":dilling!~dilling@cable.virginm.net NICK :imchanged\r\n", + [ + 'prefix' => ':dilling!~dilling@cable.virginm.net', + 'nickname' => 'dilling', + 'username' => '~dilling', + 'hostname' => 'cable.virginm.net', + 'servername' => '', + 'command' => 'NICK', + 'params' => ':imchanged', + ] + ], + 'Safety: JOIN command' => [ + ":dilling!~dilling@cable.virginm.net JOIN #phpoxford\r\n", + [ + 'prefix' => ':dilling!~dilling@cable.virginm.net', + 'nickname' => 'dilling', + 'username' => '~dilling', + 'hostname' => 'cable.virginm.net', + 'servername' => '', + 'command' => 'JOIN', + 'params' => '#phpoxford', + ] + ], + 'Safety: PART command' => [ + ":dilling!~dilling@cable.virginm.net PART #phpoxford :Leaving\r\n", + [ + 'prefix' => ':dilling!~dilling@cable.virginm.net', + 'nickname' => 'dilling', + 'username' => '~dilling', + 'hostname' => 'cable.virginm.net', + 'servername' => '', + 'command' => 'PART', + 'params' => '#phpoxford :Leaving', + ] + ], + 'Safety: QUIT command' => [ + ":dilling!~dilling@cable.virginm.net QUIT :Quit: I'm outta here\r\n", + [ + 'prefix' => ':dilling!~dilling@cable.virginm.net', + 'nickname' => 'dilling', + 'username' => '~dilling', + 'hostname' => 'cable.virginm.net', + 'servername' => '', + 'command' => 'QUIT', + 'params' => ':Quit: I\'m outta here', + ] + ], + ]; + } +}