diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..02a92bd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,13 @@
+# OSX
+.DS_Store
+
+# Composer
+/vendor/
+
+# PHPStorm (except class maps)
+/.idea/*
+!/.idea/*.iml
+!/.idea/modules.xml
+
+# PHPUnit
+/.phpunit.result.cache
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/php-mysql-binary-protocol.iml" filepath="$PROJECT_DIR$/.idea/php-mysql-binary-protocol.iml" />
+    </modules>
+  </component>
+</project>
\ 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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="EcomDev\MySQLBinaryProtocol" />
+      <sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" packagePrefix="EcomDev\MySQLBinaryProtocol" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..eb756cd
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,32 @@
+{
+    "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": "stable",
+    "require": {
+        "php": "~7.1"
+    },
+    "autoload": {
+        "psr-4": {
+            "EcomDev\\MySQLBinaryProtocol\\": "src/"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "EcomDev\\MySQLBinaryProtocol\\": "tests/"
+        }
+    },
+    "require-dev": {
+        "phpunit/phpunit": "^8.1"
+    },
+    "scripts": {
+        "test": "phpunit --colors --coverage-text"
+    }
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..056d938
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,1540 @@
+{
+    "_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#installing-dependencies",
+        "This file is @generated automatically"
+    ],
+    "content-hash": "aff8eb49ceefb3611292997c36e8967e",
+    "packages": [],
+    "packages-dev": [
+        {
+            "name": "doctrine/instantiator",
+            "version": "1.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/instantiator.git",
+                "reference": "a2c590166b2133a4633738648b6b064edae0814a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a",
+                "reference": "a2c590166b2133a4633738648b6b064edae0814a",
+                "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-03-17T17:37:11+00:00"
+        },
+        {
+            "name": "myclabs/deep-copy",
+            "version": "1.9.1",
+            "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-07T13:18:21+00:00"
+        },
+        {
+            "name": "phar-io/manifest",
+            "version": "1.0.3",
+            "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-08T19:23:20+00:00"
+        },
+        {
+            "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-08T19:19:57+00:00"
+        },
+        {
+            "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-11T18:02:19+00:00"
+        },
+        {
+            "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-30T17:48:53+00:00"
+        },
+        {
+            "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-14T14:27:02+00:00"
+        },
+        {
+            "name": "phpspec/prophecy",
+            "version": "1.8.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpspec/prophecy.git",
+                "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/1927e75f4ed19131ec9bcc3b002e07fb1173ee76",
+                "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76",
+                "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": "2019-06-13T12:50:23+00:00"
+        },
+        {
+            "name": "phpunit/php-code-coverage",
+            "version": "7.0.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+                "reference": "aed67b57d459dcab93e84a5c9703d3deb5025dff"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/aed67b57d459dcab93e84a5c9703d3deb5025dff",
+                "reference": "aed67b57d459dcab93e84a5c9703d3deb5025dff",
+                "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-06-06T12:28:18+00:00"
+        },
+        {
+            "name": "phpunit/php-file-iterator",
+            "version": "2.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+                "reference": "050bedf145a257b1ff02746c31894800e5122946"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946",
+                "reference": "050bedf145a257b1ff02746c31894800e5122946",
+                "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": "2018-09-13T20:33:42+00:00"
+        },
+        {
+            "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-21T13:50:34+00:00"
+        },
+        {
+            "name": "phpunit/php-timer",
+            "version": "2.1.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-timer.git",
+                "reference": "1038454804406b0b5f5f520358e78c1c2f71501e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e",
+                "reference": "1038454804406b0b5f5f520358e78c1c2f71501e",
+                "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-06-07T04:22:29+00:00"
+        },
+        {
+            "name": "phpunit/php-token-stream",
+            "version": "3.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-token-stream.git",
+                "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c99e3be9d3e85f60646f152f9002d46ed7770d18",
+                "reference": "c99e3be9d3e85f60646f152f9002d46ed7770d18",
+                "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": "2018-10-30T05:52:18+00:00"
+        },
+        {
+            "name": "phpunit/phpunit",
+            "version": "8.2.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/phpunit.git",
+                "reference": "047f771e34dccacb6c432a1a70e9980e087eac92"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/047f771e34dccacb6c432a1a70e9980e087eac92",
+                "reference": "047f771e34dccacb6c432a1a70e9980e087eac92",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/instantiator": "^1.2.0",
+                "ext-dom": "*",
+                "ext-json": "*",
+                "ext-libxml": "*",
+                "ext-mbstring": "*",
+                "ext-xml": "*",
+                "ext-xmlwriter": "*",
+                "myclabs/deep-copy": "^1.9.1",
+                "phar-io/manifest": "^1.0.3",
+                "phar-io/version": "^2.0.1",
+                "php": "^7.2",
+                "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.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.1",
+                "sebastian/type": "^1.1.0",
+                "sebastian/version": "^2.0.1"
+            },
+            "require-dev": {
+                "ext-pdo": "*"
+            },
+            "suggest": {
+                "ext-soap": "*",
+                "ext-xdebug": "*",
+                "phpunit/php-invoker": "^2.0.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-06-07T14:04:13+00:00"
+        },
+        {
+            "name": "sebastian/code-unit-reverse-lookup",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+                "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
+                "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
+                "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": "2017-03-04T06:30:41+00:00"
+        },
+        {
+            "name": "sebastian/comparator",
+            "version": "3.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/comparator.git",
+                "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da",
+                "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da",
+                "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": "2018-07-12T15:12:46+00:00"
+        },
+        {
+            "name": "sebastian/diff",
+            "version": "3.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/diff.git",
+                "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29",
+                "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29",
+                "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-02-04T06:01:07+00:00"
+        },
+        {
+            "name": "sebastian/environment",
+            "version": "4.2.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/environment.git",
+                "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/f2a2c8e1c97c11ace607a7a667d73d47c19fe404",
+                "reference": "f2a2c8e1c97c11ace607a7a667d73d47c19fe404",
+                "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-05T09:05:15+00:00"
+        },
+        {
+            "name": "sebastian/exporter",
+            "version": "3.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/exporter.git",
+                "reference": "234199f4528de6d12aaa58b612e98f7d36adb937"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937",
+                "reference": "234199f4528de6d12aaa58b612e98f7d36adb937",
+                "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": "2017-04-03T13:19:02+00:00"
+        },
+        {
+            "name": "sebastian/global-state",
+            "version": "3.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/global-state.git",
+                "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4",
+                "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4",
+                "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-02-01T05:30:01+00:00"
+        },
+        {
+            "name": "sebastian/object-enumerator",
+            "version": "3.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+                "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5",
+                "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5",
+                "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": "2017-08-03T12:35:26+00:00"
+        },
+        {
+            "name": "sebastian/object-reflector",
+            "version": "1.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/object-reflector.git",
+                "reference": "773f97c67f28de00d397be301821b06708fca0be"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be",
+                "reference": "773f97c67f28de00d397be301821b06708fca0be",
+                "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": "2017-03-29T09:07:27+00:00"
+        },
+        {
+            "name": "sebastian/recursion-context",
+            "version": "3.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/recursion-context.git",
+                "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
+                "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
+                "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": "2017-03-03T06:23:57+00:00"
+        },
+        {
+            "name": "sebastian/resource-operations",
+            "version": "2.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/resource-operations.git",
+                "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9",
+                "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9",
+                "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": "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",
+            "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-03T07:35:21+00:00"
+        },
+        {
+            "name": "symfony/polyfill-ctype",
+            "version": "v1.11.0",
+            "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-06T07:57:58+00:00"
+        },
+        {
+            "name": "theseer/tokenizer",
+            "version": "1.1.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/theseer/tokenizer.git",
+                "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9",
+                "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9",
+                "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-06-13T22:48:21+00:00"
+        },
+        {
+            "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-25T11:19:39+00:00"
+        }
+    ],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": [],
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": {
+        "php": "~7.1"
+    },
+    "platform-dev": []
+}
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit
+        colors                      = "true"
+        convertErrorsToExceptions   = "true"
+        convertNoticesToExceptions  = "true"
+        convertWarningsToExceptions = "true"
+        bootstrap                   = "vendor/autoload.php">
+
+    <testsuites>
+        <testsuite name="Unit Tests">
+            <directory suffix="Test.php">tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>src/</directory>
+        </whitelist>
+    </filter>
+
+</phpunit>
diff --git a/src/BinaryIntegerReader.php b/src/BinaryIntegerReader.php
new file mode 100644
index 0000000..a0213f5
--- /dev/null
+++ b/src/BinaryIntegerReader.php
@@ -0,0 +1,72 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+declare(strict_types=1);
+
+namespace EcomDev\MySQLBinaryProtocol;
+
+use function unpack;
+
+/**
+ * @internal
+ */
+class BinaryIntegerReader
+{
+    public function readFixed(string $binary, int $size)
+    {
+        if ($size === 1) {
+            return $this->readUnsigned1ByteInteger($binary);
+        }
+
+        if ($size === 2) {
+            return $this->readUnsigned2ByteInteger($binary);
+        }
+
+        if ($size === 3) {
+            return $this->readUnsigned4ByteInteger($binary."\x00");
+        }
+
+        if ($size === 4) {
+            return $this->readUnsigned4ByteInteger($binary);
+        }
+
+        if ($size === 8) {
+            return $this->readUnsigned8ByteInteger($binary);
+        }
+
+        if ($size > 8) {
+            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/src/BufferPayloadReader.php b/src/BufferPayloadReader.php
new file mode 100644
index 0000000..64860d4
--- /dev/null
+++ b/src/BufferPayloadReader.php
@@ -0,0 +1,144 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+declare(strict_types=1);
+
+namespace EcomDev\MySQLBinaryProtocol;
+
+
+class BufferPayloadReader implements PayloadReader
+{
+    private const LENGTH_MARKERS = [
+        0xfc => 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..768306a
--- /dev/null
+++ b/src/BufferPayloadReaderFactory.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+declare(strict_types=1);
+
+namespace EcomDev\MySQLBinaryProtocol;
+
+
+class BufferPayloadReaderFactory
+{
+    /**
+     * @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, $this->binaryIntegerReader);
+    }
+
+    public function createFromString(string $data): BufferPayloadReader
+    {
+        $buffer = new ReadBuffer();
+        $buffer->append($data);
+
+        return $this->createFromBuffer($buffer, [strlen($data)]);
+    }
+}
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 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+declare(strict_types=1);
+
+namespace EcomDev\MySQLBinaryProtocol;
+
+/**
+ * Collection of capability flags for MySQL communication
+ *
+ * @see https://dev.mysql.com/doc/internals/en/capability-flags.html#packet-Protocol::CapabilityFlags
+ */
+class CapabilityFlags
+{
+    public const CLIENT_LONG_PASSWORD = 0x01;
+    public const CLIENT_FOUND_ROWS = 0x02;
+    public const CLIENT_LONG_FLAG = 0x04;
+    public const CLIENT_CONNECT_WITH_DB = 0x08;
+    public const CLIENT_NO_SCHEMA = 0x10;
+    public const CLIENT_COMPRESS = 0x20;
+    public const CLIENT_IGNORE_SPACE = 0x0100;
+    public const CLIENT_PROTOCOL_41 = 0x0200;
+    public const CLIENT_INTERACTIVE = 0x0400;
+    public const CLIENT_SSL = 0x0800;
+    public const CLIENT_TRANSACTIONS = 0x2000;
+    public const CLIENT_SECURE_CONNECTION = 0x8000;
+    public const CLIENT_MULTI_STATEMENTS = 0x010000;
+    public const CLIENT_MULTI_RESULTS = 0x020000;
+    public const CLIENT_PS_MULTI_RESULTS = 0x040000;
+    public const CLIENT_PLUGIN_AUTH = 0x080000;
+    public const CLIENT_CONNECT_ATTRS = 0x100000;
+    public const CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA = 0x200000;
+    public const CLIENT_SESSION_TRACK = 0x800000;
+    public const CLIENT_DEPRECATE_EOF = 0x01000000;
+}
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 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+declare(strict_types=1);
+
+namespace EcomDev\MySQLBinaryProtocol;
+
+
+class CharsetIdentifiers
+{
+    public const UTF8 = 33;
+    public const LATIN1 = 8;
+    public const UTF8MB4 = 255;
+}
diff --git a/src/DefaultPacketReaderFactory.php b/src/DefaultPacketReaderFactory.php
new file mode 100644
index 0000000..e14b91a
--- /dev/null
+++ b/src/DefaultPacketReaderFactory.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+declare(strict_types=1);
+
+namespace EcomDev\MySQLBinaryProtocol;
+
+
+class DefaultPacketReaderFactory
+{
+    public function createWithDefaultSettings(): UncompressedPacketReader
+    {
+        return new UncompressedPacketReader(
+            new BinaryIntegerReader(),
+            new ReadBuffer(),
+            new BufferPayloadReaderFactory()
+        );
+    }
+}
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 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+declare(strict_types=1);
+
+namespace EcomDev\MySQLBinaryProtocol;
+
+interface Frame
+{
+
+}
diff --git a/src/Frame/HandshakeV10.php b/src/Frame/HandshakeV10.php
new file mode 100644
index 0000000..891d971
--- /dev/null
+++ b/src/Frame/HandshakeV10.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+declare(strict_types=1);
+
+namespace EcomDev\MySQLBinaryProtocol\Frame;
+
+
+class HandshakeV10
+{
+    public $serverVersion;
+
+    public $connectionId;
+
+    public $authData;
+
+    public $capabilities;
+
+    public $charset;
+
+    public $status;
+
+    public $authPlugin;
+
+    public function __construct(
+        string $serverVersion,
+        int $connectionId,
+        string $authData,
+        int $capabilities,
+        int $charset = 0,
+        int $status = 0,
+        string $authPlugin = ''
+    )
+    {
+        $this->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 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+declare(strict_types=1);
+
+namespace EcomDev\MySQLBinaryProtocol\Frame;
+
+
+class HandshakeV10Builder
+{
+
+    /**
+     * @var string
+     */
+    private $serverVersion;
+    /**
+     * @var int
+     */
+    private $clientId;
+
+    /**
+     * @var string
+     */
+    private $authData;
+
+    /**
+     * @var int
+     */
+    private $capabilities = 0;
+
+    /**
+     * @var int
+     */
+    private $charset = 0;
+
+    /**
+     * @var int
+     */
+    private $status = 0;
+
+    /**
+     * @var string
+     */
+    private $authPlugin = '';
+
+    public function withServerInfo(string $serverVersion, int $clientId): self
+    {
+        $builder = clone $this;
+        $builder->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/FrameParser.php b/src/FrameParser.php
new file mode 100644
index 0000000..e124b07
--- /dev/null
+++ b/src/FrameParser.php
@@ -0,0 +1,21 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+declare(strict_types=1);
+
+namespace EcomDev\MySQLBinaryProtocol;
+
+
+interface FrameParser
+{
+    /**
+     * Parses frames from payload reader
+     *
+     * @throws InvalidBinaryDataException
+     * @throws IncompleteBufferException
+     */
+    public function parse(PayloadReader $payload, int $length, int $sequenceNumber): Frame;
+}
diff --git a/src/HandshakeParser.php b/src/HandshakeParser.php
new file mode 100644
index 0000000..a6b8813
--- /dev/null
+++ b/src/HandshakeParser.php
@@ -0,0 +1,66 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+declare(strict_types=1);
+
+namespace EcomDev\MySQLBinaryProtocol;
+
+
+use EcomDev\MySQLBinaryProtocol\Frame\HandshakeV10Builder;
+
+class HandshakeParser
+{
+
+    /**
+     * @var HandshakeV10Builder
+     */
+    private $frameBuilder;
+    /**
+     * @var callable
+     */
+    private $frameReceiver;
+
+    public function __construct(HandshakeV10Builder $frameBuilder, callable $frameReceiver)
+    {
+
+        $this->frameBuilder = $frameBuilder;
+        $this->frameReceiver = $frameReceiver;
+    }
+
+    public function __invoke(PayloadReader $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/src/IncompleteBufferException.php b/src/IncompleteBufferException.php
new file mode 100644
index 0000000..e0ec68b
--- /dev/null
+++ b/src/IncompleteBufferException.php
@@ -0,0 +1,15 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+declare(strict_types=1);
+
+namespace EcomDev\MySQLBinaryProtocol;
+
+
+class IncompleteBufferException extends \LengthException
+{
+
+}
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 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+declare(strict_types=1);
+
+namespace EcomDev\MySQLBinaryProtocol;
+
+class InvalidBinaryDataException extends \RuntimeException
+{
+
+}
diff --git a/src/PacketReader.php b/src/PacketReader.php
new file mode 100644
index 0000000..c55d285
--- /dev/null
+++ b/src/PacketReader.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+declare(strict_types=1);
+
+namespace EcomDev\MySQLBinaryProtocol;
+
+interface PacketReader
+{
+    /**
+     * Appends data into buffer
+     */
+    public function append(string $data): void;
+
+    /**
+     * 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 readPayload(callable $reader): bool;
+}
diff --git a/src/PayloadReader.php b/src/PayloadReader.php
new file mode 100644
index 0000000..83ee3b4
--- /dev/null
+++ b/src/PayloadReader.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+declare(strict_types=1);
+
+namespace EcomDev\MySQLBinaryProtocol;
+
+/**
+ * Reader for buffer fragments
+ *
+ */
+interface PayloadReader
+{
+    /**
+     * Reads fixed value integer
+     *
+     * @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);
+
+    /**
+     * Reads length encoded integer
+     *
+     * @see https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_basic_dt_integers.html
+     * @return int|float
+     *
+     * @throws IncompleteBufferException
+     */
+    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;
+
+    /**
+     * 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;
+
+    /**
+     * 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;
+
+
+    /**
+     * 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;
+}
diff --git a/src/ReadBuffer.php b/src/ReadBuffer.php
new file mode 100644
index 0000000..7033965
--- /dev/null
+++ b/src/ReadBuffer.php
@@ -0,0 +1,89 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+declare(strict_types=1);
+
+namespace EcomDev\MySQLBinaryProtocol;
+
+use function strlen;
+use function substr;
+use function strpos;
+
+class ReadBuffer
+{
+    const ONE_MEGABYTE = 1024*1024;
+
+    /**
+     * @var string
+     */
+    private $buffer = '';
+
+    /** @var int */
+    private $currentBufferOffset = 0;
+
+    /** @var int */
+    private $readBufferOffset = 0;
+
+    /**
+     * @var int
+     */
+    private $bufferSize;
+
+    public function __construct(int $bufferSize = self::ONE_MEGABYTE)
+    {
+        $this->bufferSize = $bufferSize;
+    }
+
+    public function append(string $data): void
+    {
+        $this->buffer .= $data;
+    }
+
+    public function read(int $length): string
+    {
+        if (!$this->isReadable($length)) {
+            $this->currentBufferOffset = $this->readBufferOffset;
+            throw new IncompleteBufferException();
+        }
+
+        $data = substr($this->buffer, $this->currentBufferOffset, $length);
+
+        $this->currentBufferOffset += $length;
+        return $data;
+    }
+
+    public function isReadable(int $length): bool
+    {
+        return strlen($this->buffer) - $this->currentBufferOffset >= $length;
+    }
+
+    public function flush(): int
+    {
+        $bytesRead = $this->currentPosition();
+
+        $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 currentPosition(): int
+    {
+        return $this->currentBufferOffset - $this->readBufferOffset;
+    }
+}
diff --git a/src/StatusFlags.php b/src/StatusFlags.php
new file mode 100644
index 0000000..4ac0217
--- /dev/null
+++ b/src/StatusFlags.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+declare(strict_types=1);
+
+namespace EcomDev\MySQLBinaryProtocol;
+
+/**
+ * Collection of server status flags sent in OK packet
+ *
+ * @see https://dev.mysql.com/doc/internals/en/status-flags.html
+ */
+class StatusFlags
+{
+    public const SERVER_STATUS_IN_TRANS = 0x01;
+    public const SERVER_STATUS_AUTOCOMMIT = 0x02;
+    public const SERVER_MORE_RESULTS_EXISTS = 0x08;
+    public const SERVER_STATUS_NO_GOOD_INDEX_USED = 0x10;
+    public const SERVER_STATUS_NO_INDEX_USED = 0x20;
+    public const SERVER_STATUS_CURSOR_EXISTS = 0x40;
+    public const SERVER_STATUS_LAST_ROW_SENT = 0x80;
+    public const SERVER_STATUS_DB_DROPPED = 0x0100;
+    public const SERVER_STATUS_NO_BACKSLASH_ESCAPES = 0x0200;
+    public const SERVER_STATUS_METADATA_CHANGED = 0x0400;
+    public const SERVER_QUERY_WAS_SLOW = 0x0800;
+    public const SERVER_PS_OUT_PARAMS = 0x1000;
+    public const SERVER_STATUS_IN_TRANS_READONLY = 0x2000;
+    public const SERVER_SESSION_STATE_CHANGED = 0x4000;
+}
diff --git a/src/UncompressedPacketReader.php b/src/UncompressedPacketReader.php
new file mode 100644
index 0000000..1fe9d85
--- /dev/null
+++ b/src/UncompressedPacketReader.php
@@ -0,0 +1,130 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+declare(strict_types=1);
+
+namespace EcomDev\MySQLBinaryProtocol;
+
+
+class UncompressedPacketReader implements PacketReader
+{
+    private const LENGTH = 0;
+    private const SEQUENCE = 1;
+
+    /**
+     * @var int
+     */
+    private $awaitedPacketLength = 0;
+
+    /**
+     * Registry of packets
+     *
+     * [
+     *   [lengthOfPacket, sequence],
+     *   [lengthOfPacket, sequence],
+     *   [lengthOfPacket, sequence],
+     * ]
+     * @var array
+     */
+    private $packets = [];
+
+    /** @var int[] */
+    private $remainingPacketLength = [];
+
+    /** @var BinaryIntegerReader */
+    private $binaryIntegerReader;
+
+    /**
+     * @var ReadBuffer
+     */
+    private $readBuffer;
+    /**
+     * @var BufferPayloadReaderFactory
+     */
+    private $payloadReaderFactory;
+
+    public function __construct(
+        BinaryIntegerReader $binaryIntegerReader,
+        ReadBuffer $readBuffer,
+        BufferPayloadReaderFactory $payloadReaderFactory
+    )
+    {
+        $this->binaryIntegerReader = $binaryIntegerReader;
+        $this->readBuffer = $readBuffer;
+        $this->payloadReaderFactory = $payloadReaderFactory;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function append(string $data): void
+    {
+        do {
+            $data = $this->registerPacket($data);
+        } while ($data !== '');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function readPayload(callable $reader): bool
+    {
+        try {
+            $reader(
+                $this->payloadReaderFactory->createFromBuffer($this->readBuffer, $this->remainingPacketLength),
+                $this->packets[0][self::LENGTH],
+                $this->packets[0][self::SEQUENCE]
+            );
+
+            $this->advancePacketLength($this->readBuffer->flush());
+        } catch (IncompleteBufferException $exception) {
+            return false;
+        }
+
+        return true;
+    }
+
+
+    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)
+        ];
+
+        $this->remainingPacketLength[] = $this->awaitedPacketLength;
+
+        return substr($dataToParse, 4);
+    }
+
+
+    private function advancePacketLength(int $readLength): void
+    {
+        while ($this->remainingPacketLength[0] <= $readLength) {
+            $readLength -= $this->remainingPacketLength[0];
+            array_shift($this->packets);
+            array_shift($this->remainingPacketLength);
+
+            if (!$this->packets) {
+                return;
+            }
+        }
+
+        $this->remainingPacketLength[0] -= $readLength;
+    }
+}
diff --git a/tests/BinaryIntegerReaderTest.php b/tests/BinaryIntegerReaderTest.php
new file mode 100644
index 0000000..0ae406b
--- /dev/null
+++ b/tests/BinaryIntegerReaderTest.php
@@ -0,0 +1,138 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+namespace EcomDev\MySQLBinaryProtocol;
+
+use PHPUnit\Framework\TestCase;
+
+class BinaryIntegerReaderTest extends TestCase
+{
+    /**
+     * @var BinaryIntegerReader
+     */
+    private $reader;
+
+    protected function setUp(): void
+    {
+        $this->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
+     * @dataProvider fourByteIntegers
+     */
+    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));
+    }
+
+    /**
+     * @test
+     * @dataProvider eightByteIntegers
+     */
+    public function readsFixedEightByteIntegers(string $binary, $expectedResult)
+    {
+        $this->assertEquals($expectedResult, $this->reader->readFixed($binary, 8));
+    }
+
+    /** @test */
+    public function fixedReaderDoesNotSupportValuesHigherThan8Bytes()
+    {
+        $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);
+    }
+
+    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],
+            'random' => ["\x20\x8a", 35360],
+            '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],
+            'random' => ["\x68\xD6\xEF", 15717992],
+            'max' => ["\xFF\xFF\xFF", 16777215],
+        ];
+    }
+
+    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 [
+            '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],
+        ];
+    }
+}
diff --git a/tests/BufferPayloadReaderTest.php b/tests/BufferPayloadReaderTest.php
new file mode 100644
index 0000000..a2a4de8
--- /dev/null
+++ b/tests/BufferPayloadReaderTest.php
@@ -0,0 +1,317 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+declare(strict_types=1);
+
+namespace EcomDev\MySQLBinaryProtocol;
+
+use PHPUnit\Framework\TestCase;
+
+class BufferPayloadReaderTest extends TestCase
+{
+    /** @var BufferPayloadReaderFactory */
+    private $payloadReaderFactory;
+
+    /** @var ReadBuffer */
+    private $buffer;
+
+    protected function setUp(): void
+    {
+        $this->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->createFromBuffer($buffer, $packetLength);
+    }
+}
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 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+declare(strict_types=1);
+
+namespace EcomDev\MySQLBinaryProtocol\Frame;
+
+
+use EcomDev\MySQLBinaryProtocol\CapabilityFlags;
+use EcomDev\MySQLBinaryProtocol\CharsetIdentifiers;
+use EcomDev\MySQLBinaryProtocol\StatusFlags;
+use PHPUnit\Framework\TestCase;
+
+class HandshakeBuilderV10Test extends TestCase
+{
+    /** @var HandshakeV10Builder */
+    private $builder;
+
+    protected function setUp(): void
+    {
+        $this->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 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+declare(strict_types=1);
+
+namespace EcomDev\MySQLBinaryProtocol;
+
+
+use EcomDev\MySQLBinaryProtocol\Frame\HandshakeV10;
+use EcomDev\MySQLBinaryProtocol\Frame\HandshakeV10Builder;
+use PHPUnit\Framework\TestCase;
+
+class HandshakeParserTest extends TestCase
+{
+    /** @var HandshakeV10Builder */
+    private $frameBuilder;
+
+    /**
+     * @var PacketReader
+     */
+    private $packetReader;
+
+    /**
+     * @var HandshakeParser
+     */
+    private $parser;
+
+    /**
+     * @var HandshakeV10[]
+     */
+    private $frames = [];
+
+    protected function setUp(): void
+    {
+        $this->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)
+        );
+
+    }
+
+
+}
diff --git a/tests/PacketReaderTest.php b/tests/PacketReaderTest.php
new file mode 100644
index 0000000..62d3202
--- /dev/null
+++ b/tests/PacketReaderTest.php
@@ -0,0 +1,372 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+declare(strict_types=1);
+
+namespace EcomDev\MySQLBinaryProtocol;
+
+use PHPUnit\Framework\TestCase;
+
+class PacketReaderTest extends TestCase
+{
+    /**
+     * @var PacketReader
+     */
+    private $reader;
+
+    protected function setUp(): void
+    {
+        $this->reader = (new DefaultPacketReaderFactory())->createWithDefaultSettings();
+    }
+
+    /** @test */
+    public function allowsToReadSingleByteIntegerFromPayload()
+    {
+        $this->reader->append("\x01\x00\x00\x00\xF1");
+
+        $data = [];
+        $this->reader->readPayload(function (PayloadReader $fragment) use (&$data) {
+            $data[] = $fragment->readFixedInteger(1);
+        });
+
+        $this->assertEquals([241], $data);
+    }
+
+    /** @test */
+    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 (PayloadReader $fragment) use (&$data) {
+            $data[] = $fragment->readFixedInteger(1);
+        };
+
+        $this->reader->readPayload($readOneByte);
+        $this->reader->readPayload($readOneByte);
+        $this->reader->readPayload($readOneByte);
+        $this->reader->readPayload($readOneByte);
+
+        $this->assertEquals([241, 242, 243, 244], $data);
+    }
+
+    /** @test */
+    public function reportsFragmentIsRead()
+    {
+        $this->reader->append("\x01\x00\x00\x00\x01");
+
+        $this->assertEquals(
+            true,
+            $this->reader->readPayload(function (PayloadReader $reader) {
+                $reader->readFixedInteger(1);
+            })
+        );
+    }
+
+    /** @test */
+    public function reportsFragmentIsNotRead()
+    {
+        $this->reader->append("\x02\x00\x00\x00\x01");
+
+        $this->assertEquals(
+            false,
+            $this->reader->readPayload(function (PayloadReader $reader) {
+                $reader->readFixedInteger(1);
+                $reader->readFixedInteger(1);
+            })
+        );
+    }
+
+    /** @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->readPayload(function (PayloadReader $fragment) {
+            return [
+                $fragment->readFixedInteger(2), // 512
+                $fragment->readFixedInteger(3), // 2
+                $fragment->readFixedInteger(8), // 67553994410557440
+            ];
+        });
+
+        $this->assertEquals(
+            [
+                512,
+                2,
+                67553994410557440
+            ],
+            $data
+        );
+    }
+    
+    /** @test */
+    public function allowsReadingDifferentLengthEncodedIntegers()
+    {
+        $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->readPayload(function (PayloadReader $fragment) {
+            return [
+                $fragment->readLengthEncodedIntegerOrNull(), // 249
+                $fragment->readLengthEncodedIntegerOrNull(), // 250
+                $fragment->readLengthEncodedIntegerOrNull(), // 251
+                $fragment->readLengthEncodedIntegerOrNull(), // 15794175
+                $fragment->readLengthEncodedIntegerOrNull(), // 17365880163140632575
+            ];
+        });
+
+        $this->assertEquals(
+            [
+                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->readPayload(function (PayloadReader $fragment) {
+            $fragment->readLengthEncodedIntegerOrNull();
+        });
+    }
+    
+    /** @test */
+    public function readsNullForLengthEncodedInteger()
+    {
+        $this->reader->append(
+            "\x01\x00\x00\x00\xfb"
+        );
+
+        $this->assertEquals(
+            null,
+            $this->readPayload(function (PayloadReader $fragment) {
+                return $fragment->readLengthEncodedIntegerOrNull();
+            })
+        );
+    }
+    
+    /** @test */
+    public function readsFixedLengthString()
+    {
+        $this->reader->append("\x18\x00\x00\x00helloworld!awesomestring");
+
+        $data = $this->readPayload(function (PayloadReader $fragment) {
+            return [
+                $fragment->readFixedString(5),
+                $fragment->readFixedString(6),
+                $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->readPayload(function (PayloadReader $fragment) {
+            return [
+                $fragment->readLengthEncodedStringOrNull(),
+                $fragment->readLengthEncodedStringOrNull(),
+                $fragment->readLengthEncodedStringOrNull(),
+                $fragment->readLengthEncodedStringOrNull()
+            ];
+        });
+
+
+        $this->assertEquals(
+            [
+                $veryLongString,
+                'hello',
+                null,
+                '02'
+            ],
+            $data
+        );
+    }
+    
+    /** @test */
+    public function readsMultipleNullTerminatedStrings()
+    {
+        $this->reader->append("\x31\x00\x00\x00first_string\x00second_string\x00third_string\x00");
+        $data = $this->readPayload(function (PayloadReader $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 (PayloadReader $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 (PayloadReader $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 (PayloadReader $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 (PayloadReader $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 (PayloadReader $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 (PayloadReader $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 (PayloadReader $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
new file mode 100644
index 0000000..414d145
--- /dev/null
+++ b/tests/ReadBufferTest.php
@@ -0,0 +1,258 @@
+<?php
+/**
+ * Copyright © EcomDev B.V. All rights reserved.
+ * See LICENSE for license details.
+ */
+
+declare(strict_types=1);
+
+namespace EcomDev\MySQLBinaryProtocol;
+
+
+use PHPUnit\Framework\TestCase;
+
+class ReadBufferTest extends TestCase
+{
+    /** @var ReadBuffer */
+    private $readBuffer;
+
+    protected function setUp(): void
+    {
+        $this->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());
+    }
+    
+    /** @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 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());
+
+    }
+}
