diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..a12c4dc --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: VitexSoftware +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..c80b6ce --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,43 @@ + + +All code comments should be written in English. + +All messages, including error messages, should be written in English. + +All code should be written in PHP 8.4 or later. + +All code should follow the PSR-12 coding standard. + +When writing code, always include a docblock for functions and classes, describing their purpose, parameters, and return types. + +When writing tests, use PHPUnit and follow the PSR-12 coding standard. + +When writing documentation, use MarkDown format. + +When writing commit messages, use the imperative mood and keep them concise. + +When writing code comments, use complete sentences and proper grammar. + +When writing code, always use meaningful variable names that describe their purpose. + +When writing code, avoid using magic numbers or strings; instead, define constants for them. + +When writing code, always handle exceptions properly and provide meaningful error messages. + +When writing code, always include type hints for function parameters and return types. + +We are using the i18n library for internationalization, so always use the _() functions for strings that need to be translated. + +When writing code, always ensure that it is secure and does not expose any sensitive information. + +When writing code, always consider performance and optimize where necessary. + +When writing code, always ensure that it is compatible with the latest version of PHP and the libraries we are using. + +When writing code, always ensure that it is well-tested and includes unit tests where applicable. + +When writing code, always ensure that it is maintainable and follows best practices. + +When create new class or update existing class, always create or update its phpunit test files. + +After every single edit to a PHP file, always run `php -l` on the edited file to lint it and ensure code sanity before proceeding further. This is mandatory for all PHP code changes. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..a9f4ba3 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,14 @@ +version: 2 +updates: + - package-ecosystem: "composer" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + versioning-strategy: increase + commit-message: + prefix: "composer" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml new file mode 100644 index 0000000..d195b3c --- /dev/null +++ b/.github/workflows/testing.yml @@ -0,0 +1,21 @@ +name: PHPUnit + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@master + env: + DEBIAN_FRONTEND: "noninteractive" + LC_ALL: "en_US.UTF-8" + LANG: "en_US.UTF-8" + - name: Prepare Environment + run: | + sudo apt-get install -y composer exim4 + composer update + - name: Run tests + run: make phpunit diff --git a/.gitignore b/.gitignore index f7f8ac3..977242a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,9 @@ /.idea/ /vendor/ +debian/php-vitexsoftware-fluentpdo.debhelper.log +debian/.debhelper/ +debian/php-vitexsoftware-fluentpdo/ +debian/debhelper-build-stamp +debian/php-vitexsoftware-fluentpdo.substvars +nbproject/ +.build/ diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..bd96143 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Ergebnis\PhpCsFixer\Config\Factory; +use Ergebnis\PhpCsFixer\Config\Rules; +use Ergebnis\PhpCsFixer\Config\RuleSet\Php81; + +$header = <<<'HEADER' +This file is part of the FluentPDO package. + +FluentPDO is a quick and light PHP library for rapid query building. It features a smart join builder, which automatically creates table joins. + +For more information see readme.md + +@link https://github.com/VitexSoftware/fluentpdo +@author Chris Bornhoft, start@env.ms +@copyright 2012-2020 envms - Chris Bornhoft, Marek Lichtner +@license https://www.gnu.org/licenses/gpl-3.0.en.html GNU General Public License, version 3.0 + +(G) 2025-2026 Vítězslav Dvořák + +For the full copyright and license information, please view the LICENSE +file that was distributed with this source code. +HEADER; + +$ruleSet = Php81::create()->withHeader($header)->withRules(Rules::fromArray([ + 'blank_line_before_statement' => [ + 'statements' => [ + 'break', + 'continue', + 'declare', + 'default', + 'do', + 'exit', + 'for', + 'foreach', + 'goto', + 'if', + 'include', + 'include_once', + 'require', + 'require_once', + 'return', + 'switch', + 'throw', + 'try', + 'while', + ], + ], + 'concat_space' => [ + 'spacing' => 'none', + ], + 'date_time_immutable' => false, + 'error_suppression' => false, + 'final_class' => false, + 'mb_str_functions' => false, + 'native_function_invocation' => [ + 'exclude' => [ + 'sprintf', + ], + 'include' => [ + '@compiler_optimized', + ], + 'scope' => 'all', + 'strict' => false, + ], + 'php_unit_internal_class' => false, + 'php_unit_test_annotation' => [ + 'style' => 'prefix', + ], + 'php_unit_test_class_requires_covers' => false, + 'return_to_yield_from' => false, + 'phpdoc_array_type' => false, + 'phpdoc_list_type' => false, + 'attribute_empty_parentheses' => false, + 'final_public_method_for_abstract_class' => false, + 'class_attributes_separation' => [ + 'elements' => [ + 'const' => 'only_if_meta', + 'property' => 'only_if_meta', + 'trait_import' => 'none', + 'case' => 'none', + ], + ], + 'yoda_style' => false, + 'php_unit_test_case_static_method_calls' => false, +])); + +$config = Factory::fromRuleSet($ruleSet)->setUnsupportedPhpVersionAllowed(true); + +$config->getFinder() + ->append([ + __DIR__.'/.php-cs-fixer.dist.php', + ]) + ->append([ + __DIR__.'/rector.php', + ]) + ->in('src') + ->in('tests'); + +$config->setCacheFile(__DIR__.'/.build/php-cs-fixer/.php-cs-fixer.cache'); + +return $config; diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..cddbb74 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,23 @@ +{ + "php.version": "8.2", + "workbench.colorCustomizations": { + "activityBar.activeBackground": "#225d9c", + "activityBar.background": "#225d9c", + "activityBar.foreground": "#e7e7e7", + "activityBar.inactiveForeground": "#e7e7e799", + "activityBarBadge.background": "#dd629d", + "activityBarBadge.foreground": "#15202b", + "commandCenter.border": "#e7e7e799", + "sash.hoverBorder": "#225d9c", + "statusBar.background": "#194472", + "statusBar.foreground": "#e7e7e7", + "statusBarItem.hoverBackground": "#225d9c", + "statusBarItem.remoteBackground": "#194472", + "statusBarItem.remoteForeground": "#e7e7e7", + "titleBar.activeBackground": "#194472", + "titleBar.activeForeground": "#e7e7e7", + "titleBar.inactiveBackground": "#19447299", + "titleBar.inactiveForeground": "#e7e7e799" + }, + "peacock.color": "#194472" +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..6fe51ba --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,51 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [3.0.0] - 2025-01-21 + +### Added +- **PHP 8.1+ Support**: Complete modernization for PHP 8.1 and higher +- **Strict Type Declarations**: All files now use `declare(strict_types=1)` +- **Modern Type System**: Full use of PHP 8.1+ type declarations including: + - Union types (e.g., `string|null`, `array|string|null`) + - Mixed types for flexible parameters + - Proper return type declarations + - Typed properties throughout the codebase +- **Array Parameter Handling**: Automatic JSON serialization of array parameters +- **Enhanced IDE Support**: Better autocomplete and error detection with proper type hints +- **Updated Dependencies**: Modern development tools and PHPUnit 12.4+ + +### Changed +- **Breaking**: Minimum PHP version increased from 7.3 to 8.1 +- **Package Name**: Changed from `envms/fluentpdo` to `vitexsoftware/fluentpdo` +- **Type Safety**: All method signatures updated with proper type declarations +- **Error Handling**: Improved error messages and debugging capabilities +- **Performance**: Better memory usage through strict typing + +### Fixed +- **Array to String Conversion**: Fixed PHP warnings when arrays are passed as parameters +- **Type Compatibility**: Resolved all PHP 8.1+ compatibility issues +- **Method Signatures**: Updated all method signatures to match modern PHP standards + +### Removed +- **PHP < 8.1 Support**: No longer supports PHP versions below 8.1 + +## About This Fork + +This is a modernized fork of the original [envms/fluentpdo](https://github.com/envms/fluentpdo) project. The original project has been inactive since 2021, with the last commit being 3+ years old. This fork aims to: + +- Provide PHP 8.1+ compatibility with modern features +- Maintain API compatibility with the original project +- Add enhanced type safety and developer experience improvements +- Keep the library up-to-date with current PHP best practices + +## Migration from Original FluentPDO + +If you're migrating from the original `envms/fluentpdo`, the API remains compatible, but you'll need: + +1. PHP 8.1 or higher +2. Update your composer requirement to `vitexsoftware/fluentpdo` +3. Optionally add `declare(strict_types=1)` to your files for better performance + +The core API and functionality remain unchanged, so your existing code should work without modifications. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..24852a5 --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +# vim: set tabstop=8 softtabstop=8 noexpandtab: +.PHONY: help +help: ## Displays this list of targets with descriptions + @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[32m%-30s\033[0m %s\n", $$1, $$2}' + +.PHONY: cs +cs: vendor ## Normalizes composer.json with ergebnis/composer-normalize and fixes code style issues with friendsofphp/php-cs-fixer + mkdir -p .build/php-cs-fixer + vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.dist.php --diff --verbose + +phpdoc: clean ## Generate PHPDoc + mkdir -p docs + phpdoc --defaultpackagename=MainPackage + mv .phpdoc/build/* docs + +apigen: ## Build Apigen documentation + rm -rfv docs ; mkdir docs + VERSION=`cat debian/composer.json | grep version | awk -F'"' '{print $4}'`; \ + apigen generate --destination=docs --title "FluentPDO ${VERSION}" --charset UTF-8 --access-levels public --access-levels protected --php --tree -- src/ diff --git a/WARP.md b/WARP.md new file mode 100644 index 0000000..2bb1be7 --- /dev/null +++ b/WARP.md @@ -0,0 +1,99 @@ +# WARP.md + +This file provides guidance to WARP (warp.dev) when working with code in this repository. + +## Project Overview + +FluentPDO - PHP 8.1+ Edition is a modernized fork of the original FluentPDO project. It's a PHP SQL query builder library using PDO with a smart join builder that automatically creates table joins. This version (3.x) requires PHP 8.1+ and includes modern type declarations, strict typing, and enhanced features. + +## Common Development Commands + +### Testing +```bash +# Run all tests +phpunit --configuration phpunit.xml + +# Install dependencies first if needed +composer install + +# Set up test database (for local development) +mysql -u $DB_USER < tests/_resources/fluentdb.sql +``` + +### Development Setup +```bash +# Install dependencies +composer install + +# For testing, you need a MySQL database named 'fluentdb' +# Check tests/_resources/init.php for database configuration +``` + +## Code Architecture + +### Core Classes Structure +- `Query` - Main entry point, creates query objects (SELECT, INSERT, UPDATE, DELETE) +- `Structure` - Handles table relationships, primary/foreign key conventions +- `Base` - Abstract base class for all query types, handles execution and parameters +- `Common` - Shared functionality for SELECT/UPDATE/DELETE queries (WHERE, JOIN clauses) + +### Query Types (src/Queries/) +- `Select` - SELECT query builder with smart joins, implements Countable +- `Insert` - INSERT query builder +- `Update` - UPDATE query builder +- `Delete` - DELETE query builder +- `Json` - JSON query support + +### Key Features +- **Smart Join Builder**: Automatically creates JOINs based on column references (e.g., `user.name` auto-joins user table) +- **Fluent Interface**: Method chaining for building queries +- **PDO Integration**: Built on top of PDO for database portability +- **Type Conversion**: Optional type conversion for read/write operations + +### Database Conventions +- Primary keys default to 'id' +- Foreign keys follow pattern '%s_id' (e.g., `user_id` for user table) +- These conventions can be customized via `Structure` class + +### Usage Patterns +```php +// Basic setup +$fluent = new \Envms\FluentPDO\Query($pdo); + +// Smart joins automatically created +$query = $fluent->from('comment') + ->where('article.published_at > ?', $date) // Auto-joins article table + ->orderBy('published_at DESC'); + +// Shorthand methods for single-row operations +$user = $fluent->from('user', 1)->fetch(); // WHERE id = 1 +$fluent->update('article', $data, 1)->execute(); // WHERE id = 1 +``` + +## Testing Environment + +Tests use MySQL with database 'fluentdb'. Test configuration: +- Travis CI: Uses root user with no password +- Local development: Uses 'vagrant'/'vagrant' credentials +- Database schema: `tests/_resources/fluentdb.sql` +- Test initialization: `tests/_resources/init.php` + +## Development Notes + +- **Modern PHP 8.1+**: This fork requires PHP 8.1+ with strict typing throughout +- **Type Safety**: All classes use `declare(strict_types=1)` and proper type declarations +- **Array Handling**: Arrays are automatically converted to JSON strings for database storage +- **Union Types**: Uses modern PHP union types where appropriate (e.g., `mixed`, `string|null`) +- **PSR-4 Autoloading**: Namespace remains `Envms\FluentPDO` for compatibility +- **Iterator Support**: All query builders implement `IteratorAggregate` for direct iteration +- **Debugging**: Debug mode available via `$fluent->debug` property (mixed type) +- **Error Handling**: Exception handling configurable via `exceptionOnError` property +- **Fetch Modes**: Supports both object and array fetch modes with enhanced type safety + +## Fork Information + +This is a modernized fork of [envms/fluentpdo](https://github.com/envms/fluentpdo) (inactive since 2021). Changes include: +- PHP 8.1+ compatibility with modern type system +- Fixed array parameter handling (JSON serialization) +- Enhanced IDE support with proper type hints +- Updated development dependencies and tools diff --git a/composer.json b/composer.json index 8b2c70f..33b3d4f 100644 --- a/composer.json +++ b/composer.json @@ -1,26 +1,63 @@ { - "name": "envms/fluentpdo", - "description": "FluentPDO is a quick and light PHP library for rapid query building. It features a smart join builder, which automatically creates table joins.", - "keywords": ["db", "database", "dbal", "pdo", "fluent", "query", "builder", "mysql", "oracle"], - "homepage": "https://github.com/envms/fluentpdo", - "license": ["Apache-2.0", "GPL-2.0+"], - "authors": [ - { - "name": "envms", - "homepage": "https://env.ms" - } - ], - "autoload": { - "psr-4": { - "Envms\\FluentPDO\\": "src/" - } - }, + "name": "vitexsoftware/fluentpdo", + "description": "FluentPDO - PHP 8.1+ Edition. A modernized fork with strict typing, union types, and enhanced features. Quick and light PHP library for rapid query building with smart join builder.", + "type": "library", + "keywords": [ + "db", + "database", + "dbal", + "pdo", + "fluent", + "query", + "builder", + "mysql", + "oracle", + "mssql", + "php8", + "php81", + "strict-types", + "union-types", + "modern-php" + ], + "homepage": "https://github.com/VitexSoftware/php-vitexsoftware-fluentpdo", + "license": [ + "Apache-2.0", + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "envms", + "homepage": "https://env.ms" + }, + { + "name": "vitex", + "homepage": "https://vitexsoftware.com" + } + ], + "autoload": { + "psr-4": { + "Envms\\FluentPDO\\": "src/" + } + }, "require": { - "php": ">=7.1", + "php": ">=8.1", "ext-pdo": "*" }, - "require-dev": { - "phpunit/phpunit": "^8.0", - "envms/fluent-test": "^1.0" - } + "require-dev": { + "phpunit/phpunit": "^12.4", + "envms/fluent-test": "^1.0", + "roave/security-advisories": "dev-latest", + "phpstan/phpstan": "*", + "friendsofphp/php-cs-fixer": "^3.87", + "ergebnis/composer-normalize": "^2.48", + "ergebnis/php-cs-fixer-config": "^6.54" + }, + "config": { + "allow-plugins": { + "ergebnis/composer-normalize": true + } + }, + "replace": { + "envms/fluentpdo": "^2.2" + } } diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 7143cb3..0000000 --- a/composer.lock +++ /dev/null @@ -1,1626 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "43294b3845978b000b9d0d3002f2c3d3", - "packages": [], - "packages-dev": [ - { - "name": "doctrine/instantiator", - "version": "1.3.1", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "f350df0268e904597e3bd9c4685c53e0e333feea" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f350df0268e904597e3bd9c4685c53e0e333feea", - "reference": "f350df0268e904597e3bd9c4685c53e0e333feea", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^6.0", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.13", - "phpstan/phpstan-phpunit": "^0.11", - "phpstan/phpstan-shim": "^0.11", - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2020-05-29T17:27:14+00:00" - }, - { - "name": "envms/fluent-test", - "version": "v1.0.1", - "source": { - "type": "git", - "url": "https://github.com/envms/fluent-test.git", - "reference": "2db2a96ce65f64b7c6328040b7da5d2240782f23" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/envms/fluent-test/zipball/2db2a96ce65f64b7c6328040b7da5d2240782f23", - "reference": "2db2a96ce65f64b7c6328040b7da5d2240782f23", - "shasum": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Envms\\FluentTest\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0", - "GPL-2.0+" - ], - "authors": [ - { - "name": "envms", - "homepage": "http://env.ms" - } - ], - "description": "A library for testing FluentPDO with mock data and classes", - "homepage": "https://github.com/envms/fluent-test", - "keywords": [ - "fluentpdo", - "library", - "test" - ], - "time": "2018-08-23T17:42:04+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.10.1", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/969b211f9a51aa1f6c01d1d2aef56d3bd91598e5", - "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "replace": { - "myclabs/deep-copy": "self.version" - }, - "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^7.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, - "files": [ - "src/DeepCopy/deep_copy.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2020-06-29T13:22:24+00:00" - }, - { - "name": "phar-io/manifest", - "version": "1.0.3", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", - "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "phar-io/version": "^2.0", - "php": "^5.6 || ^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2018-07-08T19:23:20+00:00" - }, - { - "name": "phar-io/version", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", - "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "time": "2018-07-08T19:19:57+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.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": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "5.2.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "d870572532cd70bc3fab58f2e23ad423c8404c44" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d870572532cd70bc3fab58f2e23ad423c8404c44", - "reference": "d870572532cd70bc3fab58f2e23ad423c8404c44", - "shasum": "" - }, - "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" - }, - "require-dev": { - "mockery/mockery": "~1.3.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.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" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2020-08-15T11:14:08+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "e878a14a65245fbe78f8080eba03b47c3b705651" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e878a14a65245fbe78f8080eba03b47c3b705651", - "reference": "e878a14a65245fbe78f8080eba03b47c3b705651", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.0" - }, - "require-dev": { - "ext-tokenizer": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "time": "2020-06-27T10:12:23+00:00" - }, - { - "name": "phpspec/prophecy", - "version": "1.11.1", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "b20034be5efcdab4fb60ca3a29cba2949aead160" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/b20034be5efcdab4fb60ca3a29cba2949aead160", - "reference": "b20034be5efcdab4fb60ca3a29cba2949aead160", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2", - "php": "^7.2", - "phpdocumentor/reflection-docblock": "^5.0", - "sebastian/comparator": "^3.0 || ^4.0", - "sebastian/recursion-context": "^3.0 || ^4.0" - }, - "require-dev": { - "phpspec/phpspec": "^6.0", - "phpunit/phpunit": "^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.11.x-dev" - } - }, - "autoload": { - "psr-4": { - "Prophecy\\": "src/Prophecy" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "time": "2020-07-08T12:44:21+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "7.0.10", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f1884187926fbb755a9aaf0b3836ad3165b478bf", - "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-xmlwriter": "*", - "php": "^7.2", - "phpunit/php-file-iterator": "^2.0.2", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^3.1.1", - "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^4.2.2", - "sebastian/version": "^2.0.1", - "theseer/tokenizer": "^1.1.3" - }, - "require-dev": { - "phpunit/phpunit": "^8.2.2" - }, - "suggest": { - "ext-xdebug": "^2.7.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "7.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "time": "2019-11-20T13:55:58+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "050bedf145a257b1ff02746c31894800e5122946" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", - "reference": "050bedf145a257b1ff02746c31894800e5122946", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "time": "2018-09-13T20:33:42+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "time": "2015-06-21T13:50:34+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "2.1.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "1038454804406b0b5f5f520358e78c1c2f71501e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e", - "reference": "1038454804406b0b5f5f520358e78c1c2f71501e", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "time": "2019-06-07T04:22:29+00:00" - }, - { - "name": "phpunit/php-token-stream", - "version": "3.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/995192df77f63a59e47f025390d2d1fdf8f425ff", - "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": "^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-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" - ], - "abandoned": true, - "time": "2019-09-17T06:23:10+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "8.5.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "34c18baa6a44f1d1fbf0338907139e9dce95b997" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/34c18baa6a44f1d1fbf0338907139e9dce95b997", - "reference": "34c18baa6a44f1d1fbf0338907139e9dce95b997", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2.0", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.9.1", - "phar-io/manifest": "^1.0.3", - "phar-io/version": "^2.0.1", - "php": "^7.2", - "phpspec/prophecy": "^1.8.1", - "phpunit/php-code-coverage": "^7.0.7", - "phpunit/php-file-iterator": "^2.0.2", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^2.1.2", - "sebastian/comparator": "^3.0.2", - "sebastian/diff": "^3.0.2", - "sebastian/environment": "^4.2.2", - "sebastian/exporter": "^3.1.1", - "sebastian/global-state": "^3.0.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^2.0.1", - "sebastian/type": "^1.1.3", - "sebastian/version": "^2.0.1" - }, - "require-dev": { - "ext-pdo": "*" - }, - "suggest": { - "ext-soap": "*", - "ext-xdebug": "*", - "phpunit/php-invoker": "^2.0.0" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "8.5-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" - ], - "funding": [ - { - "url": "https://phpunit.de/donate.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-06-22T07:06:58+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "shasum": "" - }, - "require": { - "php": "^5.6 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04T06:30:41+00:00" - }, - { - "name": "sebastian/comparator", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", - "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", - "shasum": "" - }, - "require": { - "php": "^7.1", - "sebastian/diff": "^3.0", - "sebastian/exporter": "^3.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "time": "2018-07-12T15:12:46+00:00" - }, - { - "name": "sebastian/diff", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", - "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.5 || ^8.0", - "symfony/process": "^2 || ^3.3 || ^4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], - "time": "2019-02-04T06:01:07+00:00" - }, - { - "name": "sebastian/environment", - "version": "4.2.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/464c90d7bdf5ad4e8a6aea15c091fec0603d4368", - "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^7.5" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "time": "2019-11-20T08:46:58+00:00" - }, - { - "name": "sebastian/exporter", - "version": "3.1.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e", - "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e", - "shasum": "" - }, - "require": { - "php": "^7.0", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "time": "2019-09-14T09:02:43+00:00" - }, - { - "name": "sebastian/global-state", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", - "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", - "shasum": "" - }, - "require": { - "php": "^7.2", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^8.0" - }, - "suggest": { - "ext-uopz": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "time": "2019-02-01T05:30:01+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "shasum": "" - }, - "require": { - "php": "^7.0", - "sebastian/object-reflector": "^1.1.1", - "sebastian/recursion-context": "^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-08-03T12:35:26+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "1.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "773f97c67f28de00d397be301821b06708fca0be" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", - "reference": "773f97c67f28de00d397be301821b06708fca0be", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2017-03-29T09:07:27+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "shasum": "" - }, - "require": { - "php": "^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-03T06:23:57+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", - "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2018-10-04T04:07:39+00:00" - }, - { - "name": "sebastian/type", - "version": "1.1.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/3aaaa15fa71d27650d62a948be022fe3b48541a3", - "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3", - "shasum": "" - }, - "require": { - "php": "^7.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", - "time": "2019-07-02T08:10:15+00:00" - }, - { - "name": "sebastian/version", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03T07:35:21+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.18.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "1c302646f6efc070cd46856e600e5e0684d6b454" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/1c302646f6efc070cd46856e600e5e0684d6b454", - "reference": "1c302646f6efc070cd46856e600e5e0684d6b454", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.18-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-07-14T12:35:20+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "75a63c33a8577608444246075ea0af0d052e452a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a", - "reference": "75a63c33a8577608444246075ea0af0d052e452a", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2020-07-12T23:59:07+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.9.1", - "source": { - "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", - "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0 || ^8.0", - "symfony/polyfill-ctype": "^1.8" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<3.9.1" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.36 || ^7.5.13" - }, - "type": "library", - "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": "2020-07-08T17:02:28+00:00" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": [], - "platform-dev": [], - "plugin-api-version": "1.1.0" -} diff --git a/debian/Jenkinsfile b/debian/Jenkinsfile new file mode 100644 index 0000000..18ba308 --- /dev/null +++ b/debian/Jenkinsfile @@ -0,0 +1,133 @@ +#!groovy + +// Aktuální verze Pipeline: https://github.com/VitexSoftware/BuildImages/blob/main/Test/Jenkinsfile-parael + +String[] distributions = [ + 'debian:bookworm', + 'debian:trixie', + 'debian:forky', + 'ubuntu:jammy', + 'ubuntu:noble' +] + +String vendor = 'vitexsoftware' +String imagePrefix = 'multiflexi-' + +properties([ + copyArtifactPermission('*') +]) + +node { + ansiColor('xterm') { + stage('SCM Checkout') { + checkout scm + } + } +} + +def branches = [:] + +distributions.each { distro -> + branches[distro] = { + def (distroFamily, distroCode) = distro.split(':') + def imageName = "${vendor}/${imagePrefix}${distroCode}:latest" + def buildImage + def artifacts = [] + def buildVer + + node { + ansiColor('xterm') { + stage("Checkout ${distro}") { + checkout scm + buildImage = docker.image(imageName) + + sh 'git checkout debian/changelog' + buildVer = sh( + script: "dpkg-parsechangelog --show-field Version", + returnStdout: true + ).trim() + ".${env.BUILD_NUMBER}~${distroCode}" + } + + stage("Build ${distro}") { + buildImage.inside('--ipc=host') { + sh """ + dch -b -v ${buildVer} "${env.BUILD_TAG}" + sudo apt-get update --allow-releaseinfo-change + sudo chown -R jenkins:jenkins . .. + debuild-pbuilder -r'sudo -E' -i -us -uc -b + mkdir -p \$WORKSPACE/dist/debian/ + rm -rf \$WORKSPACE/dist/debian/* + for deb in \$(awk '{print \$1}' debian/files); do + mv "../\$deb" \$WORKSPACE/dist/debian/ + done + """ + artifacts = sh( + script: "awk '{print \$1}' debian/files", + returnStdout: true + ).trim().split('\n') + } + } + + stage("Test ${distro}") { + buildImage.inside('--ipc=host') { + def debconf_debug = 0 + sh """ + cd \$WORKSPACE/dist/debian/ + dpkg-scanpackages . /dev/null > Packages + gzip -9c Packages > Packages.gz + cd \$WORKSPACE + echo "deb [trusted=yes] file://///\$WORKSPACE/dist/debian/ ./" | sudo tee /etc/apt/sources.list.d/local.list + sudo apt-get update --allow-releaseinfo-change + """ + artifacts.each { deb_file -> + if (deb_file.endsWith('.deb')) { + def pkgName = deb_file.tokenize('/')[-1].replaceFirst(/_.*/, '') + def distroCodename = sh( + script: "lsb_release -sc", + returnStdout: true + ).trim() + echo "Installing ${pkgName} on ${distroCodename}" + sh """ + sudo DEBIAN_FRONTEND=noninteractive DEBCONF_DEBUG=${debconf_debug} \ + apt-get -y install ${pkgName} \ + || sudo apt-get -y -f install + """ + } + } + } + } + + stage("Archive artifacts ${distro}") { + buildImage.inside('--ipc=host') { + artifacts.each { deb_file -> + println "Archiving artifact: ${deb_file}" + archiveArtifacts artifacts: "dist/debian/${deb_file}" + } + + sh ''' + set -e + if [ -f debian/files ]; then + while read -r file _; do + [ -n "$file" ] || continue + rm -f "dist/debian/$file" || true + rm -f "../$file" || true + rm -f "$WORKSPACE/$file" || true + done < debian/files + fi + ''' + } + } + + } + } + } +} + + +parallel branches + +node { + stage('Publish to Aptly') { + publishDebToAptly() + } +} diff --git a/debian/Jenkinsfile.release b/debian/Jenkinsfile.release new file mode 100644 index 0000000..3491cce --- /dev/null +++ b/debian/Jenkinsfile.release @@ -0,0 +1,117 @@ +#!groovy + +String[] distributions = ['debian:bookworm', 'debian:trixie', 'debian:forky', 'ubuntu:jammy', 'ubuntu:noble'] + +String vendor = 'vitexsoftware' +String imagePrefix = 'multiflexi-' +//String distroFamily = '' + +properties([ + copyArtifactPermission('*') +]) +node() { + ansiColor('xterm') { + stage('SCM Checkout') { + sh 'sudo chown -R jenkins:jenkins . || true' + checkout scm + } + } +} + +def branches = [:] +distributions.each { distro -> + branches[distro] = { + def distroName = distro + println "Dist:" + distroName + + def dist = distroName.split(':') + def distroCode = dist[1] + def buildImage = '' + def artifacts = [] + def buildVer = '' + + node { + ansiColor('xterm') { + stage('Checkout ' + distroName) { + sh 'sudo chown -R jenkins:jenkins . || true' + checkout scm + def imageName = vendor + '/' + imagePrefix + dist[0] + ':' + distroCode + buildImage = docker.image(imageName) + sh 'git checkout debian/changelog' + def version = sh ( + script: 'dpkg-parsechangelog --show-field Version', + returnStdout: true + ).trim() + buildVer = version + '.' + env.BUILD_NUMBER + '~' + distroCode + } + stage('Build ' + distroName) { + buildImage.inside('--init') { + sh 'dch -b -v ' + buildVer + ' "' + env.BUILD_TAG + '"' + sh 'sudo apt-get update --allow-releaseinfo-change' + sh 'sudo chown -R jenkins:jenkins . ..' + sh 'sudo rm -rf debian/$(dpkg-parsechangelog --show-field Source)/ debian/.debhelper/ debian/tmp/' + sh 'debuild-pbuilder -r"sudo -E" -i -us -uc -b' + sh 'mkdir -p $WORKSPACE/dist/debian/ ; rm -rf $WORKSPACE/dist/debian/* ; for deb in $(cat debian/files | awk \'{print $1}\'); do mv "../$deb" $WORKSPACE/dist/debian/; done' + artifacts = sh ( + script: "cat debian/files | awk '{print \$1}'", + returnStdout: true + ).trim().split('\n') + } + } + + stage('Test ' + distroName) { + buildImage.inside('--init') { + def debconf_debug = 0 //Set to "5" or "developer" to debug debconf + sh 'cd $WORKSPACE/dist/debian/ ; dpkg-scanpackages . /dev/null | gzip -9c > Packages.gz; cd $WORKSPACE' + sh 'echo "deb [trusted=yes] file://///$WORKSPACE/dist/debian/ ./" | sudo tee /etc/apt/sources.list.d/local.list' + sh 'sudo apt-get update --allow-releaseinfo-change' + sh 'echo "INSTALATION"' + artifacts.each { deb_file -> + if (deb_file.endsWith('.deb')) { + sh 'echo -e "${GREEN} installing ' + deb_file + ' on `lsb_release -sc` ${ENDCOLOR} "' + sh 'sudo DEBIAN_FRONTEND=noninteractive DEBCONF_DEBUG=' + debconf_debug + ' apt-get -y install $WORKSPACE/dist/debian/' + deb_file + } + } + } + } + stage('Archive artifacts ' + distroName ) { + // Only run if previous stages (Build and Test) succeeded + buildImage.inside('--init') { + // Archive all produced artifacts listed in debian/files + artifacts.each { deb_file -> + println "Archiving artifact: " + deb_file + archiveArtifacts artifacts: 'dist/debian/' + deb_file + } + // Cleanup: remove any produced files named in debian/files + // Try both the dist location and any potential original locations referenced by debian/files + sh ''' + set -e + if [ -f debian/files ]; then + while read -r file _; do + [ -n "$file" ] || continue + rm -f "dist/debian/$file" || true + rm -f "../$file" || true + rm -f "$WORKSPACE/$file" || true + done < debian/files + fi + ''' + } + } + } + } + } +} +parallel branches + +if (!currentBuild.result || currentBuild.result == 'SUCCESS') { + build job: 'MultiFlexi-publish', + wait: false, + parameters: [ + string(name: 'UPSTREAM_JOB', value: env.JOB_NAME), + string(name: 'UPSTREAM_BUILD', value: env.BUILD_NUMBER), + string(name: 'REMOTE_SSH', value: 'multirepo@repo.multiflexi.eu'), + string(name: 'REMOTE_REPO_DIR', value: '/var/lib/multirepo/public/multiflexi'), + string(name: 'COMPONENT', value: 'main'), + string(name: 'DEB_DIST', value: '') + ] +} diff --git a/debian/README.Debian b/debian/README.Debian new file mode 100644 index 0000000..a71cc62 --- /dev/null +++ b/debian/README.Debian @@ -0,0 +1,76 @@ +php-vitexsoftware-fluentpdo for Debian +======================================= + +PHP Autoloader +-------------- + +This package uses the **Debian PHP autoloader system** (pkg-php-tools). + +Classes are installed in: `/usr/share/php/Envms/FluentPDO` +Autoloader: `/usr/share/php/Envms/FluentPDO/autoload.php` + +To use in your PHP code: + +```php +require_once '/usr/share/php/Envms/FluentPDO/autoload.php'; +``` + +The autoloader is automatically generated by `phpab` during package build and +includes all package classes with proper dependency loading. + +**Note:** This package NO LONGER ships vendor/autoload.php from Composer. +Use the Debian autoloader instead for proper system integration. + +Installation +------------ + +For Debian, Ubuntu & friends please use repo: + +```shell +sudo apt install lsb-release wget +echo "deb http://repo.vitexsoftware.cz $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/vitexsoftware.list +sudo wget -O /etc/apt/trusted.gpg.d/vitexsoftware.gpg http://repo.vitexsoftware.cz/keyring.gpg +sudo apt update +sudo apt install php-vitexsoftware-fluentpdo +``` + +Composer Integration +-------------------- + +If using Composer in your application, you can reference the Debian package: + +```json +{ + "require": { + "deb/vitexsoftware-fluentpdo": "*" + }, + "repositories": [ + { + "type": "path", + "url": "/usr/share/php/Envms/FluentPDO", + "options": { + "symlink": true + } + } + ] +} +``` + +Migration from Composer vendor/autoload.php +-------------------------------------------- + +If you're migrating from Composer's autoloader, replace: + +```php +require_once __DIR__ . '/../vendor/autoload.php'; +``` + +with: + +```php +require_once '/usr/share/php/Envms/FluentPDO/autoload.php'; +``` + +This ensures you're using the system-wide Debian packages instead of bundled dependencies. + + -- VitexSoftware Tue Mar 03 23:31:41 CET 2026 diff --git a/debian/README.source b/debian/README.source new file mode 100644 index 0000000..c6a982c --- /dev/null +++ b/debian/README.source @@ -0,0 +1,14 @@ +php-vitexsoftware-fluentpdo for Debian + +See Debian policy manual section 4.14. + https://www.debian.org/doc/debian-policy/ch-source.html#source-package-handling-debian-readme-source + +If running dpkg-source -x on a source package doesn’t produce the source of the +package, ready for editing, and allow one to make changes and run +dpkg-buildpackage to produce a modified package without taking any additional +steps, creating this file explain how to do all of these is recommended. +Otherwise, remove this file. + + (Automatically generated by debmake Version 4.5.1) + + -- Vítězslav Dvořák Tue, 21 Oct 2025 23:54:19 +0200 diff --git a/debian/autoload.php b/debian/autoload.php new file mode 100644 index 0000000..9243d0c --- /dev/null +++ b/debian/autoload.php @@ -0,0 +1,21 @@ + Mon, 02 Feb 2026 02:56:19 +0100 + +php-vitexsoftware-fluentpdo (3.0.3) unstable; urgency=medium + + [ Vítězslav Vitex Dvořák ] + * Debian loading issues fixed + + -- vitex Mon, 02 Feb 2026 02:56:09 +0100 + +php-vitexsoftware-fluentpdo (3.0.0) unstable; urgency=medium + + [ Vítězslav Dvořák ] + * Initial Debian packaging of FluentPDO - PHP 8.1+ Edition + * Modernized fork of FluentPDO with strict typing and union types + * Requires PHP 8.1+ with PDO extension + * Smart join builder with automatic table joins + * Modern type declarations and enhanced features + + -- Vítězslav Vitex Dvořák Sat, 25 Oct 2025 13:50:49 +0200 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..0e63c51 --- /dev/null +++ b/debian/control @@ -0,0 +1,39 @@ +Source: php-vitexsoftware-fluentpdo +Section: php +Priority: optional +Maintainer: Vítězslav Dvořák +Build-Depends: + debhelper-compat (= 13), + pkg-php-tools, + phpunit, + php-cli +Standards-Version: 4.7.0 +Homepage: https://github.com/VitexSoftware/php-vitexsoftware-fluentpdo +Rules-Requires-Root: no +Vcs-Git: https://github.com/VitexSoftware/php-vitexsoftware-fluentpdo.git +Vcs-Browser: https://github.com/VitexSoftware/php-vitexsoftware-fluentpdo + +Package: php-vitexsoftware-fluentpdo +Architecture: all +Multi-Arch: foreign +Depends: + ${misc:Depends}, + ${phpcomposer:Debian-require} +Provides: + php-envms-fluentpdo +Replaces: + php-envms-fluentpdo +Suggests: + php-mysql | php-sqlite3 | php-pgsql +Description: FluentPDO - PHP 8.1+ Edition with modern features + FluentPDO is a modernized PHP SQL query builder library using PDO with a smart + join builder that automatically creates table joins. This version (3.x) requires + PHP 8.1+ and includes modern type declarations, strict typing, and enhanced + features. + . + Key features: + - Smart Join Builder: Automatically creates JOINs based on column references + - Fluent Interface: Method chaining for building queries + - PDO Integration: Built on top of PDO for database portability + - Type Safety: Modern PHP 8.1+ with strict typing throughout + - Union Types: Uses modern PHP union types where appropriate diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..64c6d41 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,48 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: php-vitexsoftware-fluentpdo +Upstream-Contact: Vítězslav Dvořák +Source: https://github.com/VitexSoftware/php-vitexsoftware-fluentpdo + +Files: * +Copyright: 2024 Vítězslav Dvořák + Original FluentPDO by envms +License: Apache-2.0 or GPL-2.0+ + +Files: src/* +Copyright: 2024 Vítězslav Dvořák + Original FluentPDO by envms +License: Apache-2.0 or GPL-2.0+ + +License: Apache-2.0 + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + . + http://www.apache.org/licenses/LICENSE-2.0 + . + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + . + On Debian systems, the complete text of the Apache License 2.0 + can be found in "/usr/share/common-licenses/Apache-2.0". + +License: GPL-2.0+ + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + . + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + . + On Debian systems, the complete text of the GNU General Public License + can be found in "/usr/share/common-licenses/GPL-2". \ No newline at end of file diff --git a/debian/files b/debian/files new file mode 100644 index 0000000..cca8b7d --- /dev/null +++ b/debian/files @@ -0,0 +1,2 @@ +php-vitexsoftware-fluentpdo_3.0.3_all.deb php optional +php-vitexsoftware-fluentpdo_3.0.3_amd64.buildinfo php optional diff --git a/debian/gbp.conf b/debian/gbp.conf new file mode 100644 index 0000000..4a97dfa --- /dev/null +++ b/debian/gbp.conf @@ -0,0 +1 @@ +# You must remove unused comment lines for the released package. diff --git a/debian/io.github.vitexsoftware.php_vitexsoftware_fluentpdo.metainfo.xml b/debian/io.github.vitexsoftware.php_vitexsoftware_fluentpdo.metainfo.xml new file mode 100644 index 0000000..9e23b3f --- /dev/null +++ b/debian/io.github.vitexsoftware.php_vitexsoftware_fluentpdo.metainfo.xml @@ -0,0 +1,25 @@ + + + io.github.vitexsoftware.php_vitexsoftware_fluentpdo + MIT + [ + "Apache-2.0", + "GPL-2.0-or-later" +] + php-vitexsoftware-fluentpdo + FluentPDO - PHP 8.1+ Edition with modern features + +

