From 55981e8de97e52bcbe9b0741e7858259fca1d88c Mon Sep 17 00:00:00 2001 From: Ivan Chepurnyi Date: Fri, 31 May 2019 14:24:46 +0200 Subject: [PATCH 01/23] Base project skeleton --- .gitignore | 11 + .idea/modules.xml | 8 + .idea/php-mysql-binary-protocol.iml | 11 + composer.json | 27 + composer.lock | 1539 +++++++++++++++++++++++++++ src/ReadBuffer.php | 35 + 6 files changed, 1631 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/modules.xml create mode 100644 .idea/php-mysql-binary-protocol.iml create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 src/ReadBuffer.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c361ba7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# OSX +.DS_Store + +# Composer +/vendor/ + +# PHPStorm (except class maps) +/.idea/* +!/.idea/*.iml +!/.idea/modules.xml + diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..fe17300 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/php-mysql-binary-protocol.iml b/.idea/php-mysql-binary-protocol.iml new file mode 100644 index 0000000..807507e --- /dev/null +++ b/.idea/php-mysql-binary-protocol.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..7dbc239 --- /dev/null +++ b/composer.json @@ -0,0 +1,27 @@ +{ + "name": "ecomdv/mysql-binary-protocol", + "description": "Implementation of MySQL binary protocol independent from async frameworks", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Ivan Chepurnyi", + "email": "ivan.chepurnyi@ecomdev.org" + } + ], + "minimum-stability": "dev", + "require": {}, + "autoload": { + "psr-4": { + "EcomDev\\MySQLBinaryProtocol\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "EcomDev\\MySQLBinaryProtocol\\": "tests/" + } + }, + "require-dev": { + "phpunit/phpunit": "^8.1" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..ddc34d9 --- /dev/null +++ b/composer.lock @@ -0,0 +1,1539 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "2de4be3196229246eed07eab97a2c08c", + "content-hash": "401ef717f2e491fdbe6fdf1b4b8f400f", + "packages": [], + "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "fb21dea73761426178f8665f6b567c58fee488ff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/fb21dea73761426178f8665f6b567c58fee488ff", + "reference": "fb21dea73761426178f8665f6b567c58fee488ff", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "doctrine/coding-standard": "^6.0", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.13", + "phpstan/phpstan-phpunit": "^0.11", + "phpstan/phpstan-shim": "^0.11", + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2019-05-24 18:15:18" + }, + { + "name": "myclabs/deep-copy", + "version": "1.x-dev", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", + "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "replace": { + "myclabs/deep-copy": "self.version" + }, + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "time": "2019-04-07 13:18:21" + }, + { + "name": "phar-io/manifest", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "phar-io/version": "^2.0", + "php": "^5.6 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "time": "2018-07-08 19:23:20" + }, + { + "name": "phar-io/version", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "time": "2018-07-08 19:19:57" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2017-09-11 18:02:19" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "4.3.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c", + "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c", + "shasum": "" + }, + "require": { + "php": "^7.0", + "phpdocumentor/reflection-common": "^1.0.0", + "phpdocumentor/type-resolver": "^0.4.0", + "webmozart/assert": "^1.0" + }, + "require-dev": { + "doctrine/instantiator": "~1.0.5", + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2019-04-30 17:48:53" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "phpdocumentor/reflection-common": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^5.2||^4.8.24" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "time": "2017-07-14 14:27:02" + }, + { + "name": "phpspec/prophecy", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "7e272180527c34a97680de85eb5aba0847a664e0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/7e272180527c34a97680de85eb5aba0847a664e0", + "reference": "7e272180527c34a97680de85eb5aba0847a664e0", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", + "sebastian/comparator": "^1.1|^2.0|^3.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0" + }, + "require-dev": { + "phpspec/phpspec": "^2.5|^3.2", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2018-12-18 15:40:51" + }, + { + "name": "phpunit/php-code-coverage", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "d720d14ef701c38cc7d50be1843a34d744e919f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d720d14ef701c38cc7d50be1843a34d744e919f6", + "reference": "d720d14ef701c38cc7d50be1843a34d744e919f6", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xmlwriter": "*", + "php": "^7.2", + "phpunit/php-file-iterator": "^2.0.2", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-token-stream": "^3.0.1", + "sebastian/code-unit-reverse-lookup": "^1.0.1", + "sebastian/environment": "^4.1", + "sebastian/version": "^2.0.1", + "theseer/tokenizer": "^1.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "suggest": { + "ext-xdebug": "^2.6.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2019-05-29 11:51:37" + }, + { + "name": "phpunit/php-file-iterator", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "80ca798a9fb7b60c6a2169d8c6cad9f00eb3a06a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/80ca798a9fb7b60c6a2169d8c6cad9f00eb3a06a", + "reference": "80ca798a9fb7b60c6a2169d8c6cad9f00eb3a06a", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2019-05-24 06:36:01" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2015-06-21 13:50:34" + }, + { + "name": "phpunit/php-timer", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "e826e3ab11b1ca789b10a00b1a2188feb381beb5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e826e3ab11b1ca789b10a00b1a2188feb381beb5", + "reference": "e826e3ab11b1ca789b10a00b1a2188feb381beb5", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2019-05-24 06:37:41" + }, + { + "name": "phpunit/php-token-stream", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "2d574d6767341959316726121431ecb9c4a61037" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/2d574d6767341959316726121431ecb9c4a61037", + "reference": "2d574d6767341959316726121431ecb9c4a61037", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2019-05-24 06:39:22" + }, + { + "name": "phpunit/phpunit", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "e4390fae89bf940f430604f6fd4b58804a530816" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e4390fae89bf940f430604f6fd4b58804a530816", + "reference": "e4390fae89bf940f430604f6fd4b58804a530816", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.7", + "phar-io/manifest": "^1.0.2", + "phar-io/version": "^2.0", + "php": "^7.2", + "phpspec/prophecy": "^1.7", + "phpunit/php-code-coverage": "^7.0", + "phpunit/php-file-iterator": "^2.0.1", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-timer": "^2.1", + "sebastian/comparator": "^3.0", + "sebastian/diff": "^3.0", + "sebastian/environment": "^4.2.1", + "sebastian/exporter": "^3.1", + "sebastian/global-state": "^3.0", + "sebastian/object-enumerator": "^3.0.3", + "sebastian/resource-operations": "^2.0", + "sebastian/type": "^1.0", + "sebastian/version": "^2.0.1" + }, + "require-dev": { + "ext-pdo": "*" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*", + "phpunit/php-invoker": "^2.0" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2019-05-29 04:38:41" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "aa169192fe98c7270af0ec5c57e631094ad930df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/aa169192fe98c7270af0ec5c57e631094ad930df", + "reference": "aa169192fe98c7270af0ec5c57e631094ad930df", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "time": "2019-05-24 06:36:34" + }, + { + "name": "sebastian/comparator", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "6ddb43a0106887320958344fc6e55aa033154b9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/6ddb43a0106887320958344fc6e55aa033154b9d", + "reference": "6ddb43a0106887320958344fc6e55aa033154b9d", + "shasum": "" + }, + "require": { + "php": "^7.1", + "sebastian/diff": "^3.0", + "sebastian/exporter": "^3.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2019-05-24 06:32:56" + }, + { + "name": "sebastian/diff", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "1d90f91424a056ebd763d7046ee5957d160c1c24" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/1d90f91424a056ebd763d7046ee5957d160c1c24", + "reference": "1d90f91424a056ebd763d7046ee5957d160c1c24", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^8.0", + "symfony/process": "^2 || ^3.3 || ^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "time": "2019-05-24 06:38:22" + }, + { + "name": "sebastian/environment", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "48c9235dc43ce325b15d950f53996da748f2d571" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/48c9235dc43ce325b15d950f53996da748f2d571", + "reference": "48c9235dc43ce325b15d950f53996da748f2d571", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.5" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2019-05-24 06:34:26" + }, + { + "name": "sebastian/exporter", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "cf3a70cdc3af7b80ad571adae1ab718eb578be2b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/cf3a70cdc3af7b80ad571adae1ab718eb578be2b", + "reference": "cf3a70cdc3af7b80ad571adae1ab718eb578be2b", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2019-05-24 06:35:43" + }, + { + "name": "sebastian/global-state", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "6ddc744049960cad6878d2b9c82d7ccd37afb061" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/6ddc744049960cad6878d2b9c82d7ccd37afb061", + "reference": "6ddc744049960cad6878d2b9c82d7ccd37afb061", + "shasum": "" + }, + "require": { + "php": "^7.2", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^8.0" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2019-05-24 06:34:46" + }, + { + "name": "sebastian/object-enumerator", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "eb45b4b8fd5f845d36851767a6fe00ee15cb8952" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/eb45b4b8fd5f845d36851767a6fe00ee15cb8952", + "reference": "eb45b4b8fd5f845d36851767a6fe00ee15cb8952", + "shasum": "" + }, + "require": { + "php": "^7.0", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "time": "2019-05-24 06:37:20" + }, + { + "name": "sebastian/object-reflector", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "a2539861984780016bb3d75420452d95f798e25d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/a2539861984780016bb3d75420452d95f798e25d", + "reference": "a2539861984780016bb3d75420452d95f798e25d", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "time": "2019-05-24 06:35:22" + }, + { + "name": "sebastian/recursion-context", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "b6a7e76741fee33ec10ccb55772e65d817a8f2fd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b6a7e76741fee33ec10ccb55772e65d817a8f2fd", + "reference": "b6a7e76741fee33ec10ccb55772e65d817a8f2fd", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2019-05-24 06:37:03" + }, + { + "name": "sebastian/resource-operations", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "366bbb128ea2fd570ab9986e6d79a7167fd5c041" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/366bbb128ea2fd570ab9986e6d79a7167fd5c041", + "reference": "366bbb128ea2fd570ab9986e6d79a7167fd5c041", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "time": "2019-05-24 06:40:48" + }, + { + "name": "sebastian/type", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "5de1c9e0753b992b0e6d711a3bb6f8f94ed56d62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/5de1c9e0753b992b0e6d711a3bb6f8f94ed56d62", + "reference": "5de1c9e0753b992b0e6d711a3bb6f8f94ed56d62", + "shasum": "" + }, + "require": { + "php": "^7.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "time": "2019-05-26 06:11:23" + }, + { + "name": "sebastian/version", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2016-10-03 07:35:21" + }, + { + "name": "symfony/polyfill-ctype", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "82ebae02209c21113908c229e9883c419720738a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a", + "reference": "82ebae02209c21113908c229e9883c419720738a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.11-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + }, + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "time": "2019-02-06 07:57:58" + }, + { + "name": "theseer/tokenizer", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "1c42705be2b6c1de5904f8afacef5895cab44bf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/1c42705be2b6c1de5904f8afacef5895cab44bf8", + "reference": "1c42705be2b6c1de5904f8afacef5895cab44bf8", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "time": "2019-04-04 09:56:43" + }, + { + "name": "webmozart/assert", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9", + "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0", + "symfony/polyfill-ctype": "^1.8" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "time": "2018-12-25 11:19:39" + } + ], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} diff --git a/src/ReadBuffer.php b/src/ReadBuffer.php new file mode 100644 index 0000000..fe68142 --- /dev/null +++ b/src/ReadBuffer.php @@ -0,0 +1,35 @@ + Date: Fri, 31 May 2019 23:15:29 +0200 Subject: [PATCH 02/23] Add base skeleton --- src/InMemoryReadBuffer.php | 52 +++++++++++++++++++++++++++ src/IncompleteBufferException.php | 15 ++++++++ src/ReadBuffer.php | 17 +++++---- src/ReadBufferFragment.php | 59 +++++++++++++++++++++++++++++++ tests/InMemoryReadBufferTest.php | 40 +++++++++++++++++++++ 5 files changed, 176 insertions(+), 7 deletions(-) create mode 100644 src/InMemoryReadBuffer.php create mode 100644 src/IncompleteBufferException.php create mode 100644 src/ReadBufferFragment.php create mode 100644 tests/InMemoryReadBufferTest.php diff --git a/src/InMemoryReadBuffer.php b/src/InMemoryReadBuffer.php new file mode 100644 index 0000000..692a05e --- /dev/null +++ b/src/InMemoryReadBuffer.php @@ -0,0 +1,52 @@ +buffer = $data; + $this->bufferSize = 0; + } + + /** + * {@inheritDoc} + */ + public function readFragment(callable $reader): bool + { + // TODO: Implement readFragment() method. + } + + /** + * {@inheritDoc} + */ + public function isFullPacket(): bool + { + return false; + } + + /** + * {@inheritDoc} + */ + public function nextPacket(): void + { + // TODO: Implement nextPacket() method. + } +} diff --git a/src/IncompleteBufferException.php b/src/IncompleteBufferException.php new file mode 100644 index 0000000..e0ec68b --- /dev/null +++ b/src/IncompleteBufferException.php @@ -0,0 +1,15 @@ +buffer = new InMemoryReadBuffer(); + } + + /** @test */ + public function reportsIncompleteBufferWhenNotFullLengthProvided() + { + $this->buffer->append("\x00\x00\x01\x00"); + + $this->assertFalse($this->buffer->isFullPacket()); + } + + /** @test */ + public function reportsCompleteBufferWhenAllDataIsProvidedAtOnce() + { + $this->buffer->append("\x01\x00\x00\x00\x01"); + + $this->assertTrue($this->buffer->isFullPacket()); + } +} From c22b14f77c13c96dedf5e1dd743e4958621cde4a Mon Sep 17 00:00:00 2001 From: Ivan Chepurnyi Date: Sun, 2 Jun 2019 01:45:54 +0200 Subject: [PATCH 03/23] Rename buffer to reflect its implementation, create integer reader for buffer --- composer.json | 6 +- composer.lock | 237 +++++++----------- src/BinaryIntegerReader.php | 50 ++++ src/ReadBuffer.php | 9 +- ...oryReadBuffer.php => StringReadBuffer.php} | 39 ++- tests/BinaryIntegerReaderTest.php | 114 +++++++++ ...ufferTest.php => StringReadBufferTest.php} | 11 +- 7 files changed, 309 insertions(+), 157 deletions(-) create mode 100644 src/BinaryIntegerReader.php rename src/{InMemoryReadBuffer.php => StringReadBuffer.php} (55%) create mode 100644 tests/BinaryIntegerReaderTest.php rename tests/{InMemoryReadBufferTest.php => StringReadBufferTest.php} (77%) diff --git a/composer.json b/composer.json index 7dbc239..9b31b9f 100644 --- a/composer.json +++ b/composer.json @@ -9,8 +9,10 @@ "email": "ivan.chepurnyi@ecomdev.org" } ], - "minimum-stability": "dev", - "require": {}, + "minimum-stability": "stable", + "require": { + "ext-bcmath": "" + }, "autoload": { "psr-4": { "EcomDev\\MySQLBinaryProtocol\\": "src/" diff --git a/composer.lock b/composer.lock index ddc34d9..3ff1432 100644 --- a/composer.lock +++ b/composer.lock @@ -4,22 +4,22 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "2de4be3196229246eed07eab97a2c08c", - "content-hash": "401ef717f2e491fdbe6fdf1b4b8f400f", + "hash": "9c7a720e2cbf882dc19d228afcddcfcd", + "content-hash": "c49bf819e0695a2361ebfe74ca8378ee", "packages": [], "packages-dev": [ { "name": "doctrine/instantiator", - "version": "dev-master", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "fb21dea73761426178f8665f6b567c58fee488ff" + "reference": "a2c590166b2133a4633738648b6b064edae0814a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/fb21dea73761426178f8665f6b567c58fee488ff", - "reference": "fb21dea73761426178f8665f6b567c58fee488ff", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a", + "reference": "a2c590166b2133a4633738648b6b064edae0814a", "shasum": "" }, "require": { @@ -62,11 +62,11 @@ "constructor", "instantiate" ], - "time": "2019-05-24 18:15:18" + "time": "2019-03-17 17:37:11" }, { "name": "myclabs/deep-copy", - "version": "1.x-dev", + "version": "1.9.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", @@ -114,7 +114,7 @@ }, { "name": "phar-io/manifest", - "version": "dev-master", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", @@ -368,16 +368,16 @@ }, { "name": "phpspec/prophecy", - "version": "dev-master", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "7e272180527c34a97680de85eb5aba0847a664e0" + "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/7e272180527c34a97680de85eb5aba0847a664e0", - "reference": "7e272180527c34a97680de85eb5aba0847a664e0", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", "shasum": "" }, "require": { @@ -398,8 +398,8 @@ } }, "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" + "psr-0": { + "Prophecy\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -427,20 +427,20 @@ "spy", "stub" ], - "time": "2018-12-18 15:40:51" + "time": "2018-08-05 17:53:17" }, { "name": "phpunit/php-code-coverage", - "version": "dev-master", + "version": "7.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "d720d14ef701c38cc7d50be1843a34d744e919f6" + "reference": "6024c8a6cb962d496b7bd049ed8f48473824176d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d720d14ef701c38cc7d50be1843a34d744e919f6", - "reference": "d720d14ef701c38cc7d50be1843a34d744e919f6", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6024c8a6cb962d496b7bd049ed8f48473824176d", + "reference": "6024c8a6cb962d496b7bd049ed8f48473824176d", "shasum": "" }, "require": { @@ -490,20 +490,20 @@ "testing", "xunit" ], - "time": "2019-05-29 11:51:37" + "time": "2019-05-29 09:59:31" }, { "name": "phpunit/php-file-iterator", - "version": "dev-master", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "80ca798a9fb7b60c6a2169d8c6cad9f00eb3a06a" + "reference": "050bedf145a257b1ff02746c31894800e5122946" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/80ca798a9fb7b60c6a2169d8c6cad9f00eb3a06a", - "reference": "80ca798a9fb7b60c6a2169d8c6cad9f00eb3a06a", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", + "reference": "050bedf145a257b1ff02746c31894800e5122946", "shasum": "" }, "require": { @@ -540,7 +540,7 @@ "filesystem", "iterator" ], - "time": "2019-05-24 06:36:01" + "time": "2018-09-13 20:33:42" }, { "name": "phpunit/php-text-template", @@ -585,16 +585,16 @@ }, { "name": "phpunit/php-timer", - "version": "dev-master", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "e826e3ab11b1ca789b10a00b1a2188feb381beb5" + "reference": "8b389aebe1b8b0578430bda0c7c95a829608e059" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e826e3ab11b1ca789b10a00b1a2188feb381beb5", - "reference": "e826e3ab11b1ca789b10a00b1a2188feb381beb5", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/8b389aebe1b8b0578430bda0c7c95a829608e059", + "reference": "8b389aebe1b8b0578430bda0c7c95a829608e059", "shasum": "" }, "require": { @@ -630,20 +630,20 @@ "keywords": [ "timer" ], - "time": "2019-05-24 06:37:41" + "time": "2019-02-20 10:12:59" }, { "name": "phpunit/php-token-stream", - "version": "dev-master", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "2d574d6767341959316726121431ecb9c4a61037" + "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/2d574d6767341959316726121431ecb9c4a61037", - "reference": "2d574d6767341959316726121431ecb9c4a61037", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c99e3be9d3e85f60646f152f9002d46ed7770d18", + "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18", "shasum": "" }, "require": { @@ -679,20 +679,20 @@ "keywords": [ "tokenizer" ], - "time": "2019-05-24 06:39:22" + "time": "2018-10-30 05:52:18" }, { "name": "phpunit/phpunit", - "version": "dev-master", + "version": "8.1.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "e4390fae89bf940f430604f6fd4b58804a530816" + "reference": "e3c9da6e645492c461e0a11eca117f83f4f4c840" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e4390fae89bf940f430604f6fd4b58804a530816", - "reference": "e4390fae89bf940f430604f6fd4b58804a530816", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e3c9da6e645492c461e0a11eca117f83f4f4c840", + "reference": "e3c9da6e645492c461e0a11eca117f83f4f4c840", "shasum": "" }, "require": { @@ -714,12 +714,11 @@ "phpunit/php-timer": "^2.1", "sebastian/comparator": "^3.0", "sebastian/diff": "^3.0", - "sebastian/environment": "^4.2.1", + "sebastian/environment": "^4.1", "sebastian/exporter": "^3.1", "sebastian/global-state": "^3.0", "sebastian/object-enumerator": "^3.0.3", "sebastian/resource-operations": "^2.0", - "sebastian/type": "^1.0", "sebastian/version": "^2.0.1" }, "require-dev": { @@ -736,7 +735,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "8.2-dev" + "dev-master": "8.1-dev" } }, "autoload": { @@ -762,20 +761,20 @@ "testing", "xunit" ], - "time": "2019-05-29 04:38:41" + "time": "2019-05-28 11:53:42" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "dev-master", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "aa169192fe98c7270af0ec5c57e631094ad930df" + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/aa169192fe98c7270af0ec5c57e631094ad930df", - "reference": "aa169192fe98c7270af0ec5c57e631094ad930df", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", "shasum": "" }, "require": { @@ -807,20 +806,20 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2019-05-24 06:36:34" + "time": "2017-03-04 06:30:41" }, { "name": "sebastian/comparator", - "version": "dev-master", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "6ddb43a0106887320958344fc6e55aa033154b9d" + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/6ddb43a0106887320958344fc6e55aa033154b9d", - "reference": "6ddb43a0106887320958344fc6e55aa033154b9d", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", "shasum": "" }, "require": { @@ -871,20 +870,20 @@ "compare", "equality" ], - "time": "2019-05-24 06:32:56" + "time": "2018-07-12 15:12:46" }, { "name": "sebastian/diff", - "version": "dev-master", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "1d90f91424a056ebd763d7046ee5957d160c1c24" + "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/1d90f91424a056ebd763d7046ee5957d160c1c24", - "reference": "1d90f91424a056ebd763d7046ee5957d160c1c24", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", + "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", "shasum": "" }, "require": { @@ -927,20 +926,20 @@ "unidiff", "unified diff" ], - "time": "2019-05-24 06:38:22" + "time": "2019-02-04 06:01:07" }, { "name": "sebastian/environment", - "version": "dev-master", + "version": "4.2.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "48c9235dc43ce325b15d950f53996da748f2d571" + "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/48c9235dc43ce325b15d950f53996da748f2d571", - "reference": "48c9235dc43ce325b15d950f53996da748f2d571", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/f2a2c8e1c97c11ace607a7a667d73d47c19fe404", + "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404", "shasum": "" }, "require": { @@ -980,20 +979,20 @@ "environment", "hhvm" ], - "time": "2019-05-24 06:34:26" + "time": "2019-05-05 09:05:15" }, { "name": "sebastian/exporter", - "version": "dev-master", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "cf3a70cdc3af7b80ad571adae1ab718eb578be2b" + "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/cf3a70cdc3af7b80ad571adae1ab718eb578be2b", - "reference": "cf3a70cdc3af7b80ad571adae1ab718eb578be2b", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", + "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", "shasum": "" }, "require": { @@ -1047,20 +1046,20 @@ "export", "exporter" ], - "time": "2019-05-24 06:35:43" + "time": "2017-04-03 13:19:02" }, { "name": "sebastian/global-state", - "version": "dev-master", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "6ddc744049960cad6878d2b9c82d7ccd37afb061" + "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/6ddc744049960cad6878d2b9c82d7ccd37afb061", - "reference": "6ddc744049960cad6878d2b9c82d7ccd37afb061", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", + "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", "shasum": "" }, "require": { @@ -1101,20 +1100,20 @@ "keywords": [ "global state" ], - "time": "2019-05-24 06:34:46" + "time": "2019-02-01 05:30:01" }, { "name": "sebastian/object-enumerator", - "version": "dev-master", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "eb45b4b8fd5f845d36851767a6fe00ee15cb8952" + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/eb45b4b8fd5f845d36851767a6fe00ee15cb8952", - "reference": "eb45b4b8fd5f845d36851767a6fe00ee15cb8952", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", "shasum": "" }, "require": { @@ -1148,20 +1147,20 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2019-05-24 06:37:20" + "time": "2017-08-03 12:35:26" }, { "name": "sebastian/object-reflector", - "version": "dev-master", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "a2539861984780016bb3d75420452d95f798e25d" + "reference": "773f97c67f28de00d397be301821b06708fca0be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/a2539861984780016bb3d75420452d95f798e25d", - "reference": "a2539861984780016bb3d75420452d95f798e25d", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", + "reference": "773f97c67f28de00d397be301821b06708fca0be", "shasum": "" }, "require": { @@ -1193,20 +1192,20 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2019-05-24 06:35:22" + "time": "2017-03-29 09:07:27" }, { "name": "sebastian/recursion-context", - "version": "dev-master", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "b6a7e76741fee33ec10ccb55772e65d817a8f2fd" + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b6a7e76741fee33ec10ccb55772e65d817a8f2fd", - "reference": "b6a7e76741fee33ec10ccb55772e65d817a8f2fd", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", "shasum": "" }, "require": { @@ -1246,20 +1245,20 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2019-05-24 06:37:03" + "time": "2017-03-03 06:23:57" }, { "name": "sebastian/resource-operations", - "version": "dev-master", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "366bbb128ea2fd570ab9986e6d79a7167fd5c041" + "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/366bbb128ea2fd570ab9986e6d79a7167fd5c041", - "reference": "366bbb128ea2fd570ab9986e6d79a7167fd5c041", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", "shasum": "" }, "require": { @@ -1288,53 +1287,7 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2019-05-24 06:40:48" - }, - { - "name": "sebastian/type", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "5de1c9e0753b992b0e6d711a3bb6f8f94ed56d62" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/5de1c9e0753b992b0e6d711a3bb6f8f94ed56d62", - "reference": "5de1c9e0753b992b0e6d711a3bb6f8f94ed56d62", - "shasum": "" - }, - "require": { - "php": "^7.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", - "time": "2019-05-26 06:11:23" + "time": "2018-10-04 04:07:39" }, { "name": "sebastian/version", @@ -1381,7 +1334,7 @@ }, { "name": "symfony/polyfill-ctype", - "version": "dev-master", + "version": "v1.11.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -1530,7 +1483,7 @@ } ], "aliases": [], - "minimum-stability": "dev", + "minimum-stability": "stable", "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, diff --git a/src/BinaryIntegerReader.php b/src/BinaryIntegerReader.php new file mode 100644 index 0000000..f8b94c5 --- /dev/null +++ b/src/BinaryIntegerReader.php @@ -0,0 +1,50 @@ +readFixed(substr($binary, 0, 4), 4); + $high = $this->readFixed(substr($binary, 4, 4), 4); + + return hexdec(dechex($low) . dechex($high)); + } + + if ($size > 8) { + throw new \RuntimeException('Cannot read integers above 8 bytes'); + } + + if ($size > 4) { + return current( + unpack('P', $binary . str_pad("\x00", 8-$size)) + ); + } + + for ($i = 1; $i < $size; $i ++) { + $result += ord($binary[$i]) << (8*$i); + } + + return $result; + } +} diff --git a/src/ReadBuffer.php b/src/ReadBuffer.php index a8faead..bf30b71 100644 --- a/src/ReadBuffer.php +++ b/src/ReadBuffer.php @@ -16,7 +16,7 @@ interface ReadBuffer public function append(string $data): void; /** - * Executes callable to read a fragment of the buffer + * Executes callable to read a fragment of the buffer by using ReadBufferFragment * * In case of IncompleteBufferException is thrown during reading, * method returns false and same data will be returned on the next read. @@ -26,13 +26,14 @@ public function append(string $data): void; public function readFragment(callable $reader): bool; /** - * Checks if current packet readable till the end + * Checks if current packet readable completely to the end */ public function isFullPacket(): bool; /** - * Moves internal pointer to start reading next packet, - * ignoring any left data in current one + * Moves internal pointer to start reading next packet + * + * Any data that is left unread in the packet will be discarded */ public function nextPacket(): void; } diff --git a/src/InMemoryReadBuffer.php b/src/StringReadBuffer.php similarity index 55% rename from src/InMemoryReadBuffer.php rename to src/StringReadBuffer.php index 692a05e..7b8b82e 100644 --- a/src/InMemoryReadBuffer.php +++ b/src/StringReadBuffer.php @@ -9,21 +9,36 @@ namespace EcomDev\MySQLBinaryProtocol; -class InMemoryReadBuffer implements ReadBuffer +class StringReadBuffer implements ReadBuffer { - /** @var string */ - private $buffer = ''; /** @var int */ private $bufferSize = 0; + /** @var int */ + private $currentPosition = 0; + + /** + * @var int[] + */ + private $currentPacket = []; + + /** + * @var string + */ + private $buffer = ''; + /** * {@inheritDoc} */ public function append(string $data): void { - $this->buffer = $data; - $this->bufferSize = 0; + $this->buffer .= $data; + $this->bufferSize += strlen($data); + + if (!$this->currentPacket) { + $this->initPacket(); + } } /** @@ -49,4 +64,18 @@ public function nextPacket(): void { // TODO: Implement nextPacket() method. } + + private function initPacket(): void + { + $totalLength = ord($this->buffer[0]) + + (ord($this->buffer[1]) << 8) + + (ord($this->buffer[1]) << 16) + ; + $this->currentPacket = [ + + ]; + } + + + } diff --git a/tests/BinaryIntegerReaderTest.php b/tests/BinaryIntegerReaderTest.php new file mode 100644 index 0000000..4f31211 --- /dev/null +++ b/tests/BinaryIntegerReaderTest.php @@ -0,0 +1,114 @@ +reader = new BinaryIntegerReader(); + } + + /** + * @test + * @dataProvider oneByteIntegers + */ + public function readsFixedOneByteInteger(string $binary, int $expectedResult) + { + $this->assertEquals($expectedResult, $this->reader->readFixed($binary, 1)); + } + + /** + * @test + * @dataProvider twoByteIntegers + */ + public function readsFixedTwoByteInteger(string $binary, int $expectedResult) + { + $this->assertEquals($expectedResult, $this->reader->readFixed($binary, 2)); + } + + /** + * @test + * @dataProvider threeByteIntegers + */ + public function readsFixedThreeByteInteger(string $binary, int $expectedResult) + { + $this->assertEquals($expectedResult, $this->reader->readFixed($binary, 3)); + } + + /** + * @test + */ + public function readsSevenByteInteger() + { + $this->assertEquals(72057594037927935, $this->reader->readFixed("\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 7)); + } + + /** + * @test + * @dataProvider eightByteIntegers + */ + public function readsEightByteIntegers(string $binary, $expectedResult) + { + $this->assertEquals($expectedResult, $this->reader->readFixed($binary, 8)); + } + + /** @test */ + public function doesNotSupportValuesHigherThan8Bytes() + { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Cannot read integers above 8 bytes'); + + $this->reader->readFixed("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 9); + } + + public function oneByteIntegers() + { + return [ + 'zero' => ["\x00", 0], + 'one' => ["\x01", 1], + 'ten' => ["\x0A", 10], + 'hundred' => ["\x64", 100], + 'max' => ["\xFF", 255], + ]; + } + + public function twoByteIntegers() + { + return [ + 'zero' => ["\x00\x00", 0], + 'ten' => ["\x0A\x00", 10], + 'thousand' => ["\xE8\x03", 1000], + 'max' => ["\xFF\xFF", 65535], + ]; + } + + public function threeByteIntegers() + { + return [ + 'zero' => ["\x00\x00\x00", 0], + 'two_byte_max' => ["\xFF\xFF\x00", 65535], + 'two_byte_max+1' => ["\x00\x00\x01", 65536], + 'max' => ["\xFF\xFF\xFF", 16777215], + ]; + } + + public function eightByteIntegers() + { + return [ + 'seven_byte_max' => ["\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00", 72057594037927935], + 'eight_byte_max' => ["\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 18446744073709551615], + ]; + } +} diff --git a/tests/InMemoryReadBufferTest.php b/tests/StringReadBufferTest.php similarity index 77% rename from tests/InMemoryReadBufferTest.php rename to tests/StringReadBufferTest.php index 548a22e..cea8540 100644 --- a/tests/InMemoryReadBufferTest.php +++ b/tests/StringReadBufferTest.php @@ -10,7 +10,7 @@ use PHPUnit\Framework\TestCase; -class InMemoryReadBufferTest extends TestCase +class StringReadBufferTest extends TestCase { /** * @var ReadBuffer @@ -19,13 +19,16 @@ class InMemoryReadBufferTest extends TestCase protected function setUp(): void { - $this->buffer = new InMemoryReadBuffer(); + $this->buffer = new StringReadBuffer(); } - /** @test */ + /** + * @test + * + */ public function reportsIncompleteBufferWhenNotFullLengthProvided() { - $this->buffer->append("\x00\x00\x01\x00"); + $this->buffer->append("\xFF\xFF\xFF\x00Some Payload"); $this->assertFalse($this->buffer->isFullPacket()); } From 9bc963ce8a865e276090de2fded4e9ab5b722062 Mon Sep 17 00:00:00 2001 From: Ivan Chepurnyi Date: Sun, 2 Jun 2019 13:18:05 +0200 Subject: [PATCH 04/23] Implement all cases of 8byte unsigned integer decoding --- src/BinaryIntegerReader.php | 19 +++++-------------- tests/BinaryIntegerReaderTest.php | 4 ++++ 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/BinaryIntegerReader.php b/src/BinaryIntegerReader.php index f8b94c5..8a5faae 100644 --- a/src/BinaryIntegerReader.php +++ b/src/BinaryIntegerReader.php @@ -21,24 +21,15 @@ public function readFixed(string $binary, int $size) return $result; } - - if ($size === 8) { - // This is a workaround of the bug in unpack for values - // above signed int value for little endian - $low = $this->readFixed(substr($binary, 0, 4), 4); - $high = $this->readFixed(substr($binary, 4, 4), 4); - - return hexdec(dechex($low) . dechex($high)); - } - if ($size > 8) { throw new \RuntimeException('Cannot read integers above 8 bytes'); } - if ($size > 4) { - return current( - unpack('P', $binary . str_pad("\x00", 8-$size)) - ); + + if ($size === 8) { + // This is a workaround of the bug in unpack for values + // above signed int value for little endian + return hexdec(bin2hex(strrev(substr($binary, 0, 8)))); } for ($i = 1; $i < $size; $i ++) { diff --git a/tests/BinaryIntegerReaderTest.php b/tests/BinaryIntegerReaderTest.php index 4f31211..56cb83c 100644 --- a/tests/BinaryIntegerReaderTest.php +++ b/tests/BinaryIntegerReaderTest.php @@ -90,6 +90,7 @@ public function twoByteIntegers() 'zero' => ["\x00\x00", 0], 'ten' => ["\x0A\x00", 10], 'thousand' => ["\xE8\x03", 1000], + 'random' => ["\x20\x8a", 35360], 'max' => ["\xFF\xFF", 65535], ]; } @@ -100,6 +101,7 @@ public function threeByteIntegers() 'zero' => ["\x00\x00\x00", 0], 'two_byte_max' => ["\xFF\xFF\x00", 65535], 'two_byte_max+1' => ["\x00\x00\x01", 65536], + 'random' => ["\x68\xD6\xEF", 15717992], 'max' => ["\xFF\xFF\xFF", 16777215], ]; } @@ -108,7 +110,9 @@ public function eightByteIntegers() { return [ 'seven_byte_max' => ["\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00", 72057594037927935], + 'above_singed_eight_max' => ["\x00\x00\x00\x00\x00\x00\x00\x80", 9223372036854775808], 'eight_byte_max' => ["\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 18446744073709551615], + 'eight_byte_max_redundant_byte' => ["\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 18446744073709551615], ]; } } From 2139dadfb589fff83e3a1fd0bc44b0b14f98d2ad Mon Sep 17 00:00:00 2001 From: Ivan Chepurnyi Date: Sun, 2 Jun 2019 15:41:35 +0200 Subject: [PATCH 05/23] Change to unpack for better performance --- src/BinaryIntegerReader.php | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/BinaryIntegerReader.php b/src/BinaryIntegerReader.php index 8a5faae..5795c4a 100644 --- a/src/BinaryIntegerReader.php +++ b/src/BinaryIntegerReader.php @@ -8,6 +8,8 @@ namespace EcomDev\MySQLBinaryProtocol; +use function unpack; + /** * @internal */ @@ -15,27 +17,34 @@ class BinaryIntegerReader { public function readFixed(string $binary, int $size) { - $result = ord($binary); - if ($size === 1) { - return $result; + return unpack('C', $binary)[1]; } - if ($size > 8) { - throw new \RuntimeException('Cannot read integers above 8 bytes'); + if ($size === 2) { + return unpack('v', $binary)[1]; + } + + if ($size === 3) { + return unpack('V', $binary."\x00")[1]; } + if ($size === 4) { + return unpack('V', $binary)[1]; + } if ($size === 8) { - // This is a workaround of the bug in unpack for values - // above signed int value for little endian - return hexdec(bin2hex(strrev(substr($binary, 0, 8)))); + if (strlen($binary) > $size) { + $binary = substr($binary, 0, $size); + } + + return \hexdec(\bin2hex(\strrev($binary))); } - for ($i = 1; $i < $size; $i ++) { - $result += ord($binary[$i]) << (8*$i); + if ($size > 8) { + throw new \RuntimeException('Cannot read integers above 8 bytes'); } - return $result; + return unpack('P', str_pad($binary, 8, "\x00"))[1]; } } From f98d5de70fe331af9d3f83221383a69565206078 Mon Sep 17 00:00:00 2001 From: Ivan Chepurnyi Date: Sun, 2 Jun 2019 17:03:01 +0200 Subject: [PATCH 06/23] Refactor method names, add more coverage for BinaryIntegerReader --- phpunit.xml.dist | 22 ++++++++++++++++ src/BinaryIntegerReader.php | 42 +++++++++++++++++++++++-------- tests/BinaryIntegerReaderTest.php | 28 ++++++++++++++++++--- 3 files changed, 78 insertions(+), 14 deletions(-) create mode 100644 phpunit.xml.dist diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..6faa706 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,22 @@ + + + + + + + tests/ + + + + + + src/ + + + + diff --git a/src/BinaryIntegerReader.php b/src/BinaryIntegerReader.php index 5795c4a..a0213f5 100644 --- a/src/BinaryIntegerReader.php +++ b/src/BinaryIntegerReader.php @@ -18,33 +18,55 @@ class BinaryIntegerReader public function readFixed(string $binary, int $size) { if ($size === 1) { - return unpack('C', $binary)[1]; + return $this->readUnsigned1ByteInteger($binary); } if ($size === 2) { - return unpack('v', $binary)[1]; + return $this->readUnsigned2ByteInteger($binary); } if ($size === 3) { - return unpack('V', $binary."\x00")[1]; + return $this->readUnsigned4ByteInteger($binary."\x00"); } if ($size === 4) { - return unpack('V', $binary)[1]; + return $this->readUnsigned4ByteInteger($binary); } if ($size === 8) { - if (strlen($binary) > $size) { - $binary = substr($binary, 0, $size); - } - - return \hexdec(\bin2hex(\strrev($binary))); + return $this->readUnsigned8ByteInteger($binary); } if ($size > 8) { - throw new \RuntimeException('Cannot read integers above 8 bytes'); + throw new \InvalidArgumentException('Cannot read integers above 8 bytes'); } return unpack('P', str_pad($binary, 8, "\x00"))[1]; } + + private function readUnsigned8ByteInteger(string $binary) + { + // Unpack does not support unsigned 64 bit integers, + // so we have to improvise here + if (strlen($binary) > 8) { + $binary = substr($binary, 0, 8); + } + + return \hexdec(\bin2hex(\strrev($binary))); + } + + private function readUnsigned4ByteInteger(string $binary): int + { + return unpack('V', $binary)[1]; + } + + private function readUnsigned2ByteInteger(string $binary) + { + return unpack('v', $binary)[1]; + } + + private function readUnsigned1ByteInteger(string $binary) + { + return unpack('C', $binary)[1]; + } } diff --git a/tests/BinaryIntegerReaderTest.php b/tests/BinaryIntegerReaderTest.php index 56cb83c..0ae406b 100644 --- a/tests/BinaryIntegerReaderTest.php +++ b/tests/BinaryIntegerReaderTest.php @@ -49,8 +49,17 @@ public function readsFixedThreeByteInteger(string $binary, int $expectedResult) /** * @test + * @dataProvider fourByteIntegers */ - public function readsSevenByteInteger() + public function readsFixedFourByteInteger(string $binary, int $expectedResult) + { + $this->assertEquals($expectedResult, $this->reader->readFixed($binary, 4)); + } + + /** + * @test + */ + public function readsFixedSevenByteInteger() { $this->assertEquals(72057594037927935, $this->reader->readFixed("\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 7)); } @@ -59,15 +68,15 @@ public function readsSevenByteInteger() * @test * @dataProvider eightByteIntegers */ - public function readsEightByteIntegers(string $binary, $expectedResult) + public function readsFixedEightByteIntegers(string $binary, $expectedResult) { $this->assertEquals($expectedResult, $this->reader->readFixed($binary, 8)); } /** @test */ - public function doesNotSupportValuesHigherThan8Bytes() + public function fixedReaderDoesNotSupportValuesHigherThan8Bytes() { - $this->expectException(\RuntimeException::class); + $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Cannot read integers above 8 bytes'); $this->reader->readFixed("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 9); @@ -106,6 +115,17 @@ public function threeByteIntegers() ]; } + public function fourByteIntegers() + { + return [ + 'zero' => ["\x00\x00\x00\x00", 0], + 'three_byte_max' => ["\xFF\xFF\xFF\x00", 16777215], + 'three_byte_max+1' => ["\x00\x00\x00\x01", 16777216], + 'random' => ["\xd1\x7c\xe4\x0e", 249855185], + 'max' => ["\xFF\xFF\xFF\xFF", 4294967295], + ]; + } + public function eightByteIntegers() { return [ From a3274164b66c48796f134b7ed7c1c966458e58e3 Mon Sep 17 00:00:00 2001 From: Ivan Chepurnyi Date: Tue, 4 Jun 2019 23:47:58 +0200 Subject: [PATCH 07/23] Implement check for simple use case of full packet check --- src/StringReadBuffer.php | 17 ++++++++++------- src/StringReadBufferFactory.php | 18 ++++++++++++++++++ tests/StringReadBufferTest.php | 2 +- 3 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 src/StringReadBufferFactory.php diff --git a/src/StringReadBuffer.php b/src/StringReadBuffer.php index 7b8b82e..6a7ab4d 100644 --- a/src/StringReadBuffer.php +++ b/src/StringReadBuffer.php @@ -28,6 +28,14 @@ class StringReadBuffer implements ReadBuffer */ private $buffer = ''; + /** @var BinaryIntegerReader */ + private $binaryIntegerReader; + + public function __construct(BinaryIntegerReader $binaryIntegerReader) + { + $this->binaryIntegerReader = $binaryIntegerReader; + } + /** * {@inheritDoc} */ @@ -54,7 +62,7 @@ public function readFragment(callable $reader): bool */ public function isFullPacket(): bool { - return false; + return strlen($this->buffer) > $this->currentPacket[0]; } /** @@ -67,15 +75,10 @@ public function nextPacket(): void private function initPacket(): void { - $totalLength = ord($this->buffer[0]) - + (ord($this->buffer[1]) << 8) - + (ord($this->buffer[1]) << 16) - ; $this->currentPacket = [ - + $this->binaryIntegerReader->readFixed($this->buffer, 3) ]; } - } diff --git a/src/StringReadBufferFactory.php b/src/StringReadBufferFactory.php new file mode 100644 index 0000000..60d201c --- /dev/null +++ b/src/StringReadBufferFactory.php @@ -0,0 +1,18 @@ +buffer = new StringReadBuffer(); + $this->buffer = (new StringReadBufferFactory())->createWithDefaultSettings(); } /** From 0bf777b85c906dafc0972058860aab1a879207ee Mon Sep 17 00:00:00 2001 From: Ivan Chepurnyi Date: Sat, 8 Jun 2019 10:07:26 +0200 Subject: [PATCH 08/23] Add one byte reader --- src/StringReadBuffer.php | 72 ++++++++++++++++++++++++++++++++-- tests/StringReadBufferTest.php | 13 ++++++ 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/src/StringReadBuffer.php b/src/StringReadBuffer.php index 6a7ab4d..da9fa0f 100644 --- a/src/StringReadBuffer.php +++ b/src/StringReadBuffer.php @@ -9,7 +9,7 @@ namespace EcomDev\MySQLBinaryProtocol; -class StringReadBuffer implements ReadBuffer +class StringReadBuffer implements ReadBuffer, ReadBufferFragment { /** @var int */ @@ -54,7 +54,9 @@ public function append(string $data): void */ public function readFragment(callable $reader): bool { - // TODO: Implement readFragment() method. + $reader($this); + + return false; } /** @@ -76,9 +78,73 @@ public function nextPacket(): void private function initPacket(): void { $this->currentPacket = [ - $this->binaryIntegerReader->readFixed($this->buffer, 3) + $this->binaryIntegerReader->readFixed($this->read(3), 3), + $this->binaryIntegerReader->readFixed($this->read(1), 1) ]; } + private function read(int $length): string + { + $value = substr($this->buffer, $this->currentPosition, $length); + $this->currentPosition += $length; + return $value; + } + + /** + * {@inheritDoc} + */ + public function readFixedInteger(int $bytes): int + { + return $this->binaryIntegerReader->readFixed($this->read($bytes), $bytes); + } + /** + * Reads length encoded integer + * + * @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_integers.html + */ + public function readLengthEncodedInteger(): int + { + // TODO: Implement readLengthEncodedInteger() method. + } + + /** + * Reads string of specified length from buffer + * + * @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_strings.html + */ + public function readFixedString(int $length): string + { + // TODO: Implement readFixedString() method. + } + + /** + * Reads string that is has length as the first part of the fragment + * + * @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_strings.html + */ + public function readLengthEncodedStringOrNull(): ?string + { + // TODO: Implement readLengthEncodedStringOrNull() method. + } + + /** + * Reads string till x00 character + * + * @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_strings.html + */ + public function readNullTerminatedString(): string + { + // TODO: Implement readNullTerminatedString() method. + } + + /** + * Reads string that is rest of payload + * + * @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_strings.html + */ + public function readRestOfPacketString(): string + { + // TODO: Implement readRestOfPacketString() method. + } } diff --git a/tests/StringReadBufferTest.php b/tests/StringReadBufferTest.php index 19a4817..38c862b 100644 --- a/tests/StringReadBufferTest.php +++ b/tests/StringReadBufferTest.php @@ -40,4 +40,17 @@ public function reportsCompleteBufferWhenAllDataIsProvidedAtOnce() $this->assertTrue($this->buffer->isFullPacket()); } + + /** @test */ + public function allowsToReadSingleByteIntegerFromPayload() + { + $this->buffer->append("\x01\x00\x00\x00\xF1"); + + $data = []; + $this->buffer->readFragment(function (ReadBufferFragment $fragment) use (&$data) { + $data[] = $fragment->readFixedInteger(1); + }); + + $this->assertEquals([241], $data); + } } From 553fa42016a8bacc471cd459388d4bfa4a274f2f Mon Sep 17 00:00:00 2001 From: Ivan Chepurnyi Date: Mon, 10 Jun 2019 20:41:50 +0200 Subject: [PATCH 09/23] Fix composer.json - remove bcmath, as not needed anymore. Lock in 7.1 as minimum php version requirement. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 9b31b9f..0de5961 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ ], "minimum-stability": "stable", "require": { - "ext-bcmath": "" + "php": "~7.1" }, "autoload": { "psr-4": { From f7eb7338934cb87fa07e0b2a57d7b29e2eab427d Mon Sep 17 00:00:00 2001 From: Ivan Chepurnyi Date: Tue, 11 Jun 2019 20:24:38 +0200 Subject: [PATCH 10/23] Rename read buffer into packet reader Create pure buffer implementation --- src/DefaultPacketReaderFactory.php | 18 ++ ...rFragment.php => PacketFragmentReader.php} | 2 +- src/PacketReader.php | 39 ++++ src/ReadBuffer.php | 76 +++++-- ...gReadBuffer.php => StringPacketReader.php} | 66 +++--- src/StringReadBufferFactory.php | 18 -- tests/PacketReaderTest.php | 115 +++++++++++ tests/ReadBufferTest.php | 194 ++++++++++++++++++ tests/StringReadBufferTest.php | 56 ----- 9 files changed, 458 insertions(+), 126 deletions(-) create mode 100644 src/DefaultPacketReaderFactory.php rename src/{ReadBufferFragment.php => PacketFragmentReader.php} (98%) create mode 100644 src/PacketReader.php rename src/{StringReadBuffer.php => StringPacketReader.php} (68%) delete mode 100644 src/StringReadBufferFactory.php create mode 100644 tests/PacketReaderTest.php create mode 100644 tests/ReadBufferTest.php delete mode 100644 tests/StringReadBufferTest.php diff --git a/src/DefaultPacketReaderFactory.php b/src/DefaultPacketReaderFactory.php new file mode 100644 index 0000000..26aaa57 --- /dev/null +++ b/src/DefaultPacketReaderFactory.php @@ -0,0 +1,18 @@ +bufferSize = $bufferSize; + } + + public function append(string $data): void + { + $this->buffer .= $data; + } + + public function read(int $length): string + { + if (!$this->isReadable($length)) { + $this->currentPosition = $this->readPosition; + throw new IncompleteBufferException(); + } + + $data = substr($this->buffer, $this->currentPosition, $length); + + $this->currentPosition += $length; + return $data; + } + + public function isReadable(int $length): bool + { + return strlen($this->buffer) - $this->currentPosition >= $length; + } + + public function flush(): int + { + $bytesRead = $this->currentPosition - $this->readPosition; + $this->readPosition = $this->currentPosition; + + if ($this->readPosition >= $this->bufferSize) { + $this->buffer = substr($this->buffer, $this->readPosition); + $this->readPosition = 0; + $this->currentPosition = 0; + } + + return $bytesRead; + } } diff --git a/src/StringReadBuffer.php b/src/StringPacketReader.php similarity index 68% rename from src/StringReadBuffer.php rename to src/StringPacketReader.php index da9fa0f..ffb0462 100644 --- a/src/StringReadBuffer.php +++ b/src/StringPacketReader.php @@ -9,31 +9,31 @@ namespace EcomDev\MySQLBinaryProtocol; -class StringReadBuffer implements ReadBuffer, ReadBufferFragment +class StringPacketReader implements PacketReader, PacketFragmentReader { - /** @var int */ - private $bufferSize = 0; - - /** @var int */ - private $currentPosition = 0; - /** - * @var int[] + * @var int */ - private $currentPacket = []; + private $currentPacketLength; /** - * @var string + * @var int */ - private $buffer = ''; + private $currentPacketSequence; /** @var BinaryIntegerReader */ private $binaryIntegerReader; - public function __construct(BinaryIntegerReader $binaryIntegerReader) + /** + * @var ReadBuffer + */ + private $readBuffer; + + public function __construct(BinaryIntegerReader $binaryIntegerReader, ReadBuffer $readBuffer) { $this->binaryIntegerReader = $binaryIntegerReader; + $this->readBuffer = $readBuffer; } /** @@ -41,10 +41,9 @@ public function __construct(BinaryIntegerReader $binaryIntegerReader) */ public function append(string $data): void { - $this->buffer .= $data; - $this->bufferSize += strlen($data); + $this->readBuffer->append($data); - if (!$this->currentPacket) { + if ($this->currentPacketLength === null) { $this->initPacket(); } } @@ -54,9 +53,14 @@ public function append(string $data): void */ public function readFragment(callable $reader): bool { - $reader($this); + try { + $reader($this); + $this->currentPacketLength -= $this->readBuffer->flush(); + } catch (IncompleteBufferException $exception) { + return false; + } - return false; + return true; } /** @@ -64,7 +68,7 @@ public function readFragment(callable $reader): bool */ public function isFullPacket(): bool { - return strlen($this->buffer) > $this->currentPacket[0]; + return $this->readBuffer->isReadable($this->currentPacketLength); } /** @@ -72,22 +76,19 @@ public function isFullPacket(): bool */ public function nextPacket(): void { - // TODO: Implement nextPacket() method. + $this->initPacket(); } private function initPacket(): void { - $this->currentPacket = [ - $this->binaryIntegerReader->readFixed($this->read(3), 3), - $this->binaryIntegerReader->readFixed($this->read(1), 1) - ]; - } - - private function read(int $length): string - { - $value = substr($this->buffer, $this->currentPosition, $length); - $this->currentPosition += $length; - return $value; + $this->currentPacketLength = $this->binaryIntegerReader->readFixed( + $this->readBuffer->read(3), + 3 + ); + $this->currentPacketSequence = $this->binaryIntegerReader->readFixed( + $this->readBuffer->read(1), + 1 + ); } /** @@ -95,7 +96,10 @@ private function read(int $length): string */ public function readFixedInteger(int $bytes): int { - return $this->binaryIntegerReader->readFixed($this->read($bytes), $bytes); + return $this->binaryIntegerReader->readFixed( + $this->readBuffer->read($bytes), + $bytes + ); } /** diff --git a/src/StringReadBufferFactory.php b/src/StringReadBufferFactory.php deleted file mode 100644 index 60d201c..0000000 --- a/src/StringReadBufferFactory.php +++ /dev/null @@ -1,18 +0,0 @@ -reader = (new DefaultPacketReaderFactory())->createWithDefaultSettings(); + } + + /** + * @test + * + */ + public function reportsIncompleteBufferWhenNotFullLengthProvided() + { + $this->reader->append("\xFF\xFF\xFF\x00Some Payload"); + + $this->assertFalse($this->reader->isFullPacket()); + } + + /** @test */ + public function reportsCompleteBufferWhenAllDataIsProvidedAtOnce() + { + $this->reader->append("\x01\x00\x00\x00\x01"); + + $this->assertTrue($this->reader->isFullPacket()); + } + + /** @test */ + public function allowsToReadSingleByteIntegerFromPayload() + { + $this->reader->append("\x01\x00\x00\x00\xF1"); + + $data = []; + $this->reader->readFragment(function (PacketFragmentReader $fragment) use (&$data) { + $data[] = $fragment->readFixedInteger(1); + }); + + $this->assertEquals([241], $data); + } + + /** @test */ + public function allowsToReadMultiplePackets() + { + $this->reader->append("\x01\x00\x00\x00\xF1"); + $this->reader->append("\x01\x00\x00\x00\xF2"); + + $data = []; + $readOneByte = function (PacketFragmentReader $fragment) use (&$data) { + $data[] = $fragment->readFixedInteger(1); + }; + + $this->reader->readFragment($readOneByte); + $this->reader->nextPacket(); + $this->reader->readFragment($readOneByte); + + $this->assertEquals([241, 242], $data); + } + + /** @test */ + public function reportsCompletePacketAvailableEvenAfterFragmentIsRead() + { + $this->reader->append("\x02\x00\x00\x00\xF1\xF2"); + + $this->reader->readFragment( + function (PacketFragmentReader $fragment) { + $fragment->readFixedInteger(1); + } + ); + + $this->assertEquals(true, $this->reader->isFullPacket()); + } + + /** @test */ + public function reportsFragmentIsRead() + { + $this->reader->append("\x01\x00\x00\x00\x01"); + + $this->assertEquals( + true, + $this->reader->readFragment(function (PacketFragmentReader $reader) { + $reader->readFixedInteger(1); + }) + ); + } + + /** @test */ + public function reportsFragmentIsNotRead() + { + $this->reader->append("\x02\x00\x00\x00\x01"); + + $this->assertEquals( + false, + $this->reader->readFragment(function (PacketFragmentReader $reader) { + $reader->readFixedInteger(1); + $reader->readFixedInteger(1); + }) + ); + } +} diff --git a/tests/ReadBufferTest.php b/tests/ReadBufferTest.php new file mode 100644 index 0000000..cc00dbf --- /dev/null +++ b/tests/ReadBufferTest.php @@ -0,0 +1,194 @@ +readBuffer = new ReadBuffer(); + } + + + /** @test */ + public function readsBufferByLength() + { + $this->readBuffer->append('Some string'); + + $this->assertEquals('Some', $this->readBuffer->read(4)); + } + + /** @test */ + public function readsBufferByMovingPositionForward() + { + $this->readBuffer->append('TDD is awesome'); + + $this->readBuffer->read(3); + + $this->assertEquals(' is awesome', $this->readBuffer->read(11)); + } + + /** @test */ + public function throwsIncompleteBufferExceptionWhenNotBufferIsSmallerThenReadSize() + { + $this->readBuffer->append('TDD is'); + + $this->expectException(IncompleteBufferException::class); + + $this->readBuffer->read(11); + } + + /** @test */ + public function throwIncompleteBufferExceptionWhenNotEnoughDataIsLeftToRead() + { + $this->readBuffer->append('TDD is great'); + + $this->readBuffer->read(7); + + $this->expectException(IncompleteBufferException::class); + + $this->readBuffer->read(7); + } + + /** @test */ + public function allowsToReadAllAddedPiecesToBuffer() + { + $this->readBuffer->append('TDD is'); + + $this->readBuffer->read(4); + + $this->readBuffer->append(' great'); + + $this->assertEquals('is great', $this->readBuffer->read(8)); + } + + /** @test */ + public function isReadableWhenAskedBytesAreBelowBufferLength() + { + $this->readBuffer->append('Some data'); + + $this->assertEquals(true, $this->readBuffer->isReadable(4)); + } + + /** @test */ + public function isNotReadableWhenBytesAreLongerThenBufferLength() + { + $this->readBuffer->append('Some'); + + $this->assertEquals(false, $this->readBuffer->isReadable(5)); + } + + /** @test */ + public function isNotReadableWhenAskedLengthIsLowerThenRemainingBytesToRead() + { + $this->readBuffer->append('Some data'); + $this->readBuffer->read(5); + + $this->assertEquals(false, $this->readBuffer->isReadable(5)); + } + + /** @test */ + public function isReadableWhenExactAmountOfBytesAvailableToRead() + { + $this->readBuffer->append('Data in buffer'); + + $this->readBuffer->read(7); + + $this->assertEquals(true, $this->readBuffer->isReadable(7)); + } + + /** @test */ + public function allowsToReadDataAgainIfPreviousSessionWasNotReadCompletely() + { + $this->readBuffer->append('Data in buffer'); + $this->readBuffer->read(4); + $this->readBuffer->read(4); + + try { + $this->readBuffer->read(7); + } catch (IncompleteBufferException $exception) { } + + $this->assertEquals('Data in ', $this->readBuffer->read(8)); + } + + /** @test */ + public function allowsToMoveReadBufferPointerAfterRead() + { + $this->readBuffer->append('Data in buffer'); + + $this->readBuffer->read(5); + $this->readBuffer->flush(); + + try { + $this->readBuffer->read(10); + } catch (IncompleteBufferException $e) { + + } + + $this->assertEquals('in buffer', $this->readBuffer->read(9)); + } + + /** @test */ + public function clearsBufferWhenReadLimitIsReached() + { + $limitedReadBuffer = new ReadBuffer(20); + + $limitedReadBuffer->append('Some data to read 2 remainder of buffer'); + $limitedReadBuffer->read(10); + $limitedReadBuffer->read(10); + $limitedReadBuffer->flush(); + + $expectedReadBuffer = new ReadBuffer(20); + $expectedReadBuffer->append('remainder of buffer'); + + $this->assertEquals($expectedReadBuffer, $limitedReadBuffer); + } + + /** @test */ + public function clearsBufferLimitIsReachedALongTimeAgo() + { + $limitedReadBuffer = new ReadBuffer(20); + + $limitedReadBuffer->append('Some data to read 2 very long string to read remainder of buffer'); + $limitedReadBuffer->read(10); + $limitedReadBuffer->flush(); + $limitedReadBuffer->read(20); + $limitedReadBuffer->read(15); + $limitedReadBuffer->flush(); + + $expectedReadBuffer = new ReadBuffer(20); + $expectedReadBuffer->append('remainder of buffer'); + + $this->assertEquals($expectedReadBuffer, $limitedReadBuffer); + } + + /** @test */ + public function flushReturnsNumberOfReadBytes() + { + $this->readBuffer->append('Some data'); + $this->readBuffer->read(4); + $this->readBuffer->read(2); + $this->assertEquals(6, $this->readBuffer->flush()); + } + + /** @test */ + public function flushReturnsZeroWhenNoBytesRead() + { + $this->readBuffer->append('Some data'); + + $this->assertEquals(0, $this->readBuffer->flush()); + } + +} diff --git a/tests/StringReadBufferTest.php b/tests/StringReadBufferTest.php deleted file mode 100644 index 38c862b..0000000 --- a/tests/StringReadBufferTest.php +++ /dev/null @@ -1,56 +0,0 @@ -buffer = (new StringReadBufferFactory())->createWithDefaultSettings(); - } - - /** - * @test - * - */ - public function reportsIncompleteBufferWhenNotFullLengthProvided() - { - $this->buffer->append("\xFF\xFF\xFF\x00Some Payload"); - - $this->assertFalse($this->buffer->isFullPacket()); - } - - /** @test */ - public function reportsCompleteBufferWhenAllDataIsProvidedAtOnce() - { - $this->buffer->append("\x01\x00\x00\x00\x01"); - - $this->assertTrue($this->buffer->isFullPacket()); - } - - /** @test */ - public function allowsToReadSingleByteIntegerFromPayload() - { - $this->buffer->append("\x01\x00\x00\x00\xF1"); - - $data = []; - $this->buffer->readFragment(function (ReadBufferFragment $fragment) use (&$data) { - $data[] = $fragment->readFixedInteger(1); - }); - - $this->assertEquals([241], $data); - } -} From 3803e3d46180c9db76396b784acc0c8be02cfa68 Mon Sep 17 00:00:00 2001 From: Ivan Chepurnyi Date: Thu, 13 Jun 2019 21:19:39 +0200 Subject: [PATCH 11/23] Add test for reading various binary fixed integer sizes --- tests/PacketReaderTest.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/PacketReaderTest.php b/tests/PacketReaderTest.php index 87de1d4..30096e0 100644 --- a/tests/PacketReaderTest.php +++ b/tests/PacketReaderTest.php @@ -112,4 +112,28 @@ public function reportsFragmentIsNotRead() }) ); } + + /** @test */ + public function allowsReadingVariousFixedIntegers() + { + $this->reader->append("\x0D\x00\x00\x00\x00\x02\x02\x00\x00\x00\x00\x00\x00\x00\x00\xF0\x00"); + + $data = []; + $this->reader->readFragment(function (PacketFragmentReader $fragment) use (&$data) { + $data[] = $fragment->readFixedInteger(2); // 512 + $data[] = $fragment->readFixedInteger(3); // 2 + $data[] = $fragment->readFixedInteger(8); // 67553994410557440 + }); + + $this->assertEquals( + [ + 512, + 2, + 67553994410557440 + ], + $data + ); + } + + } From 14305e41ccc88eea8db70e3e7aa72cf5667058b1 Mon Sep 17 00:00:00 2001 From: Ivan Chepurnyi Date: Fri, 14 Jun 2019 14:34:04 +0200 Subject: [PATCH 12/23] Add unit test cache into ignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index c361ba7..02a92bd 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ !/.idea/*.iml !/.idea/modules.xml +# PHPUnit +/.phpunit.result.cache From 7c98c25907e0472c751dccc91e83bafe73a1d841 Mon Sep 17 00:00:00 2001 From: Ivan Chepurnyi Date: Fri, 14 Jun 2019 18:41:42 +0200 Subject: [PATCH 13/23] Work in progress tests --- composer.lock | 188 +++++++++++++++++++++++-------------- tests/PacketReaderTest.php | 20 ++++ 2 files changed, 138 insertions(+), 70 deletions(-) diff --git a/composer.lock b/composer.lock index 3ff1432..056d938 100644 --- a/composer.lock +++ b/composer.lock @@ -1,11 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "hash": "9c7a720e2cbf882dc19d228afcddcfcd", - "content-hash": "c49bf819e0695a2361ebfe74ca8378ee", + "content-hash": "aff8eb49ceefb3611292997c36e8967e", "packages": [], "packages-dev": [ { @@ -62,7 +61,7 @@ "constructor", "instantiate" ], - "time": "2019-03-17 17:37:11" + "time": "2019-03-17T17:37:11+00:00" }, { "name": "myclabs/deep-copy", @@ -110,7 +109,7 @@ "object", "object graph" ], - "time": "2019-04-07 13:18:21" + "time": "2019-04-07T13:18:21+00:00" }, { "name": "phar-io/manifest", @@ -165,7 +164,7 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2018-07-08 19:23:20" + "time": "2018-07-08T19:23:20+00:00" }, { "name": "phar-io/version", @@ -212,7 +211,7 @@ } ], "description": "Library for handling version information and constraints", - "time": "2018-07-08 19:19:57" + "time": "2018-07-08T19:19:57+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -266,7 +265,7 @@ "reflection", "static analysis" ], - "time": "2017-09-11 18:02:19" + "time": "2017-09-11T18:02:19+00:00" }, { "name": "phpdocumentor/reflection-docblock", @@ -317,7 +316,7 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2019-04-30 17:48:53" + "time": "2019-04-30T17:48:53+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -364,20 +363,20 @@ "email": "me@mikevanriel.com" } ], - "time": "2017-07-14 14:27:02" + "time": "2017-07-14T14:27:02+00:00" }, { "name": "phpspec/prophecy", - "version": "1.8.0", + "version": "1.8.1", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" + "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/1927e75f4ed19131ec9bcc3b002e07fb1173ee76", + "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76", "shasum": "" }, "require": { @@ -398,8 +397,8 @@ } }, "autoload": { - "psr-0": { - "Prophecy\\": "src/" + "psr-4": { + "Prophecy\\": "src/Prophecy" } }, "notification-url": "https://packagist.org/downloads/", @@ -427,20 +426,20 @@ "spy", "stub" ], - "time": "2018-08-05 17:53:17" + "time": "2019-06-13T12:50:23+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "7.0.4", + "version": "7.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "6024c8a6cb962d496b7bd049ed8f48473824176d" + "reference": "aed67b57d459dcab93e84a5c9703d3deb5025dff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6024c8a6cb962d496b7bd049ed8f48473824176d", - "reference": "6024c8a6cb962d496b7bd049ed8f48473824176d", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/aed67b57d459dcab93e84a5c9703d3deb5025dff", + "reference": "aed67b57d459dcab93e84a5c9703d3deb5025dff", "shasum": "" }, "require": { @@ -490,7 +489,7 @@ "testing", "xunit" ], - "time": "2019-05-29 09:59:31" + "time": "2019-06-06T12:28:18+00:00" }, { "name": "phpunit/php-file-iterator", @@ -540,7 +539,7 @@ "filesystem", "iterator" ], - "time": "2018-09-13 20:33:42" + "time": "2018-09-13T20:33:42+00:00" }, { "name": "phpunit/php-text-template", @@ -581,20 +580,20 @@ "keywords": [ "template" ], - "time": "2015-06-21 13:50:34" + "time": "2015-06-21T13:50:34+00:00" }, { "name": "phpunit/php-timer", - "version": "2.1.1", + "version": "2.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "8b389aebe1b8b0578430bda0c7c95a829608e059" + "reference": "1038454804406b0b5f5f520358e78c1c2f71501e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/8b389aebe1b8b0578430bda0c7c95a829608e059", - "reference": "8b389aebe1b8b0578430bda0c7c95a829608e059", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e", + "reference": "1038454804406b0b5f5f520358e78c1c2f71501e", "shasum": "" }, "require": { @@ -630,7 +629,7 @@ "keywords": [ "timer" ], - "time": "2019-02-20 10:12:59" + "time": "2019-06-07T04:22:29+00:00" }, { "name": "phpunit/php-token-stream", @@ -679,46 +678,47 @@ "keywords": [ "tokenizer" ], - "time": "2018-10-30 05:52:18" + "time": "2018-10-30T05:52:18+00:00" }, { "name": "phpunit/phpunit", - "version": "8.1.6", + "version": "8.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "e3c9da6e645492c461e0a11eca117f83f4f4c840" + "reference": "047f771e34dccacb6c432a1a70e9980e087eac92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e3c9da6e645492c461e0a11eca117f83f4f4c840", - "reference": "e3c9da6e645492c461e0a11eca117f83f4f4c840", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/047f771e34dccacb6c432a1a70e9980e087eac92", + "reference": "047f771e34dccacb6c432a1a70e9980e087eac92", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.1", + "doctrine/instantiator": "^1.2.0", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.7", - "phar-io/manifest": "^1.0.2", - "phar-io/version": "^2.0", + "myclabs/deep-copy": "^1.9.1", + "phar-io/manifest": "^1.0.3", + "phar-io/version": "^2.0.1", "php": "^7.2", - "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^7.0", - "phpunit/php-file-iterator": "^2.0.1", + "phpspec/prophecy": "^1.8.0", + "phpunit/php-code-coverage": "^7.0.5", + "phpunit/php-file-iterator": "^2.0.2", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^2.1", - "sebastian/comparator": "^3.0", - "sebastian/diff": "^3.0", - "sebastian/environment": "^4.1", - "sebastian/exporter": "^3.1", - "sebastian/global-state": "^3.0", + "phpunit/php-timer": "^2.1.2", + "sebastian/comparator": "^3.0.2", + "sebastian/diff": "^3.0.2", + "sebastian/environment": "^4.2.2", + "sebastian/exporter": "^3.1.0", + "sebastian/global-state": "^3.0.0", "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^2.0", + "sebastian/resource-operations": "^2.0.1", + "sebastian/type": "^1.1.0", "sebastian/version": "^2.0.1" }, "require-dev": { @@ -727,7 +727,7 @@ "suggest": { "ext-soap": "*", "ext-xdebug": "*", - "phpunit/php-invoker": "^2.0" + "phpunit/php-invoker": "^2.0.0" }, "bin": [ "phpunit" @@ -735,7 +735,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "8.1-dev" + "dev-master": "8.2-dev" } }, "autoload": { @@ -761,7 +761,7 @@ "testing", "xunit" ], - "time": "2019-05-28 11:53:42" + "time": "2019-06-07T14:04:13+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -806,7 +806,7 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04 06:30:41" + "time": "2017-03-04T06:30:41+00:00" }, { "name": "sebastian/comparator", @@ -870,7 +870,7 @@ "compare", "equality" ], - "time": "2018-07-12 15:12:46" + "time": "2018-07-12T15:12:46+00:00" }, { "name": "sebastian/diff", @@ -926,7 +926,7 @@ "unidiff", "unified diff" ], - "time": "2019-02-04 06:01:07" + "time": "2019-02-04T06:01:07+00:00" }, { "name": "sebastian/environment", @@ -979,7 +979,7 @@ "environment", "hhvm" ], - "time": "2019-05-05 09:05:15" + "time": "2019-05-05T09:05:15+00:00" }, { "name": "sebastian/exporter", @@ -1046,7 +1046,7 @@ "export", "exporter" ], - "time": "2017-04-03 13:19:02" + "time": "2017-04-03T13:19:02+00:00" }, { "name": "sebastian/global-state", @@ -1100,7 +1100,7 @@ "keywords": [ "global state" ], - "time": "2019-02-01 05:30:01" + "time": "2019-02-01T05:30:01+00:00" }, { "name": "sebastian/object-enumerator", @@ -1147,7 +1147,7 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-08-03 12:35:26" + "time": "2017-08-03T12:35:26+00:00" }, { "name": "sebastian/object-reflector", @@ -1192,7 +1192,7 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2017-03-29 09:07:27" + "time": "2017-03-29T09:07:27+00:00" }, { "name": "sebastian/recursion-context", @@ -1245,7 +1245,7 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-03 06:23:57" + "time": "2017-03-03T06:23:57+00:00" }, { "name": "sebastian/resource-operations", @@ -1287,7 +1287,53 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2018-10-04 04:07:39" + "time": "2018-10-04T04:07:39+00:00" + }, + { + "name": "sebastian/type", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "b2a7f9aac51ce18cd7ac8b31e37c8ce5646fc741" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b2a7f9aac51ce18cd7ac8b31e37c8ce5646fc741", + "reference": "b2a7f9aac51ce18cd7ac8b31e37c8ce5646fc741", + "shasum": "" + }, + "require": { + "php": "^7.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "time": "2019-06-08T04:53:27+00:00" }, { "name": "sebastian/version", @@ -1330,7 +1376,7 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03 07:35:21" + "time": "2016-10-03T07:35:21+00:00" }, { "name": "symfony/polyfill-ctype", @@ -1388,20 +1434,20 @@ "polyfill", "portable" ], - "time": "2019-02-06 07:57:58" + "time": "2019-02-06T07:57:58+00:00" }, { "name": "theseer/tokenizer", - "version": "1.1.2", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "1c42705be2b6c1de5904f8afacef5895cab44bf8" + "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/1c42705be2b6c1de5904f8afacef5895cab44bf8", - "reference": "1c42705be2b6c1de5904f8afacef5895cab44bf8", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", + "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", "shasum": "" }, "require": { @@ -1428,7 +1474,7 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2019-04-04 09:56:43" + "time": "2019-06-13T22:48:21+00:00" }, { "name": "webmozart/assert", @@ -1479,7 +1525,7 @@ "check", "validate" ], - "time": "2018-12-25 11:19:39" + "time": "2018-12-25T11:19:39+00:00" } ], "aliases": [], @@ -1487,6 +1533,8 @@ "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, - "platform": [], + "platform": { + "php": "~7.1" + }, "platform-dev": [] } diff --git a/tests/PacketReaderTest.php b/tests/PacketReaderTest.php index 30096e0..40dbbfa 100644 --- a/tests/PacketReaderTest.php +++ b/tests/PacketReaderTest.php @@ -134,6 +134,26 @@ public function allowsReadingVariousFixedIntegers() $data ); } + + /** @test */ + public function allowsReadingLengthDifferentLengthEncodedInteger() + { + $this->reader->append("\x01\x00\x00\x00\xfa\xfc\xf1\00"); + $data = []; + $this->reader->readFragment(function (PacketFragmentReader $fragment) use (&$data) { + $data[] = $fragment->readFixedInteger(2); // 512 + $data[] = $fragment->readFixedInteger(3); // 2 + $data[] = $fragment->readFixedInteger(8); // 67553994410557440 + }); + $this->assertEquals( + [ + 512, + 2, + 67553994410557440 + ], + $data + ); + } } From 59915d736afa44398161056a997a6203c9adbcbf Mon Sep 17 00:00:00 2001 From: Ivan Chepurnyi Date: Wed, 19 Jun 2019 19:30:47 +0200 Subject: [PATCH 14/23] Add string and length encoded integer reading --- src/InvalidBinaryDataException.php | 14 ++++ src/PacketFragmentReader.php | 6 +- src/StringPacketReader.php | 35 ++++++++-- tests/PacketReaderTest.php | 102 ++++++++++++++++++++++++++--- 4 files changed, 142 insertions(+), 15 deletions(-) create mode 100644 src/InvalidBinaryDataException.php diff --git a/src/InvalidBinaryDataException.php b/src/InvalidBinaryDataException.php new file mode 100644 index 0000000..d971273 --- /dev/null +++ b/src/InvalidBinaryDataException.php @@ -0,0 +1,14 @@ + 2, + 0xfd => 3, + 0xfe => 8 + ]; /** * @var int @@ -94,7 +99,7 @@ private function initPacket(): void /** * {@inheritDoc} */ - public function readFixedInteger(int $bytes): int + public function readFixedInteger(int $bytes) { return $this->binaryIntegerReader->readFixed( $this->readBuffer->read($bytes), @@ -107,9 +112,23 @@ public function readFixedInteger(int $bytes): int * * @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_integers.html */ - public function readLengthEncodedInteger(): int + public function readLengthEncodedIntegerOrNull() { - // TODO: Implement readLengthEncodedInteger() method. + $value = $this->readFixedInteger(1); + + if ($value === 0xfb) { + return null; + } + + if ($value < 251) { + return $value; + } + + if (!isset(self::INTEGER_LENGTH_MARKER[$value])) { + throw new InvalidBinaryDataException(); + } + + return $this->readFixedInteger(self::INTEGER_LENGTH_MARKER[$value]); } /** @@ -119,7 +138,7 @@ public function readLengthEncodedInteger(): int */ public function readFixedString(int $length): string { - // TODO: Implement readFixedString() method. + return $this->readBuffer->read($length); } /** @@ -129,7 +148,13 @@ public function readFixedString(int $length): string */ public function readLengthEncodedStringOrNull(): ?string { - // TODO: Implement readLengthEncodedStringOrNull() method. + $stringLength = $this->readLengthEncodedIntegerOrNull(); + + if ($stringLength === null) { + return null; + } + + return $this->readFixedString($stringLength); } /** diff --git a/tests/PacketReaderTest.php b/tests/PacketReaderTest.php index 40dbbfa..98f336c 100644 --- a/tests/PacketReaderTest.php +++ b/tests/PacketReaderTest.php @@ -136,22 +136,108 @@ public function allowsReadingVariousFixedIntegers() } /** @test */ - public function allowsReadingLengthDifferentLengthEncodedInteger() + public function allowsReadingDifferentLengthEncodedIntegers() { - $this->reader->append("\x01\x00\x00\x00\xfa\xfc\xf1\00"); + $this->reader->append( + "\x12\x00\x00\x00\xf9\xfa\xfc\xfb\00\xfd\xff\xff\xf0\xfe\xff\xff\xff\xff\xff\xff\xff\xf0" + ); $data = []; $this->reader->readFragment(function (PacketFragmentReader $fragment) use (&$data) { - $data[] = $fragment->readFixedInteger(2); // 512 - $data[] = $fragment->readFixedInteger(3); // 2 - $data[] = $fragment->readFixedInteger(8); // 67553994410557440 + $data[] = $fragment->readLengthEncodedIntegerOrNull(); // 249 + $data[] = $fragment->readLengthEncodedIntegerOrNull(); // 250 + $data[] = $fragment->readLengthEncodedIntegerOrNull(); // 251 + $data[] = $fragment->readLengthEncodedIntegerOrNull(); // 15794175 + $data[] = $fragment->readLengthEncodedIntegerOrNull(); // 17365880163140632575 }); $this->assertEquals( [ - 512, - 2, - 67553994410557440 + 249, + 250, + 251, + 15794175, + 17365880163140632575 + ], + $data + ); + } + + + /** + * @test + */ + public function throwsInvalidBinaryDataExceptionWhenLengthEncodedIntegerDoesNotMatchExpectedFormat() + { + + $this->reader->append( + "\x09\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xf0" + ); + + $this->expectException(InvalidBinaryDataException::class); + + $this->reader->readFragment(function (PacketFragmentReader $fragment) { + $fragment->readLengthEncodedIntegerOrNull(); + }); + } + + /** @test */ + public function readsNullForLengthEncodedInteger() + { + $this->reader->append( + "\x01\x00\x00\x00\xfb" + ); + + $this->reader->readFragment(function (PacketFragmentReader $fragment) { + $this->assertEquals(null, $fragment->readLengthEncodedIntegerOrNull()); + }); + } + + /** @test */ + public function readsFixedLengthString() + { + $this->reader->append("\x18\x00\x00\x00helloworld!awesomestring"); + $data = []; + + $this->reader->readFragment(function (PacketFragmentReader $fragment) use (&$data) { + $data[] = $fragment->readFixedString(5); + $data[] = $fragment->readFixedString(6); + $data[] = $fragment->readFixedString(13); + }); + + $this->assertEquals( + [ + 'hello', + 'world!', + 'awesomestring' + ], + $data + ); + } + + /** @test */ + public function readsLengthEncodedString() + { + $veryLongString = str_repeat('a', 0xff); + + + $this->reader->append("\x0c\x01\x00\x00\xfc\xff\x00$veryLongString\x05hello\xfb\x0202"); + + $data = []; + + $this->reader->readFragment(function (PacketFragmentReader $fragment) use (&$data) { + $data[] = $fragment->readLengthEncodedStringOrNull(); + $data[] = $fragment->readLengthEncodedStringOrNull(); + $data[] = $fragment->readLengthEncodedStringOrNull(); + $data[] = $fragment->readLengthEncodedStringOrNull(); + }); + + $this->assertEquals( + [ + $veryLongString, + 'hello', + null, + '02' ], $data ); From 2a4cb89025fb5b60f6a1e5572501665d43a1d0ae Mon Sep 17 00:00:00 2001 From: Ivan Chepurnyi Date: Thu, 20 Jun 2019 21:47:11 +0200 Subject: [PATCH 15/23] Added - Reading of null terminated strings - Reading of EOF strings - Multi-packet reading of payload Improved - Naming of the classes to reflect better their purpose Removed - NextPacket and isFullPacket methods as readPayload() already covers those usecases --- composer.json | 3 + src/DefaultPacketReaderFactory.php | 4 +- ...mentReader.php => PacketPayloadReader.php} | 2 +- src/PacketReader.php | 16 +- src/ReadBuffer.php | 47 ++- ...eader.php => UncompressedPacketReader.php} | 130 +++++--- tests/PacketReaderTest.php | 277 +++++++++++++----- tests/ReadBufferTest.php | 97 ++++++ 8 files changed, 429 insertions(+), 147 deletions(-) rename src/{PacketFragmentReader.php => PacketPayloadReader.php} (98%) rename src/{StringPacketReader.php => UncompressedPacketReader.php} (51%) diff --git a/composer.json b/composer.json index 0de5961..eb756cd 100644 --- a/composer.json +++ b/composer.json @@ -25,5 +25,8 @@ }, "require-dev": { "phpunit/phpunit": "^8.1" + }, + "scripts": { + "test": "phpunit --colors --coverage-text" } } diff --git a/src/DefaultPacketReaderFactory.php b/src/DefaultPacketReaderFactory.php index 26aaa57..67409c0 100644 --- a/src/DefaultPacketReaderFactory.php +++ b/src/DefaultPacketReaderFactory.php @@ -11,8 +11,8 @@ class DefaultPacketReaderFactory { - public function createWithDefaultSettings(): StringPacketReader + public function createWithDefaultSettings(): UncompressedPacketReader { - return new StringPacketReader(new BinaryIntegerReader(), new ReadBuffer()); + return new UncompressedPacketReader(new BinaryIntegerReader(), new ReadBuffer()); } } diff --git a/src/PacketFragmentReader.php b/src/PacketPayloadReader.php similarity index 98% rename from src/PacketFragmentReader.php rename to src/PacketPayloadReader.php index 7ba7a8a..0cc58f4 100644 --- a/src/PacketFragmentReader.php +++ b/src/PacketPayloadReader.php @@ -12,7 +12,7 @@ * Reader for buffer fragments * */ -interface PacketFragmentReader +interface PacketPayloadReader { /** * Reads fixed value integer diff --git a/src/PacketReader.php b/src/PacketReader.php index 2f6d097..c55d285 100644 --- a/src/PacketReader.php +++ b/src/PacketReader.php @@ -16,24 +16,12 @@ interface PacketReader public function append(string $data): void; /** - * Executes callable to read a fragment of the buffer by using ReadBufferFragment + * Executes callable to read a packed payload by using payload reader * * In case of IncompleteBufferException is thrown during reading, * method returns false and same data will be returned on the next read. * * The code of $reader MUST NOT catch this exception */ - public function readFragment(callable $reader): bool; - - /** - * Checks if current packet readable completely to the end - */ - public function isFullPacket(): bool; - - /** - * Moves internal pointer to start reading next packet - * - * Any data that is left unread in the packet will be discarded - */ - public function nextPacket(): void; + public function readPayload(callable $reader): bool; } diff --git a/src/ReadBuffer.php b/src/ReadBuffer.php index 72b2188..ce843d3 100644 --- a/src/ReadBuffer.php +++ b/src/ReadBuffer.php @@ -10,6 +10,7 @@ use function strlen; use function substr; +use function strpos; class ReadBuffer { @@ -21,10 +22,10 @@ class ReadBuffer private $buffer = ''; /** @var int */ - private $currentPosition = 0; + private $currentBufferOffset = 0; /** @var int */ - private $readPosition = 0; + private $readBufferOffset = 0; /** * @var int @@ -44,32 +45,54 @@ public function append(string $data): void public function read(int $length): string { if (!$this->isReadable($length)) { - $this->currentPosition = $this->readPosition; + $this->currentBufferOffset = $this->readBufferOffset; throw new IncompleteBufferException(); } - $data = substr($this->buffer, $this->currentPosition, $length); + $data = substr($this->buffer, $this->currentBufferOffset, $length); - $this->currentPosition += $length; + $this->currentBufferOffset += $length; return $data; } public function isReadable(int $length): bool { - return strlen($this->buffer) - $this->currentPosition >= $length; + return strlen($this->buffer) - $this->currentBufferOffset >= $length; } public function flush(): int { - $bytesRead = $this->currentPosition - $this->readPosition; - $this->readPosition = $this->currentPosition; + $bytesRead = $this->currentPosition(); - if ($this->readPosition >= $this->bufferSize) { - $this->buffer = substr($this->buffer, $this->readPosition); - $this->readPosition = 0; - $this->currentPosition = 0; + $this->readBufferOffset = $this->currentBufferOffset; + + if ($this->readBufferOffset >= $this->bufferSize) { + $this->buffer = substr($this->buffer, $this->readBufferOffset); + $this->readBufferOffset = 0; + $this->currentBufferOffset = 0; } return $bytesRead; } + + public function scan(string $pattern): int + { + $position = strpos($this->buffer, $pattern, $this->currentBufferOffset); + + return $position === false ? -1 : ($position - $this->currentBufferOffset) + 1; + } + + public function advance(int $length): void + { + if (!$this->isReadable($length)) { + throw new IncompleteBufferException(); + } + + $this->currentBufferOffset += $length; + } + + public function currentPosition(): int + { + return $this->currentBufferOffset - $this->readBufferOffset; + } } diff --git a/src/StringPacketReader.php b/src/UncompressedPacketReader.php similarity index 51% rename from src/StringPacketReader.php rename to src/UncompressedPacketReader.php index f816abf..c85250c 100644 --- a/src/StringPacketReader.php +++ b/src/UncompressedPacketReader.php @@ -9,23 +9,34 @@ namespace EcomDev\MySQLBinaryProtocol; -class StringPacketReader implements PacketReader, PacketFragmentReader +class UncompressedPacketReader implements PacketReader, PacketPayloadReader { - const INTEGER_LENGTH_MARKER = [ + private const INTEGER_LENGTH_MARKER = [ 0xfc => 2, 0xfd => 3, 0xfe => 8 ]; + private const UNREAD_LENGTH = 2; + private const LENGTH = 0; + private const SEQUENCE = 1; + /** * @var int */ - private $currentPacketLength; + private $awaitedPacketLength = 0; /** - * @var int + * Registry of packets + * + * [ + * [lengthOfPacket, sequence, remainingToReadLength], + * [lengthOfPacket, sequence, remainingToReadLength], + * [lengthOfPacket, sequence, remainingToReadLength], + * ] + * @var array */ - private $currentPacketSequence; + private $packets = []; /** @var BinaryIntegerReader */ private $binaryIntegerReader; @@ -46,21 +57,19 @@ public function __construct(BinaryIntegerReader $binaryIntegerReader, ReadBuffer */ public function append(string $data): void { - $this->readBuffer->append($data); - - if ($this->currentPacketLength === null) { - $this->initPacket(); - } + do { + $data = $this->registerPacket($data); + } while ($data !== ''); } /** * {@inheritDoc} */ - public function readFragment(callable $reader): bool + public function readPayload(callable $reader): bool { try { - $reader($this); - $this->currentPacketLength -= $this->readBuffer->flush(); + $reader($this, $this->packets[0][self::LENGTH], $this->packets[0][self::SEQUENCE]); + $this->advancePacketLength($this->readBuffer->flush()); } catch (IncompleteBufferException $exception) { return false; } @@ -68,34 +77,6 @@ public function readFragment(callable $reader): bool return true; } - /** - * {@inheritDoc} - */ - public function isFullPacket(): bool - { - return $this->readBuffer->isReadable($this->currentPacketLength); - } - - /** - * {@inheritDoc} - */ - public function nextPacket(): void - { - $this->initPacket(); - } - - private function initPacket(): void - { - $this->currentPacketLength = $this->binaryIntegerReader->readFixed( - $this->readBuffer->read(3), - 3 - ); - $this->currentPacketSequence = $this->binaryIntegerReader->readFixed( - $this->readBuffer->read(1), - 1 - ); - } - /** * {@inheritDoc} */ @@ -164,7 +145,15 @@ public function readLengthEncodedStringOrNull(): ?string */ public function readNullTerminatedString(): string { - // TODO: Implement readNullTerminatedString() method. + $nullPosition = $this->readBuffer->scan("\x00"); + + if ($nullPosition === -1) { + throw new IncompleteBufferException(); + } + + $string = $this->readBuffer->read($nullPosition - 1); + $this->readBuffer->advance(1); + return $string; } /** @@ -174,6 +163,61 @@ public function readNullTerminatedString(): string */ public function readRestOfPacketString(): string { - // TODO: Implement readRestOfPacketString() method. + return $this->readBuffer->read( + $this->remainingPacketLengthToRead() + ); + } + + private function registerPacket(string $dataToParse): string + { + if ($this->awaitedPacketLength) { + $trimLength = min(strlen($dataToParse), $this->awaitedPacketLength); + $this->readBuffer->append(substr($dataToParse, 0, $trimLength)); + $this->awaitedPacketLength -= $trimLength; + return substr($dataToParse, $trimLength); + } + + $this->awaitedPacketLength = $this->binaryIntegerReader->readFixed( + substr($dataToParse, 0, 3), + 3 + ); + + $this->packets[] = [ + self::LENGTH => $this->awaitedPacketLength, + self::SEQUENCE => $this->binaryIntegerReader->readFixed($dataToParse[3], 1), + self::UNREAD_LENGTH => $this->awaitedPacketLength + ]; + + return substr($dataToParse, 4); + } + + + private function remainingPacketLengthToRead(): int + { + $currentBufferPosition = $this->readBuffer->currentPosition(); + + $currentPacketIndex = 0; + while ($this->packets[$currentPacketIndex][self::UNREAD_LENGTH] <= $currentBufferPosition) { + $currentBufferPosition -= $this->packets[$currentPacketIndex][self::UNREAD_LENGTH]; + $currentPacketIndex++; + } + + return $this->packets[$currentPacketIndex][self::UNREAD_LENGTH] - $currentBufferPosition; + } + + private function advancePacketLength(int $readLength): void + { + + while ($this->packets[0][self::UNREAD_LENGTH] <= $readLength) { + $readLength -= $this->packets[0][self::UNREAD_LENGTH]; + array_shift($this->packets); + + if (!$this->packets) { + return; + } + } + + + $this->packets[0][self::UNREAD_LENGTH] -= $readLength; } } diff --git a/tests/PacketReaderTest.php b/tests/PacketReaderTest.php index 98f336c..d6ec9f9 100644 --- a/tests/PacketReaderTest.php +++ b/tests/PacketReaderTest.php @@ -22,32 +22,13 @@ protected function setUp(): void $this->reader = (new DefaultPacketReaderFactory())->createWithDefaultSettings(); } - /** - * @test - * - */ - public function reportsIncompleteBufferWhenNotFullLengthProvided() - { - $this->reader->append("\xFF\xFF\xFF\x00Some Payload"); - - $this->assertFalse($this->reader->isFullPacket()); - } - - /** @test */ - public function reportsCompleteBufferWhenAllDataIsProvidedAtOnce() - { - $this->reader->append("\x01\x00\x00\x00\x01"); - - $this->assertTrue($this->reader->isFullPacket()); - } - /** @test */ public function allowsToReadSingleByteIntegerFromPayload() { $this->reader->append("\x01\x00\x00\x00\xF1"); $data = []; - $this->reader->readFragment(function (PacketFragmentReader $fragment) use (&$data) { + $this->reader->readPayload(function (PacketPayloadReader $fragment) use (&$data) { $data[] = $fragment->readFixedInteger(1); }); @@ -55,37 +36,26 @@ public function allowsToReadSingleByteIntegerFromPayload() } /** @test */ - public function allowsToReadMultiplePackets() + public function allowsToReadMultiplePacketsOfIntegers() { $this->reader->append("\x01\x00\x00\x00\xF1"); $this->reader->append("\x01\x00\x00\x00\xF2"); + $this->reader->append("\x01\x00\x00\x00\xF3"); + $this->reader->append("\x01\x00\x00\x00\xF4"); $data = []; - $readOneByte = function (PacketFragmentReader $fragment) use (&$data) { + $readOneByte = function (PacketPayloadReader $fragment) use (&$data) { $data[] = $fragment->readFixedInteger(1); }; - $this->reader->readFragment($readOneByte); - $this->reader->nextPacket(); - $this->reader->readFragment($readOneByte); + $this->reader->readPayload($readOneByte); + $this->reader->readPayload($readOneByte); + $this->reader->readPayload($readOneByte); + $this->reader->readPayload($readOneByte); - $this->assertEquals([241, 242], $data); + $this->assertEquals([241, 242, 243, 244], $data); } - /** @test */ - public function reportsCompletePacketAvailableEvenAfterFragmentIsRead() - { - $this->reader->append("\x02\x00\x00\x00\xF1\xF2"); - - $this->reader->readFragment( - function (PacketFragmentReader $fragment) { - $fragment->readFixedInteger(1); - } - ); - - $this->assertEquals(true, $this->reader->isFullPacket()); - } - /** @test */ public function reportsFragmentIsRead() { @@ -93,7 +63,7 @@ public function reportsFragmentIsRead() $this->assertEquals( true, - $this->reader->readFragment(function (PacketFragmentReader $reader) { + $this->reader->readPayload(function (PacketPayloadReader $reader) { $reader->readFixedInteger(1); }) ); @@ -106,7 +76,7 @@ public function reportsFragmentIsNotRead() $this->assertEquals( false, - $this->reader->readFragment(function (PacketFragmentReader $reader) { + $this->reader->readPayload(function (PacketPayloadReader $reader) { $reader->readFixedInteger(1); $reader->readFixedInteger(1); }) @@ -118,11 +88,12 @@ public function allowsReadingVariousFixedIntegers() { $this->reader->append("\x0D\x00\x00\x00\x00\x02\x02\x00\x00\x00\x00\x00\x00\x00\x00\xF0\x00"); - $data = []; - $this->reader->readFragment(function (PacketFragmentReader $fragment) use (&$data) { - $data[] = $fragment->readFixedInteger(2); // 512 - $data[] = $fragment->readFixedInteger(3); // 2 - $data[] = $fragment->readFixedInteger(8); // 67553994410557440 + $data = $this->readPayload(function (PacketPayloadReader $fragment) { + return [ + $fragment->readFixedInteger(2), // 512 + $fragment->readFixedInteger(3), // 2 + $fragment->readFixedInteger(8), // 67553994410557440 + ]; }); $this->assertEquals( @@ -142,13 +113,14 @@ public function allowsReadingDifferentLengthEncodedIntegers() "\x12\x00\x00\x00\xf9\xfa\xfc\xfb\00\xfd\xff\xff\xf0\xfe\xff\xff\xff\xff\xff\xff\xff\xf0" ); - $data = []; - $this->reader->readFragment(function (PacketFragmentReader $fragment) use (&$data) { - $data[] = $fragment->readLengthEncodedIntegerOrNull(); // 249 - $data[] = $fragment->readLengthEncodedIntegerOrNull(); // 250 - $data[] = $fragment->readLengthEncodedIntegerOrNull(); // 251 - $data[] = $fragment->readLengthEncodedIntegerOrNull(); // 15794175 - $data[] = $fragment->readLengthEncodedIntegerOrNull(); // 17365880163140632575 + $data = $this->readPayload(function (PacketPayloadReader $fragment) { + return [ + $fragment->readLengthEncodedIntegerOrNull(), // 249 + $fragment->readLengthEncodedIntegerOrNull(), // 250 + $fragment->readLengthEncodedIntegerOrNull(), // 251 + $fragment->readLengthEncodedIntegerOrNull(), // 15794175 + $fragment->readLengthEncodedIntegerOrNull(), // 17365880163140632575 + ]; }); $this->assertEquals( @@ -162,11 +134,8 @@ public function allowsReadingDifferentLengthEncodedIntegers() $data ); } - - - /** - * @test - */ + + /** @test */ public function throwsInvalidBinaryDataExceptionWhenLengthEncodedIntegerDoesNotMatchExpectedFormat() { @@ -176,7 +145,7 @@ public function throwsInvalidBinaryDataExceptionWhenLengthEncodedIntegerDoesNotM $this->expectException(InvalidBinaryDataException::class); - $this->reader->readFragment(function (PacketFragmentReader $fragment) { + $this->reader->readPayload(function (PacketPayloadReader $fragment) { $fragment->readLengthEncodedIntegerOrNull(); }); } @@ -188,21 +157,25 @@ public function readsNullForLengthEncodedInteger() "\x01\x00\x00\x00\xfb" ); - $this->reader->readFragment(function (PacketFragmentReader $fragment) { - $this->assertEquals(null, $fragment->readLengthEncodedIntegerOrNull()); - }); + $this->assertEquals( + null, + $this->readPayload(function (PacketPayloadReader $fragment) { + return $fragment->readLengthEncodedIntegerOrNull(); + }) + ); } /** @test */ public function readsFixedLengthString() { $this->reader->append("\x18\x00\x00\x00helloworld!awesomestring"); - $data = []; - $this->reader->readFragment(function (PacketFragmentReader $fragment) use (&$data) { - $data[] = $fragment->readFixedString(5); - $data[] = $fragment->readFixedString(6); - $data[] = $fragment->readFixedString(13); + $data = $this->readPayload(function (PacketPayloadReader $fragment) { + return [ + $fragment->readFixedString(5), + $fragment->readFixedString(6), + $fragment->readFixedString(13) + ]; }); $this->assertEquals( @@ -223,15 +196,16 @@ public function readsLengthEncodedString() $this->reader->append("\x0c\x01\x00\x00\xfc\xff\x00$veryLongString\x05hello\xfb\x0202"); - $data = []; - - $this->reader->readFragment(function (PacketFragmentReader $fragment) use (&$data) { - $data[] = $fragment->readLengthEncodedStringOrNull(); - $data[] = $fragment->readLengthEncodedStringOrNull(); - $data[] = $fragment->readLengthEncodedStringOrNull(); - $data[] = $fragment->readLengthEncodedStringOrNull(); + $data = $this->readPayload(function (PacketPayloadReader $fragment) { + return [ + $fragment->readLengthEncodedStringOrNull(), + $fragment->readLengthEncodedStringOrNull(), + $fragment->readLengthEncodedStringOrNull(), + $fragment->readLengthEncodedStringOrNull() + ]; }); + $this->assertEquals( [ $veryLongString, @@ -242,4 +216,157 @@ public function readsLengthEncodedString() $data ); } + + /** @test */ + public function readsMultipleNullTerminatedStrings() + { + $this->reader->append("\x31\x00\x00\x00first_string\x00second_string\x00third_string\x00"); + $data = $this->readPayload(function (PacketPayloadReader $fragmentReader) { + return [ + $fragmentReader->readNullTerminatedString(), + $fragmentReader->readNullTerminatedString(), + $fragmentReader->readNullTerminatedString() + ]; + }); + + $this->assertEquals( + [ + 'first_string', + 'second_string', + 'third_string' + ], + $data + ); + + } + + /** @test */ + public function stopsReadingPayloadWhenNullCharacterIsNotFoundForAString() + { + $this->reader->append("\x31\x00\x00\x00first_string"); + $data = $this->readPayload(function (PacketPayloadReader $fragmentReader) { + return $fragmentReader->readNullTerminatedString(); + }); + + $this->assertEquals(null, $data); + } + + /** @test */ + public function reportIncompletePayloadReadWhenNullTerminatedStringIsNotCompletelyRead() + { + $this->reader->append("\x31\x00\x00\x00first_string"); + + $this->assertEquals(false, $this->reader->readPayload(function (PacketPayloadReader $fragmentReader) { + $fragmentReader->readNullTerminatedString(); + })); + } + + /** @test */ + public function readsStringThatRepresentsCompletePacket() + { + $this->reader->append("\x0a\x00\x00\x00This is 10\x00\x00\x00\x00"); + + $this->assertEquals('This is 10', $this->readPayload(function (PacketPayloadReader $payloadReader) { + return $payloadReader->readRestOfPacketString(); + })); + } + + /** @test */ + public function readsStringThatRepresentsRemainderOfThePacket() + { + $this->reader->append("\x0C\x00\x00\x00\x01\x02This is 10\x00\x00\x00\x00"); + + $this->assertEquals('This is 10', $this->readPayload(function (PacketPayloadReader $payloadReader) { + $payloadReader->readFixedInteger(1); + $payloadReader->readFixedInteger(1); + return $payloadReader->readRestOfPacketString(); + })); + } + + /** @test */ + public function readMultiplePacketsPacketStringsAddedAsSingleNetworkPacketInSinglePayload() + { + $this->reader->append("\x03\x00\x00\x00one\x03\x00\x00\x00two\x05\x00\x00\x00three\x04\x00\x00\x00four"); + + $this->assertEquals( + [ + 'one', + 'two', + 'three', + 'four' + ], + $this->readPayload(function (PacketPayloadReader $payloadReader) { + return [ + $payloadReader->readRestOfPacketString(), + $payloadReader->readRestOfPacketString(), + $payloadReader->readRestOfPacketString(), + $payloadReader->readRestOfPacketString() + ]; + }) + ); + } + + /** @test */ + public function readMultiplePacketsPacketStringsAddedAsSingleNetworkPacketInMultiplePayloads() + { + $this->reader->append("\x03\x00\x00\x00one\x03\x00\x00\x00two\x05\x00\x00\x00three\x04\x00\x00\x00four"); + + + $readString = function (PacketPayloadReader $payloadReader) { + return $payloadReader->readRestOfPacketString(); + }; + + $this->assertEquals( + [ + 'one', + 'two', + 'three', + 'four' + ], + [ + $this->readPayload($readString), + $this->readPayload($readString), + $this->readPayload($readString), + $this->readPayload($readString) + ] + ); + } + + /** @test */ + public function providesSequenceNumberAndPacketLengthDuringReadingOfPayload() + { + $this->reader + ->append("\x08\x00\x00\x00one\x00two\x00\x06\x00\x00\x01three\x00\x05\x00\x00\x05four\x00"); + + + $readString = function (PacketPayloadReader $payloadReader, int $length, int $sequence) { + $payloadReader->readNullTerminatedString(); + + return [$length, $sequence]; + }; + + $this->assertEquals( + [ + [8, 0], + [8, 0], + [6, 1], + [5, 5] + ], + [ + $this->readPayload($readString), + $this->readPayload($readString), + $this->readPayload($readString), + $this->readPayload($readString), + ] + ); + } + + private function readPayload(callable $fragmentCallable) + { + $this->reader->readPayload(function (...$args) use ($fragmentCallable, &$data) { + $data = $fragmentCallable(...$args); + }); + + return $data; + } } diff --git a/tests/ReadBufferTest.php b/tests/ReadBufferTest.php index cc00dbf..16b0266 100644 --- a/tests/ReadBufferTest.php +++ b/tests/ReadBufferTest.php @@ -190,5 +190,102 @@ public function flushReturnsZeroWhenNoBytesRead() $this->assertEquals(0, $this->readBuffer->flush()); } + + /** @test */ + public function returnsLengthOfReadInOrderToReadDataUpToThisCharacter() + { + $this->readBuffer->append('some:data'); + + $this->assertEquals(5, $this->readBuffer->scan(':')); + } + + /** @test */ + public function returnsNegativeIndexWhenNoMatchFoundForScan() + { + $this->readBuffer->append('some data without character'); + + $this->assertEquals(-1, $this->readBuffer->scan(':')); + } + + /** @test */ + public function returnsLengthOfReadEvenIfCharacterForSearchIsAFirstOneInBuffer() + { + $this->readBuffer->append(':some other data'); + + $this->assertEquals(1, $this->readBuffer->scan(':')); + } + + /** @test */ + public function returnsLengthOfRequiredReadForTheNextCharacterOccurrence() + { + $this->readBuffer->append('some:other:data'); + $this->readBuffer->read(5); + + $this->assertEquals(6, $this->readBuffer->scan(':')); + } + + /** @test */ + public function advancesBufferPositionFromBeginningOfBuffer() + { + $this->readBuffer->append('some very long data'); + + $this->readBuffer->advance(10); + + $this->assertEquals('long data', $this->readBuffer->read(9)); + } + + /** @test */ + public function advancesBufferPositionAfterRead() + { + $this->readBuffer->append('some other nice data after advance'); + $this->readBuffer->read(10); + $this->readBuffer->advance(11); + + $this->assertEquals('after advance', $this->readBuffer->read(13)); + } + /** @test */ + public function reportsIncompleteBufferWhenAdvanceIsLargerThanCurrentData() + { + $this->readBuffer->append('some data'); + + $this->readBuffer->read(5); + + $this->expectException(IncompleteBufferException::class); + + $this->readBuffer->advance(5); + } + + + /** @test */ + public function defaultReadPositionInBufferIsZero() + { + $this->assertEquals(0, $this->readBuffer->currentPosition()); + } + + /** @test */ + public function currentPositionIsMovedWithNumberOfReadBytes() + { + $this->readBuffer->append('Some very long string data'); + + $this->readBuffer->read(4); + $this->readBuffer->read(6); + + $this->assertEquals(10, $this->readBuffer->currentPosition()); + } + + /** @test */ + public function currentPositionIsRelativeToFlushedReadData() + { + $this->readBuffer->append('Some very long string data'); + + $this->readBuffer->read(10); + + $this->readBuffer->flush(); + + $this->readBuffer->read(3); + + $this->assertEquals(3, $this->readBuffer->currentPosition()); + + } } From ef84c6786ed19082fd2afdc8c58398e5be171f92 Mon Sep 17 00:00:00 2001 From: Ivan Chepurnyi Date: Thu, 20 Jun 2019 23:41:14 +0200 Subject: [PATCH 16/23] Remove advance method from buffer --- src/ReadBuffer.php | 9 --------- src/UncompressedPacketReader.php | 2 +- tests/ReadBufferTest.php | 33 -------------------------------- 3 files changed, 1 insertion(+), 43 deletions(-) diff --git a/src/ReadBuffer.php b/src/ReadBuffer.php index ce843d3..7033965 100644 --- a/src/ReadBuffer.php +++ b/src/ReadBuffer.php @@ -82,15 +82,6 @@ public function scan(string $pattern): int return $position === false ? -1 : ($position - $this->currentBufferOffset) + 1; } - public function advance(int $length): void - { - if (!$this->isReadable($length)) { - throw new IncompleteBufferException(); - } - - $this->currentBufferOffset += $length; - } - public function currentPosition(): int { return $this->currentBufferOffset - $this->readBufferOffset; diff --git a/src/UncompressedPacketReader.php b/src/UncompressedPacketReader.php index c85250c..6fe555a 100644 --- a/src/UncompressedPacketReader.php +++ b/src/UncompressedPacketReader.php @@ -152,7 +152,7 @@ public function readNullTerminatedString(): string } $string = $this->readBuffer->read($nullPosition - 1); - $this->readBuffer->advance(1); + $this->readBuffer->read(1); return $string; } diff --git a/tests/ReadBufferTest.php b/tests/ReadBufferTest.php index 16b0266..414d145 100644 --- a/tests/ReadBufferTest.php +++ b/tests/ReadBufferTest.php @@ -223,39 +223,6 @@ public function returnsLengthOfRequiredReadForTheNextCharacterOccurrence() $this->assertEquals(6, $this->readBuffer->scan(':')); } - - /** @test */ - public function advancesBufferPositionFromBeginningOfBuffer() - { - $this->readBuffer->append('some very long data'); - - $this->readBuffer->advance(10); - - $this->assertEquals('long data', $this->readBuffer->read(9)); - } - - /** @test */ - public function advancesBufferPositionAfterRead() - { - $this->readBuffer->append('some other nice data after advance'); - $this->readBuffer->read(10); - $this->readBuffer->advance(11); - - $this->assertEquals('after advance', $this->readBuffer->read(13)); - } - - /** @test */ - public function reportsIncompleteBufferWhenAdvanceIsLargerThanCurrentData() - { - $this->readBuffer->append('some data'); - - $this->readBuffer->read(5); - - $this->expectException(IncompleteBufferException::class); - - $this->readBuffer->advance(5); - } - /** @test */ public function defaultReadPositionInBufferIsZero() From e6887be1787a4b8c84d7688846f1ba2f0c5bcca8 Mon Sep 17 00:00:00 2001 From: Ivan Chepurnyi Date: Fri, 21 Jun 2019 15:07:07 +0200 Subject: [PATCH 17/23] Added: - Constants for Capabilities Flags - Constants for Server Status Flags --- src/CapabilityFlags.php | 38 ++++++++++++++++++++++++++++++++++++++ src/StatusFlags.php | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 src/CapabilityFlags.php create mode 100644 src/StatusFlags.php diff --git a/src/CapabilityFlags.php b/src/CapabilityFlags.php new file mode 100644 index 0000000..03d8eae --- /dev/null +++ b/src/CapabilityFlags.php @@ -0,0 +1,38 @@ + Date: Mon, 24 Jun 2019 21:21:59 +0200 Subject: [PATCH 18/23] First terrible iteration of handshake parser... A lot to refactor --- src/CharsetIdentifiers.php | 17 ++++ src/Frame/HandshakeV10.php | 48 ++++++++++ src/Frame/HandshakeV10Builder.php | 112 ++++++++++++++++++++++++ src/HandshakeParser.php | 66 ++++++++++++++ tests/Frame/HandshakeBuilderV10Test.php | 83 ++++++++++++++++++ tests/HandshakeParserTest.php | 88 +++++++++++++++++++ 6 files changed, 414 insertions(+) create mode 100644 src/CharsetIdentifiers.php create mode 100644 src/Frame/HandshakeV10.php create mode 100644 src/Frame/HandshakeV10Builder.php create mode 100644 src/HandshakeParser.php create mode 100644 tests/Frame/HandshakeBuilderV10Test.php create mode 100644 tests/HandshakeParserTest.php diff --git a/src/CharsetIdentifiers.php b/src/CharsetIdentifiers.php new file mode 100644 index 0000000..80b304e --- /dev/null +++ b/src/CharsetIdentifiers.php @@ -0,0 +1,17 @@ +serverVersion = $serverVersion; + $this->connectionId = $connectionId; + $this->authData = $authData; + $this->capabilities = $capabilities; + $this->charset = $charset; + $this->status = $status; + $this->authPlugin = $authPlugin; + } + + +} diff --git a/src/Frame/HandshakeV10Builder.php b/src/Frame/HandshakeV10Builder.php new file mode 100644 index 0000000..d2ff793 --- /dev/null +++ b/src/Frame/HandshakeV10Builder.php @@ -0,0 +1,112 @@ +serverVersion = $serverVersion; + $builder->clientId = $clientId; + return $builder; + } + + public function withAuthData(string $authData): self + { + $builder = clone $this; + $builder->authData = $authData; + return $builder; + } + + public function withCapabilities(int $flags): self + { + $builder = clone $this; + $builder->capabilities = $flags; + return $builder; + } + + public function withCharset(int $charsetId): self + { + $builder = clone $this; + + $builder->charset = $charsetId; + return $builder; + } + + + public function withStatus(int $status): self + { + $builder = clone $this; + + $builder->status = $status; + return $builder; + } + + public function withAuthPlugin(string $authPlugin): self + { + $builder = clone $this; + + $builder->authPlugin = $authPlugin; + + return $builder; + } + + public function build(): HandshakeV10 + { + return new HandshakeV10( + $this->serverVersion, + $this->clientId, + $this->authData, + $this->capabilities, + $this->charset, + $this->status, + $this->authPlugin + ); + } + + + +} diff --git a/src/HandshakeParser.php b/src/HandshakeParser.php new file mode 100644 index 0000000..a75942f --- /dev/null +++ b/src/HandshakeParser.php @@ -0,0 +1,66 @@ +frameBuilder = $frameBuilder; + $this->frameReceiver = $frameReceiver; + } + + public function __invoke(PacketPayloadReader $reader) + { + $reader->readFixedInteger(1); + + $frameBuilder = $this->frameBuilder->withServerInfo( + $reader->readNullTerminatedString(), + $reader->readFixedInteger(4) + ); + + $authData = $reader->readFixedString(8); + + $reader->readFixedString(1); + + $capabilities = $reader->readFixedInteger(2); + $frameBuilder = $frameBuilder->withCharset($reader->readFixedInteger(1)) + ->withStatus($reader->readFixedInteger(2)); + + $capabilities += $reader->readFixedInteger(2) << 16; + + $frameBuilder = $frameBuilder->withCapabilities($capabilities); + + $totalAuthDataLenght = $reader->readFixedInteger(1); + + $reader->readFixedString(10); + + $authData .= $reader->readFixedString($totalAuthDataLenght - 8); + + $frameBuilder = $frameBuilder->withAuthData($authData); + + $authPlugin = $reader->readNullTerminatedString(); + + ($this->frameReceiver)($frameBuilder->withAuthPlugin($authPlugin)->build()); + } +} diff --git a/tests/Frame/HandshakeBuilderV10Test.php b/tests/Frame/HandshakeBuilderV10Test.php new file mode 100644 index 0000000..248ce37 --- /dev/null +++ b/tests/Frame/HandshakeBuilderV10Test.php @@ -0,0 +1,83 @@ +builder = new HandshakeV10Builder(); + } + + /** @test */ + public function createHandshakeWithMinimalData() + { + $this->assertEquals( + new HandshakeV10( + '5.5.1', + 10, + "\x01\x02\x03\x04\x05\x06\x07\x08", + CapabilityFlags::CLIENT_PROTOCOL_41 + ), + $this->builder->withServerInfo('5.5.1', 10) + ->withAuthData("\x01\x02\x03\x04\x05\x06\x07\x08") + ->withCapabilities(CapabilityFlags::CLIENT_PROTOCOL_41) + ->build() + ); + } + + /** @test */ + public function createsHandshakeWithServerStatusAndCharset() + { + $this->assertEquals( + new HandshakeV10( + '1.0.0', + 1, + 'thisisstringdata', + 0, + CharsetIdentifiers::UTF8, + StatusFlags::SERVER_STATUS_AUTOCOMMIT + ), + $this->builder->withServerInfo('1.0.0', 1) + ->withAuthData('thisisstringdata') + ->withCharset(CharsetIdentifiers::UTF8) + ->withStatus(StatusFlags::SERVER_STATUS_AUTOCOMMIT) + ->build() + ); + } + + /** @test */ + public function createsHandshakeWithAuthPluginSpecified() + { + $this->assertEquals( + new HandshakeV10( + '1.0.0', + 1, + 'thisisstringdata', + 0, + 0, + 0, + 'mysql_native_password' + ), + $this->builder->withServerInfo('1.0.0', 1) + ->withAuthData('thisisstringdata') + ->withAuthPlugin('mysql_native_password') + ->build() + ); + } +} diff --git a/tests/HandshakeParserTest.php b/tests/HandshakeParserTest.php new file mode 100644 index 0000000..5edd086 --- /dev/null +++ b/tests/HandshakeParserTest.php @@ -0,0 +1,88 @@ +frameBuilder = new Frame\HandshakeV10Builder(); + $this->packetReader = (new DefaultPacketReaderFactory())->createWithDefaultSettings(); + + $this->parser = new HandshakeParser( + $this->frameBuilder, + function (HandshakeV10 $handshake) { + $this->frames[] = $handshake; + } + ); + } + + /** @test */ + public function parsesMySQL8HandshakeInitMessage() + { + $this->packetReader->append( + "\x4a\x00\x00\x00\x0a8.0.16\x00\x0d\x00\x00\x00\x10\x4a\x12\x05" + . "\x71\x5d\x78\x63\x00\xff\xff\xff\x02\x00\xff\xc3\x15\x00\x00\x00" + . "\x00\x00\x00\x00\x00\x00\x00\x6e\x48\x49\x48\x56\x78\x42\x33\x76" + . "\x39\x3d\x5c\x00\x63\x61\x63\x68\x69\x6e\x67\x5f\x73\x68\x61\x32" + . "\x5f\x70\x61\x73\x73\x77\x6f\x72\x64\x00" + ); + + $this->packetReader->readPayload($this->parser); + + $this->assertEquals( + $this->frameBuilder + ->withServerInfo( + '8.0.16', + 13 + ) + ->withStatus( + StatusFlags::SERVER_STATUS_AUTOCOMMIT + ) + ->withCharset( + CharsetIdentifiers::UTF8MB4 + ) + ->withCapabilities(0xc3ffffff) + ->withAuthData( + "\x10\x4a\x12\x05\x71\x5d\x78\x63\x6e\x48\x49\x48\x56\x78\x42\x33\x76\x39\x3d\x5c\x00" + ) + ->withAuthPlugin( + "caching_sha2_password" + ) + ->build(), + current($this->frames) + ); + + } + + +} From 12c1c4c9c09936b9e9b333bbdb36c4b891590b8e Mon Sep 17 00:00:00 2001 From: Ivan Chepurnyi Date: Thu, 27 Jun 2019 19:38:53 +0200 Subject: [PATCH 19/23] Refactored uncompressed packet reader into simplier implementation to be able use payload reader outside of packet structure --- src/BufferPayloadReader.php | 144 ++++++++ src/BufferPayloadReaderFactory.php | 18 + src/DefaultPacketReaderFactory.php | 6 +- src/HandshakeParser.php | 2 +- ...ketPayloadReader.php => PayloadReader.php} | 2 +- src/UncompressedPacketReader.php | 146 ++------ tests/BufferPayloadReaderTest.php | 317 ++++++++++++++++++ tests/PacketReaderTest.php | 36 +- 8 files changed, 534 insertions(+), 137 deletions(-) create mode 100644 src/BufferPayloadReader.php create mode 100644 src/BufferPayloadReaderFactory.php rename src/{PacketPayloadReader.php => PayloadReader.php} (98%) create mode 100644 tests/BufferPayloadReaderTest.php diff --git a/src/BufferPayloadReader.php b/src/BufferPayloadReader.php new file mode 100644 index 0000000..64860d4 --- /dev/null +++ b/src/BufferPayloadReader.php @@ -0,0 +1,144 @@ + 2, + 0xfd => 3, + 0xfe => 8 + ]; + + private const NULL_MARKER = 0xfb; + + /** @var ReadBuffer */ + private $buffer; + + /** + * @var BinaryIntegerReader + */ + private $integerReader; + + /** @var int[] */ + private $unreadPacketLength; + + + public function __construct(ReadBuffer $buffer, array $unreadPacketLength, BinaryIntegerReader $integerReader) + { + $this->buffer = $buffer; + $this->integerReader = $integerReader; + $this->unreadPacketLength = $unreadPacketLength; + } + + /** + * {@inheritDoc} + */ + public function readFixedInteger(int $bytes) + { + return $this->integerReader->readFixed( + $this->buffer->read($bytes), + $bytes + ); + } + + /** + * {@inheritDoc} + */ + public function readLengthEncodedIntegerOrNull() + { + $firstByte = $this->readFixedInteger(1); + + if ($firstByte < 251) { + return $firstByte; + } + + if ($firstByte === self::NULL_MARKER) { + return null; + } + + if (isset(self::LENGTH_MARKERS[$firstByte])) { + return $this->readFixedInteger(self::LENGTH_MARKERS[$firstByte]); + } + + throw new InvalidBinaryDataException(); + } + + /** + * Reads string of specified length from buffer + * + * @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_strings.html + */ + public function readFixedString(int $length): string + { + return $this->buffer->read($length); + } + + /** + * Reads string that is has length as the first part of the fragment + * + * @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_strings.html + */ + public function readLengthEncodedStringOrNull(): ?string + { + $length = $this->readLengthEncodedIntegerOrNull(); + + if ($length === null) { + return null; + } + + return $this->buffer->read($length); + } + + /** + * Reads string till x00 character + * + * @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_strings.html + */ + public function readNullTerminatedString(): string + { + $nullPosition = $this->buffer->scan("\x00"); + + if ($nullPosition === -1) { + throw new IncompleteBufferException(); + } + + $string = $this->buffer->read($nullPosition - 1); + $this->buffer->read(1); + + return $string; + } + + /** + * Reads string that is rest of payload + * + * @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_strings.html + */ + public function readRestOfPacketString(): string + { + return $this->buffer->read( + $this->remainingPacketLengthToRead() + ); + } + + private function remainingPacketLengthToRead(): int + { + $currentBufferPosition = $this->buffer->currentPosition(); + + $currentPacketIndex = 0; + while ($this->unreadPacketLength[$currentPacketIndex] <= $currentBufferPosition) { + $currentBufferPosition -= $this->unreadPacketLength[$currentPacketIndex]; + $currentPacketIndex++; + } + + return $this->unreadPacketLength[$currentPacketIndex] - $currentBufferPosition; + } + +} diff --git a/src/BufferPayloadReaderFactory.php b/src/BufferPayloadReaderFactory.php new file mode 100644 index 0000000..890e009 --- /dev/null +++ b/src/BufferPayloadReaderFactory.php @@ -0,0 +1,18 @@ +frameReceiver = $frameReceiver; } - public function __invoke(PacketPayloadReader $reader) + public function __invoke(PayloadReader $reader) { $reader->readFixedInteger(1); diff --git a/src/PacketPayloadReader.php b/src/PayloadReader.php similarity index 98% rename from src/PacketPayloadReader.php rename to src/PayloadReader.php index 0cc58f4..05bd7db 100644 --- a/src/PacketPayloadReader.php +++ b/src/PayloadReader.php @@ -12,7 +12,7 @@ * Reader for buffer fragments * */ -interface PacketPayloadReader +interface PayloadReader { /** * Reads fixed value integer diff --git a/src/UncompressedPacketReader.php b/src/UncompressedPacketReader.php index 6fe555a..4e5a709 100644 --- a/src/UncompressedPacketReader.php +++ b/src/UncompressedPacketReader.php @@ -9,7 +9,7 @@ namespace EcomDev\MySQLBinaryProtocol; -class UncompressedPacketReader implements PacketReader, PacketPayloadReader +class UncompressedPacketReader implements PacketReader { private const INTEGER_LENGTH_MARKER = [ 0xfc => 2, @@ -30,14 +30,17 @@ class UncompressedPacketReader implements PacketReader, PacketPayloadReader * Registry of packets * * [ - * [lengthOfPacket, sequence, remainingToReadLength], - * [lengthOfPacket, sequence, remainingToReadLength], - * [lengthOfPacket, sequence, remainingToReadLength], + * [lengthOfPacket, sequence], + * [lengthOfPacket, sequence], + * [lengthOfPacket, sequence], * ] * @var array */ private $packets = []; + /** @var int[] */ + private $remainingPacketLength = []; + /** @var BinaryIntegerReader */ private $binaryIntegerReader; @@ -45,11 +48,20 @@ class UncompressedPacketReader implements PacketReader, PacketPayloadReader * @var ReadBuffer */ private $readBuffer; + /** + * @var BufferPayloadReaderFactory + */ + private $payloadReaderFactory; - public function __construct(BinaryIntegerReader $binaryIntegerReader, ReadBuffer $readBuffer) + public function __construct( + BinaryIntegerReader $binaryIntegerReader, + ReadBuffer $readBuffer, + BufferPayloadReaderFactory $payloadReaderFactory + ) { $this->binaryIntegerReader = $binaryIntegerReader; $this->readBuffer = $readBuffer; + $this->payloadReaderFactory = $payloadReaderFactory; } /** @@ -68,7 +80,12 @@ public function append(string $data): void public function readPayload(callable $reader): bool { try { - $reader($this, $this->packets[0][self::LENGTH], $this->packets[0][self::SEQUENCE]); + $reader( + $this->payloadReaderFactory->create($this->readBuffer, $this->remainingPacketLength), + $this->packets[0][self::LENGTH], + $this->packets[0][self::SEQUENCE] + ); + $this->advancePacketLength($this->readBuffer->flush()); } catch (IncompleteBufferException $exception) { return false; @@ -77,96 +94,6 @@ public function readPayload(callable $reader): bool return true; } - /** - * {@inheritDoc} - */ - public function readFixedInteger(int $bytes) - { - return $this->binaryIntegerReader->readFixed( - $this->readBuffer->read($bytes), - $bytes - ); - } - - /** - * Reads length encoded integer - * - * @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_integers.html - */ - public function readLengthEncodedIntegerOrNull() - { - $value = $this->readFixedInteger(1); - - if ($value === 0xfb) { - return null; - } - - if ($value < 251) { - return $value; - } - - if (!isset(self::INTEGER_LENGTH_MARKER[$value])) { - throw new InvalidBinaryDataException(); - } - - return $this->readFixedInteger(self::INTEGER_LENGTH_MARKER[$value]); - } - - /** - * Reads string of specified length from buffer - * - * @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_strings.html - */ - public function readFixedString(int $length): string - { - return $this->readBuffer->read($length); - } - - /** - * Reads string that is has length as the first part of the fragment - * - * @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_strings.html - */ - public function readLengthEncodedStringOrNull(): ?string - { - $stringLength = $this->readLengthEncodedIntegerOrNull(); - - if ($stringLength === null) { - return null; - } - - return $this->readFixedString($stringLength); - } - - /** - * Reads string till x00 character - * - * @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_strings.html - */ - public function readNullTerminatedString(): string - { - $nullPosition = $this->readBuffer->scan("\x00"); - - if ($nullPosition === -1) { - throw new IncompleteBufferException(); - } - - $string = $this->readBuffer->read($nullPosition - 1); - $this->readBuffer->read(1); - return $string; - } - - /** - * Reads string that is rest of payload - * - * @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_strings.html - */ - public function readRestOfPacketString(): string - { - return $this->readBuffer->read( - $this->remainingPacketLengthToRead() - ); - } private function registerPacket(string $dataToParse): string { @@ -184,40 +111,27 @@ private function registerPacket(string $dataToParse): string $this->packets[] = [ self::LENGTH => $this->awaitedPacketLength, - self::SEQUENCE => $this->binaryIntegerReader->readFixed($dataToParse[3], 1), - self::UNREAD_LENGTH => $this->awaitedPacketLength + self::SEQUENCE => $this->binaryIntegerReader->readFixed($dataToParse[3], 1) ]; + $this->remainingPacketLength[] = $this->awaitedPacketLength; + return substr($dataToParse, 4); } - private function remainingPacketLengthToRead(): int - { - $currentBufferPosition = $this->readBuffer->currentPosition(); - - $currentPacketIndex = 0; - while ($this->packets[$currentPacketIndex][self::UNREAD_LENGTH] <= $currentBufferPosition) { - $currentBufferPosition -= $this->packets[$currentPacketIndex][self::UNREAD_LENGTH]; - $currentPacketIndex++; - } - - return $this->packets[$currentPacketIndex][self::UNREAD_LENGTH] - $currentBufferPosition; - } - private function advancePacketLength(int $readLength): void { - - while ($this->packets[0][self::UNREAD_LENGTH] <= $readLength) { - $readLength -= $this->packets[0][self::UNREAD_LENGTH]; + while ($this->remainingPacketLength[0] <= $readLength) { + $readLength -= $this->remainingPacketLength[0]; array_shift($this->packets); + array_shift($this->remainingPacketLength); if (!$this->packets) { return; } } - - $this->packets[0][self::UNREAD_LENGTH] -= $readLength; + $this->remainingPacketLength[0] -= $readLength; } } diff --git a/tests/BufferPayloadReaderTest.php b/tests/BufferPayloadReaderTest.php new file mode 100644 index 0000000..9d18d44 --- /dev/null +++ b/tests/BufferPayloadReaderTest.php @@ -0,0 +1,317 @@ +payloadReaderFactory = new BufferPayloadReaderFactory(); + $this->buffer = new ReadBuffer(); + } + + + /** @test */ + public function readsOneByteFixedInteger() + { + $payloadReader = $this->createPayloadReader("\x01\x02\x03"); + + $this->assertEquals( + [ + 1, + 2, + 3 + ], + [ + $payloadReader->readFixedInteger(1), + $payloadReader->readFixedInteger(1), + $payloadReader->readFixedInteger(1), + ] + ); + } + + /** @test */ + public function readsMultipleBytesOfFixedInteger() + { + $payloadReader = $this->createPayloadReader( + "\x00\x02\x02\x00\x00\x00\x00\x00\x00\x00\x00\xF0\x00" + ); + + $this->assertEquals( + [ + 512, + 2, + 67553994410557440 + ], + [ + $payloadReader->readFixedInteger(2), + $payloadReader->readFixedInteger(3), + $payloadReader->readFixedInteger(8), + ] + ); + } + + /** @test */ + public function readsOneByteLengthEncodedInteger() + { + $payloadReader = $this->createPayloadReader("\x00\xfa\xf9\xa0"); + + $this->assertEquals( + [ + 0, + 250, + 249, + 160 + ], + [ + $payloadReader->readLengthEncodedIntegerOrNull(), + $payloadReader->readLengthEncodedIntegerOrNull(), + $payloadReader->readLengthEncodedIntegerOrNull(), + $payloadReader->readLengthEncodedIntegerOrNull(), + ] + ); + } + + /** @test */ + public function readsTwoByteLengthEncodedInteger() + { + $payloadReader = $this->createPayloadReader("\xfc\xfb\x00\xfc\xfc\x00\xfc\xff\xf0"); + + $this->assertEquals( + [ + 251, + 252, + 61695 + ], + [ + $payloadReader->readLengthEncodedIntegerOrNull(), + $payloadReader->readLengthEncodedIntegerOrNull(), + $payloadReader->readLengthEncodedIntegerOrNull(), + ] + ); + } + + /** @test */ + public function readsThreeByteLengthEncodedInteger() + { + $payloadReader = $this->createPayloadReader("\xfd\xff\xf0\x00\xfd\xa9\xff\xf0"); + + $this->assertEquals( + [ + 61695, + 15794089 + ], + [ + $payloadReader->readLengthEncodedIntegerOrNull(), + $payloadReader->readLengthEncodedIntegerOrNull(), + ] + ); + } + + /** @test */ + public function readsEightByteLengthEncodedInteger() + { + $payloadReader = $this->createPayloadReader( + "\xfe\xa9\xff\xf0\x00\x00\x00\x00\x00\xfe\x09\xea\xca\xff\x0a\xff\xff\x0a" + ); + + $this->assertEquals( + [ + 15794089, + 792632482146740745 + ], + [ + $payloadReader->readLengthEncodedIntegerOrNull(), + $payloadReader->readLengthEncodedIntegerOrNull(), + ] + ); + } + + /** @test */ + public function readsNullValueFromLengthEncodedIntegerSpec() + { + $payloadReader = $this->createPayloadReader("\xfb"); + + $this->assertEquals( + null, + $payloadReader->readLengthEncodedIntegerOrNull() + ); + } + + /** @test */ + public function reportsIncorrectLengthEncodedIntegerGivenFirstByteIsOutOfBounds() + { + $payloadReader = $this->createPayloadReader("\xff"); + + $this->expectException(InvalidBinaryDataException::class); + $payloadReader->readLengthEncodedIntegerOrNull(); + } + + /** @test */ + public function readsFixedLengthString() + { + $payloadReader = $this->createPayloadReader('onetwothree'); + + $this->assertEquals( + [ + 'one', + 'two', + 'three' + ], + [ + $payloadReader->readFixedString(3), + $payloadReader->readFixedString(3), + $payloadReader->readFixedString(5), + ] + ); + } + + /** @test */ + public function readsLengthEncodedString() + { + $payloadReader = $this->createPayloadReader( + "\x01a\x03one\x05three" + ); + + $this->assertEquals( + [ + 'a', + 'one', + 'three' + ], + [ + $payloadReader->readLengthEncodedStringOrNull(), + $payloadReader->readLengthEncodedStringOrNull(), + $payloadReader->readLengthEncodedStringOrNull(), + ] + ); + } + + /** @test */ + public function readsLongLengthEncodedString() + { + $payloadReader = $this->createPayloadReader("\xfc\xe8\x03" . str_repeat('a', 1000)); + + $this->assertEquals( + str_repeat('a', 1000), + $payloadReader->readLengthEncodedStringOrNull() + ); + } + + + /** @test */ + public function readsNullStringValue() + { + $payloadReader = $this->createPayloadReader("\xfb"); + + $this->assertEquals( + null, + $payloadReader->readLengthEncodedStringOrNull() + ); + } + + /** @test */ + public function readsNullTerminatedString() + { + $payloadReader = $this->createPayloadReader("null_terminated_string\x00other_data"); + + $this->assertEquals('null_terminated_string', $payloadReader->readNullTerminatedString()); + } + + /** @test */ + public function readsMultipleNullTerminatedStrings() + { + $payloadReader = $this->createPayloadReader("null_terminated_string\x00other_null_terminated_string\x00"); + + $this->assertEquals( + [ + 'null_terminated_string', + 'other_null_terminated_string' + ], + [ + $payloadReader->readNullTerminatedString(), + $payloadReader->readNullTerminatedString(), + ] + ); + } + + /** @test */ + public function throwsIncompleteBufferExceptionWhenNullCharacterIsNotPresent() + { + $payloadReader = $this->createPayloadReader('some string without null character'); + + $this->expectException(IncompleteBufferException::class); + + $payloadReader->readNullTerminatedString(); + } + + /** @test */ + public function readsStringTillEndOfTheBuffer() + { + $payloadReader = $this->createPayloadReader('some string till end of buffer'); + + $this->assertEquals('some string till end of buffer', $payloadReader->readRestOfPacketString()); + } + + /** @test */ + public function readsMultipleStringsThatRepresentCompletePacket() + { + $payloadReader = $this->createPayloadReader( + 'packet1packet2packet3last packet', + 7, 7, 7, 11 + ); + + $this->assertEquals( + [ + 'packet1', + 'packet2', + 'packet3', + 'last packet' + ], + [ + $payloadReader->readRestOfPacketString(), + $payloadReader->readRestOfPacketString(), + $payloadReader->readRestOfPacketString(), + $payloadReader->readRestOfPacketString(), + ] + ); + } + + /** @test */ + public function readsRestOfPacketStringStartingFromCurrentBufferPosition() + { + $payloadReader = $this->createPayloadReader( + 'onerest of packetstring that should not be read', + 17, 30 + ); + + $payloadReader->readFixedString(3); + + $this->assertEquals( + 'rest of packet', + $payloadReader->readRestOfPacketString() + ); + } + + private function createPayloadReader(string $payload, int ...$packetLength): PayloadReader + { + $buffer = new ReadBuffer(); + $buffer->append($payload); + $packetLength = $packetLength ?: [strlen($payload)]; + return $this->payloadReaderFactory->create($buffer, $packetLength); + } +} diff --git a/tests/PacketReaderTest.php b/tests/PacketReaderTest.php index d6ec9f9..62d3202 100644 --- a/tests/PacketReaderTest.php +++ b/tests/PacketReaderTest.php @@ -28,7 +28,7 @@ public function allowsToReadSingleByteIntegerFromPayload() $this->reader->append("\x01\x00\x00\x00\xF1"); $data = []; - $this->reader->readPayload(function (PacketPayloadReader $fragment) use (&$data) { + $this->reader->readPayload(function (PayloadReader $fragment) use (&$data) { $data[] = $fragment->readFixedInteger(1); }); @@ -44,7 +44,7 @@ public function allowsToReadMultiplePacketsOfIntegers() $this->reader->append("\x01\x00\x00\x00\xF4"); $data = []; - $readOneByte = function (PacketPayloadReader $fragment) use (&$data) { + $readOneByte = function (PayloadReader $fragment) use (&$data) { $data[] = $fragment->readFixedInteger(1); }; @@ -63,7 +63,7 @@ public function reportsFragmentIsRead() $this->assertEquals( true, - $this->reader->readPayload(function (PacketPayloadReader $reader) { + $this->reader->readPayload(function (PayloadReader $reader) { $reader->readFixedInteger(1); }) ); @@ -76,7 +76,7 @@ public function reportsFragmentIsNotRead() $this->assertEquals( false, - $this->reader->readPayload(function (PacketPayloadReader $reader) { + $this->reader->readPayload(function (PayloadReader $reader) { $reader->readFixedInteger(1); $reader->readFixedInteger(1); }) @@ -88,7 +88,7 @@ public function allowsReadingVariousFixedIntegers() { $this->reader->append("\x0D\x00\x00\x00\x00\x02\x02\x00\x00\x00\x00\x00\x00\x00\x00\xF0\x00"); - $data = $this->readPayload(function (PacketPayloadReader $fragment) { + $data = $this->readPayload(function (PayloadReader $fragment) { return [ $fragment->readFixedInteger(2), // 512 $fragment->readFixedInteger(3), // 2 @@ -113,7 +113,7 @@ public function allowsReadingDifferentLengthEncodedIntegers() "\x12\x00\x00\x00\xf9\xfa\xfc\xfb\00\xfd\xff\xff\xf0\xfe\xff\xff\xff\xff\xff\xff\xff\xf0" ); - $data = $this->readPayload(function (PacketPayloadReader $fragment) { + $data = $this->readPayload(function (PayloadReader $fragment) { return [ $fragment->readLengthEncodedIntegerOrNull(), // 249 $fragment->readLengthEncodedIntegerOrNull(), // 250 @@ -145,7 +145,7 @@ public function throwsInvalidBinaryDataExceptionWhenLengthEncodedIntegerDoesNotM $this->expectException(InvalidBinaryDataException::class); - $this->reader->readPayload(function (PacketPayloadReader $fragment) { + $this->reader->readPayload(function (PayloadReader $fragment) { $fragment->readLengthEncodedIntegerOrNull(); }); } @@ -159,7 +159,7 @@ public function readsNullForLengthEncodedInteger() $this->assertEquals( null, - $this->readPayload(function (PacketPayloadReader $fragment) { + $this->readPayload(function (PayloadReader $fragment) { return $fragment->readLengthEncodedIntegerOrNull(); }) ); @@ -170,7 +170,7 @@ public function readsFixedLengthString() { $this->reader->append("\x18\x00\x00\x00helloworld!awesomestring"); - $data = $this->readPayload(function (PacketPayloadReader $fragment) { + $data = $this->readPayload(function (PayloadReader $fragment) { return [ $fragment->readFixedString(5), $fragment->readFixedString(6), @@ -196,7 +196,7 @@ public function readsLengthEncodedString() $this->reader->append("\x0c\x01\x00\x00\xfc\xff\x00$veryLongString\x05hello\xfb\x0202"); - $data = $this->readPayload(function (PacketPayloadReader $fragment) { + $data = $this->readPayload(function (PayloadReader $fragment) { return [ $fragment->readLengthEncodedStringOrNull(), $fragment->readLengthEncodedStringOrNull(), @@ -221,7 +221,7 @@ public function readsLengthEncodedString() public function readsMultipleNullTerminatedStrings() { $this->reader->append("\x31\x00\x00\x00first_string\x00second_string\x00third_string\x00"); - $data = $this->readPayload(function (PacketPayloadReader $fragmentReader) { + $data = $this->readPayload(function (PayloadReader $fragmentReader) { return [ $fragmentReader->readNullTerminatedString(), $fragmentReader->readNullTerminatedString(), @@ -244,7 +244,7 @@ public function readsMultipleNullTerminatedStrings() public function stopsReadingPayloadWhenNullCharacterIsNotFoundForAString() { $this->reader->append("\x31\x00\x00\x00first_string"); - $data = $this->readPayload(function (PacketPayloadReader $fragmentReader) { + $data = $this->readPayload(function (PayloadReader $fragmentReader) { return $fragmentReader->readNullTerminatedString(); }); @@ -256,7 +256,7 @@ public function reportIncompletePayloadReadWhenNullTerminatedStringIsNotComplete { $this->reader->append("\x31\x00\x00\x00first_string"); - $this->assertEquals(false, $this->reader->readPayload(function (PacketPayloadReader $fragmentReader) { + $this->assertEquals(false, $this->reader->readPayload(function (PayloadReader $fragmentReader) { $fragmentReader->readNullTerminatedString(); })); } @@ -266,7 +266,7 @@ public function readsStringThatRepresentsCompletePacket() { $this->reader->append("\x0a\x00\x00\x00This is 10\x00\x00\x00\x00"); - $this->assertEquals('This is 10', $this->readPayload(function (PacketPayloadReader $payloadReader) { + $this->assertEquals('This is 10', $this->readPayload(function (PayloadReader $payloadReader) { return $payloadReader->readRestOfPacketString(); })); } @@ -276,7 +276,7 @@ public function readsStringThatRepresentsRemainderOfThePacket() { $this->reader->append("\x0C\x00\x00\x00\x01\x02This is 10\x00\x00\x00\x00"); - $this->assertEquals('This is 10', $this->readPayload(function (PacketPayloadReader $payloadReader) { + $this->assertEquals('This is 10', $this->readPayload(function (PayloadReader $payloadReader) { $payloadReader->readFixedInteger(1); $payloadReader->readFixedInteger(1); return $payloadReader->readRestOfPacketString(); @@ -295,7 +295,7 @@ public function readMultiplePacketsPacketStringsAddedAsSingleNetworkPacketInSing 'three', 'four' ], - $this->readPayload(function (PacketPayloadReader $payloadReader) { + $this->readPayload(function (PayloadReader $payloadReader) { return [ $payloadReader->readRestOfPacketString(), $payloadReader->readRestOfPacketString(), @@ -312,7 +312,7 @@ public function readMultiplePacketsPacketStringsAddedAsSingleNetworkPacketInMult $this->reader->append("\x03\x00\x00\x00one\x03\x00\x00\x00two\x05\x00\x00\x00three\x04\x00\x00\x00four"); - $readString = function (PacketPayloadReader $payloadReader) { + $readString = function (PayloadReader $payloadReader) { return $payloadReader->readRestOfPacketString(); }; @@ -339,7 +339,7 @@ public function providesSequenceNumberAndPacketLengthDuringReadingOfPayload() ->append("\x08\x00\x00\x00one\x00two\x00\x06\x00\x00\x01three\x00\x05\x00\x00\x05four\x00"); - $readString = function (PacketPayloadReader $payloadReader, int $length, int $sequence) { + $readString = function (PayloadReader $payloadReader, int $length, int $sequence) { $payloadReader->readNullTerminatedString(); return [$length, $sequence]; From 6fac13f7b4bb230b8da2dde9ff380cb06b14450b Mon Sep 17 00:00:00 2001 From: Ivan Chepurnyi Date: Fri, 28 Jun 2019 16:12:42 +0200 Subject: [PATCH 20/23] Remove redundant code from packet reader --- src/UncompressedPacketReader.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/UncompressedPacketReader.php b/src/UncompressedPacketReader.php index 4e5a709..96342ac 100644 --- a/src/UncompressedPacketReader.php +++ b/src/UncompressedPacketReader.php @@ -11,13 +11,6 @@ class UncompressedPacketReader implements PacketReader { - private const INTEGER_LENGTH_MARKER = [ - 0xfc => 2, - 0xfd => 3, - 0xfe => 8 - ]; - - private const UNREAD_LENGTH = 2; private const LENGTH = 0; private const SEQUENCE = 1; From 1b039db0345e860a4a472efa6a9f83eee563eded Mon Sep 17 00:00:00 2001 From: Ivan Chepurnyi Date: Fri, 28 Jun 2019 16:13:39 +0200 Subject: [PATCH 21/23] Add @throws into PayloadReader interface --- src/PayloadReader.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/PayloadReader.php b/src/PayloadReader.php index 05bd7db..83ee3b4 100644 --- a/src/PayloadReader.php +++ b/src/PayloadReader.php @@ -19,6 +19,8 @@ interface PayloadReader * * @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_integers.html * @return int|float + * + * @throws IncompleteBufferException */ public function readFixedInteger(int $bytes); @@ -27,6 +29,8 @@ public function readFixedInteger(int $bytes); * * @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_integers.html * @return int|float + * + * @throws IncompleteBufferException */ public function readLengthEncodedIntegerOrNull(); @@ -34,6 +38,9 @@ public function readLengthEncodedIntegerOrNull(); * Reads string of specified length from buffer * * @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_strings.html + * + * + * @throws IncompleteBufferException */ public function readFixedString(int $length): string; @@ -41,6 +48,8 @@ public function readFixedString(int $length): string; * Reads string that is has length as the first part of the fragment * * @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_strings.html + * + * @throws IncompleteBufferException */ public function readLengthEncodedStringOrNull(): ?string; @@ -48,6 +57,8 @@ public function readLengthEncodedStringOrNull(): ?string; * Reads string till x00 character * * @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_strings.html + * + * @throws IncompleteBufferException */ public function readNullTerminatedString(): string; @@ -56,6 +67,8 @@ public function readNullTerminatedString(): string; * Reads string that is rest of payload * * @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_strings.html + * + * @throws IncompleteBufferException */ public function readRestOfPacketString(): string; } From 283e7c9e357d01aad30383d90cff43ee0c5a7d74 Mon Sep 17 00:00:00 2001 From: Ivan Chepurnyi Date: Fri, 28 Jun 2019 16:15:35 +0200 Subject: [PATCH 22/23] Add idea for frame parser and frame interfaces for FSM implementation --- src/Frame.php | 14 ++++++++++++++ src/FrameParser.php | 21 +++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 src/Frame.php create mode 100644 src/FrameParser.php diff --git a/src/Frame.php b/src/Frame.php new file mode 100644 index 0000000..0a365ad --- /dev/null +++ b/src/Frame.php @@ -0,0 +1,14 @@ + Date: Mon, 22 Jul 2019 17:47:46 +0200 Subject: [PATCH 23/23] Add buffer reader factory method for creating it from text --- src/BufferPayloadReaderFactory.php | 22 ++++++++++++++++++++-- src/UncompressedPacketReader.php | 2 +- tests/BufferPayloadReaderTest.php | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/BufferPayloadReaderFactory.php b/src/BufferPayloadReaderFactory.php index 890e009..768306a 100644 --- a/src/BufferPayloadReaderFactory.php +++ b/src/BufferPayloadReaderFactory.php @@ -11,8 +11,26 @@ class BufferPayloadReaderFactory { - public function create(ReadBuffer $buffer, array $unreadPacketLength): BufferPayloadReader + /** + * @var BinaryIntegerReader + */ + private $binaryIntegerReader; + + public function __construct(BinaryIntegerReader $binaryIntegerReader = null) + { + $this->binaryIntegerReader = $binaryIntegerReader ?? new BinaryIntegerReader(); + } + + public function createFromBuffer(ReadBuffer $buffer, array $unreadPacketLength): BufferPayloadReader { - return new BufferPayloadReader($buffer, $unreadPacketLength, new BinaryIntegerReader()); + return new BufferPayloadReader($buffer, $unreadPacketLength, $this->binaryIntegerReader); + } + + public function createFromString(string $data): BufferPayloadReader + { + $buffer = new ReadBuffer(); + $buffer->append($data); + + return $this->createFromBuffer($buffer, [strlen($data)]); } } diff --git a/src/UncompressedPacketReader.php b/src/UncompressedPacketReader.php index 96342ac..1fe9d85 100644 --- a/src/UncompressedPacketReader.php +++ b/src/UncompressedPacketReader.php @@ -74,7 +74,7 @@ public function readPayload(callable $reader): bool { try { $reader( - $this->payloadReaderFactory->create($this->readBuffer, $this->remainingPacketLength), + $this->payloadReaderFactory->createFromBuffer($this->readBuffer, $this->remainingPacketLength), $this->packets[0][self::LENGTH], $this->packets[0][self::SEQUENCE] ); diff --git a/tests/BufferPayloadReaderTest.php b/tests/BufferPayloadReaderTest.php index 9d18d44..a2a4de8 100644 --- a/tests/BufferPayloadReaderTest.php +++ b/tests/BufferPayloadReaderTest.php @@ -312,6 +312,6 @@ private function createPayloadReader(string $payload, int ...$packetLength): Pay $buffer = new ReadBuffer(); $buffer->append($payload); $packetLength = $packetLength ?: [strlen($payload)]; - return $this->payloadReaderFactory->create($buffer, $packetLength); + return $this->payloadReaderFactory->createFromBuffer($buffer, $packetLength); } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy