From 13487d783b58a2c16c4913b0b082df06ce667256 Mon Sep 17 00:00:00 2001 From: Martin Dilling-Hansen Date: Tue, 2 Feb 2016 23:21:19 +0000 Subject: [PATCH 1/3] The Augmented BNF for messages converted to regex Mostly direct translation of the Augmented BNF for an IRC message to a giant regex. It takes a raw IRC message and returns an array with: `prefix` // The whole prefix string `nickname` // The nickname from the prefix `username` // The username from the prefix `hostname` // The hostname from the prefix `servername` // The servername from the prefix `command` // The command `params` // The parameters I'm pretty sure this can be optimised quite a lot, but for now it parses following the specification and it works ;) --- src/Irc/Parser.php | 106 +++++++++++++++++++++ tests/Irc/ParserTest.php | 198 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 304 insertions(+) create mode 100644 src/Irc/Parser.php create mode 100644 tests/Irc/ParserTest.php diff --git a/src/Irc/Parser.php b/src/Irc/Parser.php new file mode 100644 index 0000000..a825623 --- /dev/null +++ b/src/Irc/Parser.php @@ -0,0 +1,106 @@ +$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', + ] + ], + ]; + } +} From 0ed04426062959ab11ee514e71eb01098e4eca4b Mon Sep 17 00:00:00 2001 From: Martin Dilling-Hansen Date: Tue, 2 Feb 2016 23:35:19 +0000 Subject: [PATCH 2/3] Code style fix: Some lines were too long --- src/Irc/Parser.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Irc/Parser.php b/src/Irc/Parser.php index a825623..0deab8f 100644 --- a/src/Irc/Parser.php +++ b/src/Irc/Parser.php @@ -50,7 +50,8 @@ public function parse(string $raw): array // = *14( SPACE middle ) [ SPACE ":" trailing ] // =/ 14( SPACE middle ) [ SPACE [ ":" ] trailing ] - $params = "(?:(?:$space$middle){0,14}(?:$space$colon$trailing)?|(?:$space$middle){14}(?:$space(?:$colon)?$trailing)?)"; + $params = "(?:(?:$space$middle){0,14}(?:$space$colon$trailing)?" . + "|(?:$space$middle){14}(?:$space(?:$colon)?$trailing)?)"; // = ( letter / digit ) *( letter / digit / "-" ) *( letter / digit ) $shortname = "(?:[$letter$digit][$letter$digit$dash]*[$letter$digit]*)"; @@ -81,7 +82,8 @@ public function parse(string $raw): array $user = "(?:[\x01-\x09|\x0B-\x0C|\x0E-\x1F|\x21-\x3F|\x41-\xFF]+)"; // = servername / ( nickname [ [ "!" user ] "@" host ] ) - $prefix = "(?:(?P$servername)|(?:(?P$nickname)(?:$bang(?P$user))?(?:$at(?P$host))?))"; + $prefix = "(?:(?P$servername)" . + "|(?:(?P$nickname)(?:$bang(?P$user))?(?:$at(?P$host))?))"; // = [ ":" prefix SPACE ] command [ params ] crlf $message = "(?P$colon$prefix$space)?(?P$command)(?P$params)?$crlf"; From 91124989eec7bb5bd553f12aed807557a7ce8d56 Mon Sep 17 00:00:00 2001 From: Martin Dilling-Hansen Date: Wed, 3 Feb 2016 00:04:55 +0000 Subject: [PATCH 3/3] Tweak dependency versions to work with --prefer-lowest Needed to explicitly set the minimum version we need of the phpunit/php-token-stream package. Probably somewhere in the phpunit dependency tree it have specified a minimum version of that package that doesn't support PHP 7. --- composer.json | 5 +++-- composer.lock | 16 ++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) 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": [] }