+ FluentPDO is a modernized PHP SQL query builder library using PDO with a smart join builder that automatically creates table joins. This version (3.x) requires PHP 8.1+ and includes modern type declarations, strict typing, and enhanced features. Key features: - Smart Join Builder: Automatically creates JOINs based on column references - Fluent Interface: Method chaining for building queries - PDO Integration: Built on top of PDO for database portability - Type Safety: Modern PHP 8.1+ with strict typing throughout - Union Types: Uses modern PHP union types where appropriate +

+
+ + Utility + + https://github.com/VitexSoftware/php-vitexsoftware-fluentpdo + https://github.com/VitexSoftware/php-vitexsoftware-fluentpdo/issues + + VitexSoftware + + +
diff --git a/debian/maintscript.ex b/debian/maintscript.ex new file mode 100644 index 0000000..34601ea --- /dev/null +++ b/debian/maintscript.ex @@ -0,0 +1,7 @@ +# see: dh_installdeb(1), dpkg-maintscript-helper(1) +# For renaming a conffile, you are advised to uncomment and edit the following +#dpkg-maintscript-helper mv_conffile old-conffile new-conffile prior-version package +# For switching a symlink to directory, you are advised to uncomment and edit the following +#dpkg-maintscript-helper symlink_to_dir pathname old-target prior-version package +# For switching a directory to symlink, you are advised to uncomment and edit the following +#dpkg-maintscript-helper dir_to_symlink pathname new-target prior-version package diff --git a/debian/php-vitexsoftware-fluentpdo.install b/debian/php-vitexsoftware-fluentpdo.install new file mode 100644 index 0000000..cf57d64 --- /dev/null +++ b/debian/php-vitexsoftware-fluentpdo.install @@ -0,0 +1,2 @@ +src/* usr/share/php/Envms/FluentPDO/ +debian/autoload.php usr/share/php/Envms/FluentPDO/ diff --git a/debian/postinst.ex b/debian/postinst.ex new file mode 100644 index 0000000..220990b --- /dev/null +++ b/debian/postinst.ex @@ -0,0 +1,7 @@ +#!/bin/sh +# If no user provided script is activated in this file, you should remove this file. +set -e + +#DEBHELPER# + +exit 0 diff --git a/debian/postrm.ex b/debian/postrm.ex new file mode 100644 index 0000000..220990b --- /dev/null +++ b/debian/postrm.ex @@ -0,0 +1,7 @@ +#!/bin/sh +# If no user provided script is activated in this file, you should remove this file. +set -e + +#DEBHELPER# + +exit 0 diff --git a/debian/preinst.ex b/debian/preinst.ex new file mode 100644 index 0000000..220990b --- /dev/null +++ b/debian/preinst.ex @@ -0,0 +1,7 @@ +#!/bin/sh +# If no user provided script is activated in this file, you should remove this file. +set -e + +#DEBHELPER# + +exit 0 diff --git a/debian/prerm.ex b/debian/prerm.ex new file mode 100644 index 0000000..220990b --- /dev/null +++ b/debian/prerm.ex @@ -0,0 +1,7 @@ +#!/bin/sh +# If no user provided script is activated in this file, you should remove this file. +set -e + +#DEBHELPER# + +exit 0 diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..6307277 --- /dev/null +++ b/debian/rules @@ -0,0 +1,13 @@ +#!/usr/bin/make -f + +%: + dh $@ --with phpcomposer + +override_dh_install: + dh_install + mkdir -p debian/php-vitexsoftware-fluentpdo/usr/share/pkg-php-tools/autoloaders + cp debian/autoloader.php debian/php-vitexsoftware-fluentpdo/usr/share/pkg-php-tools/autoloaders/php-vitexsoftware-fluentpdo + +override_dh_auto_test: + # Skip tests during package build + : diff --git a/debian/salsa-ci.yml b/debian/salsa-ci.yml new file mode 100644 index 0000000..661e26b --- /dev/null +++ b/debian/salsa-ci.yml @@ -0,0 +1,25 @@ +# For more information on what jobs are run see: +# https://salsa.debian.org/salsa-ci-team/pipeline +# +# To enable the jobs, go to your repository (at salsa.debian.org) +# and click over Settings > CI/CD > Expand (in General pipelines). +# In "CI/CD configuration file" write debian/salsa-ci.yml and click +# in "Save Changes". The CI tests will run after the next commit. +--- +include: + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/recipes/debian.yml + +# # This commented-out sample disables all default tests, only disable those +# # CI tests that is required to get the CI process to succeed. +# +# variables: +# SALSA_CI_DISABLE_APTLY: 1 +# SALSA_CI_DISABLE_AUTOPKGTEST: 1 +# SALSA_CI_DISABLE_BLHC: 1 +# SALSA_CI_DISABLE_LINTIAN: 1 +# SALSA_CI_DISABLE_PIUPARTS: 1 +# SALSA_CI_DISABLE_REPROTEST: 1 +# SALSA_CI_DISABLE_BUILD_PACKAGE_ALL: 1 +# SALSA_CI_DISABLE_BUILD_PACKAGE_ANY: 1 +# SALSA_CI_DISABLE_BUILD_PACKAGE_I386: 1 +# SALSA_CI_DISABLE_CROSSBUILD_ARM64: 1 diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..435dd71 --- /dev/null +++ b/debian/source/format @@ -0,0 +1,2 @@ +3.0 (native) + diff --git a/debian/tests/control b/debian/tests/control new file mode 100644 index 0000000..f61fd44 --- /dev/null +++ b/debian/tests/control @@ -0,0 +1,9 @@ +# +# DEP-8: autopkgtest - automatic as-installed package testing +# Please check * https://dep-team.pages.debian.net/deps/dep8/ +# * /usr/share/doc/autopkgtest +# +# !!! Please make sure to edit this to a valid test, otherwise build will fails +# +#Tests: testcode.sh +#Restrictions: allow-stderr, breaks-testbed, needs-internet, needs-root diff --git a/debian/upstream/metadata b/debian/upstream/metadata new file mode 100644 index 0000000..dc2feb0 --- /dev/null +++ b/debian/upstream/metadata @@ -0,0 +1,16 @@ +# +# DEP-12: Per-package machine-readable metadata about Upstream +# Please check * https://dep-team.pages.debian.net/deps/dep12/ +# * https://wiki.debian.org/UpstreamMetadata +Reference: + Author: + Title: + Journal: + Year: + Volume: + Number: + Pages: + DOI: + PMID: + URL: + eprint: diff --git a/debian/watch b/debian/watch new file mode 100644 index 0000000..6ebdc86 --- /dev/null +++ b/debian/watch @@ -0,0 +1,3 @@ +# You must remove unused comment lines for the released package. +# Compulsory line, this is a version 4 file +version=4 diff --git a/readme.md b/readme.md index 228db07..8b4982a 100644 --- a/readme.md +++ b/readme.md @@ -1,7 +1,21 @@ -# FluentPDO [![Build Status](https://secure.travis-ci.org/envms/fluentpdo.png?branch=master)](http://travis-ci.org/envms/fluentpdo) [![Maintainability](https://api.codeclimate.com/v1/badges/19210ca91c7055b89705/maintainability)](https://codeclimate.com/github/fpdo/fluentpdo/maintainability) +# FluentPDO - PHP 8.1+ Edition + +**A modernized fork of the original FluentPDO project, updated for PHP 8.1+ with modern type declarations and enhanced features.** FluentPDO is a PHP SQL query builder using PDO. It's a quick and light library featuring a smart join builder, which automatically creates table joins for you. +## About This Fork + +This is a modernized fork of the original [envms/fluentpdo](https://github.com/envms/fluentpdo) project. Since the original project has been inactive for 3+ years (last commit in 2021), this fork provides: + +- **PHP 8.1+ compatibility** with modern type declarations +- **Strict typing** throughout the codebase +- **Union types** and **mixed types** where appropriate +- **Typed properties** for better IDE support +- **Array parameter handling** with automatic JSON serialization +- **Enhanced error handling** and debugging capabilities +- **Updated dependencies** and development tools + ## Features - Easy interface for creating robust queries @@ -9,25 +23,22 @@ FluentPDO is a PHP SQL query builder using PDO. It's a quick and light library f - Ability to build complex SELECT, INSERT, UPDATE & DELETE queries with little code - Type hinting for magic methods with code completion in smart IDEs -## Versions +## PHP Version Requirements -#### Version 2.x +**This fork requires PHP 8.1 or higher.** -The stable release of FluentPDO and actively maintained. Officially supports PHP 7.3 to PHP 8.0, -but it can work with previous versions of PHP 7. +### What's New in This PHP 8.1+ Edition -#### Version 1.x +- **Modern Type System**: Full use of PHP 8.1+ type declarations including union types +- **Strict Types**: All files use `declare(strict_types=1)` for better type safety +- **Array Handling**: Automatic JSON serialization of array parameters to prevent SQL errors +- **Enhanced Performance**: Better memory usage and performance through strict typing +- **Developer Experience**: Improved IDE support with proper type hints and autocomplete +- **Future Ready**: Prepared for upcoming PHP versions -The legacy release of FluentPDO. It is no longer supported and will not be maintained or updated. -This version works with PHP 5.4 to 7.1. +### Migrating from Original FluentPDO -#### Version 3.x - alpha - -This version is a full rewrite of Fluent from the ground up. Its main advantage is -significantly less memory usage and much greater performance in query building. It also places -a few additional restrictions to make queries easier to read and maintain. Documentation has also -been a very common request, and version 3 is being fully documented alongside development. -Details and metrics will be posted once available. +If you're upgrading from the original FluentPDO (v2.x), this version maintains API compatibility while requiring PHP 8.1+. The main changes are internal improvements and type safety enhancements. ## Reference @@ -37,34 +48,42 @@ Details and metrics will be posted once available. ### Composer -The preferred way to install FluentPDO is via [composer](http://getcomposer.org/). - -Add the following line in your `composer.json` file: +The preferred way to install this PHP 8.1+ edition of FluentPDO is via [composer](http://getcomposer.org/). - "require": { - ... - "envms/fluentpdo": "^2.2.0" - } +```bash +composer require vitexsoftware/fluentpdo +``` -update your dependencies with `composer update`, and you're done! +Or add the following line in your `composer.json` file: -### Download Zip +```json +{ + "require": { + "vitexsoftware/fluentpdo": "^3.0" + } +} +``` -If you prefer not to use composer, download the latest release, create the directory `Envms/FluentPDO` in your library directory, and drop this repository into it. Finally, add: +Then run `composer update` and you're done! -```php -require '[lib-dir]/Envms/FluentPDO/src/Query.php'; -``` +### Requirements -to the top of your application. **Note:** You will need an autoloader to use FluentPDO without changing its source code. +- **PHP 8.1+** (required) +- **PDO extension** (required) +- Any PDO-compatible database (MySQL, PostgreSQL, SQLite, etc.) ## Getting Started Create a new PDO instance, and pass the instance to FluentPDO: ```php +insertInto('article', $values)->execute(); // shorter version ##### UPDATE ```php -$set = array('published_at' => new FluentLiteral('NOW()')); +use Envms\FluentPDO\Literal; + +$set = ['published_at' => new Literal('NOW()')]; $query = $fluent->update('article')->set($set)->where('id', 1)->execute(); $query = $fluent->update('article', $set, 1)->execute(); // shorter version if updating one row by primary key @@ -174,6 +195,43 @@ $query = $fluent->deleteFrom('article', 1)->execute(); // shorter version if del ***Note**: INSERT, UPDATE and DELETE queries will only run after you call `->execute()`* +## Modern PHP 8.1+ Features + +### Array Parameter Handling + +This edition automatically handles array parameters by converting them to JSON strings: + +```php +// Arrays are automatically converted to JSON +$data = [ + 'name' => 'John Doe', + 'tags' => ['php', 'mysql', 'programming'], // Automatically converts to JSON + 'metadata' => ['created' => '2025-01-01', 'active' => true] +]; + +$fluent->insertInto('users', $data)->execute(); +``` + +### Strict Type Declarations + +All classes use strict typing for better performance and error detection: + +```php + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Envms\FluentPDO; /** - * Class Exception + * FluentPDO Exception class. + * + * Custom exception class for FluentPDO specific errors. + * + * @author Chris Bornhoft, start@env.ms + * @author Vítězslav Dvořák */ class Exception extends \Exception { - } diff --git a/src/Literal.php b/src/Literal.php index 57bc755..3a0003e 100644 --- a/src/Literal.php +++ b/src/Literal.php @@ -1,34 +1,57 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Envms\FluentPDO; /** - * SQL literal value + * SQL literal value class. + * + * Represents a literal SQL value that should not be escaped or quoted. + * Used for SQL functions, keywords, and raw SQL expressions. + * + * @author Chris Bornhoft, start@env.ms + * @author Vítězslav Dvořák */ -class Literal +class Literal implements \Stringable { - - /** @var string */ - protected $value = ''; + protected string $value = ''; /** - * Create literal value + * Create literal value. * - * @param string $value + * @param string $value The literal SQL value */ - function __construct($value) + public function __construct(string $value) { $this->value = $value; } /** - * Get literal value + * Get literal value as string. * - * @return string + * @return string The literal SQL value */ - function __toString() + public function __toString(): string { return $this->value; } - } diff --git a/src/Queries/Base.php b/src/Queries/Base.php index b05aae1..0bc2dd8 100644 --- a/src/Queries/Base.php +++ b/src/Queries/Base.php @@ -1,56 +1,66 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ -abstract class Base implements IteratorAggregate -{ - - /** @var float */ - private $totalTime; - - /** @var float */ - private $executionTime; - - /** @var bool */ - private $object = false; - - /** @var Query */ - protected $fluent; - /** @var PDOStatement|null|bool */ - protected $result; - - /** @var array - definition clauses */ - protected $clauses = []; - /** @var array */ - protected $statements = []; - /** @var array */ - protected $parameters = []; +namespace Envms\FluentPDO\Queries; - /** @var Regex */ - protected $regex; +use Envms\FluentPDO\Exception; +use Envms\FluentPDO\Literal; +use Envms\FluentPDO\Query; +use Envms\FluentPDO\Regex; +use Envms\FluentPDO\Structure; +use Envms\FluentPDO\Utilities; +use IteratorAggregate; +use PDO; +use PDOStatement; - /** @var string */ - protected $message = ''; +/** + * Base query builder. + */ +abstract class Base implements \IteratorAggregate, \Stringable +{ + protected Query $fluent; + protected null|bool|\PDOStatement $result; - /** @var int */ - protected $currentFetchMode; + /** + * @var array - definition clauses + */ + protected array $clauses = []; + protected array $statements = []; + protected array $parameters = []; + protected Regex $regex; + protected string $message = ''; + protected int $currentFetchMode; + private float $totalTime; + private float $executionTime; + private bool $object = false; /** * BaseQuery constructor. * - * @param Query $fluent - * @param $clauses + * @param mixed $clauses */ protected function __construct(Query $fluent, $clauses) { - $this->currentFetchMode = defined('PDO::FETCH_DEFAULT') ? PDO::FETCH_DEFAULT : PDO::FETCH_BOTH; + $this->currentFetchMode = \defined('PDO::FETCH_DEFAULT') ? \PDO::FETCH_DEFAULT : \PDO::FETCH_BOTH; $this->fluent = $fluent; $this->clauses = $clauses; $this->result = null; @@ -62,11 +72,11 @@ protected function __construct(Query $fluent, $clauses) /** * Return formatted query when request class representation - * ie: echo $query - * - * @return string - formatted query + * ie: echo $query. * * @throws Exception + * + * @return string - formatted query */ public function __toString() { @@ -74,117 +84,24 @@ public function __toString() } /** - * Initialize statement and parameter clauses. - */ - private function initClauses(): void - { - foreach ($this->clauses as $clause => $value) { - if ($value) { - $this->statements[$clause] = []; - $this->parameters[$clause] = []; - } else { - $this->statements[$clause] = null; - $this->parameters[$clause] = null; - } - } - } - - /** - * Add statement for all clauses except WHERE - * - * @param $clause - * @param $statement - * @param array $parameters - * - * @return $this - */ - protected function addStatement($clause, $statement, $parameters = []) - { - if ($statement === null) { - return $this->resetClause($clause); - } - - if ($this->clauses[$clause]) { - if (is_array($statement)) { - $this->statements[$clause] = array_merge($this->statements[$clause], $statement); - } else { - $this->statements[$clause][] = $statement; - } - - $this->parameters[$clause] = array_merge($this->parameters[$clause], $parameters); - } else { - $this->statements[$clause] = $statement; - $this->parameters[$clause] = $parameters; - } - - return $this; - } - - /** - * Add statement for all kind of clauses - * - * @param $statement - * @param string $separator - should be AND or OR - * @param array $parameters - * - * @return $this - */ - protected function addWhereStatement($statement, string $separator = 'AND', $parameters = []) - { - if ($statement === null) { - return $this->resetClause('WHERE'); - } - - if (is_array($statement)) { - foreach ($statement as $s) { - $this->statements['WHERE'][] = [$separator, $s]; - } - } else { - $this->statements['WHERE'][] = [$separator, $statement]; - } - - $this->parameters['WHERE'] = array_merge($this->parameters['WHERE'], $parameters); - - return $this; - } - - /** - * Remove all prev defined statements - * - * @param $clause - * - * @return $this - */ - protected function resetClause($clause) - { - $this->statements[$clause] = null; - $this->parameters[$clause] = []; - if (isset($this->clauses[$clause]) && $this->clauses[$clause]) { - $this->statements[$clause] = []; - } - - return $this; - } - - /** - * Implements method from IteratorAggregate - * - * @return PDOStatement + * Implements method from IteratorAggregate. * * @throws Exception + * + * @return \PDOStatement */ #[\ReturnTypeWillChange] - public function getIterator() + public function getIterator(): \Traversable { return $this->execute(); } /** - * Execute query with earlier added parameters - * - * @return PDOStatement + * Execute query with earlier added parameters. * * @throws Exception + * + * @return \PDOStatement */ public function execute() { @@ -208,17 +125,9 @@ public function execute() } /** - * @return Structure - */ - protected function getStructure(): Structure - { - return $this->fluent->getStructure(); - } - - /** - * Get PDOStatement result + * Get PDOStatement result. * - * @return PDOStatement|null|bool + * @return null|bool|\PDOStatement */ public function getResult() { @@ -226,9 +135,9 @@ public function getResult() } /** - * Get query parameters + * Get query parameters. * - * @return array + * @return array Array of query parameters */ public function getParameters(): array { @@ -236,7 +145,9 @@ public function getParameters(): array } /** - * @return array + * Get raw clauses array. + * + * @return array Raw clauses data */ public function getRawClauses(): array { @@ -244,25 +155,22 @@ public function getRawClauses(): array } /** - * @return array + * Get raw statements array. + * + * @return array Raw statements data */ public function getRawStatements(): array { return $this->statements; } - /** - * @return array - */ public function getRawParameters(): array { return $this->parameters; } /** - * Gets the total time of query building, preparation and execution - * - * @return float + * Gets the total time of query building, preparation and execution. */ public function getTotalTime(): float { @@ -270,29 +178,23 @@ public function getTotalTime(): float } /** - * Gets the query execution time - * - * @return float + * Gets the query execution time. */ public function getExecutionTime(): float { return $this->executionTime; } - /** - * @return string - */ public function getMessage(): string { return $this->message; } /** - * Get query string + * Get query string. * * @param bool $formatted - Return formatted query * - * @return string * @throws Exception */ public function getQuery(bool $formatted = true): string @@ -307,11 +209,11 @@ public function getQuery(bool $formatted = true): string } /** - * Select an item as object + * Select an item as object. * - * @param object|boolean $object If set to true, items are returned as stdClass, otherwise a class - * name can be passed and a new instance of this class is returned. - * Can be set to false to return items as an associative array. + * @param bool|object $object If set to true, items are returned as stdClass, otherwise a class + * name can be passed and a new instance of this class is returned. + * Can be set to false to return items as an associative array. * * @return $this */ @@ -323,16 +225,99 @@ public function asObject($object = true) } /** - * Converts php null values to Literal instances to be inserted into a database + * Add statement for all clauses except WHERE. + * + * @param mixed $clause + * @param mixed $statement + * @param array $parameters + * + * @return $this + */ + protected function addStatement($clause, $statement, $parameters = []) + { + if ($statement === null) { + return $this->resetClause($clause); + } + + if ($this->clauses[$clause]) { + if (\is_array($statement)) { + $this->statements[$clause] = array_merge($this->statements[$clause], $statement); + } else { + $this->statements[$clause][] = $statement; + } + + $this->parameters[$clause] = array_merge($this->parameters[$clause], $parameters); + } else { + $this->statements[$clause] = $statement; + $this->parameters[$clause] = $parameters; + } + + return $this; + } + + /** + * Add statement for all kind of clauses. + * + * @param mixed $statement + * @param string $separator - should be AND or OR + * @param array $parameters + * + * @return $this + */ + protected function addWhereStatement($statement, string $separator = 'AND', $parameters = []) + { + if ($statement === null) { + return $this->resetClause('WHERE'); + } + + if (\is_array($statement)) { + foreach ($statement as $s) { + $this->statements['WHERE'][] = [$separator, $s]; + } + } else { + $this->statements['WHERE'][] = [$separator, $statement]; + } + + $this->parameters['WHERE'] = array_merge($this->parameters['WHERE'], $parameters); + + return $this; + } + + /** + * Remove all prev defined statements. + * + * @param mixed $clause + * + * @return $this + */ + protected function resetClause($clause) + { + $this->statements[$clause] = null; + $this->parameters[$clause] = []; + + if (isset($this->clauses[$clause]) && $this->clauses[$clause]) { + $this->statements[$clause] = []; + } + + return $this; + } + + protected function getStructure(): Structure + { + return $this->fluent->getStructure(); + } + + /** + * Converts php null values to Literal instances to be inserted into a database. */ protected function convertNullValues(): void { $filterList = ['VALUES', 'ON DUPLICATE KEY UPDATE', 'SET']; foreach ($this->statements as $clause => $statement) { - if (in_array($clause, $filterList)) { + if (\in_array($clause, $filterList, true)) { if (isset($statement[0])) { - for ($i = 0, $iMax = count($statement); $i < $iMax; $i++) { + for ($i = 0, $iMax = \count($statement); $i < $iMax; ++$i) { foreach ($statement[$i] as $key => $value) { $this->statements[$clause][$i][$key] = Utilities::nullToLiteral($value); } @@ -347,10 +332,11 @@ protected function convertNullValues(): void } /** - * Generate query + * Generate query. * - * @return string * @throws Exception + * + * @return string */ protected function buildQuery() { @@ -362,14 +348,14 @@ protected function buildQuery() foreach ($this->clauses as $clause => $separator) { if ($this->clauseNotEmpty($clause)) { - if (is_string($separator)) { - $query .= " {$clause} " . implode($separator, $this->statements[$clause]); + if (\is_string($separator)) { + $query .= " {$clause} ".implode($separator, $this->statements[$clause]); } elseif ($separator === null) { $query .= " {$clause} {$this->statements[$clause]}"; - } elseif (is_callable($separator)) { + } elseif (\is_callable($separator)) { $query .= $separator(); } else { - throw new Exception("Clause '$clause' is incorrectly set to '$separator'."); + throw new Exception("Clause '{$clause}' is incorrectly set to '{$separator}'."); } } } @@ -377,26 +363,34 @@ protected function buildQuery() return trim(str_replace(['\.', '\:'], ['.', ':'], $query)); } - /** - * @return array - */ protected function buildParameters(): array { $parameters = []; + foreach ($this->parameters as $clauses) { if ($this->fluent->convertWrite === true) { $clauses = Utilities::convertSqlWriteValues($clauses); } - if (is_array($clauses)) { + if (\is_array($clauses)) { foreach ($clauses as $key => $value) { - if (strpos($key, ':') === 0) { // these are named params e.g. (':name' => 'Mark') + // Convert arrays to JSON strings for PDO compatibility + if (\is_array($value)) { + $value = json_encode($value); + } + + if (\is_string($key) && str_starts_with($key, ':')) { // these are named params e.g. (':name' => 'Mark') $parameters += [$key => $value]; } else { $parameters[] = $value; } } } elseif ($clauses !== false && $clauses !== null) { + // Convert arrays to JSON strings for PDO compatibility + if (\is_array($clauses)) { + $clauses = json_encode($clauses); + } + $parameters[] = $clauses; } } @@ -405,23 +399,24 @@ protected function buildParameters(): array } /** - * @param $value + * @param mixed $value * * @return string */ protected function quote($value) { if (!isset($value)) { - return "NULL"; + return 'NULL'; } - if (is_array($value)) { // (a, b) IN ((1, 2), (3, 4)) - return "(" . implode(", ", array_map([$this, 'quote'], $value)) . ")"; + if (\is_array($value)) { // (a, b) IN ((1, 2), (3, 4)) + return '('.implode(', ', array_map([$this, 'quote'], $value)).')'; } - $value = $this->formatValue($value); - if (is_float($value)) { - return sprintf("%F", $value); // otherwise depends on setlocale() + $value = self::formatValue($value); + + if (\is_float($value)) { + return sprintf('%F', $value); // otherwise depends on setlocale() } if ($value === true) { @@ -432,25 +427,41 @@ protected function quote($value) return 0; } - if (is_int($value) || $value instanceof Literal) { // number or SQL code - for example "NOW()" - return (string)$value; + if (\is_int($value) || $value instanceof Literal) { // number or SQL code - for example "NOW()" + return (string) $value; } return $this->fluent->getPdo()->quote($value); } /** - * @param $clause + * Initialize statement and parameter clauses. + */ + private function initClauses(): void + { + foreach ($this->clauses as $clause => $value) { + if ($value) { + $this->statements[$clause] = []; + $this->parameters[$clause] = []; + } else { + $this->statements[$clause] = null; + $this->parameters[$clause] = null; + } + } + } + + /** + * @param mixed $clause * * @return bool */ private function clauseNotEmpty($clause) { - if ((Utilities::isCountable($this->statements[$clause])) && $this->clauses[$clause]) { - return (bool)count($this->statements[$clause]); + if (Utilities::isCountable($this->statements[$clause]) && $this->clauses[$clause]) { + return (bool) \count($this->statements[$clause]); } - return (bool)$this->statements[$clause]; + return (bool) $this->statements[$clause]; } /** @@ -458,9 +469,9 @@ private function clauseNotEmpty($clause) * * @return mixed */ - private function formatValue($val) + private static function formatValue($val) { - if ($val instanceof DateTime) { + if ($val instanceof \DateTime) { return $val->format('Y-m-d H:i:s'); // may be driver specific } @@ -484,7 +495,7 @@ private function prepareQuery($query): void prepare function until we call PDOStatement::execute() below. If PDO::prepare() was consistent, this is where we would check for prepare errors, such as invalid SQL. - */ + */ if ($this->result === false) { $error = $this->fluent->getPdo()->errorInfo(); @@ -520,41 +531,38 @@ private function executeQuery($parameters, $startTime, $execTime): void } } - /** - * @param PDOStatement $result - */ - private function setObjectFetchMode(PDOStatement $result): void + private function setObjectFetchMode(\PDOStatement $result): void { if ($this->object !== false) { if (class_exists($this->object)) { - $this->currentFetchMode = PDO::FETCH_CLASS; + $this->currentFetchMode = \PDO::FETCH_CLASS; $result->setFetchMode($this->currentFetchMode, $this->object); } else { - $this->currentFetchMode = PDO::FETCH_OBJ; + $this->currentFetchMode = \PDO::FETCH_OBJ; $result->setFetchMode($this->currentFetchMode); } - } elseif ($this->fluent->getPdo()->getAttribute(PDO::ATTR_DEFAULT_FETCH_MODE) === PDO::FETCH_BOTH) { - $this->currentFetchMode = PDO::FETCH_ASSOC; + } elseif ($this->fluent->getPdo()->getAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE) === \PDO::FETCH_BOTH) { + $this->currentFetchMode = \PDO::FETCH_ASSOC; $result->setFetchMode($this->currentFetchMode); } } /** - * Echo/pass a debug string + * Echo/pass a debug string. * * @throws Exception */ - private function debug() + private function debug(): void { if (!empty($this->fluent->debug)) { - if (!is_callable($this->fluent->debug)) { + if (!\is_callable($this->fluent->debug)) { $backtrace = ''; $query = $this->getQuery(); $parameters = $this->getParameters(); $debug = ''; if ($parameters) { - $debug = '# parameters: ' . implode(', ', array_map([$this, 'quote'], $parameters)) . "\n"; + $debug = '# parameters: '.implode(', ', array_map([$this, 'quote'], $parameters))."\n"; } $debug .= $query; @@ -566,13 +574,13 @@ private function debug() } } - $time = sprintf('%0.3f', $this->totalTime * 1000) . 'ms'; + $time = sprintf('%0.3f', $this->totalTime * 1000).'ms'; $rows = ($this->result) ? $this->result->rowCount() : 0; $finalString = "# {$backtrace['file']}:{$backtrace['line']} ({$time}; rows = {$rows})\n{$debug}\n\n"; // if STDERR is set, send there, otherwise just output the string - if (defined('STDERR') && is_resource(STDERR)) { - fwrite(STDERR, $finalString); + if (\defined('STDERR') && \is_resource(\STDERR)) { + fwrite(\STDERR, $finalString); } else { echo $finalString; } diff --git a/src/Queries/Common.php b/src/Queries/Common.php index fefd253..5927a44 100644 --- a/src/Queries/Common.php +++ b/src/Queries/Common.php @@ -1,33 +1,66 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Envms\FluentPDO\Queries; -use Envms\FluentPDO\{Exception, Literal, Utilities}; +use Envms\FluentPDO\Exception; +use Envms\FluentPDO\Literal; +use Envms\FluentPDO\Utilities; /** - * CommonQuery add JOIN and WHERE clauses for (SELECT, UPDATE, DELETE) + * CommonQuery add JOIN and WHERE clauses for (SELECT, UPDATE, DELETE). * - * @method $this from(string $table) - add FROM to DELETE query - * @method $this leftJoin(string $statement) - add LEFT JOIN to query - * $statement can be the 'table' name only or 'table:' to back reference the join - * @method $this rightJoin(string $statement) - add RIGHT JOIN to query - * @method $this innerJoin(string $statement) - add INNER JOIN to query - * @method $this outerJoin(string $statement) - add OUTER JOIN to query - * @method $this fullJoin(string $statement) - add FULL JOIN to query - * @method $this group(string $column) - add GROUP BY to query - * @method $this groupBy(string $column) - add GROUP BY to query - * @method $this having(string $column) - add HAVING query - * @method $this order(string $column) - add ORDER BY to query - * @method $this orderBy(string $column) - add ORDER BY to query - * @method $this limit(int $limit) - add LIMIT to query - * @method $this offset(int $offset) - add OFFSET to query * @method $this comment(string $comment) - add COMMENT (--) to query + * @method $this from(string $table) - add FROM to DELETE query + * @method $this fullJoin(string $statement) - add FULL JOIN to query + * @method $this group(string $column) - add GROUP BY to query + * @method $this groupBy(string $column) - add GROUP BY to query + * @method $this having(string $column) - add HAVING query + * @method $this innerJoin(string $statement) - add INNER JOIN to query + * @method $this leftJoin(string $statement) - add LEFT JOIN to query + * $statement can be the 'table' name only or 'table:' to back reference the join + * @method $this limit(int $limit) - add LIMIT to query + * @method $this offset(int $offset) - add OFFSET to query + * @method $this order(string $column) - add ORDER BY to query + * @method $this orderBy(string $column) - add ORDER BY to query + * @method $this outerJoin(string $statement) - add OUTER JOIN to query + * @method $this rightJoin(string $statement) - add RIGHT JOIN to query */ abstract class Common extends Base { + /** + * @var array - Query tables (also include table from clause FROM) + */ + protected array $joins = []; + + /** + * @var bool - Disable adding undefined joins to query? + */ + protected bool $isSmartJoinEnabled = true; - /** @var array - methods which are allowed to be called by the magic method __call() */ - private $validMethods = [ + /** + * @var array - methods which are allowed to be called by the magic method __call() + */ + private array $validMethods = [ 'comment', 'from', 'fullJoin', @@ -42,40 +75,38 @@ abstract class Common extends Base 'order', 'orderBy', 'outerJoin', - 'rightJoin' + 'rightJoin', ]; - /** @var array - Query tables (also include table from clause FROM) */ - protected $joins = []; - - /** @var bool - Disable adding undefined joins to query? */ - protected $isSmartJoinEnabled = true; - /** - * @param string $name - * @param array $parameters - first is $statement followed by $parameters + * Magic method for handling dynamic method calls. * - * @return $this + * @param string $name The method name being called + * @param array $parameters Method parameters, first is $statement followed by $parameters + * + * @throws Exception When calling invalid method + * + * @return $this Fluent interface */ public function __call($name, $parameters = []) { - if (!in_array($name, $this->validMethods)) { - trigger_error("Call to invalid method " . get_class($this) . "::{$name}()", E_USER_ERROR); + if (!\in_array($name, $this->validMethods, true)) { + throw new Exception('Call to invalid method '.\get_class($this)."::{$name}()"); } $clause = Utilities::toUpperWords($name); - if ($clause == 'GROUP' || $clause == 'ORDER') { + if ($clause === 'GROUP' || $clause === 'ORDER') { $clause = "{$clause} BY"; } - if ($clause == 'COMMENT') { + if ($clause === 'COMMENT') { $clause = "\n--"; } $statement = array_shift($parameters); - if (strpos($clause, 'JOIN') !== false) { + if (str_contains($clause, 'JOIN')) { return $this->addJoinStatements($clause, $statement, $parameters); } @@ -83,7 +114,21 @@ public function __call($name, $parameters = []) } /** - * @return $this + * Clone method to properly handle cloned objects. + */ + public function __clone() + { + foreach ($this->clauses as $clause => $value) { + if (\is_array($value) && $value[0] instanceof self) { + $this->clauses[$clause][0] = $this; + } + } + } + + /** + * Enable smart join functionality. + * + * @return $this Fluent interface */ public function enableSmartJoin() { @@ -93,7 +138,9 @@ public function enableSmartJoin() } /** - * @return $this + * Disable smart join functionality. + * + * @return $this Fluent interface */ public function disableSmartJoin() { @@ -103,7 +150,9 @@ public function disableSmartJoin() } /** - * @return bool + * Check if smart join is enabled. + * + * @return bool True if smart join is enabled, false otherwise */ public function isSmartJoinEnabled() { @@ -111,11 +160,11 @@ public function isSmartJoinEnabled() } /** - * Add where condition, defaults to appending with AND + * Add where condition, defaults to appending with AND. * - * @param string|array $condition - possibly containing ? or :name (PDO syntax) + * @param array|string $condition - possibly containing ? or :name (PDO syntax) * @param mixed $parameters - * @param string $separator - should be AND or OR + * @param string $separator - should be AND or OR * * @return $this */ @@ -129,7 +178,7 @@ public function where($condition, $parameters = [], $separator = 'AND') return $this; } - if (is_array($condition)) { // where(["column1 > ?" => 1, "column2 < ?" => 2]) + if (\is_array($condition)) { // where(["column1 > ?" => 1, "column2 < ?" => 2]) foreach ($condition as $key => $val) { $this->where($key, $val); } @@ -137,7 +186,7 @@ public function where($condition, $parameters = [], $separator = 'AND') return $this; } - $args = func_get_args(); + $args = \func_get_args(); if ($parameters === []) { return $this->addWhereStatement($condition, $separator); @@ -148,16 +197,20 @@ public function where($condition, $parameters = [], $separator = 'AND') * a parameter (? or :name), add them; it's up to the dev to be valid sql. Otherwise it's probably * just an identifier, so construct a new condition based on the passed parameter value. */ - if (count($args) >= 2 && !$this->regex->sqlParameter($condition)) { + if (\count($args) >= 2 && !$this->regex->sqlParameter($condition)) { // condition is column only - if (is_null($parameters)) { - return $this->addWhereStatement("$condition IS NULL", $separator); - } elseif ($args[1] === []) { + if (null === $parameters) { + return $this->addWhereStatement("{$condition} IS NULL", $separator); + } + + if ($args[1] === []) { return $this->addWhereStatement('FALSE', $separator); - } elseif (is_array($args[1])) { + } + + if (\is_array($args[1])) { $in = $this->quote($args[1]); - return $this->addWhereStatement("$condition IN $in", $separator); + return $this->addWhereStatement("{$condition} IN {$in}", $separator); } // don't parameterize the value if it's an instance of Literal @@ -165,15 +218,15 @@ public function where($condition, $parameters = [], $separator = 'AND') $condition = "{$condition} = {$parameters}"; return $this->addWhereStatement($condition, $separator); - } else { - $condition = "$condition = ?"; } + + $condition = "{$condition} = ?"; } $args = [0 => $args[1]]; // parameters can be passed as [1, 2, 3] and it will fill a condition like: id IN (?, ?, ?) - if (is_array($parameters) && !empty($parameters)) { + if (\is_array($parameters) && !empty($parameters)) { $args = $parameters; } @@ -181,7 +234,7 @@ public function where($condition, $parameters = [], $separator = 'AND') } /** - * Add where appending with OR + * Add where appending with OR. * * @param string $condition - possibly containing ? or :name (PDO syntax) * @param mixed $parameters @@ -190,7 +243,7 @@ public function where($condition, $parameters = [], $separator = 'AND') */ public function whereOr($condition, $parameters = []) { - if (is_array($condition)) { // where(["column1 > ?" => 1, "column2 < ?" => 2]) + if (\is_array($condition)) { // where(["column1 > ?" => 1, "column2 < ?" => 2]) foreach ($condition as $key => $val) { $this->whereOr($key, $val); } @@ -212,7 +265,8 @@ protected function getClauseJoin() /** * @return string */ - protected function getClauseWhere() { + protected function getClauseWhere() + { $firstStatement = array_shift($this->statements['WHERE']); $query = " WHERE {$firstStatement[1]}"; // append first statement to WHERE without condition @@ -229,10 +283,43 @@ protected function getClauseWhere() { } /** - * Statement can contain more tables (e.g. "table1.table2:table3:") + * @throws Exception + * + * @return string + */ + protected function buildQuery() + { + // first create extra join from statements with columns with referenced tables + $statementsWithReferences = ['WHERE', 'SELECT', 'GROUP BY', 'ORDER BY']; + + foreach ($statementsWithReferences as $clause) { + if (\array_key_exists($clause, $this->statements)) { + $this->statements[$clause] = array_map([$this, 'createUndefinedJoins'], $this->statements[$clause]); + } + } + + return parent::buildQuery(); + } + + /** + * @param mixed $statement + * + * @return bool + */ + protected function isEscapedJoin($statement) + { + if (\is_array($statement)) { + $statement = $statement[1]; + } + + return !$this->isSmartJoinEnabled || str_contains($statement, '\.') || str_contains($statement, '\:'); + } + + /** + * Statement can contain more tables (e.g. "table1.table2:table3:"). * - * @param $clause - * @param $statement + * @param mixed $clause + * @param mixed $statement * @param array $parameters * * @return $this @@ -245,20 +332,20 @@ private function addJoinStatements($clause, $statement, $parameters = []) return $this->resetClause('JOIN'); } - if (array_search(substr($statement, 0, -1), $this->joins) !== false) { + if (array_search(substr($statement, 0, -1), $this->joins, true) !== false) { return $this; } - list($joinAlias, $joinTable) = $this->setJoinNameAlias($statement); + [$joinAlias, $joinTable] = $this->setJoinNameAlias($statement); - if (strpos(strtoupper($statement), ' ON ') !== false || strpos(strtoupper($statement), ' USING') !== false) { + if (str_contains(strtoupper($statement), ' ON ') || str_contains(strtoupper($statement), ' USING')) { return $this->addRawJoins($clause, $statement, $parameters, $joinAlias, $joinTable); } $mainTable = $this->setMainTable(); // if $joinTable does not end with a dot or colon, append one - if (!in_array(substr($joinTable, -1), ['.', ':'])) { + if (!\in_array(substr($joinTable, -1), ['.', ':'], true)) { $joinTable .= '.'; } @@ -266,10 +353,10 @@ private function addJoinStatements($clause, $statement, $parameters = []) // used for applying the table alias $lastItem = array_pop($matches[1]); - array_push($matches[1], $lastItem); + $matches[1][] = $lastItem; foreach ($matches[1] as $joinItem) { - if ($this->matchTableWithJoin($mainTable, $joinItem)) { + if (self::matchTableWithJoin($mainTable, $joinItem)) { // this is still the same table so we don't need to add the same join continue; } @@ -281,18 +368,18 @@ private function addJoinStatements($clause, $statement, $parameters = []) } /** - * Create join string + * Create join string. * - * @param $clause - * @param $mainTable - * @param $joinTable + * @param mixed $clause + * @param mixed $mainTable + * @param mixed $joinTable * @param string $joinAlias * * @return string */ private function createJoinStatement($clause, $mainTable, $joinTable, $joinAlias = '') { - if (in_array(substr($mainTable, -1), [':', '.'])) { + if (\in_array(substr($mainTable, -1), [':', '.'], true)) { $mainTable = substr($mainTable, 0, -1); } @@ -301,32 +388,32 @@ private function createJoinStatement($clause, $mainTable, $joinTable, $joinAlias $asJoinAlias = ''; if (!empty($joinAlias)) { - $asJoinAlias = " AS $joinAlias"; + $asJoinAlias = " AS {$joinAlias}"; } else { $joinAlias = $joinTable; } - if (in_array($joinAlias, $this->joins)) { // if the join exists don't create it again + if (\in_array($joinAlias, $this->joins, true)) { // if the join exists don't create it again return ''; - } else { - $this->joins[] = $joinAlias; } - if ($referenceDirection == ':') { // back reference + $this->joins[] = $joinAlias; + + if ($referenceDirection === ':') { // back reference $primaryKey = $this->getStructure()->getPrimaryKey($mainTable); $foreignKey = $this->getStructure()->getForeignKey($mainTable); - return " $clause $joinTable$asJoinAlias ON $joinAlias.$foreignKey = $mainTable.$primaryKey"; - } else { - $primaryKey = $this->getStructure()->getPrimaryKey($joinTable); - $foreignKey = $this->getStructure()->getForeignKey($joinTable); - - return " $clause $joinTable$asJoinAlias ON $joinAlias.$primaryKey = $mainTable.$foreignKey"; + return " {$clause} {$joinTable}{$asJoinAlias} ON {$joinAlias}.{$foreignKey} = {$mainTable}.{$primaryKey}"; } + + $primaryKey = $this->getStructure()->getPrimaryKey($joinTable); + $foreignKey = $this->getStructure()->getForeignKey($joinTable); + + return " {$clause} {$joinTable}{$asJoinAlias} ON {$joinAlias}.{$primaryKey} = {$mainTable}.{$foreignKey}"; } /** - * Create undefined joins from statement with column with referenced tables + * Create undefined joins from statement with column with referenced tables. * * @param string $statement * @@ -339,8 +426,9 @@ private function createUndefinedJoins($statement) } $separator = null; + // if we're in here, this is a where clause - if (is_array($statement)) { + if (\is_array($statement)) { $separator = $statement[0]; $statement = $statement[1]; } @@ -351,19 +439,19 @@ private function createUndefinedJoins($statement) foreach ($matches[1] as $join) { // remove the trailing dot and compare with the joins we already have - if (!in_array(substr($join, 0, -1), $this->joins)) { + if (!\in_array(substr($join, 0, -1), $this->joins, true)) { $this->addJoinStatements('LEFT JOIN', $join); } } // don't rewrite table from other databases foreach ($this->joins as $join) { - if (strpos($join, '.') !== false && strpos($statement, $join) === 0) { + if (str_contains($join, '.') && str_starts_with($statement, $join)) { // rebuild the where statement if ($separator !== null) { $statement = [$separator, $statement]; } - + return $statement; } } @@ -379,40 +467,7 @@ private function createUndefinedJoins($statement) } /** - * @throws Exception - * - * @return string - */ - protected function buildQuery() - { - // first create extra join from statements with columns with referenced tables - $statementsWithReferences = ['WHERE', 'SELECT', 'GROUP BY', 'ORDER BY']; - - foreach ($statementsWithReferences as $clause) { - if (array_key_exists($clause, $this->statements)) { - $this->statements[$clause] = array_map([$this, 'createUndefinedJoins'], $this->statements[$clause]); - } - } - - return parent::buildQuery(); - } - - /** - * @param $statement - * - * @return bool - */ - protected function isEscapedJoin($statement) - { - if (is_array($statement)) { - $statement = $statement[1]; - } - - return !$this->isSmartJoinEnabled || strpos($statement, '\.') !== false || strpos($statement, '\:') !== false; - } - - /** - * @param $statement + * @param mixed $statement * * @return array */ @@ -424,7 +479,8 @@ private function setJoinNameAlias($statement) if ($matches) { $joinTable = $matches[1]; - if (isset($matches[4]) && !in_array(strtoupper($matches[4]), ['ON', 'USING'])) { + + if (isset($matches[4]) && !\in_array(strtoupper($matches[4]), ['ON', 'USING'], true)) { $joinAlias = $matches[4]; } } @@ -433,22 +489,22 @@ private function setJoinNameAlias($statement) } /** - * @param $table - * @param $joinItem + * @param mixed $table + * @param mixed $joinItem * * @return bool */ - private function matchTableWithJoin($table, $joinItem) + private static function matchTableWithJoin($table, $joinItem) { - return $table == substr($joinItem, 0, -1); + return $table === substr($joinItem, 0, -1); } /** - * @param $clause - * @param $statement - * @param $parameters - * @param $joinAlias - * @param $joinTable + * @param mixed $clause + * @param mixed $statement + * @param mixed $parameters + * @param mixed $joinAlias + * @param mixed $joinTable * * @return $this */ @@ -458,14 +514,14 @@ private function addRawJoins($clause, $statement, $parameters, $joinAlias, $join $joinAlias = $joinTable; } - if (in_array($joinAlias, $this->joins)) { + if (\in_array($joinAlias, $this->joins, true)) { return $this; - } else { - $this->joins[] = $joinAlias; - $statement = " $clause $statement"; - - return $this->addStatement('JOIN', $statement, $parameters); } + + $this->joins[] = $joinAlias; + $statement = " {$clause} {$statement}"; + + return $this->addStatement('JOIN', $statement, $parameters); } /** @@ -475,7 +531,9 @@ private function setMainTable() { if (isset($this->statements['FROM'])) { return $this->statements['FROM']; - } elseif (isset($this->statements['UPDATE'])) { + } + + if (isset($this->statements['UPDATE'])) { return $this->statements['UPDATE']; } @@ -483,12 +541,12 @@ private function setMainTable() } /** - * @param $clause - * @param $parameters - * @param $mainTable - * @param $joinItem - * @param $lastItem - * @param $joinAlias + * @param mixed $clause + * @param mixed $parameters + * @param mixed $mainTable + * @param mixed $joinItem + * @param mixed $lastItem + * @param mixed $joinAlias * * @return mixed */ @@ -496,7 +554,7 @@ private function applyTableJoin($clause, $parameters, $mainTable, $joinItem, $la { $alias = ''; - if ($joinItem == $lastItem) { + if ($joinItem === $lastItem) { $alias = $joinAlias; // use $joinAlias only for $lastItem } @@ -508,13 +566,4 @@ private function applyTableJoin($clause, $parameters, $mainTable, $joinItem, $la return $joinItem; } - - public function __clone() - { - foreach ($this->clauses as $clause => $value) { - if (is_array($value) && $value[0] instanceof Common) { - $this->clauses[$clause][0] = $this; - } - } - } } diff --git a/src/Queries/Delete.php b/src/Queries/Delete.php index 49c74d9..050a438 100644 --- a/src/Queries/Delete.php +++ b/src/Queries/Delete.php @@ -1,41 +1,56 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Envms\FluentPDO\Queries; -use Envms\FluentPDO\{Exception, Query}; +use Envms\FluentPDO\Exception; +use Envms\FluentPDO\Query; /** - * DELETE query builder + * DELETE query builder. * - * @method Delete leftJoin(string $statement) add LEFT JOIN to query - * ($statement can be 'table' name only or 'table:' means back reference) - * @method Delete innerJoin(string $statement) add INNER JOIN to query - * ($statement can be 'table' name only or 'table:' means back reference) - * @method Delete from(string $table) add LIMIT to query - * @method Delete orderBy(string $column) add ORDER BY to query - * @method Delete limit(int $limit) add LIMIT to query + * @method Delete innerJoin(string $statement) add INNER JOIN to query + * ($statement can be 'table' name only or 'table:' means back reference) + * @method Delete from(string $table) add LIMIT to query + * @method Delete limit(int $limit) add LIMIT to query + * @method Delete orderBy(string $column) add ORDER BY to query */ class Delete extends Common { - - private $ignore = false; + private bool $ignore = false; /** - * Delete constructor - * - * @param Query $fluent - * @param string $table + * Delete constructor. */ public function __construct(Query $fluent, string $table) { $clauses = [ 'DELETE FROM' => [$this, 'getClauseDeleteFrom'], - 'DELETE' => [$this, 'getClauseDelete'], - 'FROM' => null, - 'JOIN' => [$this, 'getClauseJoin'], - 'WHERE' => [$this, 'getClauseWhere'], - 'ORDER BY' => ', ', - 'LIMIT' => null, + 'DELETE' => [$this, 'getClauseDelete'], + 'FROM' => null, + 'JOIN' => [$this, 'getClauseJoin'], + 'WHERE' => [$this, 'getClauseWhere'], + 'ORDER BY' => ', ', + 'LIMIT' => null, ]; parent::__construct($fluent, $clauses); @@ -45,7 +60,7 @@ public function __construct(Query $fluent, string $table) } /** - * Forces delete operation to fail silently + * Forces delete operation to fail silently. * * @return Delete */ @@ -57,23 +72,7 @@ public function ignore() } /** - * @throws Exception - * - * @return string - */ - protected function buildQuery() - { - if ($this->statements['FROM']) { - unset($this->clauses['DELETE FROM']); - } else { - unset($this->clauses['DELETE']); - } - - return parent::buildQuery(); - } - - /** - * Execute DELETE query + * Execute DELETE query. * * @throws Exception * @@ -86,6 +85,7 @@ public function execute() } $result = parent::execute(); + if ($result) { return $result->rowCount(); } @@ -93,12 +93,28 @@ public function execute() return false; } + /** + * @throws Exception + * + * @return string + */ + protected function buildQuery() + { + if ($this->statements['FROM']) { + unset($this->clauses['DELETE FROM']); + } else { + unset($this->clauses['DELETE']); + } + + return parent::buildQuery(); + } + /** * @return string */ protected function getClauseDelete() { - return 'DELETE' . ($this->ignore ? " IGNORE" : '') . ' ' . $this->statements['DELETE']; + return 'DELETE'.($this->ignore ? ' IGNORE' : '').' '.$this->statements['DELETE']; } /** @@ -106,7 +122,6 @@ protected function getClauseDelete() */ protected function getClauseDeleteFrom() { - return 'DELETE' . ($this->ignore ? " IGNORE" : '') . ' FROM ' . $this->statements['DELETE FROM']; + return 'DELETE'.($this->ignore ? ' IGNORE' : '').' FROM '.$this->statements['DELETE FROM']; } - } diff --git a/src/Queries/Insert.php b/src/Queries/Insert.php index ff0b302..f7e3fbc 100644 --- a/src/Queries/Insert.php +++ b/src/Queries/Insert.php @@ -1,39 +1,54 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Envms\FluentPDO\Queries; -use Envms\FluentPDO\{Exception, Literal, Query}; +use Envms\FluentPDO\Exception; +use Envms\FluentPDO\Literal; +use Envms\FluentPDO\Query; -/** INSERT query builder +/** + * INSERT query builder. */ class Insert extends Base { - - /** @var array */ - private $columns = []; - - /** @var array */ - private $firstValue = []; - - /** @var bool */ - private $ignore = false; - /** @var bool */ - private $delayed = false; + private array $columns = []; + private array $firstValue = []; + private bool $ignore = false; + private bool $delayed = false; /** * InsertQuery constructor. * - * @param Query $fluent - * @param string $table - * @param $values + * @param string $table + * @param mixed $values * * @throws Exception */ public function __construct(Query $fluent, $table, $values) { $clauses = [ - 'INSERT INTO' => [$this, 'getClauseInsertInto'], - 'VALUES' => [$this, 'getClauseValues'], + 'INSERT INTO' => [$this, 'getClauseInsertInto'], + 'VALUES' => [$this, 'getClauseValues'], 'ON DUPLICATE KEY UPDATE' => [$this, 'getClauseOnDuplicateKeyUpdate'], ]; parent::__construct($fluent, $clauses); @@ -43,7 +58,7 @@ public function __construct(Query $fluent, $table, $values) } /** - * Force insert operation to fail silently + * Force insert operation to fail silently. * * @return Insert */ @@ -54,7 +69,8 @@ public function ignore() return $this; } - /** Force insert operation delay support + /** + * Force insert operation delay support. * * @return Insert */ @@ -66,24 +82,26 @@ public function delayed() } /** - * Add VALUES + * Add VALUES. * - * @param $values + * @param mixed $values * - * @return Insert * @throws Exception + * + * @return Insert */ public function values($values) { - if (!is_array($values)) { + if (!\is_array($values)) { throw new Exception('Param VALUES for INSERT query must be array'); } $first = current($values); - if (is_string(key($values))) { + + if (\is_string(key($values))) { // is one row array $this->addOneValue($values); - } elseif (is_array($first) && is_string(key($first))) { + } elseif (\is_array($first) && \is_string(key($first))) { // this is multi values foreach ($values as $oneValue) { $this->addOneValue($oneValue); @@ -94,7 +112,7 @@ public function values($values) } /** - * Add ON DUPLICATE KEY UPDATE + * Add ON DUPLICATE KEY UPDATE. * * @param array $values * @@ -103,20 +121,21 @@ public function values($values) public function onDuplicateKeyUpdate($values) { $this->statements['ON DUPLICATE KEY UPDATE'] = array_merge( - $this->statements['ON DUPLICATE KEY UPDATE'], $values + $this->statements['ON DUPLICATE KEY UPDATE'], + $values, ); return $this; } /** - * Execute insert query + * Execute insert query. * * @param mixed $sequence * * @throws Exception * - * @return int|bool - Last inserted primary key + * @return bool|int - Last inserted primary key */ public function execute($sequence = null) { @@ -152,7 +171,7 @@ public function executeWithoutId($sequence = null) */ protected function getClauseInsertInto() { - return 'INSERT' . ($this->ignore ? " IGNORE" : '') . ($this->delayed ? " DELAYED" : '') . ' INTO ' . $this->statements['INSERT INTO']; + return 'INSERT'.($this->ignore ? ' IGNORE' : '').($this->delayed ? ' DELAYED' : '').' INTO '.$this->statements['INSERT INTO']; } /** @@ -161,60 +180,61 @@ protected function getClauseInsertInto() protected function getClauseValues() { $valuesArray = []; + foreach ($this->statements['VALUES'] as $rows) { // literals should not be parametrized. // They are commonly used to call engine functions or literals. // Eg: NOW(), CURRENT_TIMESTAMP etc $placeholders = array_map([$this, 'parameterGetValue'], $rows); - $valuesArray[] = '(' . implode(', ', $placeholders) . ')'; + $valuesArray[] = '('.implode(', ', $placeholders).')'; } $columns = implode(', ', $this->columns); $values = implode(', ', $valuesArray); - return " ($columns) VALUES $values"; + return " ({$columns}) VALUES {$values}"; } - /** * @return string */ protected function getClauseOnDuplicateKeyUpdate() { $result = []; + foreach ($this->statements['ON DUPLICATE KEY UPDATE'] as $key => $value) { - $result[] = "$key = " . $this->parameterGetValue($value); + $result[] = "{$key} = ".$this->parameterGetValue($value); } - return ' ON DUPLICATE KEY UPDATE ' . implode(', ', $result); + return ' ON DUPLICATE KEY UPDATE '.implode(', ', $result); } /** - * @param $param + * @param mixed $param * * @return string */ protected function parameterGetValue($param) { - return $param instanceof Literal ? (string)$param : '?'; + return $param instanceof Literal ? (string) $param : '?'; } /** * Removes all Literal instances from the argument - * since they are not to be used as PDO parameters but rather injected directly into the query + * since they are not to be used as PDO parameters but rather injected directly into the query. * - * @param $statements + * @param mixed $statements * * @return array */ protected function filterLiterals($statements) { - $f = function ($item) { + $f = static function ($item) { return !$item instanceof Literal; }; - return array_map(function ($item) use ($f) { - if (is_array($item)) { + return array_map(static function ($item) use ($f) { + if (\is_array($item)) { return array_filter($item, $f); } @@ -222,14 +242,11 @@ protected function filterLiterals($statements) }, array_filter($statements, $f)); } - /** - * @return array - */ protected function buildParameters(): array { $this->parameters = array_merge( $this->filterLiterals($this->statements['VALUES']), - $this->filterLiterals($this->statements['ON DUPLICATE KEY UPDATE']) + $this->filterLiterals($this->statements['ON DUPLICATE KEY UPDATE']), ); return parent::buildParameters(); @@ -240,24 +257,27 @@ protected function buildParameters(): array * * @throws Exception */ - private function addOneValue($oneValue) + private function addOneValue($oneValue): void { // check if all $keys are strings foreach ($oneValue as $key => $value) { - if (!is_string($key)) { + if (!\is_string($key)) { throw new Exception('INSERT query: All keys of value array have to be strings.'); } } + if (!$this->firstValue) { $this->firstValue = $oneValue; } + if (!$this->columns) { $this->columns = array_keys($oneValue); } - if ($this->columns != array_keys($oneValue)) { + + if ($this->columns !== array_keys($oneValue)) { throw new Exception('INSERT query: All VALUES have to same keys (columns).'); } + $this->statements['VALUES'][] = $oneValue; } - } diff --git a/src/Queries/Json.php b/src/Queries/Json.php index a37cf72..5767074 100644 --- a/src/Queries/Json.php +++ b/src/Queries/Json.php @@ -1,41 +1,53 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Envms\FluentPDO\Queries; + +use Envms\FluentPDO\Query; + +/** + * Class Json. */ class Json extends Common { - - /** @var mixed */ - protected $fromTable; - /** @var mixed */ - protected $fromAlias; - /** @var boolean */ - protected $convertTypes = false; + protected mixed $fromTable; + protected mixed $fromAlias; + protected bool $convertTypes = false; /** - * Json constructor - * - * @param Query $fluent - * @param string $table + * Json constructor. */ public function __construct(Query $fluent, string $table) { $clauses = [ - 'SELECT' => ', ', - 'JOIN' => [$this, 'getClauseJoin'], - 'WHERE' => [$this, 'getClauseWhere'], + 'SELECT' => ', ', + 'JOIN' => [$this, 'getClauseJoin'], + 'WHERE' => [$this, 'getClauseWhere'], 'GROUP BY' => ',', - 'HAVING' => ' AND ', + 'HAVING' => ' AND ', 'ORDER BY' => ', ', - 'LIMIT' => null, - 'OFFSET' => null, - "\n--" => "\n--", + 'LIMIT' => null, + 'OFFSET' => null, + "\n--" => "\n--", ]; parent::__construct($fluent, $clauses); @@ -52,5 +64,4 @@ public function __construct(Query $fluent, string $table) $this->convertTypes = true; } } - -} \ No newline at end of file +} diff --git a/src/Queries/Select.php b/src/Queries/Select.php index 31ef371..014233f 100644 --- a/src/Queries/Select.php +++ b/src/Queries/Select.php @@ -1,39 +1,57 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Envms\FluentPDO\Queries; -use Envms\FluentPDO\{Exception, Query, Utilities}; +use Envms\FluentPDO\Exception; +use Envms\FluentPDO\Query; +use Envms\FluentPDO\Utilities; /** - * SELECT query builder + * SELECT query builder. */ class Select extends Common implements \Countable { - - /** @var mixed */ - private $fromTable; - /** @var mixed */ - private $fromAlias; + private mixed $fromTable; + private mixed $fromAlias; /** * SelectQuery constructor. * - * @param Query $fluent - * @param $from + * @param mixed $from */ - function __construct(Query $fluent, $from) + public function __construct(Query $fluent, $from) { $clauses = [ - 'SELECT' => ', ', - 'FROM' => null, - 'JOIN' => [$this, 'getClauseJoin'], - 'WHERE' => [$this, 'getClauseWhere'], + 'SELECT' => ', ', + 'FROM' => null, + 'JOIN' => [$this, 'getClauseJoin'], + 'WHERE' => [$this, 'getClauseWhere'], 'GROUP BY' => ',', - 'HAVING' => ' AND ', + 'HAVING' => ' AND ', 'ORDER BY' => ', ', - 'LIMIT' => null, - 'OFFSET' => null, - "\n--" => "\n--" + 'LIMIT' => null, + 'OFFSET' => null, + "\n--" => "\n--", ]; parent::__construct($fluent, $clauses); @@ -43,13 +61,12 @@ function __construct(Query $fluent, $from) $this->fromAlias = end($fromParts); $this->statements['FROM'] = $from; - $this->statements['SELECT'][] = $this->fromAlias . '.*'; + $this->statements['SELECT'][] = $this->fromAlias.'.*'; $this->joins[] = $this->fromAlias; } /** * @param mixed $columns - * @param bool $overrideDefault * * @return $this */ @@ -67,7 +84,9 @@ public function select($columns, bool $overrideDefault = false) } /** - * Return table name from FROM clause + * Return table name from FROM clause. + * + * @return null|string The table name from FROM clause */ public function getFromTable() { @@ -75,7 +94,9 @@ public function getFromTable() } /** - * Return table alias from FROM clause + * Return table alias from FROM clause. + * + * @return null|string The table alias from FROM clause */ public function getFromAlias() { @@ -83,13 +104,13 @@ public function getFromAlias() } /** - * Returns a single column + * Returns a single column. * - * @param int $columnNumber + * @param int $columnNumber The zero-based column index to retrieve * - * @throws Exception + * @throws Exception When query execution fails * - * @return string + * @return false|string The column value or false if no data */ public function fetchColumn(int $columnNumber = 0) { @@ -101,14 +122,14 @@ public function fetchColumn(int $columnNumber = 0) } /** - * Fetch first row or column + * Fetch first row or column. * - * @param string $column - column name or empty string for the whole row - * @param int $cursorOrientation + * @param null|string $column Column name or null for the whole row + * @param int $cursorOrientation Cursor orientation for fetch operation * - * @throws Exception + * @throws Exception When query execution fails * - * @return mixed string, array or false if there is no row + * @return mixed Row data as string, array, object or false if there is no row */ public function fetch(?string $column = null, int $cursorOrientation = \PDO::FETCH_ORI_NEXT) { @@ -127,44 +148,45 @@ public function fetch(?string $column = null, int $cursorOrientation = \PDO::FET } if ($row && $column !== null) { - if (is_object($row)) { + if (\is_object($row)) { return $row->{$column}; - } else { - return $row[$column]; } + + return $row[$column]; } return $row; } /** - * Fetch pairs + * Fetch pairs as key-value array. * - * @param $key - * @param $value - * @param $object + * @param mixed $key The column name or expression to use as array keys + * @param mixed $value The column name or expression to use as array values + * @param mixed $object Whether to fetch as objects or arrays * - * @throws Exception + * @throws Exception When query execution fails * - * @return array|\PDOStatement + * @return array|false|\PDOStatement Array of key-value pairs or PDOStatement on success, false on failure */ public function fetchPairs($key, $value, $object = false) { - if (($s = $this->select("$key, $value", true)->asObject($object)->execute()) !== false) { + if (($s = $this->select("{$key}, {$value}", true)->asObject($object)->execute()) !== false) { return $s->fetchAll(\PDO::FETCH_KEY_PAIR); } return $s; } - /** Fetch all row + /** + * Fetch all rows. * - * @param string $index - specify index column. Allows for data organization by field using 'field[]' - * @param string $selectOnly - select columns which could be fetched + * @param string $index Specify index column. Allows for data organization by field using 'field[]' + * @param string $selectOnly Select specific columns which should be fetched * - * @throws Exception + * @throws Exception When query execution fails * - * @return array|bool - fetched rows + * @return array|bool Array of fetched rows or false on failure */ public function fetchAll($index = '', $selectOnly = '') { @@ -175,26 +197,26 @@ public function fetchAll($index = '', $selectOnly = '') } if ($selectOnly) { - $this->select($index . ', ' . $selectOnly, true); + $this->select($index.', '.$selectOnly, true); } if ($index) { return $this->buildSelectData($index, $indexAsArray); - } else { - if (($result = $this->execute()) !== false) { - if ($this->fluent->convertRead === true) { - return Utilities::stringToNumeric($result, $result->fetchAll()); - } else { - return $result->fetchAll(); - } + } + + if (($result = $this->execute()) !== false) { + if ($this->fluent->convertRead === true) { + return Utilities::stringToNumeric($result, $result->fetchAll()); } - return false; + return $result->fetchAll(); } + + return false; } /** - * \Countable interface doesn't break current select query + * \Countable interface doesn't break current select query. * * @throws Exception * @@ -205,7 +227,7 @@ public function count() { $fluent = clone $this; - return (int)$fluent->select('COUNT(*)', true)->fetchColumn(); + return (int) $fluent->select('COUNT(*)', true)->fetchColumn(); } /** @@ -213,18 +235,18 @@ public function count() * * @return \ArrayIterator|\PDOStatement */ - public function getIterator() + public function getIterator(): \Traversable { if ($this->fluent->convertRead === true) { return new \ArrayIterator($this->fetchAll()); - } else { - return $this->execute(); } + + return $this->execute(); } /** - * @param $index - * @param $indexAsArray + * @param mixed $index + * @param mixed $indexAsArray * * @return array */ @@ -233,7 +255,7 @@ private function buildSelectData($index, $indexAsArray) $data = []; foreach ($this as $row) { - if (is_object($row)) { + if (\is_object($row)) { $key = $row->{$index}; } else { $key = $row[$index]; diff --git a/src/Queries/Update.php b/src/Queries/Update.php index d3ebd8b..bb04e02 100644 --- a/src/Queries/Update.php +++ b/src/Queries/Update.php @@ -1,37 +1,53 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Envms\FluentPDO\Queries; -use Envms\FluentPDO\{Exception, Literal, Query}; +use Envms\FluentPDO\Exception; +use Envms\FluentPDO\Literal; +use Envms\FluentPDO\Query; /** - * UPDATE query builder + * UPDATE query builder. * - * @method Update leftJoin(string $statement) add LEFT JOIN to query - * ($statement can be 'table' name only or 'table:' means back reference) - * @method Update innerJoin(string $statement) add INNER JOIN to query - * ($statement can be 'table' name only or 'table:' means back reference) - * @method Update orderBy(string $column) add ORDER BY to query - * @method Update limit(int $limit) add LIMIT to query + * @method Update innerJoin(string $statement) add INNER JOIN to query + * ($statement can be 'table' name only or 'table:' means back reference) + * @method Update limit(int $limit) add LIMIT to query + * @method Update orderBy(string $column) add ORDER BY to query */ class Update extends Common { - /** - * UpdateQuery constructor - * - * @param Query $fluent - * @param string $table + * UpdateQuery constructor. */ public function __construct(Query $fluent, string $table) { $clauses = [ - 'UPDATE' => [$this, 'getClauseUpdate'], - 'JOIN' => [$this, 'getClauseJoin'], - 'SET' => [$this, 'getClauseSet'], - 'WHERE' => [$this, 'getClauseWhere'], + 'UPDATE' => [$this, 'getClauseUpdate'], + 'JOIN' => [$this, 'getClauseJoin'], + 'SET' => [$this, 'getClauseSet'], + 'WHERE' => [$this, 'getClauseWhere'], 'ORDER BY' => ', ', - 'LIMIT' => null, + 'LIMIT' => null, ]; parent::__construct($fluent, $clauses); @@ -42,9 +58,9 @@ public function __construct(Query $fluent, string $table) } /** - * In Update's case, parameters are not assigned until the query is built, since this method + * In Update's case, parameters are not assigned until the query is built, since this method. * - * @param string|array $fieldOrArray + * @param array|string $fieldOrArray * @param bool|string $value * * @throws Exception @@ -56,15 +72,16 @@ public function set($fieldOrArray, $value = false) if (!$fieldOrArray) { return $this; } - if (is_string($fieldOrArray) && $value !== false) { + + if (\is_string($fieldOrArray) && $value !== false) { $this->statements['SET'][$fieldOrArray] = $value; } else { - if (!is_array($fieldOrArray)) { + if (!\is_array($fieldOrArray)) { throw new Exception('You must pass a value, or provide the SET list as an associative array. column => value'); - } else { - foreach ($fieldOrArray as $field => $value) { - $this->statements['SET'][$field] = $value; - } + } + + foreach ($fieldOrArray as $field => $value) { + $this->statements['SET'][$field] = $value; } } @@ -72,13 +89,13 @@ public function set($fieldOrArray, $value = false) } /** - * Execute update query + * Execute update query. * - * @param boolean $getResultAsPdoStatement true to return the pdo statement instead of row count + * @param bool $getResultAsPdoStatement true to return the pdo statement instead of row count * * @throws Exception * - * @return int|boolean|\PDOStatement + * @return bool|int|\PDOStatement */ public function execute($getResultAsPdoStatement = false) { @@ -104,7 +121,7 @@ public function execute($getResultAsPdoStatement = false) */ protected function getClauseUpdate() { - return 'UPDATE ' . $this->statements['UPDATE']; + return 'UPDATE '.$this->statements['UPDATE']; } /** @@ -113,22 +130,21 @@ protected function getClauseUpdate() protected function getClauseSet() { $setArray = []; + foreach ($this->statements['SET'] as $field => $value) { // named params are being used here - if (is_array($value) && strpos(key($value), ':') === 0) { + if (\is_array($value) && str_starts_with(key($value), ':')) { $key = key($value); - $setArray[] = $field . ' = ' . $key; + $setArray[] = $field.' = '.$key; $this->parameters['SET'][$key] = $value[$key]; - } - elseif ($value instanceof Literal) { - $setArray[] = $field . ' = ' . $value; + } elseif ($value instanceof Literal) { + $setArray[] = $field.' = '.$value; } else { - $setArray[] = $field . ' = ?'; + $setArray[] = $field.' = ?'; $this->parameters['SET'][$field] = $value; } } - return ' SET ' . implode(', ', $setArray); + return ' SET '.implode(', ', $setArray); } - } diff --git a/src/Query.php b/src/Query.php index 4c5d49d..864de50 100644 --- a/src/Query.php +++ b/src/Query.php @@ -1,62 +1,102 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Envms\FluentPDO; +use Envms\FluentPDO\Queries\Delete; +use Envms\FluentPDO\Queries\Insert; +use Envms\FluentPDO\Queries\Select; +use Envms\FluentPDO\Queries\Update; use PDO; -use Envms\FluentPDO\Queries\{Insert, Select, Update, Delete}; /** - * FluentPDO is a quick and light PHP library for rapid query building. It features a smart join builder, which automatically creates table joins. + * FluentPDO Query Builder. + * + * FluentPDO is a quick and light PHP library for rapid query building. + * It features a smart join builder, which automatically creates table joins. * * For more information see readme.md * - * @link https://github.com/envms/fluentpdo + * @see https://github.com/VitexSoftware/fluentpdo + * * @author Chris Bornhoft, start@env.ms + * @author Vítězslav Dvořák * @copyright 2012-2020 envms - Chris Bornhoft, Marek Lichtner + * @copyright 2025-2026 VitexSoftware * @license https://www.gnu.org/licenses/gpl-3.0.en.html GNU General Public License, version 3.0 */ /** - * Class Query - * @method debug(Queries\Base $param) + * Main Query class for FluentPDO. + * + * @method debug(Queries\Base $param) Enable debug mode for query execution */ class Query { - /** @var PDO */ - protected $pdo; - /** @var Structure */ - protected $structure; - - /** @var bool|callable */ - public $debug = false; + /** + * Debug mode setting. + * + * @var bool|callable Debug flag or callable function for custom debug handling + */ + public mixed $debug = false; - /** @var bool - Determines whether to convert types when fetching rows from Select */ - public $convertRead = false; - /** @var bool - Determines whether to convert types within Base::buildParameters() */ - public $convertWrite = false; + /** + * Type conversion setting for read operations. + * + * @var bool Determines whether to convert types when fetching rows from Select + */ + public bool $convertRead = false; - /** @var bool - If a query errors, this determines how to handle it */ - public $exceptionOnError = false; + /** + * Type conversion setting for write operations. + * + * @var bool Determines whether to convert types within Base::buildParameters() + */ + public bool $convertWrite = false; - /** @var string */ - protected $table; - /** @var string */ - protected $prefix; - /** @var string */ - protected $separator; + /** + * Exception handling setting. + * + * @var bool If a query errors, this determines how to handle it + */ + public bool $exceptionOnError = false; + protected \PDO $pdo; + protected Structure $structure; + protected string $table; + protected string $prefix; + protected string $separator; /** - * Query constructor + * Query constructor. * - * @param PDO $pdo - * @param ?Structure $structure + * @param \PDO $pdo PDO database connection instance + * @param null|Structure $structure Optional structure for table relationships */ - public function __construct(PDO $pdo, ?Structure $structure = null) + public function __construct(\PDO $pdo, ?Structure $structure = null) { $this->pdo = $pdo; // if exceptions are already activated in PDO, activate them in Fluent as well - if ($this->pdo->getAttribute(PDO::ATTR_ERRMODE) === PDO::ERRMODE_EXCEPTION) { + if ($this->pdo->getAttribute(\PDO::ATTR_ERRMODE) === \PDO::ERRMODE_EXCEPTION) { $this->throwExceptionOnError(true); } @@ -64,41 +104,41 @@ public function __construct(PDO $pdo, ?Structure $structure = null) } /** - * Create SELECT query from $table + * Create SELECT query from $table. * - * @param ?string $table - db table name - * @param ?int $primaryKey - return one row by primary key + * @param null|string $table Database table name + * @param null|int $primaryKey Return one row by primary key * - * @return Select + * @throws Exception When table name is invalid * - * @throws Exception + * @return Select SELECT query builder instance */ public function from(?string $table = null, ?int $primaryKey = null): Select { $this->setTableName($table); - $table = $this->getFullTableName(); + $tableName = $this->getFullTableName(); - $query = new Select($this, $table); + $query = new Select($this, $tableName); if ($primaryKey !== null) { $tableTable = $query->getFromTable(); $tableAlias = $query->getFromAlias(); $primaryKeyName = $this->structure->getPrimaryKey($tableTable); - $query = $query->where("$tableAlias.$primaryKeyName", $primaryKey); + $query = $query->where("{$tableAlias}.{$primaryKeyName}", $primaryKey); } return $query; } /** - * Create INSERT INTO query + * Create INSERT INTO query. * - * @param ?string $table - * @param array $values - accepts one or multiple rows, @see docs + * @param null|string $table Database table name + * @param array $values Accepts one or multiple rows of data to insert * - * @return Insert + * @throws Exception When table name is invalid * - * @throws Exception + * @return Insert INSERT query builder instance */ public function insertInto(?string $table = null, array $values = []): Insert { @@ -109,15 +149,15 @@ public function insertInto(?string $table = null, array $values = []): Insert } /** - * Create UPDATE query + * Create UPDATE query. * - * @param ?string $table - * @param array|string $set - * @param ?int $primaryKey + * @param null|string $table Database table name + * @param array|string $set Column-value pairs to update or SET clause string + * @param null|int $primaryKey Update only row with this primary key * - * @return Update + * @throws Exception When table name is invalid * - * @throws Exception + * @return Update UPDATE query builder instance */ public function update(?string $table = null, $set = [], ?int $primaryKey = null): Update { @@ -127,6 +167,7 @@ public function update(?string $table = null, $set = [], ?int $primaryKey = null $query = new Update($this, $table); $query->set($set); + if ($primaryKey) { $primaryKeyName = $this->getStructure()->getPrimaryKey($this->table); $query = $query->where($primaryKeyName, $primaryKey); @@ -136,14 +177,14 @@ public function update(?string $table = null, $set = [], ?int $primaryKey = null } /** - * Create DELETE query + * Create DELETE query. * - * @param ?string $table - * @param ?int $primaryKey delete only row by primary key + * @param null|string $table Database table name + * @param null|int $primaryKey Delete only row by primary key * - * @return Delete + * @throws Exception When table name is invalid * - * @throws Exception + * @return Delete DELETE query builder instance */ public function delete(?string $table = null, ?int $primaryKey = null): Delete { @@ -161,38 +202,34 @@ public function delete(?string $table = null, ?int $primaryKey = null): Delete } /** - * Create DELETE FROM query + * Create DELETE FROM query. * - * @param ?string $table - * @param ?int $primaryKey + * @param null|string $table Database table name + * @param null|int $primaryKey Delete only row by primary key * - * @return Delete + * @throws Exception When table name is invalid + * + * @return Delete DELETE query builder instance */ public function deleteFrom(?string $table = null, ?int $primaryKey = null): Delete { - $args = func_get_args(); + $args = \func_get_args(); - return call_user_func_array([$this, 'delete'], $args); + return \call_user_func_array([$this, 'delete'], $args); } - /** - * @return PDO - */ - public function getPdo(): PDO + public function getPdo(): \PDO { return $this->pdo; } - /** - * @return Structure - */ public function getStructure(): Structure { return $this->structure; } /** - * Closes the \PDO connection to the database + * Closes the \PDO connection to the database. */ public function close(): void { @@ -200,17 +237,13 @@ public function close(): void } /** - * Set table name comprised of prefix.separator.table + * Set table name comprised of prefix.separator.table. * - * @param ?string $table - * @param string $prefix - * @param string $separator + * @throws Exception * * @return $this - * - * @throws Exception */ - public function setTableName(?string $table = '', string $prefix = '', string $separator = ''): Query + public function setTableName(?string $table = '', string $prefix = '', string $separator = ''): self { if ($table !== null) { $this->prefix = $prefix; @@ -225,67 +258,42 @@ public function setTableName(?string $table = '', string $prefix = '', string $s return $this; } - /** - * @return string - */ public function getFullTableName(): string { - return $this->prefix . $this->separator . $this->table; + return $this->prefix.$this->separator.$this->table; } - /** - * @return string - */ public function getPrefix(): string { return $this->prefix; } - /** - * @return string - */ public function getSeparator(): string { return $this->separator; } - /** - * @return string - */ public function getTable(): string { return $this->table; } - /** - * @param bool $flag - */ public function throwExceptionOnError(bool $flag): void { $this->exceptionOnError = $flag; } - /** - * @param bool $read - * @param bool $write - */ public function convertTypes(bool $read, bool $write): void { $this->convertRead = $read; $this->convertWrite = $write; } - /** - * @param bool $flag - */ public function convertReadTypes(bool $flag): void { $this->convertRead = $flag; } - /** - * @param bool $flag - */ public function convertWriteTypes(bool $flag): void { $this->convertWrite = $flag; diff --git a/src/Regex.php b/src/Regex.php index e24b0ca..da6f0e1 100644 --- a/src/Regex.php +++ b/src/Regex.php @@ -1,27 +1,51 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Envms\FluentPDO; /** - * Regex class + * Regex class. */ class Regex { - /** @var string - All UTF-8 letter characters */ + /** + * @var string - All UTF-8 letter characters + */ public const ALPHA = '\p{L}'; - /** @var string - All UTF-8 letter and number characters */ + + /** + * @var string - All UTF-8 letter and number characters + */ public const ALNUM = '\p{L}\p{N}'; - /** @var string - All valid SQL characters except the UTF-8 groupings with quotes and wildcards */ + + /** + * @var string - All valid SQL characters except the UTF-8 groupings with quotes and wildcards + */ public const SQLCHARS = '\p{L}\p{N}\p{Pc}\p{Pd}\p{Pf}\p{Pi}'; /** - * Replace "camelCaseMethod" with "camel Case Method" - * - * @param string $subject - * - * @return null|string|string[] + * Replace "camelCaseMethod" with "camel Case Method". */ - public function camelCaseSpaced(string $subject) + public function camelCaseSpaced(string $subject): null|array|string { return preg_replace('/(.)([A-Z]+)/', '$1 $2', $subject); } @@ -30,133 +54,90 @@ public function camelCaseSpaced(string $subject) * Replace "SELECT * FROM table WHERE column = ?" with * "SELECT * * FROM table - * WHERE column = ?" - * - * @param string $subject - * - * @return null|string|string[] + * WHERE column = ?". */ - public function splitClauses(string $subject) + public function splitClauses(string $subject): null|array|string { return preg_replace( '/\b(WHERE|FROM|GROUP BY|HAVING|ORDER BY|LIMIT|OFFSET|UNION|ON DUPLICATE KEY UPDATE|VALUES|SET)\b/', "\n$0", - $subject + $subject, ); } /** * Replace SELECT t2.id FROM t1 LEFT JOIN t2 ON t2.id = t1.t2_id" with * "SELECT t2.id FROM t1 - * LEFT JOIN t2 ON t2.id = t1.t2_id" - * - * @param string $subject - * - * @return null|string|string[] + * LEFT JOIN t2 ON t2.id = t1.t2_id". */ - public function splitSubClauses(string $subject) + public function splitSubClauses(string $subject): null|array|string { return preg_replace( '/\b(INNER|OUTER|LEFT|RIGHT|FULL|CASE|WHEN|END|ELSE|AND|OR)\b/', "\n $0", - $subject + $subject, ); } /** - * Replace "WHERE column = ? " with "WHERE column = ?" - * - * @param string $subject - * - * @return null|string|string[] + * Replace "WHERE column = ? " with "WHERE column = ?". */ - public function removeLineEndWhitespace(string $subject) + public function removeLineEndWhitespace(string $subject): null|array|string { - return preg_replace("/\s+\n/", "\n", $subject); + return preg_replace("/\\s+\n/", "\n", $subject); } /** - * Replace the string "table1.table2:column" with "table2.column" - * - * @param string $subject - * - * @return null|string|string[] + * Replace the string "table1.table2:column" with "table2.column". */ - public function removeAdditionalJoins(string $subject) + public function removeAdditionalJoins(string $subject): null|array|string { return preg_replace('/(?:[^\s]*[.:])?([^\s]+)[.:]([^\s]*)/u', '$1.$2', $subject); } /** - * Match the first file outside of the Fluent source - * - * @param string $subject - * @param ?array $matches - * @param ?string $directory - * - * @return false|int + * Match the first file outside of the Fluent source. */ - public function compareLocation(string $subject, &$matches = null, $directory = null) + public function compareLocation(string $subject, mixed &$matches = null, ?string $directory = null): false|int { $directory = ($directory === null) ? preg_quote(__DIR__, '/') : preg_quote($directory, '/'); - return preg_match('/(^' . $directory . '(\\.php$|[\/\\\\]))/', $subject, $matches); + return preg_match('/(^'.$directory.'(\\.php$|[\/\\\\]))/', $subject, $matches); } /** - * Match the string "?" or ":param" - * - * @param string $subject - * @param array|null $matches - * - * @return false|int + * Match the string "?" or ":param". */ - public function sqlParameter(string $subject, &$matches = null) + public function sqlParameter(string $subject, mixed &$matches = null): false|int { return preg_match('/(\?|:\w+)/', $subject, $matches); } /** - * Match the UTF-8 string "table AS alias" - * - * @param string $subject - * @param array|null $matches - * - * @return false|int + * Match the UTF-8 string "table AS alias". */ - public function tableAlias(string $subject, &$matches = null) + public function tableAlias(string $subject, mixed &$matches = null): false|int { return preg_match( - '/`?([' . self::SQLCHARS . ']+[.:]?[' . self::SQLCHARS . '*]*)`?(\s+AS)?(\s+`?([' . self::SQLCHARS . ']*)`?)?/ui', + '/`?(['.self::SQLCHARS.']+[.:]?['.self::SQLCHARS.'*]*)`?(\s+AS)?(\s+`?(['.self::SQLCHARS.']*)`?)?/ui', $subject, - $matches + $matches, ); } /** - * Match the UTF-8 string "table" or "table." - * - * @param string $subject - * @param array|null $matches - * - * @return false|int + * Match the UTF-8 string "table" or "table.". */ - public function tableJoin(string $subject, &$matches = null) + public function tableJoin(string $subject, mixed &$matches = null): false|int { - return preg_match_all('/([' . self::SQLCHARS . ']+[.:]?)/u', $subject, $matches); + return preg_match_all('/(['.self::SQLCHARS.']+[.:]?)/u', $subject, $matches); } /** - * Match the UTF-8 string "table." or "table.column" - * - * @param string $subject - * @param array|null $matches - * - * @return false|int + * Match the UTF-8 string "table." or "table.column". */ - public function tableJoinFull(string $subject, &$matches = null) + public function tableJoinFull(string $subject, mixed &$matches = null): false|int { - return preg_match_all('/([^[:space:]()]+[.:])[' . self::SQLCHARS . ']*/u', $subject, $matches); + return preg_match_all('/([^[:space:]()]+[.:])['.self::SQLCHARS.']*/u', $subject, $matches); } - } diff --git a/src/Structure.php b/src/Structure.php index ab45bd7..25d4293 100644 --- a/src/Structure.php +++ b/src/Structure.php @@ -1,66 +1,79 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Envms\FluentPDO; /** - * Class Structure + * Database structure definition class. + * + * Handles primary key and foreign key conventions for automatic joins. + * + * @author Chris Bornhoft, start@env.ms + * @author Vítězslav Dvořák */ class Structure { - - /** @var string */ - private $primaryKey; - /** @var string */ - private $foreignKey; + private string $primaryKey; + private string $foreignKey; /** - * Structure constructor + * Structure constructor. * - * @param string $primaryKey - * @param string $foreignKey + * @param string $primaryKey Primary key column name pattern (default: 'id') + * @param string $foreignKey Foreign key column name pattern (default: '%s_id') */ - function __construct($primaryKey = 'id', $foreignKey = '%s_id') + public function __construct(string $primaryKey = 'id', string $foreignKey = '%s_id') { if ($foreignKey === null) { $foreignKey = $primaryKey; } + $this->primaryKey = $primaryKey; $this->foreignKey = $foreignKey; } /** - * @param string $table + * Get the primary key column name for a table. + * + * @param string $table The table name * - * @return string + * @return string The primary key column name */ - public function getPrimaryKey($table) + public function getPrimaryKey(string $table): string { - return $this->key($this->primaryKey, $table); + return self::key($this->primaryKey, $table); } - /** - * @param string $table - * - * @return string - */ - public function getForeignKey($table) + public function getForeignKey(string $table): string { - return $this->key($this->foreignKey, $table); + return self::key($this->foreignKey, $table); } - /** - * @param string|callback $key - * @param string $table - * - * @return string - */ - private function key($key, $table) + private static function key(callable|string $key, string $table): string { - if (is_callable($key)) { + if (\is_callable($key)) { return $key($table); } return sprintf($key, $table); } - } diff --git a/src/Utilities.php b/src/Utilities.php index 3c939f7..8fb4792 100644 --- a/src/Utilities.php +++ b/src/Utilities.php @@ -1,52 +1,74 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Envms\FluentPDO; /** - * Class Utilities + * Class Utilities. */ class Utilities { /** - * Convert "camelCaseWord" to "CAMEL CASE WORD" + * Convert "camelCaseWord" to "CAMEL CASE WORD". * - * @param string $string + * @param string $string The camelCase string to convert * - * @return string + * @return string The converted upper case string with spaces */ - public static function toUpperWords($string) + public static function toUpperWords(string $string): string { $regex = new Regex(); + return trim(strtoupper($regex->camelCaseSpaced($string))); } /** - * @param string $query + * Format SQL query by splitting clauses and removing line end whitespace. * - * @return string + * @param string $query The SQL query string to format + * + * @return string The formatted query string */ - public static function formatQuery($query) + public static function formatQuery(string $query): string { $regex = new Regex(); $query = $regex->splitClauses($query); $query = $regex->splitSubClauses($query); - $query = $regex->removeLineEndWhitespace($query); - return $query; + return $regex->removeLineEndWhitespace($query); } /** - * Converts columns from strings to types according to PDOStatement::columnMeta() + * Converts columns from strings to types according to PDOStatement::columnMeta(). * - * @param \PDOStatement $statement - * @param array|\Traversable $rows - provided by PDOStatement::fetch with PDO::FETCH_ASSOC + * @param \PDOStatement $statement The PDO statement to get column metadata from + * @param mixed $rows Rows provided by PDOStatement::fetch with PDO::FETCH_ASSOC * - * @return array|\Traversable + * @return mixed The rows with numeric columns converted to proper types */ - public static function stringToNumeric(\PDOStatement $statement, $rows) + public static function stringToNumeric(\PDOStatement $statement, mixed $rows): mixed { - for ($i = 0; ($columnMeta = $statement->getColumnMeta($i)) !== false; $i++) { + for ($i = 0; ($columnMeta = $statement->getColumnMeta($i)) !== false; ++$i) { $type = $columnMeta['native_type']; switch ($type) { @@ -60,18 +82,21 @@ public static function stringToNumeric(\PDOStatement $statement, $rows) case 'SHORT': case 'TINY': if (isset($rows[$columnMeta['name']])) { - $rows[$columnMeta['name']] = $rows[$columnMeta['name']] + 0; + $rows[$columnMeta['name']] += 0; } else { - if (is_array($rows) || $rows instanceof \Traversable) { + if (\is_array($rows) || $rows instanceof \Traversable) { foreach ($rows as &$row) { if (isset($row[$columnMeta['name']])) { - $row[$columnMeta['name']] = $row[$columnMeta['name']] + 0; + $row[$columnMeta['name']] += 0; } } + unset($row); } } + break; + default: // return as string break; @@ -82,13 +107,15 @@ public static function stringToNumeric(\PDOStatement $statement, $rows) } /** - * @param $value + * Convert SQL write values, handling arrays and individual values. * - * @return bool + * @param mixed $value The value or array of values to convert + * + * @return mixed The converted value(s) */ - public static function convertSqlWriteValues($value) + public static function convertSqlWriteValues(mixed $value): mixed { - if (is_array($value)) { + if (\is_array($value)) { foreach ($value as $k => $v) { $value[$k] = self::convertValue($v); } @@ -100,18 +127,27 @@ public static function convertSqlWriteValues($value) } /** - * @param $value + * Convert a single value for SQL writing (boolean to int, array to JSON). + * + * @param mixed $value The value to convert * - * @return int|string + * @return mixed The converted value */ - public static function convertValue($value) + public static function convertValue(mixed $value): mixed { - switch (gettype($value)) { + switch (\gettype($value)) { case 'boolean': $conversion = ($value) ? 1 : 0; + + break; + case 'array': + $conversion = json_encode($value); + break; + default: $conversion = $value; + break; } @@ -119,21 +155,25 @@ public static function convertValue($value) } /** - * @param $subject + * Check if a subject is countable (array or implements Countable interface). * - * @return bool + * @param mixed $subject The subject to check + * + * @return bool True if the subject is countable, false otherwise */ - public static function isCountable($subject) + public static function isCountable(mixed $subject): bool { - return (is_array($subject) || ($subject instanceof \Countable)); + return \is_array($subject) || ($subject instanceof \Countable); } /** - * @param $value + * Convert null values to Literal('NULL') for SQL queries. + * + * @param mixed $value The value to check and convert * - * @return Literal|mixed + * @return mixed The original value or Literal('NULL') if value was null */ - public static function nullToLiteral($value) + public static function nullToLiteral(mixed $value): mixed { if ($value === null) { return new Literal('NULL'); diff --git a/tests/Queries/CommonTest.php b/tests/Queries/CommonTest.php index c0ddb3e..705850e 100644 --- a/tests/Queries/CommonTest.php +++ b/tests/Queries/CommonTest.php @@ -1,23 +1,41 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/../_resources/init.php'; -use PHPUnit\Framework\TestCase; -use Envms\FluentTest\Model\User; use Envms\FluentPDO\Query; +use Envms\FluentTest\Model\User; +use PHPUnit\Framework\TestCase; /** - * Class CommonTest + * Class CommonTest. * * @covers \Envms\FluentPDO\Queries\Common */ class CommonTest extends TestCase { + protected Query $fluent; - /** @var Query */ - protected $fluent; - - public function setUp(): void + protected function setUp(): void { global $pdo; @@ -26,7 +44,7 @@ public function setUp(): void $this->fluent = new Query($pdo); } - public function testFullJoin() + public function testFullJoin(): void { $query = $this->fluent->from('article') ->select('user.name') @@ -34,18 +52,20 @@ public function testFullJoin() ->orderBy('article.title'); $returnValue = ''; + foreach ($query as $row) { - $returnValue .= "$row[name] - $row[title] "; + $returnValue .= "{$row['name']} - {$row['title']} "; } - self::assertEquals('SELECT article.*, user.name FROM article LEFT JOIN user ON user.id = article.user_id ORDER BY article.title', - $query->getQuery(false)); + self::assertEquals( + 'SELECT article.*, user.name FROM article LEFT JOIN user ON user.id = article.user_id ORDER BY article.title', + $query->getQuery(false), + ); self::assertEquals('Marek - article 1 Robert - article 2 Marek - article 3 Kevin - artïcle 4 Chris - article 5 Chris - სარედაქციო 6 ', $returnValue); } - public function testShortJoin() + public function testShortJoin(): void { - $query = $this->fluent->from('article')->leftJoin('user'); $query2 = $this->fluent->from('article')->leftJoin('user author'); $query3 = $this->fluent->from('article')->leftJoin('user AS author'); @@ -55,40 +75,50 @@ public function testShortJoin() self::assertEquals('SELECT article.* FROM article LEFT JOIN user AS author ON author.id = article.user_id', $query3->getQuery(false)); } - public function testJoinShortBackRef() + public function testJoinShortBackRef(): void { $query = $this->fluent->from('user')->innerJoin('article:'); $query2 = $this->fluent->from('user')->innerJoin('article: with_articles'); $query3 = $this->fluent->from('user')->innerJoin('article: AS with_articles'); self::assertEquals('SELECT user.* FROM user INNER JOIN article ON article.user_id = user.id', $query->getQuery(false)); - self::assertEquals('SELECT user.* FROM user INNER JOIN article AS with_articles ON with_articles.user_id = user.id', - $query2->getQuery(false)); - self::assertEquals('SELECT user.* FROM user INNER JOIN article AS with_articles ON with_articles.user_id = user.id', - $query3->getQuery(false)); + self::assertEquals( + 'SELECT user.* FROM user INNER JOIN article AS with_articles ON with_articles.user_id = user.id', + $query2->getQuery(false), + ); + self::assertEquals( + 'SELECT user.* FROM user INNER JOIN article AS with_articles ON with_articles.user_id = user.id', + $query3->getQuery(false), + ); } - public function testJoinShortMulti() + public function testJoinShortMulti(): void { $query = $this->fluent->from('comment') ->leftJoin('article.user'); - self::assertEquals('SELECT comment.* FROM comment LEFT JOIN article ON article.id = comment.article_id LEFT JOIN user ON user.id = article.user_id', - $query->getQuery(false)); + self::assertEquals( + 'SELECT comment.* FROM comment LEFT JOIN article ON article.id = comment.article_id LEFT JOIN user ON user.id = article.user_id', + $query->getQuery(false), + ); } - public function testJoinMultiBackRef() + public function testJoinMultiBackRef(): void { $query = $this->fluent->from('article') ->innerJoin('comment:user AS comment_user'); - self::assertEquals('SELECT article.* FROM article INNER JOIN comment ON comment.article_id = article.id INNER JOIN user AS comment_user ON comment_user.id = comment.user_id', - $query->getQuery(false)); - self::assertEquals(['id' => '1', 'user_id' => '1', 'published_at' => '2011-12-10 12:10:00', 'title' => 'article 1', 'content' => 'content 1'], - $query->fetch()); + self::assertEquals( + 'SELECT article.* FROM article INNER JOIN comment ON comment.article_id = article.id INNER JOIN user AS comment_user ON comment_user.id = comment.user_id', + $query->getQuery(false), + ); + self::assertEquals( + ['id' => '1', 'user_id' => '1', 'published_at' => '2011-12-10 12:10:00', 'title' => 'article 1', 'content' => 'content 1'], + $query->fetch(), + ); } - public function testJoinShortTwoSameTable() + public function testJoinShortTwoSameTable(): void { $query = $this->fluent->from('article') ->leftJoin('user') @@ -97,60 +127,70 @@ public function testJoinShortTwoSameTable() self::assertEquals('SELECT article.* FROM article LEFT JOIN user ON user.id = article.user_id', $query->getQuery(false)); } - public function testJoinShortTwoTables() + public function testJoinShortTwoTables(): void { $query = $this->fluent->from('comment') ->where('comment.id', 2) ->leftJoin('user comment_author')->select('comment_author.name AS comment_name') ->leftJoin('article.user AS article_author')->select('article_author.name AS author_name'); - self::assertEquals('SELECT comment.*, comment_author.name AS comment_name, article_author.name AS author_name FROM comment LEFT JOIN user AS comment_author ON comment_author.id = comment.user_id LEFT JOIN article ON article.id = comment.article_id LEFT JOIN user AS article_author ON article_author.id = article.user_id WHERE comment.id = ?', - $query->getQuery(false)); + self::assertEquals( + 'SELECT comment.*, comment_author.name AS comment_name, article_author.name AS author_name FROM comment LEFT JOIN user AS comment_author ON comment_author.id = comment.user_id LEFT JOIN article ON article.id = comment.article_id LEFT JOIN user AS article_author ON article_author.id = article.user_id WHERE comment.id = ?', + $query->getQuery(false), + ); self::assertEquals([ - 'id' => '2', - 'article_id' => '1', - 'user_id' => '2', - 'content' => 'comment 1.2', + 'id' => '2', + 'article_id' => '1', + 'user_id' => '2', + 'content' => 'comment 1.2', 'comment_name' => 'Robert', - 'author_name' => 'Marek' + 'author_name' => 'Marek', ], $query->fetch()); } - public function testJoinInWhere() + public function testJoinInWhere(): void { $query = $this->fluent->from('article')->where('comment:content <> "" AND user.country.id = ?', 1); - self::assertEquals('SELECT article.* FROM article LEFT JOIN comment ON comment.article_id = article.id LEFT JOIN user ON user.id = article.user_id LEFT JOIN country ON country.id = user.country_id WHERE comment.content <> "" AND country.id = ?', - $query->getQuery(false)); + self::assertEquals( + 'SELECT article.* FROM article LEFT JOIN comment ON comment.article_id = article.id LEFT JOIN user ON user.id = article.user_id LEFT JOIN country ON country.id = user.country_id WHERE comment.content <> "" AND country.id = ?', + $query->getQuery(false), + ); } - public function testJoinInSelect() + public function testJoinInSelect(): void { $query = $this->fluent->from('article')->select('user.name AS author'); self::assertEquals('SELECT article.*, user.name AS author FROM article LEFT JOIN user ON user.id = article.user_id', $query->getQuery(false)); } - public function testJoinInOrderBy() + public function testJoinInOrderBy(): void { $query = $this->fluent->from('article')->orderBy('user.name, article.title'); - self::assertEquals('SELECT article.* FROM article LEFT JOIN user ON user.id = article.user_id ORDER BY user.name, article.title', - $query->getQuery(false)); + self::assertEquals( + 'SELECT article.* FROM article LEFT JOIN user ON user.id = article.user_id ORDER BY user.name, article.title', + $query->getQuery(false), + ); } - public function testJoinInGroupBy() + public function testJoinInGroupBy(): void { $query = $this->fluent->from('article')->groupBy('user.type') ->select(null)->select('user.type, count(article.id) AS article_count'); - self::assertEquals('SELECT user.type, count(article.id) AS article_count FROM article LEFT JOIN user ON user.id = article.user_id GROUP BY user.type', - $query->getQuery(false)); - self::assertEquals(['0' => ['type' => 'admin', 'article_count' => '4'], '1' => ['type' => 'author', 'article_count' => '2']], - $query->fetchAll()); + self::assertEquals( + 'SELECT user.type, count(article.id) AS article_count FROM article LEFT JOIN user ON user.id = article.user_id GROUP BY user.type', + $query->getQuery(false), + ); + self::assertEquals( + ['0' => ['type' => 'admin', 'article_count' => '4'], '1' => ['type' => 'author', 'article_count' => '2']], + $query->fetchAll(), + ); } - public function testEscapeJoin() + public function testEscapeJoin(): void { $query = $this->fluent->from('article') ->where('user\.name = ?', 'Chris'); @@ -161,11 +201,13 @@ public function testEscapeJoin() ->where('comment.id = :id', 1) ->where('user\.name = :name', 'Chris'); - self::assertEquals('SELECT article.* FROM article LEFT JOIN comment ON comment.id = article.comment_id WHERE comment.id = :id AND user.name = :name', - $query->getQuery(false)); + self::assertEquals( + 'SELECT article.* FROM article LEFT JOIN comment ON comment.id = article.comment_id WHERE comment.id = :id AND user.name = :name', + $query->getQuery(false), + ); } - public function testDontCreateDuplicateJoins() + public function testDontCreateDuplicateJoins(): void { $query = $this->fluent->from('article') ->innerJoin('user AS author ON article.user_id = author.id') @@ -183,35 +225,43 @@ public function testDontCreateDuplicateJoins() ->innerJoin('user ON article.user_id = user.id') ->select('user.country.name'); - self::assertEquals('SELECT article.*, author.name FROM article INNER JOIN user AS author ON article.user_id = author.id', - $query->getQuery(false)); + self::assertEquals( + 'SELECT article.*, author.name FROM article INNER JOIN user AS author ON article.user_id = author.id', + $query->getQuery(false), + ); self::assertEquals('SELECT article.*, user.name FROM article INNER JOIN user ON article.user_id = user.id', $query2->getQuery(false)); - self::assertEquals('SELECT article.*, country.name FROM article INNER JOIN user AS author ON article.user_id = author.id LEFT JOIN country ON country.id = author.country_id', - $query3->getQuery(false)); - self::assertEquals('SELECT article.*, country.name FROM article INNER JOIN user ON article.user_id = user.id LEFT JOIN country ON country.id = user.country_id', - $query4->getQuery(false)); + self::assertEquals( + 'SELECT article.*, country.name FROM article INNER JOIN user AS author ON article.user_id = author.id LEFT JOIN country ON country.id = author.country_id', + $query3->getQuery(false), + ); + self::assertEquals( + 'SELECT article.*, country.name FROM article INNER JOIN user ON article.user_id = user.id LEFT JOIN country ON country.id = user.country_id', + $query4->getQuery(false), + ); } - public function testClauseWithRefBeforeJoin() + public function testClauseWithRefBeforeJoin(): void { $query = $this->fluent->from('article')->select('user.name')->innerJoin('user'); $query2 = $this->fluent->from('article')->select('author.name')->innerJoin('user AS author'); $query3 = $this->fluent->from('user')->select('article:title')->innerJoin('article:'); self::assertEquals('SELECT article.*, user.name FROM article INNER JOIN user ON user.id = article.user_id', $query->getQuery(false)); - self::assertEquals('SELECT article.*, author.name FROM article INNER JOIN user AS author ON author.id = article.user_id', - $query2->getQuery(false)); + self::assertEquals( + 'SELECT article.*, author.name FROM article INNER JOIN user AS author ON author.id = article.user_id', + $query2->getQuery(false), + ); self::assertEquals('SELECT user.*, article.title FROM user INNER JOIN article ON article.user_id = user.id', $query3->getQuery(false)); } - public function testFromOtherDB() + public function testFromOtherDB(): void { $queryPrint = $this->fluent->from('db2.user')->where('db2.user.name', 'name')->order('db2.user.name')->getQuery(false); self::assertEquals('SELECT db2.user.* FROM db2.user WHERE db2.user.name = ? ORDER BY db2.user.name', $queryPrint); } - public function testJoinTableWithUsing() + public function testJoinTableWithUsing(): void { $query = $this->fluent->from('article') ->innerJoin('user USING (user_id)') @@ -233,13 +283,13 @@ public function testJoinTableWithUsing() self::assertEquals('SELECT article.*, u.* FROM article INNER JOIN user AS u USING (user_id)', $query3); } - public function testDisableSmartJoin() + public function testDisableSmartJoin(): void { $query = $this->fluent->from('comment') ->select('user.name') ->orderBy('article.published_at') ->getQuery(false); - $printQuery = "-- Plain: $query"; + $printQuery = "-- Plain: {$query}"; $query2 = $this->fluent->from('comment') ->select('user.name') @@ -247,7 +297,7 @@ public function testDisableSmartJoin() ->orderBy('article.published_at') ->getQuery(false); - $printQuery2 = "-- Disable: $query2"; + $printQuery2 = "-- Disable: {$query2}"; $query3 = $this->fluent->from('comment') ->disableSmartJoin() @@ -255,16 +305,20 @@ public function testDisableSmartJoin() ->enableSmartJoin() ->orderBy('article.published_at') ->getQuery(false); - $printQuery3 = "-- Disable and enable: $query3"; + $printQuery3 = "-- Disable and enable: {$query3}"; - self::assertEquals('-- Plain: SELECT comment.*, user.name FROM comment LEFT JOIN user ON user.id = comment.user_id LEFT JOIN article ON article.id = comment.article_id ORDER BY article.published_at', - $printQuery); + self::assertEquals( + '-- Plain: SELECT comment.*, user.name FROM comment LEFT JOIN user ON user.id = comment.user_id LEFT JOIN article ON article.id = comment.article_id ORDER BY article.published_at', + $printQuery, + ); self::assertEquals('-- Disable: SELECT comment.*, user.name FROM comment ORDER BY article.published_at', $printQuery2); - self::assertEquals('-- Disable and enable: SELECT comment.*, user.name FROM comment LEFT JOIN user ON user.id = comment.user_id LEFT JOIN article ON article.id = comment.article_id ORDER BY article.published_at', - $printQuery3); + self::assertEquals( + '-- Disable and enable: SELECT comment.*, user.name FROM comment LEFT JOIN user ON user.id = comment.user_id LEFT JOIN article ON article.id = comment.article_id ORDER BY article.published_at', + $printQuery3, + ); } - public function testPDOFetchObj() + public function testPDOFetchObj(): void { $query = $this->fluent->from('user')->where('id > ?', 0)->orderBy('name'); $query = $query->where('name = ?', 'Marek'); @@ -280,7 +334,7 @@ public function testPDOFetchObj() self::assertEquals($expectObj, $query->fetch()); } - public function testFromIdAsObject() + public function testFromIdAsObject(): void { $query = $this->fluent->from('user', 2)->asObject(); @@ -294,7 +348,7 @@ public function testFromIdAsObject() self::assertEquals($expectObj, $query->fetch()); } - public function testFromIdAsObjectUser() + public function testFromIdAsObjectUser(): void { $expectedUser = new User(); $expectedUser->id = 2; @@ -308,5 +362,4 @@ public function testFromIdAsObjectUser() self::assertEquals('SELECT user.* FROM user WHERE user.id = ?', $query->getQuery(false)); self::assertEquals($expectedUser, $user); } - } diff --git a/tests/Queries/DeleteTest.php b/tests/Queries/DeleteTest.php index 5abd677..8c13ea3 100644 --- a/tests/Queries/DeleteTest.php +++ b/tests/Queries/DeleteTest.php @@ -1,22 +1,40 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/../_resources/init.php'; -use PHPUnit\Framework\TestCase; use Envms\FluentPDO\Query; +use PHPUnit\Framework\TestCase; /** - * Class DeleteTest + * Class DeleteTest. * * @covers \Envms\FluentPDO\Queries\Delete */ class DeleteTest extends TestCase { + protected Query $fluent; - /** @var Query */ - protected $fluent; - - public function setUp(): void + protected function setUp(): void { global $pdo; @@ -25,7 +43,7 @@ public function setUp(): void $this->fluent = new Query($pdo); } - public function testDelete() + public function testDelete(): void { $query = $this->fluent->deleteFrom('user') ->where('id', 1); @@ -34,7 +52,7 @@ public function testDelete() self::assertEquals(['0' => '1'], $query->getParameters()); } - public function testDeleteIgnore() + public function testDeleteIgnore(): void { $query = $this->fluent->deleteFrom('user') ->ignore() @@ -44,7 +62,7 @@ public function testDeleteIgnore() self::assertEquals(['0' => '1'], $query->getParameters()); } - public function testDeleteOrderLimit() + public function testDeleteOrderLimit(): void { $query = $this->fluent->deleteFrom('user') ->where('id', 2) @@ -55,7 +73,7 @@ public function testDeleteOrderLimit() self::assertEquals(['0' => '2'], $query->getParameters()); } - public function testDeleteExpanded() + public function testDeleteExpanded(): void { $query = $this->fluent->delete('t1, t2') ->from('t1') @@ -63,12 +81,14 @@ public function testDeleteExpanded() ->innerJoin('t3 ON t2.id = t3.id') ->where('t1.id', 1); - self::assertEquals('DELETE t1, t2 FROM t1 INNER JOIN t2 ON t1.id = t2.id INNER JOIN t3 ON t2.id = t3.id WHERE t1.id = ?', - $query->getQuery(false)); + self::assertEquals( + 'DELETE t1, t2 FROM t1 INNER JOIN t2 ON t1.id = t2.id INNER JOIN t3 ON t2.id = t3.id WHERE t1.id = ?', + $query->getQuery(false), + ); self::assertEquals(['0' => '1'], $query->getParameters()); } - public function testDeleteShortcut() + public function testDeleteShortcut(): void { $query = $this->fluent->deleteFrom('user', 1); @@ -76,11 +96,11 @@ public function testDeleteShortcut() self::assertEquals(['0' => '1'], $query->getParameters()); } - public function testAddFromAfterDelete() + public function testAddFromAfterDelete(): void { $query = $this->fluent->delete('user', 1)->from('user'); self::assertEquals('DELETE user FROM user WHERE id = ?', $query->getQuery(false)); self::assertEquals(['0' => '1'], $query->getParameters()); } -} \ No newline at end of file +} diff --git a/tests/Queries/InsertTest.php b/tests/Queries/InsertTest.php index 73d2de0..6927a2d 100644 --- a/tests/Queries/InsertTest.php +++ b/tests/Queries/InsertTest.php @@ -1,22 +1,40 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/../_resources/init.php'; -use PHPUnit\Framework\TestCase; use Envms\FluentPDO\Query; +use PHPUnit\Framework\TestCase; /** - * Class InsertTest + * Class InsertTest. * * @covers \Envms\FluentPDO\Queries\Insert */ class InsertTest extends TestCase { + protected Envms\FluentPDO\Query $fluent; - /** @var Envms\FluentPDO\Query */ - protected $fluent; - - public function setUp(): void + protected function setUp(): void { global $pdo; @@ -25,25 +43,25 @@ public function setUp(): void $this->fluent = new Query($pdo); } - public function testInsertStatement() + public function testInsertStatement(): void { $query = $this->fluent->insertInto('article', [ 'user_id' => 1, - 'title' => 'new title', - 'content' => 'new content' + 'title' => 'new title', + 'content' => 'new content', ]); self::assertEquals('INSERT INTO article (user_id, title, content) VALUES (?, ?, ?)', $query->getQuery(false)); self::assertEquals(['0' => '1', '1' => 'new title', '2' => 'new content'], $query->getParameters()); } - public function testInsertUpdate() + public function testInsertUpdate(): void { $query = $this->fluent->insertInto('article', ['id' => 1]) ->onDuplicateKeyUpdate([ 'published_at' => '2011-12-10 12:10:00', - 'title' => 'article 1b', - 'content' => new Envms\FluentPDO\Literal('abs(-1)') // let's update with a literal and a parameter value + 'title' => 'article 1b', + 'content' => new Envms\FluentPDO\Literal('abs(-1)'), // let's update with a literal and a parameter value ]); $q = $this->fluent->from('article', 1); @@ -51,7 +69,7 @@ public function testInsertUpdate() $query2 = $this->fluent->insertInto('article', ['id' => 1]) ->onDuplicateKeyUpdate([ 'published_at' => '2011-12-10 12:10:00', - 'title' => 'article 1', + 'title' => 'article 1', 'content' => 'content 1', ]); @@ -59,38 +77,46 @@ public function testInsertUpdate() self::assertEquals('INSERT INTO article (id) VALUES (?) ON DUPLICATE KEY UPDATE published_at = ?, title = ?, content = abs(-1)', $query->getQuery(false)); self::assertEquals([0 => '1', 1 => '2011-12-10 12:10:00', 2 => 'article 1b'], $query->getParameters()); - self::assertEquals('last_inserted_id = 1', 'last_inserted_id = ' . $query->execute()); - self::assertEquals(['id' => '1', 'user_id' => '1', 'published_at' => '2011-12-10 12:10:00', 'title' => 'article 1b', 'content' => '1'], - $q->fetch()); - self::assertEquals('last_inserted_id = 1', 'last_inserted_id = ' . $query2->execute()); - self::assertEquals(['id' => '1', 'user_id' => '1', 'published_at' => '2011-12-10 12:10:00', 'title' => 'article 1', 'content' => 'content 1'], - $q2->fetch()); + self::assertEquals('last_inserted_id = 1', 'last_inserted_id = '.$query->execute()); + self::assertEquals( + ['id' => '1', 'user_id' => '1', 'published_at' => '2011-12-10 12:10:00', 'title' => 'article 1b', 'content' => '1'], + $q->fetch(), + ); + self::assertEquals('last_inserted_id = 1', 'last_inserted_id = '.$query2->execute()); + self::assertEquals( + ['id' => '1', 'user_id' => '1', 'published_at' => '2011-12-10 12:10:00', 'title' => 'article 1', 'content' => 'content 1'], + $q2->fetch(), + ); } - public function testInsertWithLiteral() + public function testInsertWithLiteral(): void { - $query = $this->fluent->insertInto('article', + $query = $this->fluent->insertInto( + 'article', [ - 'user_id' => 1, + 'user_id' => 1, 'updated_at' => new Envms\FluentPDO\Literal('NOW()'), - 'title' => 'new title', - 'content' => 'new content', - ]); + 'title' => 'new title', + 'content' => 'new content', + ], + ); self::assertEquals('INSERT INTO article (user_id, updated_at, title, content) VALUES (?, NOW(), ?, ?)', $query->getQuery(false)); self::assertEquals(['0' => '1', '1' => 'new title', '2' => 'new content'], $query->getParameters()); } - public function testInsertIgnore() + public function testInsertIgnore(): void { - $query = $this->fluent->insertInto('article', + $query = $this->fluent->insertInto( + 'article', [ 'user_id' => 1, - 'title' => 'new title', + 'title' => 'new title', 'content' => 'new content', - ])->ignore(); + ], + )->ignore(); self::assertEquals('INSERT IGNORE INTO article (user_id, title, content) VALUES (?, ?, ?)', $query->getQuery(false)); self::assertEquals(['0' => '1', '1' => 'new title', '2' => 'new content'], $query->getParameters()); } -} \ No newline at end of file +} diff --git a/tests/Queries/SelectTest.php b/tests/Queries/SelectTest.php index ea22d69..4547b64 100644 --- a/tests/Queries/SelectTest.php +++ b/tests/Queries/SelectTest.php @@ -1,22 +1,40 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/../_resources/init.php'; -use PHPUnit\Framework\TestCase; use Envms\FluentPDO\Query; +use PHPUnit\Framework\TestCase; /** - * Class SelectTest + * Class SelectTest. * * @covers \Envms\FluentPDO\Queries\Select */ class SelectTest extends TestCase { + protected Query $fluent; - /** @var Query */ - protected $fluent; - - public function setUp(): void + protected function setUp(): void { global $pdo; @@ -25,7 +43,7 @@ public function setUp(): void $this->fluent = new Query($pdo); } - public function testBasicQuery() + public function testBasicQuery(): void { $query = $this->fluent ->from('user') @@ -39,9 +57,8 @@ public function testBasicQuery() self::assertEquals([0 => 0, 1 => 'Marek'], $query->getParameters()); } - public function testReturnQueryWithHaving() + public function testReturnQueryWithHaving(): void { - $query = $this->fluent ->from('user') ->select(null) @@ -51,11 +68,13 @@ public function testReturnQueryWithHaving() ->having('type_count > ?', 1) ->orderBy('name'); - self::assertEquals("SELECT type, count(id) AS type_count FROM user WHERE id > ? GROUP BY type HAVING type_count > ? ORDER BY name", - $query->getQuery(false)); + self::assertEquals( + 'SELECT type, count(id) AS type_count FROM user WHERE id > ? GROUP BY type HAVING type_count > ? ORDER BY name', + $query->getQuery(false), + ); } - public function testReturnParameterWithId() + public function testReturnParameterWithId(): void { $query = $this->fluent ->from('user', 2); @@ -64,7 +83,7 @@ public function testReturnParameterWithId() self::assertEquals('SELECT user.* FROM user WHERE user.id = ?', $query->getQuery(false)); } - public function testFromWithAlias() + public function testFromWithAlias(): void { $query = $this->fluent->from('user author')->getQuery(false); $query2 = $this->fluent->from('user AS author')->getQuery(false); @@ -77,20 +96,20 @@ public function testFromWithAlias() self::assertEquals('SELECT author.*, country.name FROM user AS author LEFT JOIN country ON country.id = user AS author.country_id', $query4); } - public function testWhereArrayParameter() + public function testWhereArrayParameter(): void { $query = $this->fluent ->from('user') ->where([ 'id' => 2, - 'type' => 'author' + 'type' => 'author', ]); self::assertEquals('SELECT user.* FROM user WHERE id = ? AND type = ?', $query->getQuery(false)); self::assertEquals([0 => 2, 1 => 'author'], $query->getParameters()); } - public function testWhereColumnValue() + public function testWhereColumnValue(): void { $query = $this->fluent->from('user') ->where('type', 'author'); @@ -99,7 +118,7 @@ public function testWhereColumnValue() self::assertEquals([0 => 'author'], $query->getParameters()); } - public function testWhereColumnNull() + public function testWhereColumnNull(): void { $query = $this->fluent ->from('user') @@ -108,7 +127,7 @@ public function testWhereColumnNull() self::assertEquals('SELECT user.* FROM user WHERE type IS NULL', $query->getQuery(false)); } - public function testWhereColumnArray() + public function testWhereColumnArray(): void { $query = $this->fluent ->from('user') @@ -118,7 +137,7 @@ public function testWhereColumnArray() self::assertEquals([], $query->getParameters()); } - public function testWherePreparedArray() + public function testWherePreparedArray(): void { $query = $this->fluent ->from('user') @@ -128,13 +147,14 @@ public function testWherePreparedArray() self::assertEquals([0 => 1, 1 => 2, 2 => 3], $query->getParameters()); } - public function testWhereColumnName() + public function testWhereColumnName(): void { $query = $this->fluent->from('user') ->where('type = :type', [':type' => 'author']) ->where('id > :id AND name <> :name', [':id' => 3, ':name' => 'Marek']); $returnValue = ''; + foreach ($query as $row) { $returnValue = $row['name']; } @@ -144,18 +164,20 @@ public function testWhereColumnName() self::assertEquals('Kevin', $returnValue); } - public function testWhereOr() + public function testWhereOr(): void { $query = $this->fluent->from('comment') ->where('comment.id = :id', [':id' => 1]) ->whereOr('user.id = :userId', [':userId' => 2]); - self::assertEquals('SELECT comment.* FROM comment LEFT JOIN user ON user.id = comment.user_id WHERE comment.id = :id OR user.id = :userId', - $query->getQuery(false)); + self::assertEquals( + 'SELECT comment.* FROM comment LEFT JOIN user ON user.id = comment.user_id WHERE comment.id = :id OR user.id = :userId', + $query->getQuery(false), + ); self::assertEquals([':id' => '1', ':userId' => '2'], $query->getParameters()); } - public function testWhereReset() + public function testWhereReset(): void { $query = $this->fluent->from('user')->where('id > ?', 0)->orderBy('name'); $query = $query->where(null)->where('name = ?', 'Marek'); @@ -165,9 +187,7 @@ public function testWhereReset() self::assertEquals(['id' => '1', 'country_id' => '1', 'type' => 'admin', 'name' => 'Marek'], $query->fetch()); } - - - public function testSelectArrayParam() + public function testSelectArrayParam(): void { $query = $this->fluent ->from('user') @@ -180,7 +200,7 @@ public function testSelectArrayParam() self::assertEquals(['id' => '1', 'name' => 'Marek'], $query->fetch()); } - public function testGroupByArrayParam() + public function testGroupByArrayParam(): void { $query = $this->fluent ->from('user') @@ -192,7 +212,7 @@ public function testGroupByArrayParam() self::assertEquals(['total_count' => '1'], $query->fetch()); } - public function testCountable() + public function testCountable(): void { $articles = $this->fluent ->from('article') @@ -201,26 +221,27 @@ public function testCountable() ->where('id > 1') ->where('id < 4'); - $count = count($articles); + $count = \count($articles); self::assertEquals(2, $count); self::assertEquals([0 => ['title' => 'article 2'], 1 => ['title' => 'article 3']], $articles->fetchAll()); } - public function testWhereNotArray() + public function testWhereNotArray(): void { $query = $this->fluent->from('article')->where('NOT id', [1, 2]); self::assertEquals('SELECT article.* FROM article WHERE NOT id IN (1, 2)', $query->getQuery(false)); } - public function testWhereColNameEscaped() + public function testWhereColNameEscaped(): void { $query = $this->fluent->from('user') ->where('`type` = :type', [':type' => 'author']) ->where('`id` > :id AND `name` <> :name', [':id' => 3, ':name' => 'Marek']); $rowDisplay = ''; + foreach ($query as $row) { $rowDisplay = $row['name']; } @@ -230,14 +251,14 @@ public function testWhereColNameEscaped() self::assertEquals('Kevin', $rowDisplay); } - public function testAliasesForClausesGroupbyOrderBy() + public function testAliasesForClausesGroupbyOrderBy(): void { $query = $this->fluent->from('article')->group('user_id')->order('id'); self::assertEquals('SELECT article.* FROM article GROUP BY user_id ORDER BY id', $query->getQuery(false)); } - public function testFetch() + public function testFetch(): void { $queryPrint = $this->fluent->from('user', 1)->fetch('name'); $queryPrint2 = $this->fluent->from('user', 1)->fetch(); @@ -246,11 +267,11 @@ public function testFetch() self::assertEquals('Marek', $queryPrint); self::assertEquals(['id' => '1', 'country_id' => '1', 'type' => 'admin', 'name' => 'Marek'], $queryPrint2); - self::assertEquals(false, $statement); - self::assertEquals(false, $statement2); + self::assertFalse($statement); + self::assertFalse($statement2); } - public function testFetchPairsFetchAll() + public function testFetchPairsFetchAll(): void { $result = $this->fluent->from('user')->fetchPairs('id', 'name'); $result2 = $this->fluent->from('user')->fetchAll(); @@ -260,20 +281,22 @@ public function testFetchPairsFetchAll() 0 => ['id' => '1', 'country_id' => '1', 'type' => 'admin', 'name' => 'Marek'], 1 => ['id' => '2', 'country_id' => '1', 'type' => 'author', 'name' => 'Robert'], 2 => ['id' => '3', 'country_id' => '2', 'type' => 'admin', 'name' => 'Chris'], - 3 => ['id' => '4', 'country_id' => '2', 'type' => 'author', 'name' => 'Kevin'] + 3 => ['id' => '4', 'country_id' => '2', 'type' => 'author', 'name' => 'Kevin'], ], $result2); } - public function testFetchAllWithParams() + public function testFetchAllWithParams(): void { $result = $this->fluent->from('user')->fetchAll('id', 'type, name'); - self::assertEquals([1 => ['id' => '1', 'type' => 'admin', 'name' => 'Marek'], 2 => ['id' => '2', 'type' => 'author', 'name' => 'Robert'], - 3 => ['id' => '3', 'type' => 'admin', 'name' => 'Chris'], 4 => ['id' => '4', 'type' => 'author', 'name' => 'Kevin']], - $result); + self::assertEquals( + [1 => ['id' => '1', 'type' => 'admin', 'name' => 'Marek'], 2 => ['id' => '2', 'type' => 'author', 'name' => 'Robert'], + 3 => ['id' => '3', 'type' => 'admin', 'name' => 'Chris'], 4 => ['id' => '4', 'type' => 'author', 'name' => 'Kevin']], + $result, + ); } - public function testFetchColumn() + public function testFetchColumn(): void { $printColumn = $this->fluent->from('user', 3)->fetchColumn(); $printColumn2 = $this->fluent->from('user', 3)->fetchColumn(3); @@ -282,7 +305,7 @@ public function testFetchColumn() self::assertEquals(3, $printColumn); self::assertEquals('Chris', $printColumn2); - self::assertEquals(false, $statement); - self::assertEquals(false, $statement2); + self::assertFalse($statement); + self::assertFalse($statement2); } -} \ No newline at end of file +} diff --git a/tests/Queries/UpdateTest.php b/tests/Queries/UpdateTest.php index 4e231d5..cd59000 100644 --- a/tests/Queries/UpdateTest.php +++ b/tests/Queries/UpdateTest.php @@ -1,22 +1,40 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/../_resources/init.php'; -use PHPUnit\Framework\TestCase; use Envms\FluentPDO\Query; +use PHPUnit\Framework\TestCase; /** - * Class UpdateTest + * Class UpdateTest. * * @covers \Envms\FluentPDO\Queries\Update */ class UpdateTest extends TestCase { + protected Query $fluent; - /** @var Query */ - protected $fluent; - - public function setUp(): void + protected function setUp(): void { global $pdo; @@ -25,7 +43,7 @@ public function setUp(): void $this->fluent = new Query($pdo); } - public function testUpdate() + public function testUpdate(): void { $query = $this->fluent->update('country')->set('name', 'aikavolS')->where('id', 1); $query->execute(); @@ -42,7 +60,7 @@ public function testUpdate() self::assertEquals(['id' => '1', 'name' => 'Slovakia', 'details' => '{"gdp": 90.75, "pop": 5456300, "name": "Slovensko"}'], $query3->fetch()); } - public function testUpdateLiteral() + public function testUpdateLiteral(): void { $query = $this->fluent->update('article')->set('published_at', new Envms\FluentPDO\Literal('NOW()'))->where('user_id', 1); @@ -50,7 +68,7 @@ public function testUpdateLiteral() self::assertEquals(['0' => '1'], $query->getParameters()); } - public function testUpdateFromArray() + public function testUpdateFromArray(): void { $query = $this->fluent->update('user')->set(['name' => 'keraM', '`type`' => 'author'])->where('id', 1); @@ -58,30 +76,34 @@ public function testUpdateFromArray() self::assertEquals([0 => 'keraM', 1 => 'author', 2 => '1'], $query->getParameters()); } - public function testUpdateLeftJoin() + public function testUpdateLeftJoin(): void { $query = $this->fluent->update('user') ->outerJoin('country ON country.id = user.country_id') ->set(['name' => 'keraM', '`type`' => 'author']) ->where('id', 1); - self::assertEquals('UPDATE user OUTER JOIN country ON country.id = user.country_id SET name = ?, `type` = ? WHERE id = ?', - $query->getQuery(false)); + self::assertEquals( + 'UPDATE user OUTER JOIN country ON country.id = user.country_id SET name = ?, `type` = ? WHERE id = ?', + $query->getQuery(false), + ); self::assertEquals([0 => 'keraM', 1 => 'author', 2 => '1'], $query->getParameters()); } - public function testUpdateSmartJoin() + public function testUpdateSmartJoin(): void { $query = $this->fluent->update('user') ->set(['type' => 'author']) ->where('country.id', 1); - self::assertEquals('UPDATE user LEFT JOIN country ON country.id = user.country_id SET type = ? WHERE country.id = ?', - $query->getQuery(false)); + self::assertEquals( + 'UPDATE user LEFT JOIN country ON country.id = user.country_id SET type = ? WHERE country.id = ?', + $query->getQuery(false), + ); self::assertEquals([0 => 'author', 1 => '1'], $query->getParameters()); } - public function testUpdateOrderLimit() + public function testUpdateOrderLimit(): void { $query = $this->fluent->update('user') ->set(['type' => 'author']) @@ -93,7 +115,7 @@ public function testUpdateOrderLimit() self::assertEquals([0 => 'author', 1 => '2'], $query->getParameters()); } - public function testUpdateShortCut() + public function testUpdateShortCut(): void { $query = $this->fluent->update('user', ['type' => 'admin'], 1); @@ -101,7 +123,7 @@ public function testUpdateShortCut() self::assertEquals([0 => 'admin', 1 => '1'], $query->getParameters()); } - public function testUpdateZero() + public function testUpdateZero(): void { $this->fluent->update('article')->set('content', '')->where('id', 1)->execute(); $user = $this->fluent->from('article')->where('id', 1)->fetch(); @@ -118,35 +140,40 @@ public function testUpdateZero() self::assertEquals('ID: 1 - content: content 1', $printQuery2); } - public function testUpdateWhere() + public function testUpdateWhere(): void { $query = $this->fluent->update('users') - ->set("`users`.`active`", 1) - ->where("`country`.`name`", 'Slovakia') - ->where("`users`.`name`", 'Marek'); + ->set('`users`.`active`', 1) + ->where('`country`.`name`', 'Slovakia') + ->where('`users`.`name`', 'Marek'); $query2 = $this->fluent->update('users') - ->set("[users].[active]", 1) - ->where("[country].[name]", 'Slovakia') - ->where("[users].[name]", 'Marek'); - - self::assertEquals('UPDATE users LEFT JOIN country ON country.id = users.country_id SET `users`.`active` = ? WHERE `country`.`name` = ? AND `users`.`name` = ?', - $query->getQuery(false)); + ->set('[users].[active]', 1) + ->where('[country].[name]', 'Slovakia') + ->where('[users].[name]', 'Marek'); + + self::assertEquals( + 'UPDATE users LEFT JOIN country ON country.id = users.country_id SET `users`.`active` = ? WHERE `country`.`name` = ? AND `users`.`name` = ?', + $query->getQuery(false), + ); self::assertEquals([0 => '1', 1 => 'Slovakia', 2 => 'Marek'], $query->getParameters()); - self::assertEquals('UPDATE users LEFT JOIN country ON country.id = users.country_id SET [users].[active] = ? WHERE [country].[name] = ? AND [users].[name] = ?', - $query2->getQuery(false)); + self::assertEquals( + 'UPDATE users LEFT JOIN country ON country.id = users.country_id SET [users].[active] = ? WHERE [country].[name] = ? AND [users].[name] = ?', + $query2->getQuery(false), + ); self::assertEquals([0 => '1', 1 => 'Slovakia', 2 => 'Marek'], $query2->getParameters()); } - public function testUpdateNamedParameters() + public function testUpdateNamedParameters(): void { $query = $this->fluent->update('users') - ->set("`users`.`active`", [':active' => 1]) - ->where("`country`.`name` = :country", [':country' => 'Slovakia']); + ->set('`users`.`active`', [':active' => 1]) + ->where('`country`.`name` = :country', [':country' => 'Slovakia']); - self::assertEquals('UPDATE users LEFT JOIN country ON country.id = users.country_id SET `users`.`active` = :active WHERE `country`.`name` = :country', - $query->getQuery(false)); + self::assertEquals( + 'UPDATE users LEFT JOIN country ON country.id = users.country_id SET `users`.`active` = :active WHERE `country`.`name` = :country', + $query->getQuery(false), + ); self::assertEquals([':active' => '1', ':country' => 'Slovakia'], $query->getParameters()); } - } diff --git a/tests/RegexTest.php b/tests/RegexTest.php index 3cbd82c..0f3dcdb 100644 --- a/tests/RegexTest.php +++ b/tests/RegexTest.php @@ -1,115 +1,133 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/_resources/init.php'; -use PHPUnit\Framework\TestCase; use Envms\FluentPDO\Regex; +use PHPUnit\Framework\TestCase; /** - * Class StructureTest + * Class StructureTest. * * @covers \Envms\FluentPDO\Structure */ class RegexTest extends TestCase { - /** @var Regex */ - protected $regex; + protected Regex $regex; - public function setUp(): void + protected function setUp(): void { $this->regex = new Regex(); } - public function testCamelCasedSpaced() + public function testCamelCasedSpaced(): void { - $name = $this->regex->camelCaseSpaced("magicCallMethod"); + $name = $this->regex->camelCaseSpaced('magicCallMethod'); - self::assertEquals("magic Call Method", $name); + self::assertEquals('magic Call Method', $name); } - public function testSplitClauses() + public function testSplitClauses(): void { - $query = $this->regex->splitClauses("SELECT * FROM user WHERE id = 1 OR id = 2 ORDER BY id ASC"); + $query = $this->regex->splitClauses('SELECT * FROM user WHERE id = 1 OR id = 2 ORDER BY id ASC'); self::assertEquals("SELECT * \nFROM user \nWHERE id = 1 OR id = 2 \nORDER BY id ASC", $query); } - public function testSplitSubClauses() + public function testSplitSubClauses(): void { - $query = $this->regex->splitSubClauses("SELECT * FROM user LEFT JOIN article WHERE 1 OR 2"); + $query = $this->regex->splitSubClauses('SELECT * FROM user LEFT JOIN article WHERE 1 OR 2'); self::assertEquals("SELECT * FROM user \n LEFT JOIN article WHERE 1 \n OR 2", $query); } - public function testRemoveLineEndWhitespace() + public function testRemoveLineEndWhitespace(): void { $query = $this->regex->removeLineEndWhitespace("SELECT * \n FROM user \n"); self::assertEquals("SELECT *\n FROM user\n", $query); } - public function testRemoveAdditionalJoins() + public function testRemoveAdditionalJoins(): void { - $join = $this->regex->removeAdditionalJoins("user.article:id"); + $join = $this->regex->removeAdditionalJoins('user.article:id'); - self::assertEquals("article.id", $join); + self::assertEquals('article.id', $join); } - public function testSqlParameter() + public function testSqlParameter(): void { - $isParam = $this->regex->sqlParameter("id = :id"); + $isParam = $this->regex->sqlParameter('id = :id'); self::assertEquals(1, $isParam); - $isParam = $this->regex->sqlParameter("name = ?"); + $isParam = $this->regex->sqlParameter('name = ?'); self::assertEquals(1, $isParam); - $isParam = $this->regex->sqlParameter("count IN (22, 77)"); + $isParam = $this->regex->sqlParameter('count IN (22, 77)'); self::assertEquals(0, $isParam); } - public function testTableAlias() + public function testTableAlias(): void { - $isAlias = $this->regex->tableAlias("user AS u"); + $isAlias = $this->regex->tableAlias('user AS u'); self::assertEquals(1, $isAlias); - $isAlias = $this->regex->tableAlias("user.*"); + $isAlias = $this->regex->tableAlias('user.*'); self::assertEquals(1, $isAlias); - $isAlias = $this->regex->tableAlias(" "); + $isAlias = $this->regex->tableAlias(' '); self::assertEquals(0, $isAlias); - $isAlias = $this->regex->tableAlias("0.00 AS ཎ"); + $isAlias = $this->regex->tableAlias('0.00 AS ཎ'); self::assertEquals(1, $isAlias); } - public function testTableJoin() + public function testTableJoin(): void { - $join = $this->regex->tableJoin("user"); + $join = $this->regex->tableJoin('user'); self::assertEquals(1, $join); - $join = $this->regex->tableJoin("`user`."); + $join = $this->regex->tableJoin('`user`.'); self::assertEquals(1, $join); $join = $this->regex->tableJoin("'''"); self::assertEquals(0, $join); - $join = $this->regex->tableJoin("ឃឡឱ."); + $join = $this->regex->tableJoin('ឃឡឱ.'); self::assertEquals(1, $join); } - public function testTableJoinFull() + public function testTableJoinFull(): void { - $join = $this->regex->tableJoinFull("user."); + $join = $this->regex->tableJoinFull('user.'); self::assertEquals(1, $join); - $join = $this->regex->tableJoinFull("`user`.`column`"); + $join = $this->regex->tableJoinFull('`user`.`column`'); self::assertEquals(1, $join); - $join = $this->regex->tableJoinFull("user .column"); + $join = $this->regex->tableJoinFull('user .column'); self::assertEquals(0, $join); - $join = $this->regex->tableJoinFull("ㇽㇺㇴ.ㇱ"); + $join = $this->regex->tableJoinFull('ㇽㇺㇴ.ㇱ'); self::assertEquals(1, $join); } - } diff --git a/tests/StructureTest.php b/tests/StructureTest.php index c02dbf2..9bd49bc 100644 --- a/tests/StructureTest.php +++ b/tests/StructureTest.php @@ -1,19 +1,38 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/_resources/init.php'; -use PHPUnit\Framework\TestCase; use Envms\FluentPDO\Structure; +use PHPUnit\Framework\TestCase; /** - * Class StructureTest + * Class StructureTest. * * @covers \Envms\FluentPDO\Structure */ class StructureTest extends TestCase { - - public function testBasicKey() + public function testBasicKey(): void { $structure = new Structure(); @@ -21,7 +40,7 @@ public function testBasicKey() self::assertEquals('user_id', $structure->getForeignKey('user')); } - public function testCustomKey() + public function testCustomKey(): void { $structure = new Structure('whatAnId', '%s_\xid'); @@ -29,7 +48,7 @@ public function testCustomKey() self::assertEquals('user_\xid', $structure->getForeignKey('user')); } - public function testMethodKey() + public function testMethodKey(): void { $structure = new Structure('id', ['StructureTest', 'suffix']); @@ -38,13 +57,12 @@ public function testMethodKey() } /** - * @param $table + * @param mixed $table * * @return string */ public static function suffix($table) { - return $table . '_id'; + return $table.'_id'; } - } diff --git a/tests/UtilitiesTest.php b/tests/UtilitiesTest.php index 87ce580..a4f4c93 100644 --- a/tests/UtilitiesTest.php +++ b/tests/UtilitiesTest.php @@ -1,20 +1,39 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +require __DIR__.'/_resources/init.php'; + +use Envms\FluentPDO\Query; +use Envms\FluentPDO\Utilities; use PHPUnit\Framework\TestCase; -use Envms\FluentPDO\{Query,Utilities}; /** - * Class UtilitiesTest + * Class UtilitiesTest. */ class UtilitiesTest extends TestCase { + protected Envms\FluentPDO\Query $fluent; - /** @var Envms\FluentPDO\Query */ - protected $fluent; - - public function setUp(): void + protected function setUp(): void { global $pdo; @@ -23,9 +42,8 @@ public function setUp(): void $this->fluent = new Query($pdo); } - public function testFluentUtil() + public function testFluentUtil(): void { - $value = Utilities::toUpperWords('one'); $value2 = Utilities::toUpperWords(' one '); $value3 = Utilities::toUpperWords('oneTwo'); @@ -41,7 +59,7 @@ public function testFluentUtil() self::assertEquals('ONE TWO THREE', $value6); } - public function testFormatQuery() + public function testFormatQuery(): void { $query = $this->fluent ->from('user') @@ -53,7 +71,7 @@ public function testFormatQuery() self::assertEquals("SELECT user.*\nFROM user\nWHERE id > ?\nORDER BY name", $formattedQuery); } - public function testConvertToNativeType() + public function testConvertToNativeType(): void { $query = $this->fluent ->from('user') @@ -69,7 +87,7 @@ public function testConvertToNativeType() self::assertEquals(['id' => 1], $forceInt); } - public function testConvertSqlWriteValues() + public function testConvertSqlWriteValues(): void { $valueArray = Utilities::convertSqlWriteValues(['string', 1, 2, false, true, null, 'false']); $value1 = Utilities::convertSqlWriteValues(false); @@ -80,7 +98,7 @@ public function testConvertSqlWriteValues() self::assertEquals(1, $value2); } - public function testisCountable() + public function testisCountable(): void { $selectQuery = $this->fluent ->from('user') @@ -92,8 +110,7 @@ public function testisCountable() ->deleteFrom('user') ->where('id', 1); - self::assertEquals(true, Utilities::isCountable($selectQuery)); - self::assertEquals(false, Utilities::isCountable($deleteQuery)); + self::assertTrue(Utilities::isCountable($selectQuery)); + self::assertFalse(Utilities::isCountable($deleteQuery)); } - -} \ No newline at end of file +} diff --git a/tests/_resources/init.php b/tests/_resources/init.php index b639ca2..cbb0e69 100644 --- a/tests/_resources/init.php +++ b/tests/_resources/init.php @@ -1,11 +1,29 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + if (getenv('TRAVIS')) { - $pdo = new PDO("mysql:dbname=fluentdb;host=localhost;charset=utf8", "root"); -} -else { - $pdo = new PDO("mysql:dbname=fluentdb;host=localhost;charset=utf8", "vagrant", "vagrant"); + $pdo = new PDO('mysql:dbname=fluentdb;host=localhost;charset=utf8', 'root'); +} else { + $pdo = new PDO('mysql:dbname=fluentdb;host=localhost;charset=utf8', 'vagrant', 'vagrant'); } $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);