From 31d6b046bf42903fd96886d0cecb6e6693fa4068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sat, 25 Nov 2017 19:07:05 +0100 Subject: [PATCH 01/27] Argument unpacking is available in PHP 5.6 [...] --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 0d35415..cfb50b0 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ } ], "require": { - "php": ">=5.5.0", + "php": ">=5.6", "usmanhalalit/viocon": "1.0.1" }, "require-dev": { From 6ccab3733a850c9ee73f282b700a3fc1352092ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sat, 25 Nov 2017 19:50:15 +0100 Subject: [PATCH 02/27] phpDoc --- src/Pecee/Pixie/ConnectionAdapters/Mysql.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Pecee/Pixie/ConnectionAdapters/Mysql.php b/src/Pecee/Pixie/ConnectionAdapters/Mysql.php index ed19207..439367c 100644 --- a/src/Pecee/Pixie/ConnectionAdapters/Mysql.php +++ b/src/Pecee/Pixie/ConnectionAdapters/Mysql.php @@ -11,6 +11,7 @@ class Mysql extends BaseAdapter { /** * @param array $config + * * @return mixed * @throws Exception */ @@ -34,6 +35,9 @@ protected function doConnect(array $config) $connectionString .= ";unix_socket={$config['unix_socket']}"; } + /** + * @var \PDO $connection + */ $connection = $this->container->build( \PDO::class, [$connectionString, $config['username'], $config['password'], $config['options']] From ae0778c9df87879c862517e6fbb613ff8c2f7878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sat, 25 Nov 2017 19:52:02 +0100 Subject: [PATCH 03/27] phpDoc --- src/Pecee/Pixie/ConnectionAdapters/Pgsql.php | 4 ++++ src/Pecee/Pixie/ConnectionAdapters/Sqlite.php | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Pecee/Pixie/ConnectionAdapters/Pgsql.php b/src/Pecee/Pixie/ConnectionAdapters/Pgsql.php index 3dbee8c..3bb5cd1 100644 --- a/src/Pecee/Pixie/ConnectionAdapters/Pgsql.php +++ b/src/Pecee/Pixie/ConnectionAdapters/Pgsql.php @@ -11,6 +11,7 @@ class Pgsql extends BaseAdapter { /** * @param array $config + * * @return mixed * @throws Exception */ @@ -26,6 +27,9 @@ protected function doConnect(array $config) $connectionString .= ";port={$config['port']}"; } + /** + * @var \PDO $connection + */ $connection = $this->container->build( \PDO::class, [$connectionString, $config['username'], $config['password'], $config['options']] diff --git a/src/Pecee/Pixie/ConnectionAdapters/Sqlite.php b/src/Pecee/Pixie/ConnectionAdapters/Sqlite.php index 9b8454d..7fa6b4c 100644 --- a/src/Pecee/Pixie/ConnectionAdapters/Sqlite.php +++ b/src/Pecee/Pixie/ConnectionAdapters/Sqlite.php @@ -11,7 +11,8 @@ class Sqlite extends BaseAdapter { /** * @param array $config - * @return mixed + * + * @return \PDO * @throws Exception */ public function doConnect(array $config) From 11353b5b3aceebaa1c6176380a105c6869d07478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Sat, 25 Nov 2017 20:21:12 +0100 Subject: [PATCH 04/27] Fixed issue with null values and added unit-test for it. --- .../QueryBuilder/Adapters/BaseAdapter.php | 53 ++++++++++--------- .../QueryBuilder/QueryBuilderHandler.php | 2 +- .../Pecee/Pixie/QueryBuilderBehaviorTest.php | 5 +- 3 files changed, 32 insertions(+), 28 deletions(-) diff --git a/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php b/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php index 25efdec..6e6ab81 100644 --- a/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php +++ b/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php @@ -421,8 +421,10 @@ protected function buildCriteria(array $statements, $bindValues = true) continue; } - if (is_array($value)) { - // where_in or between like query + + if (is_array($value) === true) { + + // Where in or between like query $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator']; if ($statement['operator'] === 'BETWEEN') { @@ -445,38 +447,39 @@ protected function buildCriteria(array $statements, $bindValues = true) if ($value instanceof Raw) { $criteria .= "{$statement['joiner']} {$key} {$statement['operator']} $value "; - } else { - - // Usual where like criteria - if ($bindValues === false) { + continue; + } - // Specially for joins - we are not binding values, lets sanitize then - $value = $this->wrapSanitizer($value); - $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' ' . $value . ' '; - continue; - } + // Usual where like criteria + if ($bindValues === false) { - if ($statement['key'] instanceof Raw) { + // Specially for joins - we are not binding values, lets sanitize then + $value = $this->wrapSanitizer($value); + $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' ' . $value . ' '; - if ($statement['operator'] !== null) { - $criteria .= "{$statement['joiner']} {$key} {$statement['operator']} ? "; - $bindings[] = (array)$statement['key']->getBindings(); - $bindings[] = (array)$value; - } else { - $criteria .= $statement['joiner'] . ' ' . $key . ' '; - $bindings[] = (array)$statement['key']->getBindings(); - } + continue; + } - continue; + if ($statement['key'] instanceof Raw) { + if ($statement['operator'] !== null) { + $criteria .= "{$statement['joiner']} {$key} {$statement['operator']} ? "; + $bindings[] = (array)$statement['key']->getBindings(); + $bindings[] = (array)$value; + } else { + $criteria .= $statement['joiner'] . ' ' . $key . ' '; + $bindings[] = (array)$statement['key']->getBindings(); } - // WHERE - $valuePlaceholder = '?'; - $bindings[] = (array)$value; - $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' ' . $valuePlaceholder . ' '; + continue; + } + + // WHERE + $valuePlaceholder = '?'; + $bindings[] = [$value]; + $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' ' . $valuePlaceholder . ' '; } $bindings = array_merge(...$bindings); diff --git a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php index 50d289c..dc6a5ed 100644 --- a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php +++ b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php @@ -342,7 +342,7 @@ protected function aggregate($type) if (is_array($row[0]) === true) { return (int)$row[0]['field']; } - if (is_object($row[0])) { + if (is_object($row[0]) === true) { return (int)$row[0]->field; } } diff --git a/tests/Pecee/Pixie/QueryBuilderBehaviorTest.php b/tests/Pecee/Pixie/QueryBuilderBehaviorTest.php index 182aea9..990bafe 100644 --- a/tests/Pecee/Pixie/QueryBuilderBehaviorTest.php +++ b/tests/Pecee/Pixie/QueryBuilderBehaviorTest.php @@ -288,10 +288,11 @@ public function testSelectQueryWithNull() ->whereNull('key1') ->orWhereNull('key2') ->whereNotNull('key3') - ->orWhereNotNull('key4'); + ->orWhereNotNull('key4') + ->orWhere('key5', '=', null); $this->assertEquals( - "SELECT * FROM `cb_my_table` WHERE `key1` IS NULL OR `key2` IS NULL AND `key3` IS NOT NULL OR `key4` IS NOT NULL", + "SELECT * FROM `cb_my_table` WHERE `key1` IS NULL OR `key2` IS NULL AND `key3` IS NOT NULL OR `key4` IS NOT NULL OR `key5` = NULL", $query->getQuery()->getRawSql() ); } From adc434533bafe9b5ec48733e0ec514c6f7e60de9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sun, 26 Nov 2017 00:13:21 +0100 Subject: [PATCH 05/27] first commit: idea settings + composer --- .gitignore | 85 +- .idea/codeStyleSettings.xml | 9 + .idea/composerJson.xml | 9 + .idea/deployment.xml | 43 + .idea/inspectionProfiles/Project_Default.xml | 15 + .idea/modules.xml | 8 + .idea/pecee-pixie.iml | 93 ++ .idea/php-test-framework.xml | 14 + .idea/php.xml | 38 + .idea/vcs.xml | 6 + composer.json | 2 +- composer.lock | 1294 ++++++++++++++++++ 12 files changed, 1611 insertions(+), 5 deletions(-) create mode 100644 .idea/codeStyleSettings.xml create mode 100644 .idea/composerJson.xml create mode 100644 .idea/deployment.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/pecee-pixie.iml create mode 100644 .idea/php-test-framework.xml create mode 100644 .idea/php.xml create mode 100644 .idea/vcs.xml create mode 100644 composer.lock diff --git a/.gitignore b/.gitignore index 18bb066..5f0b506 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,84 @@ -vendor composer.phar -composer.lock .DS_Store -.idea test.php -*~ \ No newline at end of file +*~ + +### Composer template +/vendor/ + +# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control +# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file +# composer.lock +### Windows template +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# CMake +cmake-build-debug/ + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml new file mode 100644 index 0000000..c4c9543 --- /dev/null +++ b/.idea/codeStyleSettings.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/.idea/composerJson.xml b/.idea/composerJson.xml new file mode 100644 index 0000000..4199499 --- /dev/null +++ b/.idea/composerJson.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/deployment.xml b/.idea/deployment.xml new file mode 100644 index 0000000..9cbabb7 --- /dev/null +++ b/.idea/deployment.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..3d2c9cb --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,15 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..af9f61b --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/pecee-pixie.iml b/.idea/pecee-pixie.iml new file mode 100644 index 0000000..a671bfb --- /dev/null +++ b/.idea/pecee-pixie.iml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/php-test-framework.xml b/.idea/php-test-framework.xml new file mode 100644 index 0000000..4c73bb7 --- /dev/null +++ b/.idea/php-test-framework.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml new file mode 100644 index 0000000..8cf5292 --- /dev/null +++ b/.idea/php.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/composer.json b/composer.json index cfb50b0..bf82635 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ } ], "require": { - "php": ">=5.6", + "php": ">=7.0", "usmanhalalit/viocon": "1.0.1" }, "require-dev": { diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..d5e77e5 --- /dev/null +++ b/composer.lock @@ -0,0 +1,1294 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "content-hash": "316248f27c42defd396d2c459b6e66cb", + "packages": [ + { + "name": "usmanhalalit/viocon", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/usmanhalalit/viocon.git", + "reference": "0878afee16f15355971fb95bf3c6d297aceff35d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/usmanhalalit/viocon/zipball/0878afee16f15355971fb95bf3c6d297aceff35d", + "reference": "0878afee16f15355971fb95bf3c6d297aceff35d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "type": "library", + "autoload": { + "psr-0": { + "Viocon": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Muhammad Usman", + "email": "hi@usman.it", + "role": "Developer" + } + ], + "description": "A simple and flexible Dependency Injection container for PHP.", + "homepage": "https://github.com/usmanhalalit/viocon", + "keywords": [ + "container", + "di", + "ioc", + "test" + ], + "time": "2013-07-13T19:54:56+00:00" + } + ], + "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "shasum": "" + }, + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.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://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2015-06-14T21:17:01+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v1.2.2", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "b37020aa976fa52d3de9aa904aa2522dc518f79c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/b37020aa976fa52d3de9aa904aa2522dc518f79c", + "reference": "b37020aa976fa52d3de9aa904aa2522dc518f79c", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "1.3.3", + "satooshi/php-coveralls": "dev-master" + }, + "type": "library", + "autoload": { + "classmap": [ + "hamcrest" + ], + "files": [ + "hamcrest/Hamcrest.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "time": "2015-05-11T14:41:42+00:00" + }, + { + "name": "mockery/mockery", + "version": "0.9.4", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "70bba85e4aabc9449626651f48b9018ede04f86b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/70bba85e4aabc9449626651f48b9018ede04f86b", + "reference": "70bba85e4aabc9449626651f48b9018ede04f86b", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "~1.1", + "lib-pcre": ">=7.0", + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.9.x-dev" + } + }, + "autoload": { + "psr-0": { + "Mockery": "library/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "http://blog.astrumfutura.com" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "http://davedevelopment.co.uk" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succinct API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending.", + "homepage": "http://github.com/padraic/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "time": "2015-04-02T19:54:00+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.1.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "2d3d238c433cf69caeb4842e97a3223a116f94b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/2d3d238c433cf69caeb4842e97a3223a116f94b2", + "reference": "2d3d238c433cf69caeb4842e97a3223a116f94b2", + "shasum": "" + }, + "require": { + "php": "^7.0", + "phpdocumentor/reflection-common": "^1.0@dev", + "phpdocumentor/type-resolver": "^0.4.0", + "webmozart/assert": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^4.4" + }, + "type": "library", + "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": "2017-08-30T18:51:59+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.7.3", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf", + "reference": "e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf", + "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", + "sebastian/recursion-context": "^1.0|^2.0|^3.0" + }, + "require-dev": { + "phpspec/phpspec": "^2.5|^3.2", + "phpunit/phpunit": "^4.8.35 || ^5.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.7.x-dev" + } + }, + "autoload": { + "psr-0": { + "Prophecy\\": "src/" + } + }, + "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": "2017-11-24T13:59:53+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "2.2.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": "~1.3", + "phpunit/php-text-template": "~1.2", + "phpunit/php-token-stream": "~1.3", + "sebastian/environment": "^1.3.2", + "sebastian/version": "~1.0" + }, + "require-dev": { + "ext-xdebug": ">=2.1.4", + "phpunit/phpunit": "~4" + }, + "suggest": { + "ext-dom": "*", + "ext-xdebug": ">=2.2.1", + "ext-xmlwriter": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.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": "2015-10-06T15:47:00+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.4.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "8ebba84e5bd74fc5fdeb916b38749016c7232f93" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/8ebba84e5bd74fc5fdeb916b38749016c7232f93", + "reference": "8ebba84e5bd74fc5fdeb916b38749016c7232f93", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.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": "2017-11-24T15:00:59+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": "1.0.9", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2017-02-26T11:10:40+00:00" + }, + { + "name": "phpunit/php-token-stream", + "version": "1.4.11", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7", + "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-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": "2017-02-27T10:12:30+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "4.8.36", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "46023de9a91eec7dfb06cc56cb4e260017298517" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/46023de9a91eec7dfb06cc56cb4e260017298517", + "reference": "46023de9a91eec7dfb06cc56cb4e260017298517", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=5.3.3", + "phpspec/prophecy": "^1.3.1", + "phpunit/php-code-coverage": "~2.1", + "phpunit/php-file-iterator": "~1.4", + "phpunit/php-text-template": "~1.2", + "phpunit/php-timer": "^1.0.6", + "phpunit/phpunit-mock-objects": "~2.3", + "sebastian/comparator": "~1.2.2", + "sebastian/diff": "~1.2", + "sebastian/environment": "~1.3", + "sebastian/exporter": "~1.2", + "sebastian/global-state": "~1.0", + "sebastian/version": "~1.0", + "symfony/yaml": "~2.1|~3.0" + }, + "suggest": { + "phpunit/php-invoker": "~1.1" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.8.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": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2017-06-21T08:07:12+00:00" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "2.3.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": ">=5.3.3", + "phpunit/php-text-template": "~1.2", + "sebastian/exporter": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2015-10-02T06:51:40+00:00" + }, + { + "name": "sebastian/comparator", + "version": "1.2.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", + "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/diff": "~1.2", + "sebastian/exporter": "~1.2 || ~2.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.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" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "http://www.github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2017-01-29T09:50:25+00:00" + }, + { + "name": "sebastian/diff", + "version": "1.4.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", + "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-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" + ], + "time": "2017-05-22T07:24:03+00:00" + }, + { + "name": "sebastian/environment", + "version": "1.3.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/be2c607e43ce4c89ecd60e75c6a85c126e754aea", + "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8 || ^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-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": "2016-08-18T05:49:44+00:00" + }, + { + "name": "sebastian/exporter", + "version": "1.2.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4", + "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/recursion-context": "~1.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.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": "2016-06-17T09:04:28+00:00" + }, + { + "name": "sebastian/global-state", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2015-10-12T03:26:01+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b19cc3298482a335a95f3016d2f8a6950f0fbcd7", + "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "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": "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": "2016-10-03T07:41:43+00:00" + }, + { + "name": "sebastian/version", + "version": "1.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", + "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", + "shasum": "" + }, + "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": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2015-06-21T13:59:46+00:00" + }, + { + "name": "symfony/yaml", + "version": "v3.3.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "0938408c4faa518d95230deabb5f595bf0de31b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/0938408c4faa518d95230deabb5f595bf0de31b9", + "reference": "0938408c4faa518d95230deabb5f595bf0de31b9", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "require-dev": { + "symfony/console": "~2.8|~3.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2017-11-10T18:26:04+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", + "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "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": "2016-11-23T20:04:58+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=7.0" + }, + "platform-dev": [] +} From 5545ac030ca80f35b4b54c692253db47987c8236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sun, 26 Nov 2017 00:30:52 +0100 Subject: [PATCH 06/27] my tests --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5f0b506..48f78b0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ composer.phar .DS_Store test.php *~ +/devel/ ### Composer template /vendor/ From 3d3f1595f4fccd767641b29c80d8b0376f83d291 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sun, 26 Nov 2017 00:31:14 +0100 Subject: [PATCH 07/27] Connection refactor --- src/Pecee/Pixie/Connection.php | 113 +++++++++++++++++---------------- 1 file changed, 58 insertions(+), 55 deletions(-) diff --git a/src/Pecee/Pixie/Connection.php b/src/Pecee/Pixie/Connection.php index 7e36f22..c3df741 100644 --- a/src/Pecee/Pixie/Connection.php +++ b/src/Pecee/Pixie/Connection.php @@ -13,60 +13,58 @@ class Connection { + /** + * @var Connection + */ + protected static $storedConnection; /** * @var Container */ protected $container; - /** * Name of DB adapter (i.e. Mysql, Pgsql, Sqlite) + * * @var string */ protected $adapter; - /** * @var array */ protected $adapterConfig; - /** * @var \PDO */ protected $pdoInstance; - - /** - * @var Connection - */ - protected static $storedConnection; - /** * @var EventHandler */ protected $eventHandler; /** - * @param string $adapter - * @param array $adapterConfig + * @param string $adapter + * @param array $adapterConfig * @param Container|null $container */ - public function __construct($adapter, array $adapterConfig, Container $container = null) + public function __construct(string $adapter, array $adapterConfig, Container $container = null) { - $container = $container ?: new Container(); + $this->container = $container ?? new Container(); - $this->container = $container; - - $this->setAdapter($adapter)->setAdapterConfig($adapterConfig)->connect(); + $this + ->setAdapter($adapter) + ->setAdapterConfig($adapterConfig) + ->connect() + ; // Create event dependency $this->eventHandler = $this->container->build(EventHandler::class); } /** - * Returns an instance of Query Builder + * @return Connection */ - public function getQueryBuilder() + public static function getStoredConnection(): Connection { - return $this->container->build(QueryBuilderHandler::class, [$this]); + return static::$storedConnection; } /** @@ -90,83 +88,88 @@ protected function connect() } /** - * @param \PDO $pdo - * @return static + * @return string */ - public function setPdoInstance(\PDO $pdo) + public function getAdapter(): string { - $this->pdoInstance = $pdo; - - return $this; + return $this->adapter; } /** - * @return \PDO + * @return array */ - public function getPdoInstance() + public function getAdapterConfig(): array { - return $this->pdoInstance; + return $this->adapterConfig; } /** - * @param string $adapter - * @return static + * @return Container */ - public function setAdapter($adapter) + public function getContainer(): Container { - $this->adapter = $adapter; - - return $this; + return $this->container; } /** - * @return string + * @return EventHandler */ - public function getAdapter() + public function getEventHandler(): EventHandler { - return $this->adapter; + return $this->eventHandler; } /** - * @param array $adapterConfig - * @return static + * @return \PDO */ - public function setAdapterConfig(array $adapterConfig) + public function getPdoInstance(): \PDO { - $this->adapterConfig = $adapterConfig; - - return $this; + return $this->pdoInstance; } /** - * @return array + * Returns an instance of Query Builder + * + * @return QueryBuilderHandler */ - public function getAdapterConfig() + public function getQueryBuilder(): QueryBuilderHandler { - return $this->adapterConfig; + return $this->container->build(QueryBuilderHandler::class, [$this]); } /** - * @return Container + * @param string $adapter + * + * @return \Pecee\Pixie\Connection */ - public function getContainer() + public function setAdapter(string $adapter): Connection { - return $this->container; + $this->adapter = $adapter; + + return $this; } /** - * @return EventHandler + * @param array $adapterConfig + * + * @return \Pecee\Pixie\Connection */ - public function getEventHandler() + public function setAdapterConfig(array $adapterConfig): Connection { - return $this->eventHandler; + $this->adapterConfig = $adapterConfig; + + return $this; } /** - * @return Connection + * @param \PDO $pdo + * + * @return \Pecee\Pixie\Connection */ - public static function getStoredConnection() + public function setPdoInstance(\PDO $pdo): Connection { - return static::$storedConnection; + $this->pdoInstance = $pdo; + + return $this; } } From 4060649b07e18cb67433c791ae1b28f6bc25bd95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sun, 26 Nov 2017 01:14:18 +0100 Subject: [PATCH 08/27] EventHandler refactor --- src/Pecee/Pixie/EventHandler.php | 110 +++++++++++++++++-------------- 1 file changed, 59 insertions(+), 51 deletions(-) diff --git a/src/Pecee/Pixie/EventHandler.php b/src/Pecee/Pixie/EventHandler.php index ed82776..4dce129 100644 --- a/src/Pecee/Pixie/EventHandler.php +++ b/src/Pecee/Pixie/EventHandler.php @@ -12,6 +12,10 @@ */ class EventHandler { + /** + * Fake table name for any table events + */ + const TABLE_ANY = ':any'; /** * @var array */ @@ -23,21 +27,53 @@ class EventHandler protected $firedEvents = []; /** - * @return array + * @param QueryBuilderHandler $queryBuilder + * @param string $event + * + * @return mixed */ - public function getEvents() + public function fireEvents(QueryBuilderHandler $queryBuilder, string $event) { - return $this->events; + $statements = $queryBuilder->getStatements(); + $tables = isset($statements['tables']) ? $statements['tables'] : []; + + // Events added with :any will be fired in case of any table, + // we are adding :any as a fake table at the beginning. + array_unshift($tables, static::TABLE_ANY); + + $handlerParams = func_get_args(); + unset($handlerParams[1]); + + // Fire all events + foreach ($tables as $table) { + // Fire before events for :any table + $action = $this->getEvent($event, $table); + if ($action !== null) { + + // Make an event id, with event type and table + $eventId = $event . $table; + + // Fire event and add to fired list + $this->firedEvents[] = $eventId; + $result = call_user_func_array($action, $handlerParams); + if ($result !== null) { + return $result; + } + } + } + + return null; } /** - * @param string $event - * @param string|null $table - * @return callable|null + * @param string $event + * @param string|Raw|null $table + * + * @return \Closure|null */ - public function getEvent($event, $table = null) + public function getEvent(string $event, $table = null) { - $table = $table ?: ':any'; + $table = $table ?? static::TABLE_ANY; if ($table instanceof Raw) { return null; @@ -59,14 +95,23 @@ public function getEvent($event, $table = null) } /** - * @param string $event - * @param string $table + * @return array + */ + public function getEvents(): array + { + return $this->events; + } + + /** + * @param string $event + * @param string $table * @param \Closure $action + * * @return void */ - public function registerEvent($event, $table, \Closure $action) + public function registerEvent(string $event, string $table = null, \Closure $action) { - $table = $table ?: ':any'; + $table = $table ?? static::TABLE_ANY; $this->events[$table][$event] = $action; } @@ -74,49 +119,12 @@ public function registerEvent($event, $table, \Closure $action) /** * @param string $event * @param string $table + * * @return void */ public function removeEvent($event, $table = null) { - $table = $table ?: 'any'; + $table = $table ?? static::TABLE_ANY; unset($this->events[$table][$event]); } - - /** - * @param QueryBuilderHandler $queryBuilder - * @param string $event - * @return mixed|null - */ - public function fireEvents($queryBuilder, $event) - { - $statements = $queryBuilder->getStatements(); - $tables = isset($statements['tables']) ? $statements['tables'] : []; - - // Events added with :any will be fired in case of any table, - // we are adding :any as a fake table at the beginning. - array_unshift($tables, ':any'); - - $handlerParams = func_get_args(); - unset($handlerParams[1]); - - // Fire all events - foreach ($tables as $table) { - // Fire before events for :any table - $action = $this->getEvent($event, $table); - if ($action !== null) { - - // Make an event id, with event type and table - $eventId = $event . $table; - - // Fire event and add to fired list - $this->firedEvents[] = $eventId; - $result = call_user_func_array($action, $handlerParams); - if ($result !== null) { - return $result; - } - } - } - - return null; - } } From 4525d6800d9c63239bab2a02efab5e315fff1b26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sun, 26 Nov 2017 01:14:41 +0100 Subject: [PATCH 09/27] QueryBuilderHandler refactor --- .../QueryBuilder/QueryBuilderHandler.php | 1333 +++++++++-------- 1 file changed, 699 insertions(+), 634 deletions(-) diff --git a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php index dc6a5ed..76143a0 100644 --- a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php +++ b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php @@ -70,18 +70,20 @@ class QueryBuilderHandler /** * @param \Pecee\Pixie\Connection|null $connection + * * @throws \Pecee\Pixie\Exception */ public function __construct(Connection $connection = null) { - if ($connection === null && ($connection = Connection::getStoredConnection()) === false) { + $this->connection = $connection ?? Connection::getStoredConnection(); + + if (is_null($this->connection)) { throw new Exception('No database connection found.', 1); } - $this->connection = $connection; - $this->container = $this->connection->getContainer(); - $this->pdo = $this->connection->getPdoInstance(); - $this->adapter = $this->connection->getAdapter(); + $this->container = $this->connection->getContainer(); + $this->pdo = $this->connection->getPdoInstance(); + $this->adapter = $this->connection->getAdapter(); $this->adapterConfig = $this->connection->getAdapterConfig(); if (isset($this->adapterConfig['prefix']) === true) { @@ -94,62 +96,110 @@ public function __construct(Connection $connection = null) [$this->connection] ); - $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); } /** - * Add fetch parameters to the PDO-query. + * Add new statement to statement-list * - * @param mixed $parameters ... - * @return static + * @param string $key + * @param mixed $value */ - public function setFetchMode($parameters = null) + protected function addStatement(string $key, $value) { - $this->fetchParameters = func_get_args(); - - return $this; + if (array_key_exists($key, $this->statements) === false) { + $this->statements[$key] = (array)$value; + } else { + $this->statements[$key] = array_merge($this->statements[$key], (array)$value); + } } /** - * Fetch query results as object of specified type + * Add table prefix (if given) on given string. * - * @param string $className - * @param array $constructorArgs - * @return QueryBuilderHandler - */ - public function asObject($className, array $constructorArgs = []) - { - return $this->setFetchMode(\PDO::FETCH_CLASS, $className, $constructorArgs); - } - - /** - * Creates and returns new query. + * @param string|array|Raw|\Closure $values + * @param bool $tableFieldMix If we have mixes of field and table names with a "." * - * @param \Pecee\Pixie\Connection|null $connection - * @throws \Pecee\Pixie\Exception - * @return static + * @return array|string */ - public function newQuery(Connection $connection = null) + public function addTablePrefix($values, bool $tableFieldMix = true) { - if ($connection === null) { - $connection = $this->connection; + if ($this->tablePrefix === null) { + return $values; } - return new static($connection); + // $value will be an array and we will add prefix to all table names + // If supplied value is not an array then make it one + + $single = false; + if (is_array($values) === false) { + $values = [$values]; + + // We had single value, so should return a single value + $single = true; + } + + $return = []; + + foreach ($values as $key => $value) { + // It's a raw query, just add it to our return array and continue next + if ($value instanceof Raw || $value instanceof \Closure) { + $return[$key] = $value; + continue; + } + + // If key is not integer, it is likely a alias mapping, so we need to change prefix target + $target = &$value; + + if (is_int($key) === false) { + $target = &$key; + } + + if ($tableFieldMix === false || ($tableFieldMix && strpos($target, '.') !== false)) { + $target = $this->tablePrefix . $target; + } + + $return[$key] = $value; + } + + // If we had single value then we should return a single value (end value of the array) + return $single ? end($return) : $return; } /** - * Performs query. + * Performs special queries like COUNT, SUM etc based on the current query. * - * @param string $sql - * @param array $bindings - * @return static + * @param string $type + * + * @throws Exception + * @return int */ - public function query($sql, array $bindings = []) + protected function aggregate(string $type) { - list($this->pdoStatement) = $this->statement($sql, $bindings); + // Get the current selects + $mainSelects = isset($this->statements['selects']) ? $this->statements['selects'] : null; - return $this; + // Replace select with a scalar value like `count` + $this->statements['selects'] = [$this->raw($type . '(*) AS `field`')]; + $row = $this->get(); + + // Set the select as it was + if ($mainSelects !== null) { + $this->statements['selects'] = $mainSelects; + } else { + unset($this->statements['selects']); + } + + if (isset($row[0]) === true) { + if (is_array($row[0]) === true) { + return (int)$row[0]['field']; + } + if (is_object($row[0]) === true) { + return (int)$row[0]->field; + } + } + + return 0; } /** @@ -158,9 +208,10 @@ public function query($sql, array $bindings = []) * * @param string $table * @param string $alias + * * @return QueryBuilderHandler */ - public function alias($table, $alias) + public function alias(string $table, string $alias) { $this->statements['aliases'][$this->tablePrefix . $table] = strtolower($alias); @@ -168,197 +219,238 @@ public function alias($table, $alias) } /** - * Add or change table alias + * Fetch query results as object of specified type * - * Example: table AS alias + * @param string $className + * @param array $constructorArgs * - * @deprecated This method will be removed in the near future, please use QueryBuilderHandler::alias instead. - * @see QueryBuilderHandler::alias - * @param string $table - * @param string $alias * @return QueryBuilderHandler */ - public function prefix($table, $alias) + public function asObject(string $className, array $constructorArgs = []): QueryBuilderHandler { - return $this->alias($table, $alias); + return $this->setFetchMode(\PDO::FETCH_CLASS, $className, $constructorArgs); } /** - * Parses correct data-type for PDO parameter. + * Get count of rows * - * @param mixed $value - * @return int PDO-parameter type + * @throws Exception + * @return int */ - protected function parseDataType($value) + public function count(): int { - if (is_int($value) === true) { - return PDO::PARAM_INT; - } + // Get the current statements + $originalStatements = $this->statements; - if (is_bool($value) === true) { - return PDO::PARAM_BOOL; - } + unset($this->statements['orderBys'], $this->statements['limit'], $this->statements['offset']); - return PDO::PARAM_STR; + $count = $this->aggregate('count'); + $this->statements = $originalStatements; + + return $count; } /** - * Execute statement + * Forms delete on the current query. * - * @param string $sql - * @param array $bindings - * @return array PDOStatement and execution time as float + * @return \PDOStatement + * @throws Exception */ - public function statement($sql, array $bindings = []) + public function delete(): \PDOStatement { - $start = microtime(true); - - $pdoStatement = $this->pdo->prepare($sql); + /* @var $response \PDOStatement */ + $queryObject = $this->getQuery('delete'); - foreach ($bindings as $key => $value) { - $pdoStatement->bindValue( - is_int($key) ? $key + 1 : $key, - $value, - $this->parseDataType($value) - ); - } + $this->fireEvents('before-delete', $queryObject); - $pdoStatement->execute(); + list($response, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings()); + $this->fireEvents('after-delete', $queryObject, $executionTime); - return [$pdoStatement, microtime(true) - $start]; + return $response; } /** - * Get all rows + * Performs insert + * + * @param array $data + * @param string $type * * @throws Exception - * @return \stdClass[] + * @return array|string|null */ - public function get() + private function doInsert(array $data, string $type) { - $queryObject = null; - $executionTime = 0; + // If first value is not an array - it's not a batch insert + if (is_array(current($data)) === false) { + $queryObject = $this->getQuery($type, $data); - if ($this->pdoStatement === null) { - $queryObject = $this->getQuery(); - list($this->pdoStatement, $executionTime) = $this->statement( - $queryObject->getSql(), - $queryObject->getBindings() - ); + $this->fireEvents('before-insert', $queryObject); + /** + * @var $result \PDOStatement + * @var $executionTime float + */ + list($result, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings()); + $return = $result->rowCount() === 1 ? $this->pdo->lastInsertId() : null; + $this->fireEvents('after-insert', $queryObject, $return, $executionTime); + + return $return; } - $start = microtime(true); - $this->fireEvents('before-select', $queryObject); - $result = call_user_func_array([$this->pdoStatement, 'fetchAll'], $this->fetchParameters); - $executionTime += microtime(true) - $start; - $this->pdoStatement = null; - $this->fireEvents('after-select', $queryObject, $result, $executionTime); + // Perform batch insert - return $result; + $return = []; + foreach ($data as $subData) { + $queryObject = $this->getQuery($type, $subData); + + $this->fireEvents('before-insert', $queryObject); + list($result, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings()); + $result = $result->rowCount() === 1 ? $this->pdo->lastInsertId() : null; + $this->fireEvents('after-insert', $queryObject, $result, $executionTime); + + $return[] = $result; + } + + return $return; } /** - * Returns the first row + * Find by value and field name. + * + * @param string|int|float $value + * @param string $fieldName * * @throws Exception - * @return \stdClass|null + * @return null|\stdClass */ - public function first() + public function find($value, $fieldName = 'id') { - $result = $this->limit(1)->get(); - - return ($result !== null && count($result) > 0) ? $result[0] : null; + return $this->where($fieldName, '=', $value)->first(); } /** * Find all by field name and value * - * @param string $fieldName + * @param string $fieldName * @param string|int|float $value + * * @throws Exception * @return \stdClass[] */ - public function findAll($fieldName, $value) + public function findAll(string $fieldName, $value) { return $this->where($fieldName, '=', $value)->get(); } /** - * Find by value and field name. + * Fires event by given event name * - * @param string|int|float $value - * @param string $fieldName - * @throws Exception - * @return null|\stdClass + * @param string $name + * @param ... $parameters + * + * @return mixed|null */ - public function find($value, $fieldName = 'id') + public function fireEvents($name, $parameters = null) { - return $this->where($fieldName, '=', $value)->first(); + $params = func_get_args(); + array_unshift($params, $this); + + return call_user_func_array([$this->connection->getEventHandler(), 'fireEvents'], $params); } /** - * Get count of rows + * Returns the first row * * @throws Exception - * @return int + * @return \stdClass|null */ - public function count() + public function first() { - // Get the current statements - $originalStatements = $this->statements; - - unset($this->statements['orderBys'], $this->statements['limit'], $this->statements['offset']); - - $count = $this->aggregate('count'); - $this->statements = $originalStatements; + $result = $this->limit(1)->get(); - return $count; + return ($result !== null && count($result) > 0) ? $result[0] : null; } /** - * Performs special queries like COUNT, SUM etc based on the current query. + * Adds FROM statement to the current query. * - * @param string $type - * @throws Exception - * @return int + * @param string|array $tables + * + * @return static */ - protected function aggregate($type) + public function from($tables) { - // Get the current selects - $mainSelects = isset($this->statements['selects']) ? $this->statements['selects'] : null; + if (is_array($tables) === false) { + $tables = func_get_args(); + } - // Replace select with a scalar value like `count` - $this->statements['selects'] = [$this->raw($type . '(*) AS `field`')]; - $row = $this->get(); + $tables = $this->addTablePrefix($tables, false); + $this->addStatement('tables', $tables); - // Set the select as it was - if ($mainSelects !== null) { - $this->statements['selects'] = $mainSelects; - } else { - unset($this->statements['selects']); - } + return $this; + } - if (isset($row[0]) === true) { - if (is_array($row[0]) === true) { - return (int)$row[0]['field']; - } - if (is_object($row[0]) === true) { - return (int)$row[0]->field; - } + /** + * Get all rows + * + * @throws Exception + * @return \stdClass[] + */ + public function get() + { + $queryObject = null; + $executionTime = 0; + + if ($this->pdoStatement === null) { + $queryObject = $this->getQuery(); + list($this->pdoStatement, $executionTime) = $this->statement( + $queryObject->getSql(), + $queryObject->getBindings() + ); } - return 0; + $start = microtime(true); + $this->fireEvents('before-select', $queryObject); + $result = call_user_func_array([$this->pdoStatement, 'fetchAll'], $this->fetchParameters); + $executionTime += microtime(true) - $start; + $this->pdoStatement = null; + $this->fireEvents('after-select', $queryObject, $result, $executionTime); + + return $result; + } + + /** + * Get connection object + * + * @return Connection + */ + public function getConnection() + { + return $this->connection; + } + + /** + * Get event by event name + * + * @param string $name + * @param string|null $table + * + * @return \Closure|null + */ + public function getEvent(string $name, string $table = null) + { + return $this->connection->getEventHandler()->getEvent($name, $table); } /** * Returns Query-object. * - * @param string $type + * @param string $type * @param array|mixed|null $dataToBePassed + * * @return QueryObject * @throws Exception */ - public function getQuery($type = 'select', $dataToBePassed = null) + public function getQuery(string $type = 'select', $dataToBePassed = null): QueryObject { $allowedTypes = [ 'select', @@ -383,71 +475,79 @@ public function getQuery($type = 'select', $dataToBePassed = null) } /** - * Performs new sub-query. - * Call this method when you want to add a new sub-query in your where etc. + * Returns statements * - * @param QueryBuilderHandler $queryBuilder - * @param string|null $alias - * @throws Exception - * @return Raw + * @return array */ - public function subQuery(QueryBuilderHandler $queryBuilder, $alias = null) + public function getStatements(): array { - $sql = '(' . $queryBuilder->getQuery()->getRawSql() . ')'; - if ($alias !== null) { - $sql = $sql . ' AS ' . $this->adapterInstance->wrapSanitizer($alias); - } - - return $queryBuilder->raw($sql); + return $this->statements; } /** - * Performs insert + * Adds GROUP BY to the current query. * - * @param array $data - * @param string $type - * @throws Exception - * @return array|string + * @param string|Raw|\Closure|array $field + * + * @return \Pecee\Pixie\QueryBuilder\QueryBuilderHandler */ - private function doInsert($data, $type) + public function groupBy($field) { - // If first value is not an array - it's not a batch insert - if (is_array(current($data)) === false) { - $queryObject = $this->getQuery($type, $data); - - $this->fireEvents('before-insert', $queryObject); - list($result, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings()); - $return = $result->rowCount() === 1 ? $this->pdo->lastInsertId() : null; - $this->fireEvents('after-insert', $queryObject, $return, $executionTime); - - return $return; + if (($field instanceof Raw) === false) { + $field = $this->addTablePrefix($field); } - // Perform batch insert + if (is_array($field) === true) { + $this->statements['groupBys'] = array_merge($this->statements['groupBys'], $field); + } else { + $this->statements['groupBys'][] = $field; + } - $return = []; - foreach ($data as $subData) { - $queryObject = $this->getQuery($type, $subData); + return $this; + } - $this->fireEvents('before-insert', $queryObject); - list($result, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings()); - $result = $result->rowCount() === 1 ? $this->pdo->lastInsertId() : null; - $this->fireEvents('after-insert', $queryObject, $result, $executionTime); + /** + * Adds HAVING statement to the current query. + * + * @param string|Raw|\Closure $key + * @param string|mixed $operator + * @param string|mixed $value + * @param string $joiner + * + * @return \Pecee\Pixie\QueryBuilder\QueryBuilderHandler + */ + public function having($key, $operator, $value, $joiner = 'AND') + { + $key = $this->addTablePrefix($key); + $this->statements['havings'][] = compact('key', 'operator', 'value', 'joiner'); - $return[] = $result; - } + return $this; + } - return $return; + /** + * Adds new INNER JOIN statement to the current query. + * + * @param string|Raw|\Closure $table + * @param string|Raw|\Closure $key + * @param string|mixed|null $operator + * @param string|Raw|\Closure|null $value + * + * @return static + */ + public function innerJoin($table, $key, $operator = null, $value = null) + { + return $this->join($table, $key, $operator, $value); } /** * Insert key/value array * * @param array $data + * * @throws Exception * @return array|string */ - public function insert($data) + public function insert(array $data) { return $this->doInsert($data, 'insert'); } @@ -456,6 +556,7 @@ public function insert($data) * Insert with ignore key/value array * * @param array $data + * * @throws Exception * @return array|string */ @@ -465,188 +566,239 @@ public function insertIgnore($data) } /** - * Replace key/value array + * Adds new JOIN statement to the current query. * - * @param array $data - * @throws Exception - * @return array|string - */ - public function replace($data) - { - return $this->doInsert($data, 'replace'); - } - - /** - * Update key/value array + * @param string|Raw|\Closure $table + * @param string|Raw|\Closure $key + * @param string|null $operator + * @param string|Raw|\Closure $value + * @param string $type * - * @param array $data - * @throws Exception - * @return \PDOStatement + * @return static */ - public function update($data) + public function join($table, $key, $operator = null, $value = null, $type = 'inner') { + if (($key instanceof \Closure) === false) { + $key = function (JoinBuilder $joinBuilder) use ($key, $operator, $value) { + $joinBuilder->on($key, $operator, $value); + }; + } + /** - * @var $response \PDOStatement + * Build a new JoinBuilder class, keep it by reference so any changes made + * in the closure should reflect here */ - $queryObject = $this->getQuery('update', $data); - $this->fireEvents('before-update', $queryObject); + $joinBuilder = $this->container->build(JoinBuilder::class, [$this->connection]); - list($response, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings()); - $this->fireEvents('after-update', $queryObject, $executionTime); + // Call the closure with our new joinBuilder object + $key($joinBuilder); + $table = $this->addTablePrefix($table, false); - return $response; + // Get the criteria only query from the joinBuilder object + $this->statements['joins'][] = compact('type', 'table', 'joinBuilder'); + + return $this; } /** - * Update or insert key/value array + * Adds new LEFT JOIN statement to the current query. * - * @param array $data - * @return array|\PDOStatement|string - * @throws Exception + * @param string|Raw|\Closure $table + * @param string|Raw|\Closure $key + * @param string|null $operator + * @param string|Raw|\Closure|null $value + * + * @return static */ - public function updateOrInsert($data) + public function leftJoin($table, $key, $operator = null, $value = null) { - if ($this->first() !== null) { - return $this->update($data); - } - - return $this->insert($data); + return $this->join($table, $key, $operator, $value, 'left'); } /** - * Add on duplicate key statement. + * Adds LIMIT statement to the current query. + * + * @param int $limit * - * @param string $data * @return static */ - public function onDuplicateKeyUpdate($data) + public function limit($limit) { - $this->addStatement('onduplicate', $data); + $this->statements['limit'] = $limit; return $this; } /** - * Forms delete on the current query. + * Creates and returns new query. * - * @return \PDOStatement - * @throws Exception + * @param \Pecee\Pixie\Connection|null $connection + * + * @throws \Pecee\Pixie\Exception + * @return static */ - public function delete() + public function newQuery(Connection $connection = null) { - /* @var $response \PDOStatement */ - $queryObject = $this->getQuery('delete'); + if ($connection === null) { + $connection = $this->connection; + } - $this->fireEvents('before-delete', $queryObject); + return new static($connection); + } - list($response, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings()); - $this->fireEvents('after-delete', $queryObject, $executionTime); + /** + * Adds OFFSET statement to the current query. + * + * @param int $offset + * + * @return static $this + */ + public function offset($offset) + { + $this->statements['offset'] = $offset; - return $response; + return $this; } /** - * Sets the table that the query is using + * Add on duplicate key statement. + * + * @param string $data * - * @param string|array $tables Single table or multiple tables as an array or as multiple parameters - * @throws Exception * @return static */ - public function table($tables) + public function onDuplicateKeyUpdate($data) { - if (is_array($tables) === false) { - // Because a single table is converted to an array anyways, this makes sense. - $tables = func_get_args(); - } + $this->addStatement('onduplicate', $data); - $instance = new static($this->connection); - $tables = $this->addTablePrefix($tables, false); - $instance->addStatement('tables', $tables); + return $this; + } - return $instance; + /** + * Adds OR HAVING statement to the current query. + * + * @param string|Raw|\Closure $key + * @param string|Raw|\Closure $operator + * @param mixed|Raw|\Closure|null $value + * + * @return static + */ + public function orHaving($key, $operator, $value) + { + return $this->having($key, $operator, $value, 'OR'); } /** - * Adds FROM statement to the current query. + * Adds OR WHERE statement to the current query. + * + * @param string|Raw|\Closure $key + * @param string|null $operator + * @param mixed|Raw|\Closure|null $value * - * @param string|array $tables * @return static */ - public function from($tables) + public function orWhere($key, $operator = null, $value = null) { - if (is_array($tables) === false) { - $tables = func_get_args(); + // If two params are given then assume operator is = + if (func_num_args() === 2) { + $value = $operator; + $operator = '='; } - $tables = $this->addTablePrefix($tables, false); - $this->addStatement('tables', $tables); - - return $this; + return $this->whereHandler($key, $operator, $value, 'OR'); } /** - * Adds fields to select on the current query (defaults is all). - * You can use key/value array to create alias. - * Sub-queries and raw-objects are also supported. + * Adds OR WHERE BETWEEN statement to the current query. * - * Example: ['field' => 'alias'] will become `field` AS `alias` + * @param string|Raw|\Closure $key + * @param string|integer|float $valueFrom + * @param string|integer|float $valueTo * - * @param string|array $fields,... * @return static */ - public function select($fields) + public function orWhereBetween($key, $valueFrom, $valueTo) { - if (is_array($fields) === false) { - $fields = func_get_args(); - } - - $fields = $this->addTablePrefix($fields); - $this->addStatement('selects', $fields); + return $this->whereHandler($key, 'BETWEEN', [$valueFrom, $valueTo], 'OR'); + } - return $this; + /** + * Adds OR WHERE IN statement to the current query. + * + * @param string|Raw|\Closure $key + * @param array|Raw|\Closure $values + * + * @return static + */ + public function orWhereIn($key, $values) + { + return $this->whereHandler($key, 'IN', $values, 'OR'); } /** - * Performs select distinct on the current query. + * Adds OR WHERE NOT statement to the current query. + * + * @param string|Raw|\Closure $key + * @param string|array|Raw|\Closure|null $operator + * @param mixed|Raw|\Closure|null $value * - * @param string|Raw|\Closure|array $fields * @return static */ - public function selectDistinct($fields) + public function orWhereNot($key, $operator = null, $value = null) { - $this->select($fields); - $this->addStatement('distinct', true); + // If two params are given then assume operator is = + if (func_num_args() === 2) { + $value = $operator; + $operator = '='; + } - return $this; + return $this->whereHandler($key, $operator, $value, 'OR NOT'); } /** - * Adds GROUP BY to the current query. + * Adds or WHERE NOT IN statement to the current query. + * + * @param string|Raw|\Closure $key + * @param array|Raw|\Closure $values * - * @param string|Raw|\Closure|array $field * @return static */ - public function groupBy($field) + public function orWhereNotIn($key, $values) { - if (($field instanceof Raw) === false) { - $field = $this->addTablePrefix($field); - } + return $this->whereHandler($key, 'NOT IN', $values, 'OR'); + } - if (is_array($field) === true) { - $this->statements['groupBys'] = array_merge($this->statements['groupBys'], $field); - } else { - $this->statements['groupBys'][] = $field; - } + /** + * Adds OR WHERE NOT NULL statement to the current query. + * + * @param string|Raw|\Closure $key + * + * @return QueryBuilderHandler + */ + public function orWhereNotNull($key) + { + return $this->whereNullHandler($key, 'NOT', 'or'); + } - return $this; + /** + * Adds OR WHERE NULL statement to the current query. + * + * @param string|Raw|\Closure $key + * + * @return QueryBuilderHandler + */ + public function orWhereNull($key) + { + return $this->whereNullHandler($key, '', 'or'); } /** * Adds ORDER BY statement to the current query. * * @param string|Raw|\Closure|array $fields - * @param string $defaultDirection + * @param string $defaultDirection + * * @return static */ public function orderBy($fields, $defaultDirection = 'ASC') @@ -657,11 +809,11 @@ public function orderBy($fields, $defaultDirection = 'ASC') foreach ((array)$fields as $key => $value) { $field = $key; - $type = $value; + $type = $value; if (is_int($key) === true) { $field = $value; - $type = $defaultDirection; + $type = $defaultDirection; } if (($field instanceof Raw) === false) { @@ -675,313 +827,285 @@ public function orderBy($fields, $defaultDirection = 'ASC') } /** - * Adds LIMIT statement to the current query. + * Parses correct data-type for PDO parameter. * - * @param int $limit - * @return static - */ - public function limit($limit) - { - $this->statements['limit'] = $limit; - - return $this; - } - - /** - * Adds OFFSET statement to the current query. + * @param mixed $value * - * @param int $offset - * @return static $this + * @return int PDO-parameter type */ - public function offset($offset) + protected function parseDataType($value) { - $this->statements['offset'] = $offset; + if (is_int($value) === true) { + return PDO::PARAM_INT; + } - return $this; + if (is_bool($value) === true) { + return PDO::PARAM_BOOL; + } + + return PDO::PARAM_STR; } /** - * Adds HAVING statement to the current query. + * Return PDO instance * - * @param string|Raw|\Closure $key - * @param string|mixed $operator - * @param string|mixed $value - * @param string $joiner - * @return static + * @return PDO */ - public function having($key, $operator, $value, $joiner = 'AND') + public function pdo() { - $key = $this->addTablePrefix($key); - $this->statements['havings'][] = compact('key', 'operator', 'value', 'joiner'); - - return $this; + return $this->pdo; } /** - * Adds OR HAVING statement to the current query. + * Add or change table alias * - * @param string|Raw|\Closure $key - * @param string|Raw|\Closure $operator - * @param mixed|Raw|\Closure|null $value - * @return static + * Example: table AS alias + * + * @deprecated This method will be removed in the near future, please use QueryBuilderHandler::alias instead. + * @see QueryBuilderHandler::alias + * + * @param string $table + * @param string $alias + * + * @return QueryBuilderHandler */ - public function orHaving($key, $operator, $value) + public function prefix($table, $alias) { - return $this->having($key, $operator, $value, 'OR'); + return $this->alias($table, $alias); } /** - * Adds WHERE statement to the current query. + * Performs query. + * + * @param string $sql + * @param array $bindings * - * @param string|Raw|\Closure $key - * @param string|Raw|\Closure|null $operator - * @param mixed|Raw|\Closure|null $value * @return static */ - public function where($key, $operator = null, $value = null) + public function query($sql, array $bindings = []) { - // If two params are given then assume operator is = - if (func_num_args() === 2) { - $value = $operator; - $operator = '='; - } - - if (is_bool($value) === true) { - $value = (int)$value; - } + list($this->pdoStatement) = $this->statement($sql, $bindings); - return $this->whereHandler($key, $operator, $value); + return $this; } /** - * Adds OR WHERE statement to the current query. + * Adds a raw string to the current query. + * This query will be ignored from any parsing or formatting by the Query builder + * and should be used in conjunction with other statements in the query. * - * @param string|Raw|\Closure $key - * @param string|null $operator - * @param mixed|Raw|\Closure|null $value - * @return static + * For example: $qb->where('result', '>', $qb->raw('COUNT(`score`))); + * + * @param string $value + * @param array|null|mixed $bindings ... + * + * @return Raw */ - public function orWhere($key, $operator = null, $value = null) + public function raw($value, $bindings = null) { - // If two params are given then assume operator is = - if (func_num_args() === 2) { - $value = $operator; - $operator = '='; + if (is_array($bindings) === false) { + $bindings = func_get_args(); + array_shift($bindings); } - return $this->whereHandler($key, $operator, $value, 'OR'); + return $this->container->build(Raw::class, [$value, $bindings]); } /** - * Adds WHERE NOT statement to the current query. + * Register new event * - * @param string|Raw|\Closure $key - * @param string|array|Raw|\Closure|null $operator - * @param mixed|Raw|\Closure|null $value - * @return static + * @param string $name + * @param string|null $table + * @param \Closure $action + * + * @return void */ - public function whereNot($key, $operator = null, $value = null) + public function registerEvent($name, $table = null, \Closure $action) { - // If two params are given then assume operator is = - if (func_num_args() === 2) { - $value = $operator; - $operator = '='; - } - - return $this->whereHandler($key, $operator, $value, 'AND NOT'); + $this->connection->getEventHandler()->registerEvent($name, $table, $action); } /** - * Adds OR WHERE NOT statement to the current query. + * Remove event by event-name and/or table * - * @param string|Raw|\Closure $key - * @param string|array|Raw|\Closure|null $operator - * @param mixed|Raw|\Closure|null $value - * @return static + * @param string $name + * @param string|null $table + * + * @return void */ - public function orWhereNot($key, $operator = null, $value = null) + public function removeEvent($name, $table = null) { - // If two params are given then assume operator is = - if (func_num_args() === 2) { - $value = $operator; - $operator = '='; - } - - return $this->whereHandler($key, $operator, $value, 'OR NOT'); + $this->connection->getEventHandler()->removeEvent($name, $table); } /** - * Adds WHERE IN statement to the current query. + * Replace key/value array * - * @param string|Raw|\Closure $key - * @param array|Raw|\Closure $values - * @return static + * @param array $data + * + * @throws Exception + * @return array|string */ - public function whereIn($key, $values) + public function replace($data) { - return $this->whereHandler($key, 'IN', $values); + return $this->doInsert($data, 'replace'); } /** - * Adds OR WHERE NOT IN statement to the current query. + * Adds new right join statement to the current query. + * + * @param string|Raw|\Closure $table + * @param string|Raw|\Closure $key + * @param string|null $operator + * @param string|Raw|\Closure|null $value * - * @param string|Raw|\Closure $key - * @param array|Raw|\Closure $values * @return static */ - public function whereNotIn($key, $values) + public function rightJoin($table, $key, $operator = null, $value = null) { - return $this->whereHandler($key, 'NOT IN', $values); + return $this->join($table, $key, $operator, $value, 'right'); } /** - * Adds OR WHERE IN statement to the current query. + * Adds fields to select on the current query (defaults is all). + * You can use key/value array to create alias. + * Sub-queries and raw-objects are also supported. + * + * Example: ['field' => 'alias'] will become `field` AS `alias` + * + * @param string|array $fields,... * - * @param string|Raw|\Closure $key - * @param array|Raw|\Closure $values * @return static */ - public function orWhereIn($key, $values) + public function select($fields) { - return $this->whereHandler($key, 'IN', $values, 'OR'); + if (is_array($fields) === false) { + $fields = func_get_args(); + } + + $fields = $this->addTablePrefix($fields); + $this->addStatement('selects', $fields); + + return $this; } /** - * Adds or WHERE NOT IN statement to the current query. + * Performs select distinct on the current query. + * + * @param string|Raw|\Closure|array $fields * - * @param string|Raw|\Closure $key - * @param array|Raw|\Closure $values * @return static */ - public function orWhereNotIn($key, $values) + public function selectDistinct($fields) { - return $this->whereHandler($key, 'NOT IN', $values, 'OR'); + $this->select($fields); + $this->addStatement('distinct', true); + + return $this; } /** - * Adds WHERE BETWEEN statement to the current query. + * Set connection object + * + * @param Connection $connection * - * @param string|Raw|\Closure $key - * @param string|integer|float $valueFrom - * @param string|integer|float $valueTo * @return static */ - public function whereBetween($key, $valueFrom, $valueTo) + public function setConnection(Connection $connection) { - return $this->whereHandler($key, 'BETWEEN', [$valueFrom, $valueTo]); + $this->connection = $connection; + + return $this; } /** - * Adds OR WHERE BETWEEN statement to the current query. + * Add fetch parameters to the PDO-query. + * + * @param mixed $parameters ... * - * @param string|Raw|\Closure $key - * @param string|integer|float $valueFrom - * @param string|integer|float $valueTo * @return static */ - public function orWhereBetween($key, $valueFrom, $valueTo) + public function setFetchMode($parameters = null) { - return $this->whereHandler($key, 'BETWEEN', [$valueFrom, $valueTo], 'OR'); - } + $this->fetchParameters = func_get_args(); - /** - * Adds WHERE NULL statement to the current query. - * - * @param string|Raw|\Closure $key - * @return QueryBuilderHandler - */ - public function whereNull($key) - { - return $this->whereNullHandler($key); + return $this; } /** - * Adds WHERE NOT NULL statement to the current query. + * Execute statement * - * @param string|Raw|\Closure $key - * @return QueryBuilderHandler - */ - public function whereNotNull($key) - { - return $this->whereNullHandler($key, 'NOT'); - } - - /** - * Adds OR WHERE NULL statement to the current query. + * @param string $sql + * @param array $bindings * - * @param string|Raw|\Closure $key - * @return QueryBuilderHandler + * @return array PDOStatement and execution time as float */ - public function orWhereNull($key) + public function statement(string $sql, array $bindings = []) { - return $this->whereNullHandler($key, '', 'or'); - } + $start = microtime(true); - /** - * Adds OR WHERE NOT NULL statement to the current query. - * - * @param string|Raw|\Closure $key - * @return QueryBuilderHandler - */ - public function orWhereNotNull($key) - { - return $this->whereNullHandler($key, 'NOT', 'or'); + $pdoStatement = $this->pdo->prepare($sql); + + foreach ($bindings as $key => $value) { + $pdoStatement->bindValue( + is_int($key) ? $key + 1 : $key, + $value, + $this->parseDataType($value) + ); + } + + $pdoStatement->execute(); + + return [$pdoStatement, microtime(true) - $start]; } /** - * Handles WHERE NULL statements. + * Performs new sub-query. + * Call this method when you want to add a new sub-query in your where etc. * - * @param string|Raw|\Closure $key - * @param string $prefix - * @param string $operator - * @return mixed + * @param QueryBuilderHandler $queryBuilder + * @param string|null $alias + * + * @throws Exception + * @return Raw */ - protected function whereNullHandler($key, $prefix = '', $operator = '') + public function subQuery(QueryBuilderHandler $queryBuilder, $alias = null) { - $key = $this->adapterInstance->wrapSanitizer($this->addTablePrefix($key)); + $sql = '(' . $queryBuilder->getQuery()->getRawSql() . ')'; + if ($alias !== null) { + $sql = $sql . ' AS ' . $this->adapterInstance->wrapSanitizer($alias); + } - return $this->{$operator . 'Where'}($this->raw("{$key} IS {$prefix} NULL")); + return $queryBuilder->raw($sql); } /** - * Adds new JOIN statement to the current query. + * Sets the table that the query is using * - * @param string|Raw|\Closure $table - * @param string|Raw|\Closure $key - * @param string|null $operator - * @param string|Raw|\Closure $value - * @param string $type + * @param string|array $tables Single table or multiple tables as an array or as multiple parameters + * + * @throws Exception * @return static */ - public function join($table, $key, $operator = null, $value = null, $type = 'inner') + public function table($tables) { - if (($key instanceof \Closure) === false) { - $key = function (JoinBuilder $joinBuilder) use ($key, $operator, $value) { - $joinBuilder->on($key, $operator, $value); - }; + if (is_array($tables) === false) { + // Because a single table is converted to an array anyways, this makes sense. + $tables = func_get_args(); } - /** - * Build a new JoinBuilder class, keep it by reference so any changes made - * in the closure should reflect here - */ - - $joinBuilder = $this->container->build(JoinBuilder::class, [$this->connection]); - - // Call the closure with our new joinBuilder object - $key($joinBuilder); - $table = $this->addTablePrefix($table, false); - - // Get the criteria only query from the joinBuilder object - $this->statements['joins'][] = compact('type', 'table', 'joinBuilder'); + $instance = new static($this->connection); + $tables = $this->addTablePrefix($tables, false); + $instance->addStatement('tables', $tables); - return $this; + return $instance; } /** * Performs the transaction * * @param \Closure $callback + * * @return static */ public function transaction(\Closure $callback) @@ -1012,243 +1136,184 @@ public function transaction(\Closure $callback) } /** - * Adds new right join statement to the current query. + * Update key/value array * - * @param string|Raw|\Closure $table - * @param string|Raw|\Closure $key - * @param string|null $operator - * @param string|Raw|\Closure|null $value - * @return static - */ - public function rightJoin($table, $key, $operator = null, $value = null) - { - return $this->join($table, $key, $operator, $value, 'right'); - } - - /** - * Adds new LEFT JOIN statement to the current query. + * @param array $data * - * @param string|Raw|\Closure $table - * @param string|Raw|\Closure $key - * @param string|null $operator - * @param string|Raw|\Closure|null $value - * @return static + * @throws Exception + * @return \PDOStatement */ - public function leftJoin($table, $key, $operator = null, $value = null) + public function update($data) { - return $this->join($table, $key, $operator, $value, 'left'); - } + /** + * @var $response \PDOStatement + */ + $queryObject = $this->getQuery('update', $data); - /** - * Adds new INNER JOIN statement to the current query. - * - * @param string|Raw|\Closure $table - * @param string|Raw|\Closure $key - * @param string|mixed|null $operator - * @param string|Raw|\Closure|null $value - * @return static - */ - public function innerJoin($table, $key, $operator = null, $value = null) - { - return $this->join($table, $key, $operator, $value); + $this->fireEvents('before-update', $queryObject); + + list($response, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings()); + $this->fireEvents('after-update', $queryObject, $executionTime); + + return $response; } /** - * Adds a raw string to the current query. - * This query will be ignored from any parsing or formatting by the Query builder - * and should be used in conjunction with other statements in the query. + * Update or insert key/value array * - * For example: $qb->where('result', '>', $qb->raw('COUNT(`score`))); + * @param array $data * - * @param string $value - * @param array|null|mixed $bindings ... - * @return Raw + * @return array|\PDOStatement|string + * @throws Exception */ - public function raw($value, $bindings = null) + public function updateOrInsert($data) { - if(is_array($bindings) === false) { - $bindings = func_get_args(); - array_shift($bindings); + if ($this->first() !== null) { + return $this->update($data); } - return $this->container->build(Raw::class, [$value, $bindings]); + return $this->insert($data); } /** - * Return PDO instance + * Adds WHERE statement to the current query. * - * @return PDO - */ - public function pdo() - { - return $this->pdo; - } - - /** - * Set connection object + * @param string|Raw|\Closure $key + * @param string|Raw|\Closure|null $operator + * @param mixed|Raw|\Closure|null $value * - * @param Connection $connection * @return static */ - public function setConnection(Connection $connection) + public function where($key, $operator = null, $value = null) { - $this->connection = $connection; + // If two params are given then assume operator is = + if (func_num_args() === 2) { + $value = $operator; + $operator = '='; + } - return $this; + if (is_bool($value) === true) { + $value = (int)$value; + } + + return $this->whereHandler($key, $operator, $value); } /** - * Get connection object + * Adds WHERE BETWEEN statement to the current query. * - * @return Connection + * @param string|Raw|\Closure $key + * @param string|integer|float $valueFrom + * @param string|integer|float $valueTo + * + * @return static */ - public function getConnection() + public function whereBetween($key, $valueFrom, $valueTo) { - return $this->connection; + return $this->whereHandler($key, 'BETWEEN', [$valueFrom, $valueTo]); } /** * Handles where statements * - * @param string|Raw|\Closure $key + * @param string|Raw|\Closure $key * @param string|Raw|\Closure|null $operator * @param string|Raw|\Closure|null $value - * @param string $joiner + * @param string $joiner + * * @return static */ protected function whereHandler($key, $operator = null, $value = null, $joiner = 'AND') { - $key = $this->addTablePrefix($key); + $key = $this->addTablePrefix($key); $this->statements['wheres'][] = compact('key', 'operator', 'value', 'joiner'); return $this; } /** - * Add table prefix (if given) on given string. + * Adds WHERE IN statement to the current query. * - * @param string|array|Raw|\Closure $values - * @param bool $tableFieldMix If we have mixes of field and table names with a "." - * @return array|string + * @param string|Raw|\Closure $key + * @param array|Raw|\Closure $values + * + * @return static */ - public function addTablePrefix($values, $tableFieldMix = true) + public function whereIn($key, $values) { - if ($this->tablePrefix === null) { - return $values; - } - - // $value will be an array and we will add prefix to all table names - // If supplied value is not an array then make it one - - $single = false; - if (is_array($values) === false) { - $values = [$values]; - - // We had single value, so should return a single value - $single = true; - } - - $return = []; - - foreach ($values as $key => $value) { - // It's a raw query, just add it to our return array and continue next - if ($value instanceof Raw || $value instanceof \Closure) { - $return[$key] = $value; - continue; - } - - // If key is not integer, it is likely a alias mapping, so we need to change prefix target - $target = &$value; - - if (is_int($key) === false) { - $target = &$key; - } - - if ($tableFieldMix === false || ($tableFieldMix && strpos($target, '.') !== false)) { - $target = $this->tablePrefix . $target; - } - - $return[$key] = $value; - } - - // If we had single value then we should return a single value (end value of the array) - return $single ? end($return) : $return; + return $this->whereHandler($key, 'IN', $values); } /** - * Add new statement to statement-list + * Adds WHERE NOT statement to the current query. * - * @param string $key - * @param mixed $value + * @param string|Raw|\Closure $key + * @param string|array|Raw|\Closure|null $operator + * @param mixed|Raw|\Closure|null $value + * + * @return static */ - protected function addStatement($key, $value) + public function whereNot($key, $operator = null, $value = null) { - if (array_key_exists($key, $this->statements) === false) { - $this->statements[$key] = (array)$value; - } else { - $this->statements[$key] = array_merge($this->statements[$key], (array)$value); + // If two params are given then assume operator is = + if (func_num_args() === 2) { + $value = $operator; + $operator = '='; } - } - /** - * Get event by event name - * - * @param string $name - * @param string|null $table - * @return \Closure|null - */ - public function getEvent($name, $table = null) - { - return $this->connection->getEventHandler()->getEvent($name, $table); + return $this->whereHandler($key, $operator, $value, 'AND NOT'); } /** - * Register new event + * Adds OR WHERE NOT IN statement to the current query. * - * @param string $name - * @param string|null $table - * @param \Closure $action - * @return void + * @param string|Raw|\Closure $key + * @param array|Raw|\Closure $values + * + * @return static */ - public function registerEvent($name, $table = null, \Closure $action) + public function whereNotIn($key, $values) { - $this->connection->getEventHandler()->registerEvent($name, $table, $action); + return $this->whereHandler($key, 'NOT IN', $values); } /** - * Remove event by event-name and/or table + * Adds WHERE NOT NULL statement to the current query. * - * @param string $name - * @param string|null $table - * @return void + * @param string|Raw|\Closure $key + * + * @return QueryBuilderHandler */ - public function removeEvent($name, $table = null) + public function whereNotNull($key) { - $this->connection->getEventHandler()->removeEvent($name, $table); + return $this->whereNullHandler($key, 'NOT'); } /** - * Fires event by given event name + * Adds WHERE NULL statement to the current query. * - * @param string $name - * @param ... $parameters - * @return mixed|null + * @param string|Raw|\Closure $key + * + * @return QueryBuilderHandler */ - public function fireEvents($name, $parameters = null) + public function whereNull($key) { - $params = func_get_args(); - array_unshift($params, $this); - - return call_user_func_array([$this->connection->getEventHandler(), 'fireEvents'], $params); + return $this->whereNullHandler($key); } /** - * Returns statements + * Handles WHERE NULL statements. * - * @return array + * @param string|Raw|\Closure $key + * @param string $prefix + * @param string $operator + * + * @return mixed */ - public function getStatements() + protected function whereNullHandler($key, $prefix = '', $operator = '') { - return $this->statements; + $key = $this->adapterInstance->wrapSanitizer($this->addTablePrefix($key)); + + return $this->{$operator . 'Where'}($this->raw("{$key} IS {$prefix} NULL")); } } From 06d618fe926e587fa6bcbf04f1cf83d50852973e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sun, 26 Nov 2017 14:22:54 +0100 Subject: [PATCH 10/27] syntax, phpDoc --- .../Pixie/ConnectionAdapters/BaseAdapter.php | 2 +- .../QueryBuilder/QueryBuilderHandler.php | 72 ++++++++++++------- src/Pecee/Pixie/QueryBuilder/QueryObject.php | 24 ++++--- src/Pecee/Pixie/QueryBuilder/Raw.php | 22 +++--- src/Pecee/Pixie/QueryBuilder/Transaction.php | 2 + 5 files changed, 75 insertions(+), 47 deletions(-) diff --git a/src/Pecee/Pixie/ConnectionAdapters/BaseAdapter.php b/src/Pecee/Pixie/ConnectionAdapters/BaseAdapter.php index 81266dc..5cfb9d7 100644 --- a/src/Pecee/Pixie/ConnectionAdapters/BaseAdapter.php +++ b/src/Pecee/Pixie/ConnectionAdapters/BaseAdapter.php @@ -28,7 +28,7 @@ public function __construct(Container $container) * @param $config * @return \PDO */ - public function connect($config) + public function connect(array $config) { if (isset($config['options']) === false) { $config['options'] = []; diff --git a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php index 76143a0..6a73bf5 100644 --- a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php +++ b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php @@ -209,7 +209,7 @@ protected function aggregate(string $type) * @param string $table * @param string $alias * - * @return QueryBuilderHandler + * @return static */ public function alias(string $table, string $alias) { @@ -224,7 +224,7 @@ public function alias(string $table, string $alias) * @param string $className * @param array $constructorArgs * - * @return QueryBuilderHandler + * @return static */ public function asObject(string $className, array $constructorArgs = []): QueryBuilderHandler { @@ -393,10 +393,16 @@ public function from($tables) * Get all rows * * @throws Exception - * @return \stdClass[] + * @return object[] */ - public function get() + public function get(): array { + /** + * @var $queryObject \Pecee\Pixie\QueryBuilder\QueryObject + * @var $executionTime float + * @var $start float + * @var $result array + */ $queryObject = null; $executionTime = 0; @@ -423,7 +429,7 @@ public function get() * * @return Connection */ - public function getConnection() + public function getConnection(): Connection { return $this->connection; } @@ -489,7 +495,7 @@ public function getStatements(): array * * @param string|Raw|\Closure|array $field * - * @return \Pecee\Pixie\QueryBuilder\QueryBuilderHandler + * @return static */ public function groupBy($field) { @@ -514,7 +520,7 @@ public function groupBy($field) * @param string|mixed $value * @param string $joiner * - * @return \Pecee\Pixie\QueryBuilder\QueryBuilderHandler + * @return static */ public function having($key, $operator, $value, $joiner = 'AND') { @@ -568,13 +574,29 @@ public function insertIgnore($data) /** * Adds new JOIN statement to the current query. * - * @param string|Raw|\Closure $table - * @param string|Raw|\Closure $key - * @param string|null $operator - * @param string|Raw|\Closure $value - * @param string $type + * @param string|Raw|\Closure|array $table + * @param string|Raw|\Closure $key + * @param string|null $operator + * @param string|Raw|\Closure $value + * @param string $type * * @return static + * ``` + * Examples: + * - basic usage + * ->join('table2', 'table2.person_id', '=', 'table1.id'); + * + * - as alias 'bar' + * ->join(['table2','bar'], 'bar.person_id', '=', 'table1.id'); + * + * - complex usage + * ->join('another_table', function($table) + * { + * $table->on('another_table.person_id', '=', 'my_table.id'); + * $table->on('another_table.person_id2', '=', 'my_table.id2'); + * $table->orOn('another_table.age', '>', $queryBuilder->raw(1)); + * }) + * ``` */ public function join($table, $key, $operator = null, $value = null, $type = 'inner') { @@ -604,10 +626,10 @@ public function join($table, $key, $operator = null, $value = null, $type = 'inn /** * Adds new LEFT JOIN statement to the current query. * - * @param string|Raw|\Closure $table - * @param string|Raw|\Closure $key - * @param string|null $operator - * @param string|Raw|\Closure|null $value + * @param string|Raw|\Closure|array $table + * @param string|Raw|\Closure $key + * @param string|null $operator + * @param string|Raw|\Closure|null $value * * @return static */ @@ -774,7 +796,7 @@ public function orWhereNotIn($key, $values) * * @param string|Raw|\Closure $key * - * @return QueryBuilderHandler + * @return static */ public function orWhereNotNull($key) { @@ -786,7 +808,7 @@ public function orWhereNotNull($key) * * @param string|Raw|\Closure $key * - * @return QueryBuilderHandler + * @return static */ public function orWhereNull($key) { @@ -867,7 +889,7 @@ public function pdo() * @param string $table * @param string $alias * - * @return QueryBuilderHandler + * @return static */ public function prefix($table, $alias) { @@ -954,10 +976,10 @@ public function replace($data) /** * Adds new right join statement to the current query. * - * @param string|Raw|\Closure $table - * @param string|Raw|\Closure $key - * @param string|null $operator - * @param string|Raw|\Closure|null $value + * @param string|Raw|\Closure|array $table + * @param string|Raw|\Closure $key + * @param string|null $operator + * @param string|Raw|\Closure|null $value * * @return static */ @@ -1282,7 +1304,7 @@ public function whereNotIn($key, $values) * * @param string|Raw|\Closure $key * - * @return QueryBuilderHandler + * @return static */ public function whereNotNull($key) { @@ -1294,7 +1316,7 @@ public function whereNotNull($key) * * @param string|Raw|\Closure $key * - * @return QueryBuilderHandler + * @return static */ public function whereNull($key) { diff --git a/src/Pecee/Pixie/QueryBuilder/QueryObject.php b/src/Pecee/Pixie/QueryBuilder/QueryObject.php index 68a5171..4b3dff7 100644 --- a/src/Pecee/Pixie/QueryBuilder/QueryObject.php +++ b/src/Pecee/Pixie/QueryBuilder/QueryObject.php @@ -29,29 +29,30 @@ class QueryObject * QueryObject constructor. * * @param string $sql - * @param array $bindings - * @param \PDO $pdo + * @param array $bindings + * @param \PDO $pdo */ - public function __construct($sql, array $bindings, \PDO $pdo) + public function __construct(string $sql, array $bindings, \PDO $pdo) { - $this->sql = (string)$sql; + $this->sql = $sql; $this->bindings = $bindings; - $this->pdo = $pdo; + $this->pdo = $pdo; } /** * @return array */ - public function getBindings() + public function getBindings(): array { return $this->bindings; } /** * Get the raw/bound sql + * * @return string */ - public function getRawSql() + public function getRawSql(): string { return $this->interpolateQuery($this->sql, $this->bindings); } @@ -59,7 +60,7 @@ public function getRawSql() /** * @return string */ - public function getSql() + public function getSql(): string { return $this->sql; } @@ -71,13 +72,14 @@ public function getSql() * * Reference: http://stackoverflow.com/a/1376838/656489 * - * @param string $query The sql query with parameter placeholders - * @param array $params The array of substitution parameters + * @param string $query The sql query with parameter placeholders + * @param array $params The array of substitution parameters + * * @return string The interpolated query */ protected function interpolateQuery($query, $params) { - $keys = []; + $keys = []; $values = $params; // build a regular expression for each parameter diff --git a/src/Pecee/Pixie/QueryBuilder/Raw.php b/src/Pecee/Pixie/QueryBuilder/Raw.php index ad5dded..fe25846 100644 --- a/src/Pecee/Pixie/QueryBuilder/Raw.php +++ b/src/Pecee/Pixie/QueryBuilder/Raw.php @@ -1,4 +1,5 @@ value = (string)$value; - $this->bindings = (array)$bindings; + $this->value = $value; + $this->bindings = $bindings; } /** - * @return array + * @return string */ - public function getBindings() + public function __toString() { - return $this->bindings; + return (string)$this->value; } /** - * @return string + * @return array */ - public function __toString() + public function getBindings() { - return (string)$this->value; + return $this->bindings; } } diff --git a/src/Pecee/Pixie/QueryBuilder/Transaction.php b/src/Pecee/Pixie/QueryBuilder/Transaction.php index ce86f82..f193f89 100644 --- a/src/Pecee/Pixie/QueryBuilder/Transaction.php +++ b/src/Pecee/Pixie/QueryBuilder/Transaction.php @@ -12,6 +12,7 @@ class Transaction extends QueryBuilderHandler /** * Commit the database changes + * * @throws TransactionHaltException */ public function commit() @@ -22,6 +23,7 @@ public function commit() /** * Rollback the database changes + * * @throws TransactionHaltException */ public function rollback() From 5407b982516853ebe31f6262fdcd23c5ed34937c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sun, 26 Nov 2017 14:27:28 +0100 Subject: [PATCH 11/27] fix --- src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php b/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php index 6e6ab81..2f02d20 100644 --- a/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php +++ b/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php @@ -50,7 +50,7 @@ public function __construct(Connection $connection) public function select(array $statements) { if (array_key_exists('selects', $statements) === false) { - $statements['selects'][] = '*'; + $statements['selects'] = ['*']; } // From From c06b64a4ee7ee719d7d1180e6dbbb59e70ef7ca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sun, 26 Nov 2017 14:27:53 +0100 Subject: [PATCH 12/27] method order --- .../QueryBuilder/Adapters/BaseAdapter.php | 685 +++++++++--------- 1 file changed, 350 insertions(+), 335 deletions(-) diff --git a/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php b/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php index 2f02d20..b3d74a1 100644 --- a/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php +++ b/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php @@ -37,109 +37,245 @@ abstract class BaseAdapter public function __construct(Connection $connection) { $this->connection = $connection; - $this->container = $this->connection->getContainer(); + $this->container = $this->connection->getContainer(); } /** - * Build select query string and bindings + * Array concatenating method, like implode. + * But it does wrap sanitizer and trims last glue + * + * @param array $pieces + * @param string $glue + * @param bool $wrapSanitizer + * + * @return string + */ + protected function arrayStr(array $pieces, $glue = ',', $wrapSanitizer = true) + { + $str = ''; + foreach ($pieces as $key => $piece) { + if ($wrapSanitizer === true) { + $piece = $this->wrapSanitizer($piece); + } + + if (is_int($key) === false) { + $piece = ($wrapSanitizer ? $this->wrapSanitizer($key) : $key) . ' AS ' . $piece; + } + + $str .= $piece . $glue; + } + + return trim($str, $glue); + } + + /** + * Build generic criteria string and bindings from statements, like "a = b and c = ?" * * @param array $statements + * @param bool $bindValues + * * @throws Exception * @return array */ - public function select(array $statements) + protected function buildCriteria(array $statements, $bindValues = true) { - if (array_key_exists('selects', $statements) === false) { - $statements['selects'] = ['*']; - } + $criteria = ''; + $bindings = [[]]; - // From - $fromEnabled = false; - $tables = ''; + foreach ($statements as $statement) { - if (isset($statements['tables']) === true) { - $tables = []; + $key = $this->wrapSanitizer($statement['key']); + $value = $statement['value']; - foreach ((array)$statements['tables'] as $table) { + if ($value === null && $key instanceof \Closure) { - $prefix = isset($statements['aliases'][$table]) ? $statements['aliases'][$table] : null; + /** + * We have a closure, a nested criteria + * Build a new NestedCriteria class, keep it by reference so any changes made in the closure should reflect here + */ - if ($prefix !== null) { - $t = ($table instanceof Raw) ? $table : '`' . $table . '` AS `' . strtolower($prefix) . '`'; + /* @var $nestedCriteria NestedCriteria */ + $nestedCriteria = $this->container->build( + NestedCriteria::class, + [$this->connection] + ); + + // Call the closure with our new nestedCriteria object + $key($nestedCriteria); + + // Get the criteria only query from the nestedCriteria object + $queryObject = $nestedCriteria->getQuery('criteriaOnly', true); + + // Merge the bindings we get from nestedCriteria object + $bindings[] = $queryObject->getBindings(); + + // Append the sql we get from the nestedCriteria object + $criteria .= $statement['joiner'] . ' (' . $queryObject->getSql() . ') '; + + continue; + } + + if (is_array($value) === true) { + + // Where in or between like query + $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator']; + + if ($statement['operator'] === 'BETWEEN') { + $bindings[] = (array)$statement['value']; + $criteria .= ' ? AND ? '; } else { - $t = ($table instanceof Raw) ? $table : '`' . $table . '`'; + $valuePlaceholder = ''; + foreach ((array)$statement['value'] as $subValue) { + $valuePlaceholder .= '?, '; + $bindings[] = (array)$subValue; + } + + $valuePlaceholder = trim($valuePlaceholder, ', '); + $criteria .= ' (' . $valuePlaceholder . ') '; } - $tables[] = $t; + continue; + } - $tables = implode(',', $tables); - $fromEnabled = true; - } + if ($value instanceof Raw) { + $criteria .= "{$statement['joiner']} {$key} {$statement['operator']} $value "; + continue; + } - // SELECT - $selects = $this->arrayStr($statements['selects'], ', '); - // WHERE - list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE'); + // Usual where like criteria + if ($bindValues === false) { - // GROUP BY - $groupBys = $this->arrayStr($statements['groupBys'], ', '); - if ($groupBys !== '' && isset($statements['groupBys']) === true) { - $groupBys = 'GROUP BY ' . $groupBys; - } + // Specially for joins - we are not binding values, lets sanitize then + $value = $this->wrapSanitizer($value); + $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' ' . $value . ' '; - // ORDER BY - $orderBys = ''; - if (isset($statements['orderBys']) && is_array($statements['orderBys'])) { - foreach ($statements['orderBys'] as $orderBy) { - $orderBys .= $this->wrapSanitizer($orderBy['field']) . ' ' . $orderBy['type'] . ', '; + continue; } - if ($orderBys = trim($orderBys, ', ')) { - $orderBys = 'ORDER BY ' . $orderBys; + if ($statement['key'] instanceof Raw) { + + if ($statement['operator'] !== null) { + $criteria .= "{$statement['joiner']} {$key} {$statement['operator']} ? "; + $bindings[] = (array)$statement['key']->getBindings(); + $bindings[] = (array)$value; + } else { + $criteria .= $statement['joiner'] . ' ' . $key . ' '; + $bindings[] = (array)$statement['key']->getBindings(); + } + + continue; + } + + // WHERE + $valuePlaceholder = '?'; + $bindings[] = [$value]; + $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' ' . $valuePlaceholder . ' '; } - // LIMIT AND OFFSET - $limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : ''; - $offset = isset($statements['offset']) ? 'OFFSET ' . $statements['offset'] : ''; + $bindings = array_merge(...$bindings); - // HAVING - list($havingCriteria, $havingBindings) = $this->buildCriteriaWithType($statements, 'havings', 'HAVING'); + // Clear all white spaces, and, or from beginning and white spaces from ending + $criteria = preg_replace('/^(\s?AND ?|\s?OR ?)|\s$/i', '', $criteria); - // JOINS - $joinString = $this->buildJoin($statements); + return [$criteria, $bindings]; + } - $sqlArray = [ - 'SELECT' . (isset($statements['distinct']) ? ' DISTINCT' : ''), - $selects, - $fromEnabled ? 'FROM' : '', - $tables, - $joinString, - $whereCriteria, - $groupBys, - $havingCriteria, - $orderBys, - $limit, - $offset, - ]; + /** + * Build criteria string and binding with various types added, like WHERE and Having + * + * @param array $statements + * @param string $key + * @param string $type + * @param bool $bindValues + * + * @return array + * @throws Exception + */ + protected function buildCriteriaWithType(array $statements, $key, $type, $bindValues = true) + { + $criteria = ''; + $bindings = []; - $sql = $this->concatenateQuery($sqlArray); + if (isset($statements[$key]) === true) { + // Get the generic/adapter agnostic criteria string from parent + list($criteria, $bindings) = $this->buildCriteria($statements[$key], $bindValues); - $bindings = array_merge( - $whereBindings, - $havingBindings - ); + if ($criteria !== null) { + $criteria = $type . ' ' . $criteria; + } + } - return compact('sql', 'bindings'); + return [$criteria, $bindings]; + } + + /** + * Build join string + * + * @param array $statements + * + * @return string + * @throws Exception + */ + protected function buildJoin(array $statements) + { + $sql = ''; + + if (array_key_exists('joins', $statements) === false || count($statements['joins']) === 0) { + return $sql; + } + + foreach ((array)$statements['joins'] as $joinArr) { + if (is_array($joinArr['table']) === true) { + list($mainTable, $aliasTable) = $joinArr['table']; + $table = $this->wrapSanitizer($mainTable) . ' AS ' . $this->wrapSanitizer($aliasTable); + } else { + $table = $joinArr['table'] instanceof Raw ? (string)$joinArr['table'] : $this->wrapSanitizer($joinArr['table']); + } + + /* @var $joinBuilder \Pecee\Pixie\QueryBuilder\QueryBuilderHandler */ + $joinBuilder = $joinArr['joinBuilder']; + + $sqlArr = [ + $sql, + strtoupper($joinArr['type']), + 'JOIN', + $table, + 'ON', + $joinBuilder->getQuery('criteriaOnly', false)->getSql(), + ]; + + $sql = $this->concatenateQuery($sqlArr); + } + + return $sql; + } + + /** + * Join different part of queries with a space. + * + * @param array $pieces + * + * @return string + */ + protected function concatenateQuery(array $pieces) + { + $str = ''; + foreach ($pieces as $piece) { + $str = trim($str) . ' ' . trim($piece); + } + + return trim($str); } /** * Build just criteria part of the query * * @param array $statements - * @param bool $bindValues + * @param bool $bindValues + * * @return array * @throws Exception */ @@ -156,11 +292,34 @@ public function criteriaOnly(array $statements, $bindValues = true) } /** - * Build a generic insert/ignore/replace query + * Build delete query * * @param array $statements - * @param array $data + * + * @return array + * @throws Exception + */ + public function delete(array $statements) + { + $table = end($statements['tables']); + + // WHERE + list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE'); + + $sqlArray = ['DELETE FROM', $this->wrapSanitizer($table), $whereCriteria]; + $sql = $this->concatenateQuery($sqlArray); + $bindings = $whereBindings; + + return compact('sql', 'bindings'); + } + + /** + * Build a generic insert/ignore/replace query + * + * @param array $statements + * @param array $data * @param string $type + * * @return array * @throws Exception */ @@ -175,7 +334,7 @@ private function doInsert(array $statements, array $data, $type) if ($value instanceof Raw) { $values[] = (string)$value; } else { - $values[] = '?'; + $values[] = '?'; $bindings[] = $value; } } @@ -196,7 +355,7 @@ private function doInsert(array $statements, array $data, $type) list($updateStatement, $updateBindings) = $this->getUpdateStatement($statements['onduplicate']); $sqlArray[] = 'ON DUPLICATE KEY UPDATE ' . $updateStatement; - $bindings = array_merge($bindings, $updateBindings); + $bindings = array_merge($bindings, $updateBindings); } @@ -205,54 +364,16 @@ private function doInsert(array $statements, array $data, $type) return compact('sql', 'bindings'); } - /** - * Build insert query - * - * @param array $statements - * @param array $data - * @return array - * @throws Exception - */ - public function insert(array $statements, array $data) - { - return $this->doInsert($statements, $data, 'INSERT'); - } - - /** - * Build insert and ignore query - * - * @param array $statements - * @param array $data - * @return array - * @throws Exception - */ - public function insertIgnore(array $statements, array $data) - { - return $this->doInsert($statements, $data, 'INSERT IGNORE'); - } - - /** - * Build replace query - * - * @param array $statements - * @param array $data - * @return array - * @throws Exception - */ - public function replace(array $statements, array $data) - { - return $this->doInsert($statements, $data, 'REPLACE'); - } - /** * Build fields assignment part of SET ... or ON DUBLICATE KEY UPDATE ... statements * * @param array $data + * * @return array */ private function getUpdateStatement(array $data) { - $bindings = []; + $bindings = []; $statement = ''; foreach ($data as $key => $value) { @@ -262,7 +383,7 @@ private function getUpdateStatement(array $data) if ($value instanceof Raw) { $statement .= $value . ','; } else { - $statement .= '?,'; + $statement .= '?,'; $bindings[] = $value; } } @@ -273,227 +394,189 @@ private function getUpdateStatement(array $data) } /** - * Build update query + * Build insert query * * @param array $statements * @param array $data + * * @return array * @throws Exception */ - public function update(array $statements, array $data) + public function insert(array $statements, array $data) { - if (count($data) < 1) { - throw new Exception('No data given.', 4); - } - - $table = end($statements['tables']); - - // UPDATE - list($updateStatement, $bindings) = $this->getUpdateStatement($data); - - // WHERE - list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE'); - - // LIMIT - $limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : ''; - - $sqlArray = [ - 'UPDATE', - $this->wrapSanitizer($table), - 'SET ' . $updateStatement, - $whereCriteria, - $limit, - ]; - - $sql = $this->concatenateQuery($sqlArray); - - $bindings = array_merge($bindings, $whereBindings); - - return compact('sql', 'bindings'); + return $this->doInsert($statements, $data, 'INSERT'); } /** - * Build delete query + * Build insert and ignore query * * @param array $statements - * @return array - * @throws Exception - */ - public function delete(array $statements) - { - $table = end($statements['tables']); - - // WHERE - list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE'); - - $sqlArray = ['DELETE FROM', $this->wrapSanitizer($table), $whereCriteria]; - $sql = $this->concatenateQuery($sqlArray); - $bindings = $whereBindings; - - return compact('sql', 'bindings'); - } - - /** - * Array concatenating method, like implode. - * But it does wrap sanitizer and trims last glue + * @param array $data * - * @param array $pieces - * @param string $glue - * @param bool $wrapSanitizer - * @return string - */ - protected function arrayStr(array $pieces, $glue = ',', $wrapSanitizer = true) - { - $str = ''; - foreach ($pieces as $key => $piece) { - if ($wrapSanitizer === true) { - $piece = $this->wrapSanitizer($piece); - } - - if (is_int($key) === false) { - $piece = ($wrapSanitizer ? $this->wrapSanitizer($key) : $key) . ' AS ' . $piece; - } - - $str .= $piece . $glue; - } - - return trim($str, $glue); + * @return array + * @throws Exception + */ + public function insertIgnore(array $statements, array $data) + { + return $this->doInsert($statements, $data, 'INSERT IGNORE'); } /** - * Join different part of queries with a space. + * Build replace query * - * @param array $pieces - * @return string + * @param array $statements + * @param array $data + * + * @return array + * @throws Exception */ - protected function concatenateQuery(array $pieces) + public function replace(array $statements, array $data) { - $str = ''; - foreach ($pieces as $piece) { - $str = trim($str) . ' ' . trim($piece); - } - - return trim($str); + return $this->doInsert($statements, $data, 'REPLACE'); } /** - * Build generic criteria string and bindings from statements, like "a = b and c = ?" + * Build select query string and bindings * * @param array $statements - * @param bool $bindValues + * * @throws Exception * @return array */ - protected function buildCriteria(array $statements, $bindValues = true) + public function select(array $statements) { - $criteria = ''; - $bindings = [[]]; - - foreach ($statements as $statement) { + if (array_key_exists('selects', $statements) === false) { + $statements['selects'] = ['*']; + } - $key = $this->wrapSanitizer($statement['key']); - $value = $statement['value']; + // From + $fromEnabled = false; + $tables = ''; - if ($value === null && $key instanceof \Closure) { + if (isset($statements['tables']) === true) { + $tables = []; - /** - * We have a closure, a nested criteria - * Build a new NestedCriteria class, keep it by reference so any changes made in the closure should reflect here - */ + foreach ((array)$statements['tables'] as $table) { - /* @var $nestedCriteria NestedCriteria */ - $nestedCriteria = $this->container->build( - NestedCriteria::class, - [$this->connection] - ); + $prefix = isset($statements['aliases'][$table]) ? $statements['aliases'][$table] : null; - // Call the closure with our new nestedCriteria object - $key($nestedCriteria); + if ($prefix !== null) { + $t = ($table instanceof Raw) ? $table : '`' . $table . '` AS `' . strtolower($prefix) . '`'; + } else { + $t = ($table instanceof Raw) ? $table : '`' . $table . '`'; + } - // Get the criteria only query from the nestedCriteria object - $queryObject = $nestedCriteria->getQuery('criteriaOnly', true); + $tables[] = $t; + } - // Merge the bindings we get from nestedCriteria object - $bindings[] = $queryObject->getBindings(); + $tables = implode(',', $tables); + $fromEnabled = true; + } - // Append the sql we get from the nestedCriteria object - $criteria .= $statement['joiner'] . ' (' . $queryObject->getSql() . ') '; + // SELECT + $selects = $this->arrayStr($statements['selects'], ', '); - continue; - } + // WHERE + list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE'); - if (is_array($value) === true) { + // GROUP BY + $groupBys = $this->arrayStr($statements['groupBys'], ', '); + if ($groupBys !== '' && isset($statements['groupBys']) === true) { + $groupBys = 'GROUP BY ' . $groupBys; + } - // Where in or between like query - $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator']; + // ORDER BY + $orderBys = ''; + if (isset($statements['orderBys']) && is_array($statements['orderBys'])) { + foreach ($statements['orderBys'] as $orderBy) { + $orderBys .= $this->wrapSanitizer($orderBy['field']) . ' ' . $orderBy['type'] . ', '; + } - if ($statement['operator'] === 'BETWEEN') { - $bindings[] = (array)$statement['value']; - $criteria .= ' ? AND ? '; - } else { - $valuePlaceholder = ''; - foreach ((array)$statement['value'] as $subValue) { - $valuePlaceholder .= '?, '; - $bindings[] = (array)$subValue; - } + if ($orderBys = trim($orderBys, ', ')) { + $orderBys = 'ORDER BY ' . $orderBys; + } + } - $valuePlaceholder = trim($valuePlaceholder, ', '); - $criteria .= ' (' . $valuePlaceholder . ') '; - } + // LIMIT AND OFFSET + $limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : ''; + $offset = isset($statements['offset']) ? 'OFFSET ' . $statements['offset'] : ''; - continue; + // HAVING + list($havingCriteria, $havingBindings) = $this->buildCriteriaWithType($statements, 'havings', 'HAVING'); - } + // JOINS + $joinString = $this->buildJoin($statements); - if ($value instanceof Raw) { - $criteria .= "{$statement['joiner']} {$key} {$statement['operator']} $value "; - continue; - } + $sqlArray = [ + 'SELECT' . (isset($statements['distinct']) ? ' DISTINCT' : ''), + $selects, + $fromEnabled ? 'FROM' : '', + $tables, + $joinString, + $whereCriteria, + $groupBys, + $havingCriteria, + $orderBys, + $limit, + $offset, + ]; + $sql = $this->concatenateQuery($sqlArray); - // Usual where like criteria - if ($bindValues === false) { + $bindings = array_merge( + $whereBindings, + $havingBindings + ); - // Specially for joins - we are not binding values, lets sanitize then - $value = $this->wrapSanitizer($value); - $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' ' . $value . ' '; + return compact('sql', 'bindings'); + } - continue; - } + /** + * Build update query + * + * @param array $statements + * @param array $data + * + * @return array + * @throws Exception + */ + public function update(array $statements, array $data) + { + if (count($data) < 1) { + throw new Exception('No data given.', 4); + } - if ($statement['key'] instanceof Raw) { + $table = end($statements['tables']); - if ($statement['operator'] !== null) { - $criteria .= "{$statement['joiner']} {$key} {$statement['operator']} ? "; - $bindings[] = (array)$statement['key']->getBindings(); - $bindings[] = (array)$value; - } else { - $criteria .= $statement['joiner'] . ' ' . $key . ' '; - $bindings[] = (array)$statement['key']->getBindings(); - } + // UPDATE + list($updateStatement, $bindings) = $this->getUpdateStatement($data); - continue; + // WHERE + list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE'); - } + // LIMIT + $limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : ''; - // WHERE - $valuePlaceholder = '?'; - $bindings[] = [$value]; - $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' ' . $valuePlaceholder . ' '; - } + $sqlArray = [ + 'UPDATE', + $this->wrapSanitizer($table), + 'SET ' . $updateStatement, + $whereCriteria, + $limit, + ]; - $bindings = array_merge(...$bindings); + $sql = $this->concatenateQuery($sqlArray); - // Clear all white spaces, and, or from beginning and white spaces from ending - $criteria = preg_replace('/^(\s?AND ?|\s?OR ?)|\s$/i', '', $criteria); + $bindings = array_merge($bindings, $whereBindings); - return [$criteria, $bindings]; + return compact('sql', 'bindings'); } /** * Wrap values with adapter's sanitizer like, '`' * * @param string|Raw|\Closure $value + * * @return string */ public function wrapSanitizer($value) @@ -518,72 +601,4 @@ public function wrapSanitizer($value) // Join these back with "." and return return implode('.', $valueArr); } - - /** - * Build criteria string and binding with various types added, like WHERE and Having - * - * @param array $statements - * @param string $key - * @param string $type - * @param bool $bindValues - * @return array - * @throws Exception - */ - protected function buildCriteriaWithType(array $statements, $key, $type, $bindValues = true) - { - $criteria = ''; - $bindings = []; - - if (isset($statements[$key]) === true) { - // Get the generic/adapter agnostic criteria string from parent - list($criteria, $bindings) = $this->buildCriteria($statements[$key], $bindValues); - - if ($criteria !== null) { - $criteria = $type . ' ' . $criteria; - } - } - - return [$criteria, $bindings]; - } - - /** - * Build join string - * - * @param array $statements - * @return string - * @throws Exception - */ - protected function buildJoin(array $statements) - { - $sql = ''; - - if (array_key_exists('joins', $statements) === false || count($statements['joins']) === 0) { - return $sql; - } - - foreach ((array)$statements['joins'] as $joinArr) { - if (is_array($joinArr['table']) === true) { - list($mainTable, $aliasTable) = $joinArr['table']; - $table = $this->wrapSanitizer($mainTable) . ' AS ' . $this->wrapSanitizer($aliasTable); - } else { - $table = $joinArr['table'] instanceof Raw ? (string)$joinArr['table'] : $this->wrapSanitizer($joinArr['table']); - } - - /* @var $joinBuilder \Pecee\Pixie\QueryBuilder\QueryBuilderHandler */ - $joinBuilder = $joinArr['joinBuilder']; - - $sqlArr = [ - $sql, - strtoupper($joinArr['type']), - 'JOIN', - $table, - 'ON', - $joinBuilder->getQuery('criteriaOnly', false)->getSql(), - ]; - - $sql = $this->concatenateQuery($sqlArr); - } - - return $sql; - } } From 08d06eef54723218df9bc3769de5f5e1d40505ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sun, 26 Nov 2017 15:02:51 +0100 Subject: [PATCH 13/27] feature: aliasing table into table() method i.e [table_one => one] --- .../Pixie/QueryBuilder/Adapters/BaseAdapter.php | 7 +++---- .../Pixie/QueryBuilder/QueryBuilderHandler.php | 13 +++++++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php b/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php index b3d74a1..c48def7 100644 --- a/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php +++ b/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php @@ -457,13 +457,12 @@ public function select(array $statements) $tables = []; foreach ((array)$statements['tables'] as $table) { - - $prefix = isset($statements['aliases'][$table]) ? $statements['aliases'][$table] : null; + $prefix = $statements['aliases'][$table] ?? null; if ($prefix !== null) { - $t = ($table instanceof Raw) ? $table : '`' . $table . '` AS `' . strtolower($prefix) . '`'; + $t = ($table instanceof Raw) ? $table : sprintf('`%s` AS `%s`', $table, strtolower($prefix)); } else { - $t = ($table instanceof Raw) ? $table : '`' . $table . '`'; + $t = ($table instanceof Raw) ? $table : sprintf('`%s`', $table); } $tables[] = $t; diff --git a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php index 6a73bf5..0cfbf2e 100644 --- a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php +++ b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php @@ -1111,14 +1111,23 @@ public function subQuery(QueryBuilderHandler $queryBuilder, $alias = null) */ public function table($tables) { + $tTables = []; if (is_array($tables) === false) { // Because a single table is converted to an array anyways, this makes sense. $tables = func_get_args(); } $instance = new static($this->connection); - $tables = $this->addTablePrefix($tables, false); - $instance->addStatement('tables', $tables); + foreach ($tables as $key => $value) { + if (is_string($key)) { + $instance->alias($key, $value); + $tTables[] = $key; + } else { + $tTables[] = $value; + } + } + $tables = $this->addTablePrefix($tTables, false); + $instance->addStatement('tables', $tTables); return $instance; } From 0d57f7f9c698146a5ecccb6479b966f961f452f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sun, 26 Nov 2017 15:16:42 +0100 Subject: [PATCH 14/27] fix: Illegal offset type in isset for Raw in table() method --- .../Pixie/QueryBuilder/Adapters/BaseAdapter.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php b/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php index c48def7..17133ef 100644 --- a/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php +++ b/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php @@ -457,12 +457,16 @@ public function select(array $statements) $tables = []; foreach ((array)$statements['tables'] as $table) { - $prefix = $statements['aliases'][$table] ?? null; - - if ($prefix !== null) { - $t = ($table instanceof Raw) ? $table : sprintf('`%s` AS `%s`', $table, strtolower($prefix)); + if ($table instanceof Raw) { + $t = $table; } else { - $t = ($table instanceof Raw) ? $table : sprintf('`%s`', $table); + $prefix = $statements['aliases'][$table] ?? null; + + if ($prefix !== null) { + $t = sprintf('`%s` AS `%s`', $table, strtolower($prefix)); + } else { + $t = sprintf('`%s`', $table); + } } $tables[] = $t; From c2460856dbbc2858aa37360a2f33626bb580f871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sun, 26 Nov 2017 15:26:16 +0100 Subject: [PATCH 15/27] table() example --- src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php index 0cfbf2e..3a3d946 100644 --- a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php +++ b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php @@ -1108,6 +1108,16 @@ public function subQuery(QueryBuilderHandler $queryBuilder, $alias = null) * * @throws Exception * @return static + * ``` + * Examples: + * - basic usage + * ->table('table_one') + * ->table(['table_one']) + * + * - with aliasing + * ->table(['table_one' => 'one']) + * ->table($qb->raw('table_one as one')) + * ``` */ public function table($tables) { From 03244c5905b9c6ef9b718c1bd840e2f68097b872 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sun, 26 Nov 2017 15:38:46 +0100 Subject: [PATCH 16/27] add event name after-* and before-* as constants --- .../QueryBuilder/QueryBuilderHandler.php | 68 ++++++++++++++++--- 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php index 3a3d946..ee602a1 100644 --- a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php +++ b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php @@ -13,6 +13,54 @@ */ class QueryBuilderHandler { + /** + * Event name + * + * @var string + */ + const EVENT_BEFORE_DELETE = 'before-delete'; + /** + * Event name + * + * @var string + */ + const EVENT_BEFORE_INSERT = 'before-insert'; + /** + * Event name + * + * @var string + */ + const EVENT_BEFORE_UPDATE = 'before-update'; + /** + * Event name + * + * @var string + */ + const EVENT_BEFORE_SELECT = 'before-select'; + /** + * Event name + * + * @var string + */ + const EVENT_AFTER_DELETE = 'after-delete'; + /** + * Event name + * + * @var string + */ + const EVENT_AFTER_INSERT = 'after-insert'; + /** + * Event name + * + * @var string + */ + const EVENT_AFTER_UPDATE = 'after-update'; + /** + * Event name + * + * @var string + */ + const EVENT_AFTER_SELECT = 'after-select'; /** * @var \Viocon\Container @@ -261,10 +309,10 @@ public function delete(): \PDOStatement /* @var $response \PDOStatement */ $queryObject = $this->getQuery('delete'); - $this->fireEvents('before-delete', $queryObject); + $this->fireEvents(static::EVENT_BEFORE_DELETE, $queryObject); list($response, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings()); - $this->fireEvents('after-delete', $queryObject, $executionTime); + $this->fireEvents(static::EVENT_AFTER_DELETE, $queryObject, $executionTime); return $response; } @@ -284,14 +332,14 @@ private function doInsert(array $data, string $type) if (is_array(current($data)) === false) { $queryObject = $this->getQuery($type, $data); - $this->fireEvents('before-insert', $queryObject); + $this->fireEvents(static::EVENT_BEFORE_INSERT, $queryObject); /** * @var $result \PDOStatement * @var $executionTime float */ list($result, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings()); $return = $result->rowCount() === 1 ? $this->pdo->lastInsertId() : null; - $this->fireEvents('after-insert', $queryObject, $return, $executionTime); + $this->fireEvents(static::EVENT_AFTER_INSERT, $queryObject, $return, $executionTime); return $return; } @@ -302,10 +350,10 @@ private function doInsert(array $data, string $type) foreach ($data as $subData) { $queryObject = $this->getQuery($type, $subData); - $this->fireEvents('before-insert', $queryObject); + $this->fireEvents(static::EVENT_BEFORE_INSERT, $queryObject); list($result, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings()); $result = $result->rowCount() === 1 ? $this->pdo->lastInsertId() : null; - $this->fireEvents('after-insert', $queryObject, $result, $executionTime); + $this->fireEvents(static::EVENT_AFTER_INSERT, $queryObject, $result, $executionTime); $return[] = $result; } @@ -415,11 +463,11 @@ public function get(): array } $start = microtime(true); - $this->fireEvents('before-select', $queryObject); + $this->fireEvents(static::EVENT_BEFORE_SELECT, $queryObject); $result = call_user_func_array([$this->pdoStatement, 'fetchAll'], $this->fetchParameters); $executionTime += microtime(true) - $start; $this->pdoStatement = null; - $this->fireEvents('after-select', $queryObject, $result, $executionTime); + $this->fireEvents(static::EVENT_AFTER_SELECT, $queryObject, $result, $executionTime); return $result; } @@ -1191,10 +1239,10 @@ public function update($data) */ $queryObject = $this->getQuery('update', $data); - $this->fireEvents('before-update', $queryObject); + $this->fireEvents(static::EVENT_BEFORE_UPDATE, $queryObject); list($response, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings()); - $this->fireEvents('after-update', $queryObject, $executionTime); + $this->fireEvents(static::EVENT_AFTER_UPDATE, $queryObject, $executionTime); return $response; } From 293671bf35dc787798c902178e17830df6a44999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sun, 26 Nov 2017 17:09:20 +0100 Subject: [PATCH 17/27] PHPUnit & Mockery update - require for PHP7 --- .idea/pecee-pixie.iml | 39 +- .idea/php.xml | 9 +- composer.json | 4 +- composer.lock | 681 +++++++++++++----- tests/Pecee/Pixie/ConnectionTest.php | 13 +- tests/Pecee/Pixie/NoTableSubQueryTest.php | 16 +- .../Pecee/Pixie/QueryBuilderBehaviorTest.php | 427 +++++------ tests/Pecee/Pixie/QueryBuilderTest.php | 83 ++- tests/TestCase.php | 86 ++- 9 files changed, 915 insertions(+), 443 deletions(-) diff --git a/.idea/pecee-pixie.iml b/.idea/pecee-pixie.iml index a671bfb..cdea25f 100644 --- a/.idea/pecee-pixie.iml +++ b/.idea/pecee-pixie.iml @@ -7,6 +7,9 @@ + + + @@ -18,14 +21,18 @@ + + + + - + @@ -38,6 +45,9 @@ + + + @@ -49,14 +59,18 @@ + + + + - + @@ -65,6 +79,9 @@ + + + @@ -76,18 +93,34 @@ + + + + - + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml index 8cf5292..c057fbd 100644 --- a/.idea/php.xml +++ b/.idea/php.xml @@ -2,7 +2,6 @@ - @@ -27,6 +26,14 @@ + + + + + + + + diff --git a/composer.json b/composer.json index bf82635..5aca082 100644 --- a/composer.json +++ b/composer.json @@ -38,8 +38,8 @@ "usmanhalalit/viocon": "1.0.1" }, "require-dev": { - "phpunit/phpunit": "^4.8", - "mockery/mockery": "0.9.4" + "phpunit/phpunit": "^6.4", + "mockery/mockery": "^1" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index d5e77e5..e198b6e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "316248f27c42defd396d2c459b6e66cb", + "content-hash": "55c47951e95f4087152d5ea0bd6f2ed4", "packages": [ { "name": "usmanhalalit/viocon", @@ -111,20 +111,20 @@ }, { "name": "hamcrest/hamcrest-php", - "version": "v1.2.2", + "version": "v2.0.0", "source": { "type": "git", "url": "https://github.com/hamcrest/hamcrest-php.git", - "reference": "b37020aa976fa52d3de9aa904aa2522dc518f79c" + "reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/b37020aa976fa52d3de9aa904aa2522dc518f79c", - "reference": "b37020aa976fa52d3de9aa904aa2522dc518f79c", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/776503d3a8e85d4f9a1148614f95b7a608b046ad", + "reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": "^5.3|^7.0" }, "replace": { "cordoval/hamcrest-php": "*", @@ -133,15 +133,18 @@ }, "require-dev": { "phpunit/php-file-iterator": "1.3.3", - "satooshi/php-coveralls": "dev-master" + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "^1.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, "autoload": { "classmap": [ "hamcrest" - ], - "files": [ - "hamcrest/Hamcrest.php" ] }, "notification-url": "https://packagist.org/downloads/", @@ -152,34 +155,34 @@ "keywords": [ "test" ], - "time": "2015-05-11T14:41:42+00:00" + "time": "2016-01-20T08:20:44+00:00" }, { "name": "mockery/mockery", - "version": "0.9.4", + "version": "1.0", "source": { "type": "git", "url": "https://github.com/mockery/mockery.git", - "reference": "70bba85e4aabc9449626651f48b9018ede04f86b" + "reference": "1bac8c362b12f522fdd1f1fa3556284c91affa38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/70bba85e4aabc9449626651f48b9018ede04f86b", - "reference": "70bba85e4aabc9449626651f48b9018ede04f86b", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1bac8c362b12f522fdd1f1fa3556284c91affa38", + "reference": "1bac8c362b12f522fdd1f1fa3556284c91affa38", "shasum": "" }, "require": { - "hamcrest/hamcrest-php": "~1.1", + "hamcrest/hamcrest-php": "~2.0", "lib-pcre": ">=7.0", - "php": ">=5.3.2" + "php": ">=5.6.0" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "~5.7|~6.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.9.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { @@ -204,7 +207,7 @@ } ], "description": "Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succinct API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending.", - "homepage": "http://github.com/padraic/mockery", + "homepage": "http://github.com/mockery/mockery", "keywords": [ "BDD", "TDD", @@ -217,7 +220,154 @@ "test double", "testing" ], - "time": "2015-04-02T19:54:00+00:00" + "time": "2017-10-06T16:20:43+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.7.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^4.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": "2017-10-19T19:58:43+00:00" + }, + { + "name": "phar-io/manifest", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0", + "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "phar-io/version": "^1.0.1", + "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": "2017-03-05T18:14:27+00:00" + }, + { + "name": "phar-io/version", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df", + "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df", + "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": "2017-03-05T17:38:23+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -430,39 +580,41 @@ }, { "name": "phpunit/php-code-coverage", - "version": "2.2.4", + "version": "5.2.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" + "reference": "8e1d2397d8adf59a3f12b2878a3aaa66d1ab189d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", - "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/8e1d2397d8adf59a3f12b2878a3aaa66d1ab189d", + "reference": "8e1d2397d8adf59a3f12b2878a3aaa66d1ab189d", "shasum": "" }, "require": { - "php": ">=5.3.3", - "phpunit/php-file-iterator": "~1.3", - "phpunit/php-text-template": "~1.2", - "phpunit/php-token-stream": "~1.3", - "sebastian/environment": "^1.3.2", - "sebastian/version": "~1.0" + "ext-dom": "*", + "ext-xmlwriter": "*", + "php": "^7.0", + "phpunit/php-file-iterator": "^1.4.2", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-token-stream": "^2.0", + "sebastian/code-unit-reverse-lookup": "^1.0.1", + "sebastian/environment": "^3.0", + "sebastian/version": "^2.0.1", + "theseer/tokenizer": "^1.1" }, "require-dev": { - "ext-xdebug": ">=2.1.4", - "phpunit/phpunit": "~4" + "ext-xdebug": "^2.5", + "phpunit/phpunit": "^6.0" }, "suggest": { - "ext-dom": "*", - "ext-xdebug": ">=2.2.1", - "ext-xmlwriter": "*" + "ext-xdebug": "^2.5.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2.x-dev" + "dev-master": "5.2.x-dev" } }, "autoload": { @@ -488,7 +640,7 @@ "testing", "xunit" ], - "time": "2015-10-06T15:47:00+00:00" + "time": "2017-11-03T13:47:33+00:00" }, { "name": "phpunit/php-file-iterator", @@ -629,29 +781,29 @@ }, { "name": "phpunit/php-token-stream", - "version": "1.4.11", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7" + "reference": "9a02332089ac48e704c70f6cefed30c224e3c0b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7", - "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/9a02332089ac48e704c70f6cefed30c224e3c0b0", + "reference": "9a02332089ac48e704c70f6cefed30c224e3c0b0", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": ">=5.3.3" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.2" + "phpunit/phpunit": "^6.2.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -674,45 +826,57 @@ "keywords": [ "tokenizer" ], - "time": "2017-02-27T10:12:30+00:00" + "time": "2017-08-20T05:47:52+00:00" }, { "name": "phpunit/phpunit", - "version": "4.8.36", + "version": "6.4.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "46023de9a91eec7dfb06cc56cb4e260017298517" + "reference": "562f7dc75d46510a4ed5d16189ae57fbe45a9932" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/46023de9a91eec7dfb06cc56cb4e260017298517", - "reference": "46023de9a91eec7dfb06cc56cb4e260017298517", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/562f7dc75d46510a4ed5d16189ae57fbe45a9932", + "reference": "562f7dc75d46510a4ed5d16189ae57fbe45a9932", "shasum": "" }, "require": { "ext-dom": "*", "ext-json": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", - "php": ">=5.3.3", - "phpspec/prophecy": "^1.3.1", - "phpunit/php-code-coverage": "~2.1", - "phpunit/php-file-iterator": "~1.4", - "phpunit/php-text-template": "~1.2", - "phpunit/php-timer": "^1.0.6", - "phpunit/phpunit-mock-objects": "~2.3", - "sebastian/comparator": "~1.2.2", - "sebastian/diff": "~1.2", - "sebastian/environment": "~1.3", - "sebastian/exporter": "~1.2", - "sebastian/global-state": "~1.0", - "sebastian/version": "~1.0", - "symfony/yaml": "~2.1|~3.0" + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "myclabs/deep-copy": "^1.6.1", + "phar-io/manifest": "^1.0.1", + "phar-io/version": "^1.0", + "php": "^7.0", + "phpspec/prophecy": "^1.7", + "phpunit/php-code-coverage": "^5.2.2", + "phpunit/php-file-iterator": "^1.4.2", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-timer": "^1.0.9", + "phpunit/phpunit-mock-objects": "^4.0.3", + "sebastian/comparator": "^2.0.2", + "sebastian/diff": "^2.0", + "sebastian/environment": "^3.1", + "sebastian/exporter": "^3.1", + "sebastian/global-state": "^2.0", + "sebastian/object-enumerator": "^3.0.3", + "sebastian/resource-operations": "^1.0", + "sebastian/version": "^2.0.1" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "3.0.2", + "phpunit/dbunit": "<3.0" + }, + "require-dev": { + "ext-pdo": "*" }, "suggest": { - "phpunit/php-invoker": "~1.1" + "ext-xdebug": "*", + "phpunit/php-invoker": "^1.1" }, "bin": [ "phpunit" @@ -720,7 +884,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.8.x-dev" + "dev-master": "6.4.x-dev" } }, "autoload": { @@ -746,30 +910,33 @@ "testing", "xunit" ], - "time": "2017-06-21T08:07:12+00:00" + "time": "2017-11-08T11:26:09+00:00" }, { "name": "phpunit/phpunit-mock-objects", - "version": "2.3.8", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" + "reference": "2f789b59ab89669015ad984afa350c4ec577ade0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", - "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/2f789b59ab89669015ad984afa350c4ec577ade0", + "reference": "2f789b59ab89669015ad984afa350c4ec577ade0", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": ">=5.3.3", - "phpunit/php-text-template": "~1.2", - "sebastian/exporter": "~1.2" + "doctrine/instantiator": "^1.0.5", + "php": "^7.0", + "phpunit/php-text-template": "^1.2.1", + "sebastian/exporter": "^3.0" + }, + "conflict": { + "phpunit/phpunit": "<6.0" }, "require-dev": { - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^6.0" }, "suggest": { "ext-soap": "*" @@ -777,7 +944,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3.x-dev" + "dev-master": "4.0.x-dev" } }, "autoload": { @@ -802,34 +969,79 @@ "mock", "xunit" ], - "time": "2015-10-02T06:51:40+00:00" + "time": "2017-08-03T14:08:16+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": "1.2.4", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" + "reference": "1174d9018191e93cb9d719edec01257fc05f8158" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", - "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1174d9018191e93cb9d719edec01257fc05f8158", + "reference": "1174d9018191e93cb9d719edec01257fc05f8158", "shasum": "" }, "require": { - "php": ">=5.3.3", - "sebastian/diff": "~1.2", - "sebastian/exporter": "~1.2 || ~2.0" + "php": "^7.0", + "sebastian/diff": "^2.0", + "sebastian/exporter": "^3.1" }, "require-dev": { - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^6.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "2.1.x-dev" } }, "autoload": { @@ -860,38 +1072,38 @@ } ], "description": "Provides the functionality to compare PHP values for equality", - "homepage": "http://www.github.com/sebastianbergmann/comparator", + "homepage": "https://github.com/sebastianbergmann/comparator", "keywords": [ "comparator", "compare", "equality" ], - "time": "2017-01-29T09:50:25+00:00" + "time": "2017-11-03T07:16:52+00:00" }, { "name": "sebastian/diff", - "version": "1.4.3", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" + "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", - "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", + "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "phpunit/phpunit": "^6.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -918,32 +1130,32 @@ "keywords": [ "diff" ], - "time": "2017-05-22T07:24:03+00:00" + "time": "2017-08-03T08:09:46+00:00" }, { "name": "sebastian/environment", - "version": "1.3.8", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea" + "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/be2c607e43ce4c89ecd60e75c6a85c126e754aea", - "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", + "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^4.8 || ^5.0" + "phpunit/phpunit": "^6.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3.x-dev" + "dev-master": "3.1.x-dev" } }, "autoload": { @@ -968,34 +1180,34 @@ "environment", "hhvm" ], - "time": "2016-08-18T05:49:44+00:00" + "time": "2017-07-01T08:51:00+00:00" }, { "name": "sebastian/exporter", - "version": "1.2.2", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4" + "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4", - "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", + "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", "shasum": "" }, "require": { - "php": ">=5.3.3", - "sebastian/recursion-context": "~1.0" + "php": "^7.0", + "sebastian/recursion-context": "^3.0" }, "require-dev": { "ext-mbstring": "*", - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3.x-dev" + "dev-master": "3.1.x-dev" } }, "autoload": { @@ -1035,27 +1247,27 @@ "export", "exporter" ], - "time": "2016-06-17T09:04:28+00:00" + "time": "2017-04-03T13:19:02+00:00" }, { "name": "sebastian/global-state", - "version": "1.1.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.2" + "phpunit/phpunit": "^6.0" }, "suggest": { "ext-uopz": "*" @@ -1063,7 +1275,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -1086,32 +1298,124 @@ "keywords": [ "global state" ], - "time": "2015-10-12T03:26:01+00:00" + "time": "2017-04-27T15:39:26+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": "1.0.5", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7" + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b19cc3298482a335a95f3016d2f8a6950f0fbcd7", - "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { @@ -1139,23 +1443,73 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2016-10-03T07:41:43+00:00" + "time": "2017-03-03T06:23:57+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "shasum": "" + }, + "require": { + "php": ">=5.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": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "time": "2015-07-28T20:34:47+00:00" }, { "name": "sebastian/version", - "version": "1.0.6", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", - "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", + "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/" @@ -1174,62 +1528,47 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "time": "2015-06-21T13:59:46+00:00" + "time": "2016-10-03T07:35:21+00:00" }, { - "name": "symfony/yaml", - "version": "v3.3.13", + "name": "theseer/tokenizer", + "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "0938408c4faa518d95230deabb5f595bf0de31b9" + "url": "https://github.com/theseer/tokenizer.git", + "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/0938408c4faa518d95230deabb5f595bf0de31b9", - "reference": "0938408c4faa518d95230deabb5f595bf0de31b9", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", + "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" - }, - "require-dev": { - "symfony/console": "~2.8|~3.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.3-dev" - } - }, "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" } ], - "description": "Symfony Yaml Component", - "homepage": "https://symfony.com", - "time": "2017-11-10T18:26:04+00:00" + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "time": "2017-04-07T12:08:54+00:00" }, { "name": "webmozart/assert", diff --git a/tests/Pecee/Pixie/ConnectionTest.php b/tests/Pecee/Pixie/ConnectionTest.php index 0f675d5..a48ce63 100644 --- a/tests/Pecee/Pixie/ConnectionTest.php +++ b/tests/Pecee/Pixie/ConnectionTest.php @@ -4,9 +4,20 @@ use Pecee\Pixie\ConnectionAdapters\Mysql; use Pecee\Pixie\QueryBuilder\QueryBuilderHandler; +/** + * Class ConnectionTest + * + * @package Pecee\Pixie + */ class ConnectionTest extends TestCase { + /** + * @var \Mockery\Mock + */ private $mysqlConnectionMock; + /** + * @var \Pecee\Pixie\Connection + */ private $connection; public function setUp() @@ -27,4 +38,4 @@ public function testConnection() $this->assertEquals('mysqlmock', $this->connection->getAdapter()); $this->assertEquals(array('prefix' => 'cb_'), $this->connection->getAdapterConfig()); } -} \ No newline at end of file +} diff --git a/tests/Pecee/Pixie/NoTableSubQueryTest.php b/tests/Pecee/Pixie/NoTableSubQueryTest.php index de146f4..f99cd64 100644 --- a/tests/Pecee/Pixie/NoTableSubQueryTest.php +++ b/tests/Pecee/Pixie/NoTableSubQueryTest.php @@ -1,9 +1,14 @@ builder->table('mail')->select($this->builder->raw('COUNT(*)')); $subQuery2 = $this->builder->table('event_message')->select($this->builder->raw('COUNT(*)')); - $count = $this->builder->select($this->builder->subQuery($subQuery1, 'row1'), $this->builder->subQuery($subQuery2, 'row2'))->first(); + $count = $this->builder->select($this->builder->subQuery($subQuery1, 'row1'), + $this->builder->subQuery($subQuery2, 'row2'))->first() + ; - $this->assertEquals('SELECT (SELECT COUNT(*) FROM `cb_mail`) AS `row1`, (SELECT COUNT(*) FROM `cb_event_message`) AS `row2` LIMIT 1', $count); + $this->assertEquals('SELECT (SELECT COUNT(*) FROM `cb_mail`) AS `row1`, (SELECT COUNT(*) FROM `cb_event_message`) AS `row2` LIMIT 1', + $count); } -} \ No newline at end of file +} diff --git a/tests/Pecee/Pixie/QueryBuilderBehaviorTest.php b/tests/Pecee/Pixie/QueryBuilderBehaviorTest.php index 990bafe..9b93357 100644 --- a/tests/Pecee/Pixie/QueryBuilderBehaviorTest.php +++ b/tests/Pecee/Pixie/QueryBuilderBehaviorTest.php @@ -1,9 +1,14 @@ builder = new QueryBuilder\QueryBuilderHandler($this->mockConnection); } - public function testSelectFlexibility() - { - $query = $this->builder - ->select('foo') - ->select(array('bar', 'baz')) - ->select('qux', 'lol', 'wut') - ->from('t'); - $this->assertEquals( - 'SELECT `foo`, `bar`, `baz`, `qux`, `lol`, `wut` FROM `cb_t`', - $query->getQuery()->getRawSql(), - 'SELECT is pretty flexible!' - ); - } - + /** + * Test alias + */ public function testAlias() { $query = $this->builder ->table(['table1']) ->alias('table1', 't1') - ->join('table2', 'table2.person_id', '=', 'foo2.id'); + ->join('table2', 'table2.person_id', '=', 'foo2.id') + ; $this->assertEquals('SELECT * FROM `cb_table1` AS `t1` INNER JOIN `cb_table2` ON `cb_table2`.`person_id` = `cb_foo2`.`id`', $query->getQuery()->getRawSql()); } - public function testSelectQuery() - { - $subQuery = $this->builder->table('person_details')->select('details')->where('person_id', '=', 3); - - $query = $this->builder->table('my_table') - ->select('my_table.*') - ->select(array($this->builder->raw('count(cb_my_table.id) AS `tot`'), $this->builder->subQuery($subQuery, 'pop'))) - ->where('value', '=', 'Ifrah') - ->whereNot('my_table.id', -1) - ->orWhereNot('my_table.id', -2) - ->orWhereIn('my_table.id', array(1, 2)) - ->groupBy(array('value', 'my_table.id', 'person_details.id')) - ->orderBy('my_table.id', 'DESC') - ->orderBy('value') - ->having('tot', '<', 2) - ->limit(1) - ->offset(0) - ->join( - 'person_details', - 'person_details.person_id', - '=', - 'my_table.id' - ) - ; - - $nestedQuery = $this->builder->table($this->builder->subQuery($query, 'bb'))->select('*'); - $this->assertEquals("SELECT * FROM (SELECT `cb_my_table`.*, count(cb_my_table.id) AS `tot`, (SELECT `details` FROM `cb_person_details` WHERE `person_id` = 3) AS `pop` FROM `cb_my_table` INNER JOIN `cb_person_details` ON `cb_person_details`.`person_id` = `cb_my_table`.`id` WHERE `value` = 'Ifrah' AND NOT `cb_my_table`.`id` = -1 OR NOT `cb_my_table`.`id` = -2 OR `cb_my_table`.`id` IN (1, 2) GROUP BY `value`, `cb_my_table`.`id`, `cb_person_details`.`id` HAVING `tot` < 2 ORDER BY `cb_my_table`.`id` DESC, `value` ASC LIMIT 1 OFFSET 0) AS `bb`" - , $nestedQuery->getQuery()->getRawSql()); - } - - public function testSelectAliases() - { - $query = $this->builder->from('my_table')->select('foo')->select(array('bar' => 'baz', 'qux')); - - $this->assertEquals( - "SELECT `foo`, `bar` AS `baz`, `qux` FROM `cb_my_table`", - $query->getQuery()->getRawSql() - ); - } - - public function testRawStatementsWithinCriteria() - { - $query = $this->builder->from('my_table') - ->where('simple', 'criteria') - ->where($this->builder->raw('RAW')) - ->where($this->builder->raw('PARAMETERIZED_ONE(?)', 'foo')) - ->where($this->builder->raw('PARAMETERIZED_SEVERAL(?, ?, ?)', array(1, '2', 'foo'))); - - $this->assertEquals( - "SELECT * FROM `cb_my_table` WHERE `simple` = 'criteria' AND RAW AND PARAMETERIZED_ONE('foo') AND PARAMETERIZED_SEVERAL(1, '2', 'foo')", - $query->getQuery()->getRawSql() - ); - } - - public function testStandaloneWhereNot() - { - $query = $this->builder->table('my_table')->whereNot('foo', 1); - $this->assertEquals("SELECT * FROM `cb_my_table` WHERE NOT `foo` = 1", $query->getQuery()->getRawSql()); - } - - public function testSelectDistinct() - { - $query = $this->builder->selectDistinct(array('name', 'surname'))->from('my_table'); - $this->assertEquals("SELECT DISTINCT `name`, `surname` FROM `cb_my_table`", $query->getQuery()->getRawSql()); - } - - public function testSelectDistinctWithSingleColumn() - { - $query = $this->builder->selectDistinct('name')->from('my_table'); - $this->assertEquals("SELECT DISTINCT `name` FROM `cb_my_table`", $query->getQuery()->getRawSql()); - } - - public function testSelectDistinctAndSelectCalls() - { - $query = $this->builder->select('name')->selectDistinct('surname')->select(array('birthday', 'address'))->from('my_table'); - $this->assertEquals("SELECT DISTINCT `name`, `surname`, `birthday`, `address` FROM `cb_my_table`", $query->getQuery()->getRawSql()); - } - - public function testSelectQueryWithNestedCriteriaAndJoins() - { - $builder = $this->builder; - - $query = $builder->table('my_table') - ->where('my_table.id', '>', 1) - ->orWhere('my_table.id', 1) - ->where(function($q) - { - $q->where('value', 'LIKE', '%sana%'); - $q->orWhere(function($q2) - { - $q2->where('key', 'LIKE', '%sana%'); - $q2->orWhere('value', 'LIKE', '%sana%'); - }); - }) - ->join(array('person_details', 'a'), 'a.person_id', '=', 'my_table.id') - - ->leftJoin(array('person_details', 'b'), function($table) use ($builder) - { - $table->on('b.person_id', '=', 'my_table.id'); - $table->on('b.deleted', '=', $builder->raw(0)); - $table->orOn('b.age', '>', $builder->raw(1)); - }) - ; - - $this->assertEquals("SELECT * FROM `cb_my_table` INNER JOIN `cb_person_details` AS `cb_a` ON `cb_a`.`person_id` = `cb_my_table`.`id` LEFT JOIN `cb_person_details` AS `cb_b` ON `cb_b`.`person_id` = `cb_my_table`.`id` AND `cb_b`.`deleted` = 0 OR `cb_b`.`age` > 1 WHERE `cb_my_table`.`id` > 1 OR `cb_my_table`.`id` = 1 AND (`value` LIKE '%sana%' OR (`key` LIKE '%sana%' OR `value` LIKE '%sana%'))" - , $query->getQuery()->getRawSql()); - } - - public function testSelectWithQueryEvents() + /** + * Test delete + */ + public function testDeleteQuery() { - $builder = $this->builder; - - $builder->registerEvent('before-select', ':any', function($qb) - { - $qb->whereIn('status', array(1, 2)); - }); + $this->builder = new QueryBuilder\QueryBuilderHandler($this->mockConnection); - $query = $builder->table('some_table')->where('name', 'Some'); - $query->get(); - $actual = $query->getQuery()->getRawSql(); + $builder = $this->builder->table('my_table')->where('value', '=', 'Amrin'); - $this->assertEquals("SELECT * FROM `cb_some_table` WHERE `name` = 'Some' AND `status` IN (1, 2)", $actual); + $this->assertEquals("DELETE FROM `cb_my_table` WHERE `value` = 'Amrin'" + , $builder->getQuery('delete')->getRawSql()); } public function testEventPropagation() @@ -190,81 +74,78 @@ public function testEventPropagation() }); } - $builder->table('foo')->insert(array('bar' => 'baz')); + $builder->table('foo')->insert(['bar' => 'baz']); $builder->from('foo')->select('bar')->get(); - $builder->table('foo')->update(array('bar' => 'baz')); + $builder->table('foo')->update(['bar' => 'baz']); $builder->from('foo')->delete(); $this->assertEquals($triggeredEvents, $events); } - public function testInsertQuery() - { - $builder = $this->builder->from('my_table'); - $data = array('key' => 'Name', - 'value' => 'Sana',); - - $this->assertEquals("INSERT INTO `cb_my_table` (`key`,`value`) VALUES ('Name','Sana')" - , $builder->getQuery('insert', $data)->getRawSql()); - } - public function testInsertIgnoreQuery() { $builder = $this->builder->from('my_table'); - $data = array('key' => 'Name', - 'value' => 'Sana',); + $data = [ + 'key' => 'Name', + 'value' => 'Sana', + ]; $this->assertEquals("INSERT IGNORE INTO `cb_my_table` (`key`,`value`) VALUES ('Name','Sana')" , $builder->getQuery('insertignore', $data)->getRawSql()); } - public function testReplaceQuery() - { - $builder = $this->builder->from('my_table'); - $data = array('key' => 'Name', - 'value' => 'Sana',); - - $this->assertEquals("REPLACE INTO `cb_my_table` (`key`,`value`) VALUES ('Name','Sana')" - , $builder->getQuery('replace', $data)->getRawSql()); - } - public function testInsertOnDuplicateKeyUpdateQuery() { - $builder = $this->builder; - $data = array( - 'name' => 'Sana', - 'counter' => 1 - ); - $dataUpdate = array( - 'name' => 'Sana', - 'counter' => 2 - ); + $builder = $this->builder; + $data = [ + 'name' => 'Sana', + 'counter' => 1, + ]; + $dataUpdate = [ + 'name' => 'Sana', + 'counter' => 2, + ]; $builder->from('my_table')->onDuplicateKeyUpdate($dataUpdate); $this->assertEquals("INSERT INTO `cb_my_table` (`name`,`counter`) VALUES ('Sana',1) ON DUPLICATE KEY UPDATE `name`='Sana',`counter`=2" , $builder->getQuery('insert', $data)->getRawSql()); } - public function testUpdateQuery() + public function testInsertQuery() { - $builder = $this->builder->table('my_table')->where('value', 'Sana'); - - $data = array( - 'key' => 'Sana', - 'value' => 'Amrin', - ); + $builder = $this->builder->from('my_table'); + $data = [ + 'key' => 'Name', + 'value' => 'Sana', + ]; - $this->assertEquals("UPDATE `cb_my_table` SET `key`='Sana',`value`='Amrin' WHERE `value` = 'Sana'" - , $builder->getQuery('update', $data)->getRawSql()); + $this->assertEquals("INSERT INTO `cb_my_table` (`key`,`value`) VALUES ('Name','Sana')" + , $builder->getQuery('insert', $data)->getRawSql()); } - public function testDeleteQuery() + public function testIsPossibleToUseSubqueryInWhereClause() { - $this->builder = new QueryBuilder\QueryBuilderHandler($this->mockConnection); - - $builder = $this->builder->table('my_table')->where('value', '=', 'Amrin'); + $sub = clone $this->builder; + $query = $this->builder->from('my_table')->whereIn('foo', $this->builder->subQuery( + $sub->from('some_table')->select('foo')->where('id', 1) + )) + ; + $this->assertEquals( + "SELECT * FROM `cb_my_table` WHERE `foo` IN (SELECT `foo` FROM `cb_some_table` WHERE `id` = 1)", + $query->getQuery()->getRawSql() + ); + } - $this->assertEquals("DELETE FROM `cb_my_table` WHERE `value` = 'Amrin'" - , $builder->getQuery('delete')->getRawSql()); + public function testIsPossibleToUseSubqueryInWhereNotClause() + { + $sub = clone $this->builder; + $query = $this->builder->from('my_table')->whereNotIn('foo', $this->builder->subQuery( + $sub->from('some_table')->select('foo')->where('id', 1) + )) + ; + $this->assertEquals( + "SELECT * FROM `cb_my_table` WHERE `foo` NOT IN (SELECT `foo` FROM `cb_some_table` WHERE `id` = 1)", + $query->getQuery()->getRawSql() + ); } public function testOrderByFlexibility() @@ -272,8 +153,9 @@ public function testOrderByFlexibility() $query = $this->builder ->from('t') ->orderBy('foo', 'DESC') - ->orderBy(array('bar', 'baz' => 'ASC', $this->builder->raw('raw1')), 'DESC') - ->orderBy($this->builder->raw('raw2'), 'DESC'); + ->orderBy(['bar', 'baz' => 'ASC', $this->builder->raw('raw1')], 'DESC') + ->orderBy($this->builder->raw('raw2'), 'DESC') + ; $this->assertEquals( 'SELECT * FROM `cb_t` ORDER BY `foo` DESC, `bar` DESC, `baz` ASC, raw1 DESC, raw2 DESC', @@ -282,42 +164,179 @@ public function testOrderByFlexibility() ); } - public function testSelectQueryWithNull() + public function testRawStatementsWithinCriteria() { $query = $this->builder->from('my_table') - ->whereNull('key1') - ->orWhereNull('key2') - ->whereNotNull('key3') - ->orWhereNotNull('key4') - ->orWhere('key5', '=', null); + ->where('simple', 'criteria') + ->where($this->builder->raw('RAW')) + ->where($this->builder->raw('PARAMETERIZED_ONE(?)', 'foo')) + ->where($this->builder->raw('PARAMETERIZED_SEVERAL(?, ?, ?)', [1, '2', 'foo'])) + ; $this->assertEquals( - "SELECT * FROM `cb_my_table` WHERE `key1` IS NULL OR `key2` IS NULL AND `key3` IS NOT NULL OR `key4` IS NOT NULL OR `key5` = NULL", + "SELECT * FROM `cb_my_table` WHERE `simple` = 'criteria' AND RAW AND PARAMETERIZED_ONE('foo') AND PARAMETERIZED_SEVERAL(1, '2', 'foo')", $query->getQuery()->getRawSql() ); } - public function testIsPossibleToUseSubqueryInWhereClause() + public function testReplaceQuery() { - $sub = clone $this->builder; - $query = $this->builder->from('my_table')->whereIn('foo', $this->builder->subQuery( - $sub->from('some_table')->select('foo')->where('id', 1) - )); + $builder = $this->builder->from('my_table'); + $data = [ + 'key' => 'Name', + 'value' => 'Sana', + ]; + + $this->assertEquals("REPLACE INTO `cb_my_table` (`key`,`value`) VALUES ('Name','Sana')" + , $builder->getQuery('replace', $data)->getRawSql()); + } + + public function testSelectAliases() + { + $query = $this->builder->from('my_table')->select('foo')->select(['bar' => 'baz', 'qux']); + $this->assertEquals( - "SELECT * FROM `cb_my_table` WHERE `foo` IN (SELECT `foo` FROM `cb_some_table` WHERE `id` = 1)", + "SELECT `foo`, `bar` AS `baz`, `qux` FROM `cb_my_table`", $query->getQuery()->getRawSql() ); } - public function testIsPossibleToUseSubqueryInWhereNotClause() + public function testSelectDistinct() { - $sub = clone $this->builder; - $query = $this->builder->from('my_table')->whereNotIn('foo', $this->builder->subQuery( - $sub->from('some_table')->select('foo')->where('id', 1) - )); + $query = $this->builder->selectDistinct(['name', 'surname'])->from('my_table'); + $this->assertEquals("SELECT DISTINCT `name`, `surname` FROM `cb_my_table`", $query->getQuery()->getRawSql()); + } + + public function testSelectDistinctAndSelectCalls() + { + $query = $this->builder->select('name')->selectDistinct('surname')->select(['birthday', 'address'])->from('my_table'); + $this->assertEquals("SELECT DISTINCT `name`, `surname`, `birthday`, `address` FROM `cb_my_table`", $query->getQuery()->getRawSql()); + } + + public function testSelectDistinctWithSingleColumn() + { + $query = $this->builder->selectDistinct('name')->from('my_table'); + $this->assertEquals("SELECT DISTINCT `name` FROM `cb_my_table`", $query->getQuery()->getRawSql()); + } + + public function testSelectFlexibility() + { + $query = $this->builder + ->select('foo') + ->select(['bar', 'baz']) + ->select('qux', 'lol', 'wut') + ->from('t') + ; $this->assertEquals( - "SELECT * FROM `cb_my_table` WHERE `foo` NOT IN (SELECT `foo` FROM `cb_some_table` WHERE `id` = 1)", + 'SELECT `foo`, `bar`, `baz`, `qux`, `lol`, `wut` FROM `cb_t`', + $query->getQuery()->getRawSql(), + 'SELECT is pretty flexible!' + ); + } + + public function testSelectQuery() + { + $subQuery = $this->builder->table('person_details')->select('details')->where('person_id', '=', 3); + + $query = $this->builder->table('my_table') + ->select('my_table.*') + ->select([$this->builder->raw('count(cb_my_table.id) AS `tot`'), $this->builder->subQuery($subQuery, 'pop')]) + ->where('value', '=', 'Ifrah') + ->whereNot('my_table.id', -1) + ->orWhereNot('my_table.id', -2) + ->orWhereIn('my_table.id', [1, 2]) + ->groupBy(['value', 'my_table.id', 'person_details.id']) + ->orderBy('my_table.id', 'DESC') + ->orderBy('value') + ->having('tot', '<', 2) + ->limit(1) + ->offset(0) + ->join( + 'person_details', + 'person_details.person_id', + '=', + 'my_table.id' + ) + ; + + $nestedQuery = $this->builder->table($this->builder->subQuery($query, 'bb'))->select('*'); + $this->assertEquals("SELECT * FROM (SELECT `cb_my_table`.*, count(cb_my_table.id) AS `tot`, (SELECT `details` FROM `cb_person_details` WHERE `person_id` = 3) AS `pop` FROM `cb_my_table` INNER JOIN `cb_person_details` ON `cb_person_details`.`person_id` = `cb_my_table`.`id` WHERE `value` = 'Ifrah' AND NOT `cb_my_table`.`id` = -1 OR NOT `cb_my_table`.`id` = -2 OR `cb_my_table`.`id` IN (1, 2) GROUP BY `value`, `cb_my_table`.`id`, `cb_person_details`.`id` HAVING `tot` < 2 ORDER BY `cb_my_table`.`id` DESC, `value` ASC LIMIT 1 OFFSET 0) AS `bb`" + , $nestedQuery->getQuery()->getRawSql()); + } + + public function testSelectQueryWithNestedCriteriaAndJoins() + { + $builder = $this->builder; + + $query = $builder->table('my_table') + ->where('my_table.id', '>', 1) + ->orWhere('my_table.id', 1) + ->where(function ($q) { + $q->where('value', 'LIKE', '%sana%'); + $q->orWhere(function ($q2) { + $q2->where('key', 'LIKE', '%sana%'); + $q2->orWhere('value', 'LIKE', '%sana%'); + }); + }) + ->join(['person_details', 'a'], 'a.person_id', '=', 'my_table.id') + ->leftJoin(['person_details', 'b'], function ($table) use ($builder) { + $table->on('b.person_id', '=', 'my_table.id'); + $table->on('b.deleted', '=', $builder->raw(0)); + $table->orOn('b.age', '>', $builder->raw(1)); + }) + ; + + $this->assertEquals("SELECT * FROM `cb_my_table` INNER JOIN `cb_person_details` AS `cb_a` ON `cb_a`.`person_id` = `cb_my_table`.`id` LEFT JOIN `cb_person_details` AS `cb_b` ON `cb_b`.`person_id` = `cb_my_table`.`id` AND `cb_b`.`deleted` = 0 OR `cb_b`.`age` > 1 WHERE `cb_my_table`.`id` > 1 OR `cb_my_table`.`id` = 1 AND (`value` LIKE '%sana%' OR (`key` LIKE '%sana%' OR `value` LIKE '%sana%'))" + , $query->getQuery()->getRawSql()); + } + + public function testSelectQueryWithNull() + { + $query = $this->builder->from('my_table') + ->whereNull('key1') + ->orWhereNull('key2') + ->whereNotNull('key3') + ->orWhereNotNull('key4') + ->orWhere('key5', '=', null) + ; + + $this->assertEquals( + "SELECT * FROM `cb_my_table` WHERE `key1` IS NULL OR `key2` IS NULL AND `key3` IS NOT NULL OR `key4` IS NOT NULL OR `key5` = NULL", $query->getQuery()->getRawSql() ); } + + public function testSelectWithQueryEvents() + { + $builder = $this->builder; + + $builder->registerEvent('before-select', ':any', function ($qb) { + $qb->whereIn('status', [1, 2]); + }); + + $query = $builder->table('some_table')->where('name', 'Some'); + $query->get(); + $actual = $query->getQuery()->getRawSql(); + + $this->assertEquals("SELECT * FROM `cb_some_table` WHERE `name` = 'Some' AND `status` IN (1, 2)", $actual); + } + + public function testStandaloneWhereNot() + { + $query = $this->builder->table('my_table')->whereNot('foo', 1); + $this->assertEquals("SELECT * FROM `cb_my_table` WHERE NOT `foo` = 1", $query->getQuery()->getRawSql()); + } + + public function testUpdateQuery() + { + $builder = $this->builder->table('my_table')->where('value', 'Sana'); + + $data = [ + 'key' => 'Sana', + 'value' => 'Amrin', + ]; + + $this->assertEquals("UPDATE `cb_my_table` SET `key`='Sana',`value`='Amrin' WHERE `value` = 'Sana'" + , $builder->getQuery('update', $data)->getRawSql()); + } } diff --git a/tests/Pecee/Pixie/QueryBuilderTest.php b/tests/Pecee/Pixie/QueryBuilderTest.php index 080cb2b..0acc645 100644 --- a/tests/Pecee/Pixie/QueryBuilderTest.php +++ b/tests/Pecee/Pixie/QueryBuilderTest.php @@ -1,10 +1,15 @@ builder = new QueryBuilderHandler($this->mockConnection); } - public function testRawQuery() + public function testFalseBoolWhere() { - $query = 'select * from cb_my_table where id = ? and name = ?'; - $bindings = array(5, 'usman'); - $queryArr = $this->builder->query($query, $bindings)->get(); - $this->assertEquals( - array( - $query, - array(array(5, PDO::PARAM_INT), array('usman', PDO::PARAM_STR)), - ), - $queryArr - ); + $result = $this->builder->table('test')->where('id', '=', false); + $this->assertEquals('SELECT * FROM `cb_test` WHERE `id` = 0', $result->getQuery()->getRawSql()); } public function testInsertQueryReturnsIdForInsert() @@ -38,17 +38,20 @@ public function testInsertQueryReturnsIdForInsert() $this->mockPdoStatement ->expects($this->once()) ->method('rowCount') - ->will($this->returnValue(1)); + ->will($this->returnValue(1)) + ; $this->mockPdo ->expects($this->once()) ->method('lastInsertId') - ->will($this->returnValue(11)); + ->will($this->returnValue(11)) + ; - $id = $this->builder->table('test')->insert(array( - 'id' => 5, - 'name' => 'usman' - )); + $id = $this->builder->table('test')->insert([ + 'id' => 5, + 'name' => 'usman', + ]) + ; $this->assertEquals(11, $id); } @@ -58,17 +61,20 @@ public function testInsertQueryReturnsIdForInsertIgnore() $this->mockPdoStatement ->expects($this->once()) ->method('rowCount') - ->will($this->returnValue(1)); + ->will($this->returnValue(1)) + ; $this->mockPdo ->expects($this->once()) ->method('lastInsertId') - ->will($this->returnValue(11)); + ->will($this->returnValue(11)) + ; - $id = $this->builder->table('test')->insertIgnore(array( - 'id' => 5, - 'name' => 'usman' - )); + $id = $this->builder->table('test')->insertIgnore([ + 'id' => 5, + 'name' => 'usman', + ]) + ; $this->assertEquals(11, $id); } @@ -78,19 +84,30 @@ public function testInsertQueryReturnsNullForIgnoredInsert() $this->mockPdoStatement ->expects($this->once()) ->method('rowCount') - ->will($this->returnValue(0)); + ->will($this->returnValue(0)) + ; - $id = $this->builder->table('test')->insertIgnore(array( - 'id' => 5, - 'name' => 'usman' - )); + $id = $this->builder->table('test')->insertIgnore([ + 'id' => 5, + 'name' => 'usman', + ]) + ; $this->assertEquals(null, $id); } - public function testFalseBoolWhere() { - $result = $this->builder->table('test')->where('id', '=', false); - $this->assertEquals('SELECT * FROM `cb_test` WHERE `id` = 0', $result->getQuery()->getRawSql()); + public function testRawQuery() + { + $query = 'select * from cb_my_table where id = ? and name = ?'; + $bindings = [5, 'usman']; + $queryArr = $this->builder->query($query, $bindings)->get(); + $this->assertEquals( + [ + $query, + [[5, PDO::PARAM_INT], ['usman', PDO::PARAM_STR]], + ], + $queryArr + ); } -} \ No newline at end of file +} diff --git a/tests/TestCase.php b/tests/TestCase.php index a07098c..afcba2b 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -1,75 +1,111 @@ container = new Container(); - $this->mockPdoStatement = $this->getMock(\PDOStatement::class); + $this->mockPdoStatement = $this->getMockBuilder(\PDOStatement::class)->getMock(); - $mockPdoStatement = & $this->mockPdoStatement; + $mockPdoStatement = &$this->mockPdoStatement; - $mockPdoStatement->bindings = array(); + $mockPdoStatement->bindings = []; $this->mockPdoStatement ->expects($this->any()) ->method('bindValue') ->will($this->returnCallback(function ($parameter, $value, $dataType) use ($mockPdoStatement) { - $mockPdoStatement->bindings[] = array($value, $dataType); - })); + $mockPdoStatement->bindings[] = [$value, $dataType]; + })) + ; $this->mockPdoStatement ->expects($this->any()) ->method('execute') - ->will($this->returnCallback(function($bindings = null) use ($mockPdoStatement) { + ->will($this->returnCallback(function ($bindings = null) use ($mockPdoStatement) { if ($bindings) { $mockPdoStatement->bindings = $bindings; } - })); + })) + ; $this->mockPdoStatement ->expects($this->any()) ->method('fetchAll') - ->will($this->returnCallback(function() use ($mockPdoStatement){ - return array($mockPdoStatement->sql, $mockPdoStatement->bindings); - })); + ->will($this->returnCallback(function () use ($mockPdoStatement) { + return [$mockPdoStatement->sql, $mockPdoStatement->bindings]; + })) + ; - $this->mockPdo = $this->getMock(MockPdo::class, array('prepare', 'setAttribute', 'quote', 'lastInsertId')); + $this->mockPdo = $this + ->getMockBuilder(MockPdo::class) + ->setMethods(['prepare', 'setAttribute', 'quote', 'lastInsertId']) + ->getMock() + ; $this->mockPdo ->expects($this->any()) ->method('prepare') - ->will($this->returnCallback(function($sql) use ($mockPdoStatement){ + ->will($this->returnCallback(function ($sql) use ($mockPdoStatement) { $mockPdoStatement->sql = $sql; + return $mockPdoStatement; - })); + })) + ; $this->mockPdo ->expects($this->any()) ->method('quote') - ->will($this->returnCallback(function($value){ + ->will($this->returnCallback(function ($value) { return "'$value'"; - })); + })) + ; $eventHandler = new EventHandler(); $this->mockConnection = m::mock(Connection::class); $this->mockConnection->shouldReceive('getPdoInstance')->andReturn($this->mockPdo); $this->mockConnection->shouldReceive('getAdapter')->andReturn('mysql'); - $this->mockConnection->shouldReceive('getAdapterConfig')->andReturn(array('prefix' => 'cb_')); + $this->mockConnection->shouldReceive('getAdapterConfig')->andReturn(['prefix' => 'cb_']); $this->mockConnection->shouldReceive('getContainer')->andReturn($this->container); $this->mockConnection->shouldReceive('getEventHandler')->andReturn($eventHandler); } @@ -78,18 +114,20 @@ public function tearDown() { m::close(); } - - public function callbackMock() - { - $args = func_get_args(); - return count($args) == 1 ? $args[0] : $args; - } } +/** + * Class MockPdo + * + * @package Pecee\Pixie + */ class MockPdo extends \PDO { + /** + * MockPdo constructor. + */ public function __construct() { } -} \ No newline at end of file +} From 8805b52fe5d584e3f1b1ab451543deb8d2ec8eb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sun, 26 Nov 2017 17:10:18 +0100 Subject: [PATCH 18/27] operator type --- .../Pixie/QueryBuilder/NestedCriteria.php | 11 +++++----- .../QueryBuilder/QueryBuilderHandler.php | 20 +++++++++---------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/Pecee/Pixie/QueryBuilder/NestedCriteria.php b/src/Pecee/Pixie/QueryBuilder/NestedCriteria.php index 1941d99..5e9b4bb 100644 --- a/src/Pecee/Pixie/QueryBuilder/NestedCriteria.php +++ b/src/Pecee/Pixie/QueryBuilder/NestedCriteria.php @@ -10,15 +10,16 @@ class NestedCriteria extends QueryBuilderHandler { /** - * @param string|Raw|\Closure $key - * @param string|Raw|\Closure|null $operator + * @param string|Raw|\Closure $key + * @param string|null $operator * @param string|Raw|\Closure|null $value - * @param string $joiner + * @param string $joiner + * * @return static */ - protected function whereHandler($key, $operator = null, $value = null, $joiner = 'AND') + protected function whereHandler($key, string $operator = null, $value = null, $joiner = 'AND') { - $key = $this->addTablePrefix($key); + $key = $this->addTablePrefix($key); $this->statements['criteria'][] = compact('key', 'operator', 'value', 'joiner'); return $this; diff --git a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php index ee602a1..394ca80 100644 --- a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php +++ b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php @@ -734,7 +734,7 @@ public function offset($offset) /** * Add on duplicate key statement. * - * @param string $data + * @param string|array $data * * @return static */ @@ -1184,7 +1184,7 @@ public function table($tables) $tTables[] = $value; } } - $tables = $this->addTablePrefix($tTables, false); + $tTables = $this->addTablePrefix($tTables, false); $instance->addStatement('tables', $tTables); return $instance; @@ -1306,13 +1306,13 @@ public function whereBetween($key, $valueFrom, $valueTo) * Handles where statements * * @param string|Raw|\Closure $key - * @param string|Raw|\Closure|null $operator + * @param string|null $operator * @param string|Raw|\Closure|null $value * @param string $joiner * * @return static */ - protected function whereHandler($key, $operator = null, $value = null, $joiner = 'AND') + protected function whereHandler($key, string $operator = null, $value = null, $joiner = 'AND') { $key = $this->addTablePrefix($key); $this->statements['wheres'][] = compact('key', 'operator', 'value', 'joiner'); @@ -1369,11 +1369,11 @@ public function whereNotIn($key, $values) /** * Adds WHERE NOT NULL statement to the current query. * - * @param string|Raw|\Closure $key + * @param string $key * * @return static */ - public function whereNotNull($key) + public function whereNotNull(string $key) { return $this->whereNullHandler($key, 'NOT'); } @@ -1393,13 +1393,13 @@ public function whereNull($key) /** * Handles WHERE NULL statements. * - * @param string|Raw|\Closure $key - * @param string $prefix - * @param string $operator + * @param string $key + * @param string $prefix + * @param string $operator * * @return mixed */ - protected function whereNullHandler($key, $prefix = '', $operator = '') + protected function whereNullHandler(string $key, $prefix = '', $operator = '') { $key = $this->adapterInstance->wrapSanitizer($this->addTablePrefix($key)); From 8f1d28970158008b5b67e23b25e93d6dff23bdc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sun, 26 Nov 2017 17:33:32 +0100 Subject: [PATCH 19/27] travis conf --- .idea/php-test-framework.xml | 2 +- .travis.yml | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.idea/php-test-framework.xml b/.idea/php-test-framework.xml index 4c73bb7..9d2103d 100644 --- a/.idea/php-test-framework.xml +++ b/.idea/php-test-framework.xml @@ -5,7 +5,7 @@ - + diff --git a/.travis.yml b/.travis.yml index aa02544..1832c6a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,7 @@ sudo: false language: php php: - - 5.4 - - 5.5 + - 5.6 - 7.0 before_script: @@ -12,4 +11,4 @@ before_script: - php composer.phar install --prefer-source --no-interaction script: - - ./vendor/bin/phpunit \ No newline at end of file + - ./vendor/bin/phpunit From 9dd8e61f54c9d0a4b4424a96d18857ee8518ed1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sun, 26 Nov 2017 17:39:50 +0100 Subject: [PATCH 20/27] CI: scrutnizer & codeclimate --- .codeclimate.yml | 22 ++++++++++++++++++++++ .scrutinizer.yml | 8 ++++++++ .travis.yml | 1 - 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 .codeclimate.yml create mode 100644 .scrutinizer.yml diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 0000000..467daf9 --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,22 @@ +engines: + phpmd: + enabled: true + checks: + Design/TooManyPublicMethods: + enabled: true + Naming/ShortVariable: + enabled: true + CleanCode/StaticAccess: + enabled: true + Controversial/CamelCaseMethodName: + enabled: true + fixme: + enabled: true + duplication: + enabled: true + config: + languages: + - php: +ratings: + paths: + - src/** diff --git a/.scrutinizer.yml b/.scrutinizer.yml new file mode 100644 index 0000000..b6680db --- /dev/null +++ b/.scrutinizer.yml @@ -0,0 +1,8 @@ +build: + tests: + override: + - + command: './vendor/bin/phpunit --coverage-clover=coverage.clover' + coverage: + file: 'coverage.clover' + format: 'clover' diff --git a/.travis.yml b/.travis.yml index 1832c6a..ee4da66 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ sudo: false language: php php: - - 5.6 - 7.0 before_script: From 390bb3a00624970db020f29ae0d000150a3c6cdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sun, 26 Nov 2017 17:53:39 +0100 Subject: [PATCH 21/27] code coverage --- .gitignore | 2 +- phpunit.xml | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 48f78b0..7186bab 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ composer.phar .DS_Store -test.php *~ /devel/ +/coverage.clover ### Composer template /vendor/ diff --git a/phpunit.xml b/phpunit.xml index 164567e..a8be32d 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -15,4 +15,9 @@ tests/Pecee/Pixie/ + + + src + + From d734acbb54e10a5aaf2f9caad60b96f3cb3aeee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sun, 26 Nov 2017 17:59:23 +0100 Subject: [PATCH 22/27] scrutnizer --- .scrutinizer.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.scrutinizer.yml b/.scrutinizer.yml index b6680db..0608e7a 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -6,3 +6,8 @@ build: coverage: file: 'coverage.clover' format: 'clover' +checks: + php: + code_rating: true + duplication: true + From 10a2558836bbc55f96b8346daf9d1ed81b86ec6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sun, 26 Nov 2017 19:14:52 +0100 Subject: [PATCH 23/27] better support for transaction --- .../QueryBuilder/QueryBuilderHandler.php | 22 +++---- src/Pecee/Pixie/QueryBuilder/Transaction.php | 57 +++++++++++++++---- .../QueryBuilder/TransactionHaltException.php | 12 ---- 3 files changed, 59 insertions(+), 32 deletions(-) delete mode 100644 src/Pecee/Pixie/QueryBuilder/TransactionHaltException.php diff --git a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php index 394ca80..7900763 100644 --- a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php +++ b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php @@ -1199,26 +1199,28 @@ public function table($tables) */ public function transaction(\Closure $callback) { + /** + * Get the Transaction class + * + * @var \Pecee\Pixie\QueryBuilder\Transaction $queryTransaction + */ + $queryTransaction = $this->container->build(Transaction::class, [$this->connection]); + $inTransaction = $queryTransaction->inTransaction(); try { // Begin the PDO transaction - $this->pdo->beginTransaction(); - - // Get the Transaction class - $transaction = $this->container->build(Transaction::class, [$this->connection]); + $queryTransaction->begin($inTransaction); // Call closure - $callback($transaction); + $callback($queryTransaction); // If no errors have been thrown or the transaction wasn't completed within the closure, commit the changes - $this->pdo->commit(); + $queryTransaction->commit($inTransaction); return $this; - } catch (TransactionHaltException $e) { - // Commit or rollback behavior has been handled in the closure, so exit - return $this; + } catch (\Exception $e) { // something happened, rollback changes - $this->pdo->rollBack(); + $queryTransaction->rollBack($inTransaction); return $this; } diff --git a/src/Pecee/Pixie/QueryBuilder/Transaction.php b/src/Pecee/Pixie/QueryBuilder/Transaction.php index f193f89..d87332c 100644 --- a/src/Pecee/Pixie/QueryBuilder/Transaction.php +++ b/src/Pecee/Pixie/QueryBuilder/Transaction.php @@ -9,26 +9,63 @@ */ class Transaction extends QueryBuilderHandler { + /** + * Check if we are in transaction + * + * @return bool + */ + public function inTransaction(): bool + { + return $this->pdo()->inTransaction(); + } /** - * Commit the database changes + * Begin transaction * - * @throws TransactionHaltException + * @param bool $inTransaction + * + * @return $this */ - public function commit() + public function begin(bool $inTransaction = false) { - $this->pdo->commit(); - throw new TransactionHaltException('Commit'); + if (false === $inTransaction) { + $this->pdo()->beginTransaction(); + } + + return $this; } /** - * Rollback the database changes + * Commit transaction + * + * @param bool $inTransaction * - * @throws TransactionHaltException + * @return $this */ - public function rollback() + public function commit(bool $inTransaction = false) { - $this->pdo->rollBack(); - throw new TransactionHaltException('Rollback'); + if (false === $inTransaction) { + $this->pdo()->commit(); + } + + return $this; } + + /** + * RollBack transaction + * + * @param bool $inTransaction + * + * @return $this + */ + public function rollBack(bool $inTransaction = false) + { + if (false === $inTransaction) { + $this->pdo()->rollBack(); + } + + return $this; + } + + } diff --git a/src/Pecee/Pixie/QueryBuilder/TransactionHaltException.php b/src/Pecee/Pixie/QueryBuilder/TransactionHaltException.php deleted file mode 100644 index 8c42b6e..0000000 --- a/src/Pecee/Pixie/QueryBuilder/TransactionHaltException.php +++ /dev/null @@ -1,12 +0,0 @@ - Date: Sun, 26 Nov 2017 19:15:28 +0100 Subject: [PATCH 24/27] throwing Exception in transaction --- src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php index 7900763..813a4f0 100644 --- a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php +++ b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php @@ -1219,10 +1219,10 @@ public function transaction(\Closure $callback) return $this; } catch (\Exception $e) { - // something happened, rollback changes + // something happened, rollback changes and throw Exception $queryTransaction->rollBack($inTransaction); - return $this; + throw $e; } } From 6251389dd9e647fc46362e0f7aa92903effbb698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Tue, 28 Nov 2017 22:02:01 +0100 Subject: [PATCH 25/27] Optimisations - Merge php issue for PDO params fix (issue: #26) - Added optcode optimisations for PHP7 function calls. - Fixed some return types. --- .idea/markdown-navigator.xml | 72 +++++++++++++ .../markdown-navigator/profiles_settings.xml | 3 + .idea/pecee-pixie.iml | 28 ----- .idea/php.xml | 2 +- src/Pecee/Pixie/ConnectionAdapters/Mysql.php | 2 +- src/Pecee/Pixie/ConnectionAdapters/Pgsql.php | 2 +- src/Pecee/Pixie/ConnectionAdapters/Sqlite.php | 2 +- src/Pecee/Pixie/EventHandler.php | 8 +- .../QueryBuilder/Adapters/BaseAdapter.php | 24 ++--- .../QueryBuilder/QueryBuilderHandler.php | 101 +++++++----------- src/Pecee/Pixie/QueryBuilder/QueryObject.php | 6 +- src/Pecee/Pixie/QueryBuilder/Raw.php | 2 +- 12 files changed, 140 insertions(+), 112 deletions(-) create mode 100644 .idea/markdown-navigator.xml create mode 100644 .idea/markdown-navigator/profiles_settings.xml diff --git a/.idea/markdown-navigator.xml b/.idea/markdown-navigator.xml new file mode 100644 index 0000000..e41dd85 --- /dev/null +++ b/.idea/markdown-navigator.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/markdown-navigator/profiles_settings.xml b/.idea/markdown-navigator/profiles_settings.xml new file mode 100644 index 0000000..57927c5 --- /dev/null +++ b/.idea/markdown-navigator/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/pecee-pixie.iml b/.idea/pecee-pixie.iml index cdea25f..0e4c427 100644 --- a/.idea/pecee-pixie.iml +++ b/.idea/pecee-pixie.iml @@ -45,9 +45,6 @@ - - - @@ -59,18 +56,13 @@ - - - - - @@ -79,9 +71,6 @@ - - - @@ -93,34 +82,17 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml index c057fbd..51cda52 100644 --- a/.idea/php.xml +++ b/.idea/php.xml @@ -39,7 +39,7 @@ - + \ No newline at end of file diff --git a/src/Pecee/Pixie/ConnectionAdapters/Mysql.php b/src/Pecee/Pixie/ConnectionAdapters/Mysql.php index 439367c..99b1edf 100644 --- a/src/Pecee/Pixie/ConnectionAdapters/Mysql.php +++ b/src/Pecee/Pixie/ConnectionAdapters/Mysql.php @@ -17,7 +17,7 @@ class Mysql extends BaseAdapter */ protected function doConnect(array $config) { - if (extension_loaded('pdo_mysql') === false) { + if (\extension_loaded('pdo_mysql') === false) { throw new Exception(sprintf('%s library not loaded', 'pdo_mysql')); } diff --git a/src/Pecee/Pixie/ConnectionAdapters/Pgsql.php b/src/Pecee/Pixie/ConnectionAdapters/Pgsql.php index 3bb5cd1..171007a 100644 --- a/src/Pecee/Pixie/ConnectionAdapters/Pgsql.php +++ b/src/Pecee/Pixie/ConnectionAdapters/Pgsql.php @@ -17,7 +17,7 @@ class Pgsql extends BaseAdapter */ protected function doConnect(array $config) { - if (extension_loaded('pdo_pgsql') === false) { + if (\extension_loaded('pdo_pgsql') === false) { throw new Exception(sprintf('%s library not loaded', 'pdo_pgsql')); } diff --git a/src/Pecee/Pixie/ConnectionAdapters/Sqlite.php b/src/Pecee/Pixie/ConnectionAdapters/Sqlite.php index 7fa6b4c..25a783d 100644 --- a/src/Pecee/Pixie/ConnectionAdapters/Sqlite.php +++ b/src/Pecee/Pixie/ConnectionAdapters/Sqlite.php @@ -17,7 +17,7 @@ class Sqlite extends BaseAdapter */ public function doConnect(array $config) { - if (extension_loaded('pdo_sqlite') === false) { + if (\extension_loaded('pdo_sqlite') === false) { throw new Exception(sprintf('%s library not loaded', 'pdo_sqlite')); } diff --git a/src/Pecee/Pixie/EventHandler.php b/src/Pecee/Pixie/EventHandler.php index 4dce129..f98588d 100644 --- a/src/Pecee/Pixie/EventHandler.php +++ b/src/Pecee/Pixie/EventHandler.php @@ -35,13 +35,13 @@ class EventHandler public function fireEvents(QueryBuilderHandler $queryBuilder, string $event) { $statements = $queryBuilder->getStatements(); - $tables = isset($statements['tables']) ? $statements['tables'] : []; + $tables = $statements['tables'] ?? []; // Events added with :any will be fired in case of any table, // we are adding :any as a fake table at the beginning. array_unshift($tables, static::TABLE_ANY); - $handlerParams = func_get_args(); + $handlerParams = \func_get_args(); unset($handlerParams[1]); // Fire all events @@ -55,7 +55,7 @@ public function fireEvents(QueryBuilderHandler $queryBuilder, string $event) // Fire event and add to fired list $this->firedEvents[] = $eventId; - $result = call_user_func_array($action, $handlerParams); + $result = \call_user_func_array($action, $handlerParams); if ($result !== null) { return $result; } @@ -91,7 +91,7 @@ public function getEvent(string $event, $table = null) } } - return isset($this->events[$table][$event]) ? $this->events[$table][$event] : null; + return $this->events[$table][$event] ?? null; } /** diff --git a/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php b/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php index 17133ef..50ad1b9 100644 --- a/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php +++ b/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php @@ -50,7 +50,7 @@ public function __construct(Connection $connection) * * @return string */ - protected function arrayStr(array $pieces, $glue = ',', $wrapSanitizer = true) + protected function arrayStr(array $pieces, $glue = ',', $wrapSanitizer = true): string { $str = ''; foreach ($pieces as $key => $piece) { @@ -58,7 +58,7 @@ protected function arrayStr(array $pieces, $glue = ',', $wrapSanitizer = true) $piece = $this->wrapSanitizer($piece); } - if (is_int($key) === false) { + if (\is_int($key) === false) { $piece = ($wrapSanitizer ? $this->wrapSanitizer($key) : $key) . ' AS ' . $piece; } @@ -115,7 +115,7 @@ protected function buildCriteria(array $statements, $bindValues = true) continue; } - if (is_array($value) === true) { + if (\is_array($value) === true) { // Where in or between like query $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator']; @@ -158,11 +158,11 @@ protected function buildCriteria(array $statements, $bindValues = true) if ($statement['operator'] !== null) { $criteria .= "{$statement['joiner']} {$key} {$statement['operator']} ? "; - $bindings[] = (array)$statement['key']->getBindings(); + $bindings[] = $statement['key']->getBindings(); $bindings[] = (array)$value; } else { $criteria .= $statement['joiner'] . ' ' . $key . ' '; - $bindings[] = (array)$statement['key']->getBindings(); + $bindings[] = $statement['key']->getBindings(); } continue; @@ -175,10 +175,10 @@ protected function buildCriteria(array $statements, $bindValues = true) $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' ' . $valuePlaceholder . ' '; } - $bindings = array_merge(...$bindings); + $bindings = \array_merge(...$bindings); // Clear all white spaces, and, or from beginning and white spaces from ending - $criteria = preg_replace('/^(\s?AND ?|\s?OR ?)|\s$/i', '', $criteria); + $criteria = \preg_replace('/^(\s?AND ?|\s?OR ?)|\s$/i', '', $criteria); return [$criteria, $bindings]; } @@ -223,12 +223,12 @@ protected function buildJoin(array $statements) { $sql = ''; - if (array_key_exists('joins', $statements) === false || count($statements['joins']) === 0) { + if (\array_key_exists('joins', $statements) === false || \count($statements['joins']) === 0) { return $sql; } foreach ((array)$statements['joins'] as $joinArr) { - if (is_array($joinArr['table']) === true) { + if (\is_array($joinArr['table']) === true) { list($mainTable, $aliasTable) = $joinArr['table']; $table = $this->wrapSanitizer($mainTable) . ' AS ' . $this->wrapSanitizer($aliasTable); } else { @@ -349,7 +349,7 @@ private function doInsert(array $statements, array $data, $type) if (isset($statements['onduplicate']) === true) { - if (count($statements['onduplicate']) < 1) { + if (\count($statements['onduplicate']) < 1) { throw new Exception('No data given.', 4); } @@ -490,7 +490,7 @@ public function select(array $statements) // ORDER BY $orderBys = ''; - if (isset($statements['orderBys']) && is_array($statements['orderBys'])) { + if (isset($statements['orderBys']) && \is_array($statements['orderBys'])) { foreach ($statements['orderBys'] as $orderBy) { $orderBys .= $this->wrapSanitizer($orderBy['field']) . ' ' . $orderBy['type'] . ', '; } @@ -545,7 +545,7 @@ public function select(array $statements) */ public function update(array $statements, array $data) { - if (count($data) < 1) { + if (\count($data) < 1) { throw new Exception('No data given.', 4); } diff --git a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php index 813a4f0..0c10971 100644 --- a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php +++ b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php @@ -80,7 +80,7 @@ class QueryBuilderHandler ]; /** - * @var \PDO + * @var PDO */ protected $pdo; @@ -125,7 +125,7 @@ public function __construct(Connection $connection = null) { $this->connection = $connection ?? Connection::getStoredConnection(); - if (is_null($this->connection)) { + if ($this->connection === null) { throw new Exception('No database connection found.', 1); } @@ -144,7 +144,7 @@ public function __construct(Connection $connection = null) [$this->connection] ); - $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } /** @@ -180,7 +180,7 @@ public function addTablePrefix($values, bool $tableFieldMix = true) // If supplied value is not an array then make it one $single = false; - if (is_array($values) === false) { + if (\is_array($values) === false) { $values = [$values]; // We had single value, so should return a single value @@ -199,7 +199,7 @@ public function addTablePrefix($values, bool $tableFieldMix = true) // If key is not integer, it is likely a alias mapping, so we need to change prefix target $target = &$value; - if (is_int($key) === false) { + if (\is_int($key) === false) { $target = &$key; } @@ -222,10 +222,10 @@ public function addTablePrefix($values, bool $tableFieldMix = true) * @throws Exception * @return int */ - protected function aggregate(string $type) + protected function aggregate(string $type): int { // Get the current selects - $mainSelects = isset($this->statements['selects']) ? $this->statements['selects'] : null; + $mainSelects = $this->statements['selects'] ?? null; // Replace select with a scalar value like `count` $this->statements['selects'] = [$this->raw($type . '(*) AS `field`')]; @@ -239,10 +239,10 @@ protected function aggregate(string $type) } if (isset($row[0]) === true) { - if (is_array($row[0]) === true) { + if (\is_array($row[0]) === true) { return (int)$row[0]['field']; } - if (is_object($row[0]) === true) { + if (\is_object($row[0]) === true) { return (int)$row[0]->field; } } @@ -276,7 +276,7 @@ public function alias(string $table, string $alias) */ public function asObject(string $className, array $constructorArgs = []): QueryBuilderHandler { - return $this->setFetchMode(\PDO::FETCH_CLASS, $className, $constructorArgs); + return $this->setFetchMode(PDO::FETCH_CLASS, $className, $constructorArgs); } /** @@ -329,7 +329,7 @@ public function delete(): \PDOStatement private function doInsert(array $data, string $type) { // If first value is not an array - it's not a batch insert - if (is_array(current($data)) === false) { + if (\is_array(current($data)) === false) { $queryObject = $this->getQuery($type, $data); $this->fireEvents(static::EVENT_BEFORE_INSERT, $queryObject); @@ -399,10 +399,10 @@ public function findAll(string $fieldName, $value) */ public function fireEvents($name, $parameters = null) { - $params = func_get_args(); + $params = \func_get_args(); array_unshift($params, $this); - return call_user_func_array([$this->connection->getEventHandler(), 'fireEvents'], $params); + return \call_user_func_array([$this->connection->getEventHandler(), 'fireEvents'], $params); } /** @@ -415,7 +415,7 @@ public function first() { $result = $this->limit(1)->get(); - return ($result !== null && count($result) > 0) ? $result[0] : null; + return ($result !== null && \count($result) > 0) ? $result[0] : null; } /** @@ -427,8 +427,8 @@ public function first() */ public function from($tables) { - if (is_array($tables) === false) { - $tables = func_get_args(); + if (\is_array($tables) === false) { + $tables = \func_get_args(); } $tables = $this->addTablePrefix($tables, false); @@ -464,7 +464,7 @@ public function get(): array $start = microtime(true); $this->fireEvents(static::EVENT_BEFORE_SELECT, $queryObject); - $result = call_user_func_array([$this->pdoStatement, 'fetchAll'], $this->fetchParameters); + $result = \call_user_func_array([$this->pdoStatement, 'fetchAll'], $this->fetchParameters); $executionTime += microtime(true) - $start; $this->pdoStatement = null; $this->fireEvents(static::EVENT_AFTER_SELECT, $queryObject, $result, $executionTime); @@ -516,7 +516,7 @@ public function getQuery(string $type = 'select', $dataToBePassed = null): Query 'criteriaonly', ]; - if (in_array(strtolower($type), $allowedTypes, true) === false) { + if (\in_array(strtolower($type), $allowedTypes, true) === false) { throw new Exception($type . ' is not a known type.', 2); } @@ -551,7 +551,7 @@ public function groupBy($field) $field = $this->addTablePrefix($field); } - if (is_array($field) === true) { + if (\is_array($field) === true) { $this->statements['groupBys'] = array_merge($this->statements['groupBys'], $field); } else { $this->statements['groupBys'][] = $field; @@ -771,7 +771,7 @@ public function orHaving($key, $operator, $value) public function orWhere($key, $operator = null, $value = null) { // If two params are given then assume operator is = - if (func_num_args() === 2) { + if (\func_num_args() === 2) { $value = $operator; $operator = '='; } @@ -818,7 +818,7 @@ public function orWhereIn($key, $values) public function orWhereNot($key, $operator = null, $value = null) { // If two params are given then assume operator is = - if (func_num_args() === 2) { + if (\func_num_args() === 2) { $value = $operator; $operator = '='; } @@ -873,7 +873,7 @@ public function orWhereNull($key) */ public function orderBy($fields, $defaultDirection = 'ASC') { - if (is_array($fields) === false) { + if (\is_array($fields) === false) { $fields = [$fields]; } @@ -881,7 +881,7 @@ public function orderBy($fields, $defaultDirection = 'ASC') $field = $key; $type = $value; - if (is_int($key) === true) { + if (\is_int($key) === true) { $field = $value; $type = $defaultDirection; } @@ -896,26 +896,6 @@ public function orderBy($fields, $defaultDirection = 'ASC') return $this; } - /** - * Parses correct data-type for PDO parameter. - * - * @param mixed $value - * - * @return int PDO-parameter type - */ - protected function parseDataType($value) - { - if (is_int($value) === true) { - return PDO::PARAM_INT; - } - - if (is_bool($value) === true) { - return PDO::PARAM_BOOL; - } - - return PDO::PARAM_STR; - } - /** * Return PDO instance * @@ -971,10 +951,10 @@ public function query($sql, array $bindings = []) * * @return Raw */ - public function raw($value, $bindings = null) + public function raw($value, $bindings = null): Raw { - if (is_array($bindings) === false) { - $bindings = func_get_args(); + if (\is_array($bindings) === false) { + $bindings = \func_get_args(); array_shift($bindings); } @@ -1049,8 +1029,8 @@ public function rightJoin($table, $key, $operator = null, $value = null) */ public function select($fields) { - if (is_array($fields) === false) { - $fields = func_get_args(); + if (\is_array($fields) === false) { + $fields = \func_get_args(); } $fields = $this->addTablePrefix($fields); @@ -1097,7 +1077,7 @@ public function setConnection(Connection $connection) */ public function setFetchMode($parameters = null) { - $this->fetchParameters = func_get_args(); + $this->fetchParameters = \func_get_args(); return $this; } @@ -1110,7 +1090,7 @@ public function setFetchMode($parameters = null) * * @return array PDOStatement and execution time as float */ - public function statement(string $sql, array $bindings = []) + public function statement(string $sql, array $bindings = []): array { $start = microtime(true); @@ -1118,9 +1098,9 @@ public function statement(string $sql, array $bindings = []) foreach ($bindings as $key => $value) { $pdoStatement->bindValue( - is_int($key) ? $key + 1 : $key, + \is_int($key) ? $key + 1 : $key, $value, - $this->parseDataType($value) + (\is_int($value) || \is_bool($value)) ? PDO::PARAM_INT : PDO::PARAM_STR ); } @@ -1139,7 +1119,7 @@ public function statement(string $sql, array $bindings = []) * @throws Exception * @return Raw */ - public function subQuery(QueryBuilderHandler $queryBuilder, $alias = null) + public function subQuery(QueryBuilderHandler $queryBuilder, $alias = null): Raw { $sql = '(' . $queryBuilder->getQuery()->getRawSql() . ')'; if ($alias !== null) { @@ -1170,14 +1150,14 @@ public function subQuery(QueryBuilderHandler $queryBuilder, $alias = null) public function table($tables) { $tTables = []; - if (is_array($tables) === false) { + if (\is_array($tables) === false) { // Because a single table is converted to an array anyways, this makes sense. - $tables = func_get_args(); + $tables = \func_get_args(); } $instance = new static($this->connection); foreach ($tables as $key => $value) { - if (is_string($key)) { + if (\is_string($key)) { $instance->alias($key, $value); $tTables[] = $key; } else { @@ -1194,7 +1174,7 @@ public function table($tables) * Performs the transaction * * @param \Closure $callback - * + * @throws \Exception * @return static */ public function transaction(\Closure $callback) @@ -1203,6 +1183,7 @@ public function transaction(\Closure $callback) * Get the Transaction class * * @var \Pecee\Pixie\QueryBuilder\Transaction $queryTransaction + * @throws \Exception */ $queryTransaction = $this->container->build(Transaction::class, [$this->connection]); $inTransaction = $queryTransaction->inTransaction(); @@ -1278,12 +1259,12 @@ public function updateOrInsert($data) public function where($key, $operator = null, $value = null) { // If two params are given then assume operator is = - if (func_num_args() === 2) { + if (\func_num_args() === 2) { $value = $operator; $operator = '='; } - if (is_bool($value) === true) { + if (\is_bool($value) === true) { $value = (int)$value; } @@ -1347,7 +1328,7 @@ public function whereIn($key, $values) public function whereNot($key, $operator = null, $value = null) { // If two params are given then assume operator is = - if (func_num_args() === 2) { + if (\func_num_args() === 2) { $value = $operator; $operator = '='; } diff --git a/src/Pecee/Pixie/QueryBuilder/QueryObject.php b/src/Pecee/Pixie/QueryBuilder/QueryObject.php index 4b3dff7..4bd1463 100644 --- a/src/Pecee/Pixie/QueryBuilder/QueryObject.php +++ b/src/Pecee/Pixie/QueryBuilder/QueryObject.php @@ -84,14 +84,14 @@ protected function interpolateQuery($query, $params) // build a regular expression for each parameter foreach ($params as $key => $value) { - $keys[] = '/' . (is_string($key) ? ':' . $key : '[?]') . '/'; + $keys[] = '/' . (\is_string($key) ? ':' . $key : '[?]') . '/'; - if (is_string($value) === true) { + if (\is_string($value) === true) { $values[$key] = $this->pdo->quote($value); continue; } - if (is_array($value) === true) { + if (\is_array($value) === true) { $values[$key] = $this->pdo->quote(implode(',', $value)); continue; } diff --git a/src/Pecee/Pixie/QueryBuilder/Raw.php b/src/Pecee/Pixie/QueryBuilder/Raw.php index fe25846..20a3803 100644 --- a/src/Pecee/Pixie/QueryBuilder/Raw.php +++ b/src/Pecee/Pixie/QueryBuilder/Raw.php @@ -43,7 +43,7 @@ public function __toString() /** * @return array */ - public function getBindings() + public function getBindings(): array { return $this->bindings; } From 1676767d04e2c1ca4175746363f129ac392fe302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Tue, 28 Nov 2017 22:11:13 +0100 Subject: [PATCH 26/27] Updated PHP version in documentation. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d8595d2..d9a0bbd 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ This library is stable, maintained and are used by many sites, including: - [BookAndBegin.com](https://bookandbegin.com) **Requirements:** -- PHP version 5.5 or higher is required. +- PHP version 7.0 or higher is required. #### Feedback and development From 26c461139af6d0dd943a7e50a9444072f36b0628 Mon Sep 17 00:00:00 2001 From: Simon Sessingo Date: Sat, 2 Dec 2017 20:21:34 +0100 Subject: [PATCH 27/27] Development - Removed depricated prefix method. - Made table parameter in alias method optional. - Removed .idea folder as it was causing issues. --- .gitignore | 1 + .idea/codeStyleSettings.xml | 9 -- .idea/composerJson.xml | 9 -- .idea/deployment.xml | 43 ------- .idea/inspectionProfiles/Project_Default.xml | 15 --- .idea/markdown-navigator.xml | 72 ------------ .../markdown-navigator/profiles_settings.xml | 3 - .idea/modules.xml | 8 -- .idea/pecee-pixie.iml | 98 ---------------- .idea/php-test-framework.xml | 14 --- .idea/php.xml | 45 ------- .idea/vcs.xml | 6 - README.md | 15 +++ composer.json | 2 +- composer.lock | 110 +++++++++--------- .../QueryBuilder/QueryBuilderHandler.php | 51 ++++---- .../Pecee/Pixie/QueryBuilderBehaviorTest.php | 31 ++++- 17 files changed, 124 insertions(+), 408 deletions(-) delete mode 100644 .idea/codeStyleSettings.xml delete mode 100644 .idea/composerJson.xml delete mode 100644 .idea/deployment.xml delete mode 100644 .idea/inspectionProfiles/Project_Default.xml delete mode 100644 .idea/markdown-navigator.xml delete mode 100644 .idea/markdown-navigator/profiles_settings.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/pecee-pixie.iml delete mode 100644 .idea/php-test-framework.xml delete mode 100644 .idea/php.xml delete mode 100644 .idea/vcs.xml diff --git a/.gitignore b/.gitignore index 7186bab..233f5da 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ composer.phar *~ /devel/ /coverage.clover +.idea/ ### Composer template /vendor/ diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml deleted file mode 100644 index c4c9543..0000000 --- a/.idea/codeStyleSettings.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/composerJson.xml b/.idea/composerJson.xml deleted file mode 100644 index 4199499..0000000 --- a/.idea/composerJson.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/deployment.xml b/.idea/deployment.xml deleted file mode 100644 index 9cbabb7..0000000 --- a/.idea/deployment.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index 3d2c9cb..0000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/markdown-navigator.xml b/.idea/markdown-navigator.xml deleted file mode 100644 index e41dd85..0000000 --- a/.idea/markdown-navigator.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/markdown-navigator/profiles_settings.xml b/.idea/markdown-navigator/profiles_settings.xml deleted file mode 100644 index 57927c5..0000000 --- a/.idea/markdown-navigator/profiles_settings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index af9f61b..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/pecee-pixie.iml b/.idea/pecee-pixie.iml deleted file mode 100644 index 0e4c427..0000000 --- a/.idea/pecee-pixie.iml +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/php-test-framework.xml b/.idea/php-test-framework.xml deleted file mode 100644 index 9d2103d..0000000 --- a/.idea/php-test-framework.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml deleted file mode 100644 index 51cda52..0000000 --- a/.idea/php.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index d9a0bbd..30d0086 100644 --- a/README.md +++ b/README.md @@ -303,6 +303,21 @@ $result = $queryBuilder ### Select +#### Select from query + +You can easily select items from another query by using + +```php +$subQuery = $queryBuilder->table('person'); +$builder = $queryBuilder->table($queryBuilder->subQuery($subQuery))->where('id', '=', 2); +``` + +Will produce the following output: + +```sql +SELECT * FROM (SELECT * FROM `person`) WHERE `id` = 2 +``` + #### Select single field ```php diff --git a/composer.json b/composer.json index 5aca082..3734d78 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,7 @@ "usmanhalalit/viocon": "1.0.1" }, "require-dev": { - "phpunit/phpunit": "^6.4", + "phpunit/phpunit": "^6.0", "mockery/mockery": "^1" }, "autoload": { diff --git a/composer.lock b/composer.lock index e198b6e..d03cd81 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "55c47951e95f4087152d5ea0bd6f2ed4", + "content-hash": "431825fe68c6a6b27cb373d5aa657c4b", "packages": [ { "name": "usmanhalalit/viocon", @@ -57,32 +57,32 @@ "packages-dev": [ { "name": "doctrine/instantiator", - "version": "1.0.5", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", + "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", "shasum": "" }, "require": { - "php": ">=5.3,<8.0-DEV" + "php": "^7.1" }, "require-dev": { "athletic/athletic": "~0.1.8", "ext-pdo": "*", "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" + "phpunit/phpunit": "^6.2.3", + "squizlabs/php_codesniffer": "^3.0.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.2.x-dev" } }, "autoload": { @@ -107,7 +107,7 @@ "constructor", "instantiate" ], - "time": "2015-06-14T21:17:01+00:00" + "time": "2017-07-22T11:58:36+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -425,29 +425,35 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "4.1.1", + "version": "4.2.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "2d3d238c433cf69caeb4842e97a3223a116f94b2" + "reference": "66465776cfc249844bde6d117abff1d22e06c2da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/2d3d238c433cf69caeb4842e97a3223a116f94b2", - "reference": "2d3d238c433cf69caeb4842e97a3223a116f94b2", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/66465776cfc249844bde6d117abff1d22e06c2da", + "reference": "66465776cfc249844bde6d117abff1d22e06c2da", "shasum": "" }, "require": { "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0@dev", + "phpdocumentor/reflection-common": "^1.0.0", "phpdocumentor/type-resolver": "^0.4.0", "webmozart/assert": "^1.0" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^4.4" + "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\\": [ @@ -466,7 +472,7 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-08-30T18:51:59+00:00" + "time": "2017-11-27T17:38:31+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -580,16 +586,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "5.2.3", + "version": "5.2.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "8e1d2397d8adf59a3f12b2878a3aaa66d1ab189d" + "reference": "033ec97498cf530cc1be4199264cad568b19be26" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/8e1d2397d8adf59a3f12b2878a3aaa66d1ab189d", - "reference": "8e1d2397d8adf59a3f12b2878a3aaa66d1ab189d", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/033ec97498cf530cc1be4199264cad568b19be26", + "reference": "033ec97498cf530cc1be4199264cad568b19be26", "shasum": "" }, "require": { @@ -598,7 +604,7 @@ "php": "^7.0", "phpunit/php-file-iterator": "^1.4.2", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^2.0", + "phpunit/php-token-stream": "^2.0.1", "sebastian/code-unit-reverse-lookup": "^1.0.1", "sebastian/environment": "^3.0", "sebastian/version": "^2.0.1", @@ -640,20 +646,20 @@ "testing", "xunit" ], - "time": "2017-11-03T13:47:33+00:00" + "time": "2017-11-27T09:00:30+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "1.4.3", + "version": "1.4.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "8ebba84e5bd74fc5fdeb916b38749016c7232f93" + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/8ebba84e5bd74fc5fdeb916b38749016c7232f93", - "reference": "8ebba84e5bd74fc5fdeb916b38749016c7232f93", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", "shasum": "" }, "require": { @@ -687,7 +693,7 @@ "filesystem", "iterator" ], - "time": "2017-11-24T15:00:59+00:00" + "time": "2017-11-27T13:52:08+00:00" }, { "name": "phpunit/php-text-template", @@ -781,16 +787,16 @@ }, { "name": "phpunit/php-token-stream", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "9a02332089ac48e704c70f6cefed30c224e3c0b0" + "reference": "791198a2c6254db10131eecfe8c06670700904db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/9a02332089ac48e704c70f6cefed30c224e3c0b0", - "reference": "9a02332089ac48e704c70f6cefed30c224e3c0b0", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db", + "reference": "791198a2c6254db10131eecfe8c06670700904db", "shasum": "" }, "require": { @@ -826,20 +832,20 @@ "keywords": [ "tokenizer" ], - "time": "2017-08-20T05:47:52+00:00" + "time": "2017-11-27T05:48:46+00:00" }, { "name": "phpunit/phpunit", - "version": "6.4.4", + "version": "6.5.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "562f7dc75d46510a4ed5d16189ae57fbe45a9932" + "reference": "24b708f2fd725bcef1c8153b366043381aa324f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/562f7dc75d46510a4ed5d16189ae57fbe45a9932", - "reference": "562f7dc75d46510a4ed5d16189ae57fbe45a9932", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/24b708f2fd725bcef1c8153b366043381aa324f2", + "reference": "24b708f2fd725bcef1c8153b366043381aa324f2", "shasum": "" }, "require": { @@ -853,12 +859,12 @@ "phar-io/version": "^1.0", "php": "^7.0", "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^5.2.2", - "phpunit/php-file-iterator": "^1.4.2", + "phpunit/php-code-coverage": "^5.2.3", + "phpunit/php-file-iterator": "^1.4.3", "phpunit/php-text-template": "^1.2.1", "phpunit/php-timer": "^1.0.9", - "phpunit/phpunit-mock-objects": "^4.0.3", - "sebastian/comparator": "^2.0.2", + "phpunit/phpunit-mock-objects": "^5.0.4", + "sebastian/comparator": "^2.1", "sebastian/diff": "^2.0", "sebastian/environment": "^3.1", "sebastian/exporter": "^3.1", @@ -884,7 +890,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "6.4.x-dev" + "dev-master": "6.5.x-dev" } }, "autoload": { @@ -910,20 +916,20 @@ "testing", "xunit" ], - "time": "2017-11-08T11:26:09+00:00" + "time": "2017-12-02T05:36:24+00:00" }, { "name": "phpunit/phpunit-mock-objects", - "version": "4.0.4", + "version": "5.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "2f789b59ab89669015ad984afa350c4ec577ade0" + "reference": "16b50f4167e5e85e81ca8a3dd105d0a5fd32009a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/2f789b59ab89669015ad984afa350c4ec577ade0", - "reference": "2f789b59ab89669015ad984afa350c4ec577ade0", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/16b50f4167e5e85e81ca8a3dd105d0a5fd32009a", + "reference": "16b50f4167e5e85e81ca8a3dd105d0a5fd32009a", "shasum": "" }, "require": { @@ -936,7 +942,7 @@ "phpunit/phpunit": "<6.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^6.5" }, "suggest": { "ext-soap": "*" @@ -944,7 +950,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0.x-dev" + "dev-master": "5.0.x-dev" } }, "autoload": { @@ -959,7 +965,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -969,7 +975,7 @@ "mock", "xunit" ], - "time": "2017-08-03T14:08:16+00:00" + "time": "2017-12-02T05:31:19+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", diff --git a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php index 0c10971..159b38f 100644 --- a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php +++ b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php @@ -254,14 +254,20 @@ protected function aggregate(string $type): int * Add or change table alias * Example: table AS alias * - * @param string $table * @param string $alias + * @param string $table * * @return static */ - public function alias(string $table, string $alias) + public function alias(string $alias, string $table = null) { - $this->statements['aliases'][$this->tablePrefix . $table] = strtolower($alias); + if($table === null && isset($this->statements['tables'][0]) === true) { + $table = $this->statements['tables'][0]; + } else { + $table = $this->tablePrefix . $table; + } + + $this->statements['aliases'][$table] = strtolower($alias); return $this; } @@ -810,7 +816,7 @@ public function orWhereIn($key, $values) * Adds OR WHERE NOT statement to the current query. * * @param string|Raw|\Closure $key - * @param string|array|Raw|\Closure|null $operator + * @param string|null $operator * @param mixed|Raw|\Closure|null $value * * @return static @@ -906,24 +912,6 @@ public function pdo() return $this->pdo; } - /** - * Add or change table alias - * - * Example: table AS alias - * - * @deprecated This method will be removed in the near future, please use QueryBuilderHandler::alias instead. - * @see QueryBuilderHandler::alias - * - * @param string $table - * @param string $alias - * - * @return static - */ - public function prefix($table, $alias) - { - return $this->alias($table, $alias); - } - /** * Performs query. * @@ -1133,9 +1121,9 @@ public function subQuery(QueryBuilderHandler $queryBuilder, $alias = null): Raw * Sets the table that the query is using * * @param string|array $tables Single table or multiple tables as an array or as multiple parameters - * * @throws Exception * @return static + * * ``` * Examples: * - basic usage @@ -1156,9 +1144,10 @@ public function table($tables) } $instance = new static($this->connection); + foreach ($tables as $key => $value) { if (\is_string($key)) { - $instance->alias($key, $value); + $instance->alias($value, $key); $tTables[] = $key; } else { $tTables[] = $value; @@ -1251,7 +1240,7 @@ public function updateOrInsert($data) * Adds WHERE statement to the current query. * * @param string|Raw|\Closure $key - * @param string|Raw|\Closure|null $operator + * @param string|null $operator * @param mixed|Raw|\Closure|null $value * * @return static @@ -1352,11 +1341,11 @@ public function whereNotIn($key, $values) /** * Adds WHERE NOT NULL statement to the current query. * - * @param string $key + * @param string|Raw|\Closure $key * * @return static */ - public function whereNotNull(string $key) + public function whereNotNull($key) { return $this->whereNullHandler($key, 'NOT'); } @@ -1376,16 +1365,16 @@ public function whereNull($key) /** * Handles WHERE NULL statements. * - * @param string $key + * @param string|Raw|\Closure $key * @param string $prefix * @param string $operator * * @return mixed */ - protected function whereNullHandler(string $key, $prefix = '', $operator = '') + protected function whereNullHandler($key, $prefix = '', $operator = '') { $key = $this->adapterInstance->wrapSanitizer($this->addTablePrefix($key)); - - return $this->{$operator . 'Where'}($this->raw("{$key} IS {$prefix} NULL")); + $prefix = ($prefix !== '') ? $prefix . ' ' : $prefix; + return $this->{$operator . 'Where'}($this->raw("$key IS {$prefix}NULL")); } } diff --git a/tests/Pecee/Pixie/QueryBuilderBehaviorTest.php b/tests/Pecee/Pixie/QueryBuilderBehaviorTest.php index 9b93357..70ebdda 100644 --- a/tests/Pecee/Pixie/QueryBuilderBehaviorTest.php +++ b/tests/Pecee/Pixie/QueryBuilderBehaviorTest.php @@ -30,7 +30,7 @@ public function testAlias() { $query = $this->builder ->table(['table1']) - ->alias('table1', 't1') + ->alias('t1') ->join('table2', 'table2.person_id', '=', 'foo2.id') ; @@ -301,7 +301,7 @@ public function testSelectQueryWithNull() ; $this->assertEquals( - "SELECT * FROM `cb_my_table` WHERE `key1` IS NULL OR `key2` IS NULL AND `key3` IS NOT NULL OR `key4` IS NOT NULL OR `key5` = NULL", + "SELECT * FROM `cb_my_table` WHERE `key1` IS NULL OR `key2` IS NULL AND `key3` IS NOT NULL OR `key4` IS NOT NULL OR `key5` = NULL", $query->getQuery()->getRawSql() ); } @@ -339,4 +339,31 @@ public function testUpdateQuery() $this->assertEquals("UPDATE `cb_my_table` SET `key`='Sana',`value`='Amrin' WHERE `value` = 'Sana'" , $builder->getQuery('update', $data)->getRawSql()); } + + public function testFromSubQuery() { + + $subQuery = $this->builder->table('person'); + $builder = $this->builder->table($this->builder->subQuery($subQuery))->where('id', '=', 2); + + $this->assertEquals('SELECT * FROM (SELECT * FROM `cb_person`) WHERE `id` = 2', $builder->getQuery()->getRawSql()); + + } + + public function testTableAlias() { + + $builder = $this->builder->table('persons')->alias('staff'); + + $this->assertEquals('SELECT * FROM `cb_persons` AS `staff`', $builder->getQuery()->getRawSql()); + + } + + public function testWhereNotNullSubQuery() { + $subQuery = $this->builder->table('persons')->alias('staff'); + + $query = $this->builder->whereNull($this->builder->subQuery($subQuery)); + + $this->assertEquals('SELECT * WHERE (SELECT * FROM `cb_persons` AS `staff`) IS NULL', $query->getQuery()->getRawSql()); + + } + }