diff --git a/.ci/mysql_fixtures.sh b/.ci/mysql_fixtures.sh
new file mode 100644
index 0000000..aa14663
--- /dev/null
+++ b/.ci/mysql_fixtures.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+echo "Configure MySQL test database"
+
+mysql --user=root --password=Password123 -e "create database laminasdb_test;"
diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml
index 2f02e70..7ed76f2 100644
--- a/.github/workflows/continuous-integration.yml
+++ b/.github/workflows/continuous-integration.yml
@@ -7,5 +7,41 @@ on:
tags:
jobs:
- ci:
- uses: laminas/workflow-continuous-integration/.github/workflows/continuous-integration.yml@1.x
\ No newline at end of file
+ matrix:
+ name: Generate job matrix
+ runs-on: ubuntu-latest
+ outputs:
+ matrix: ${{ steps.matrix.outputs.matrix }}
+ steps:
+ - name: Gather CI configuration
+ id: matrix
+ uses: laminas/laminas-ci-matrix-action@v1
+
+ qa:
+ name: QA Checks
+ needs: [matrix]
+ runs-on: ${{ matrix.operatingSystem }}
+ strategy:
+ fail-fast: false
+ matrix: ${{ fromJSON(needs.matrix.outputs.matrix) }}
+ steps:
+ - name: ${{ matrix.name }}
+ uses: laminas/laminas-continuous-integration-action@v1
+ with:
+ job: ${{ matrix.job }}
+ services:
+ mysql:
+ image: mysql:5.7
+ env:
+ MYSQL_ROOT_PASSWORD: 'password'
+ MYSQL_ROOT_HOST: '%'
+ MYSQL_USER: 'gha'
+ MYSQL_PASSWORD: 'password'
+ MYSQL_DATABASE: 'laminasdb_test'
+ options: >-
+ --health-cmd="mysqladmin ping"
+ --health-interval 10s
+ --health-timeout 5s
+ --health-retries 3
+ ports:
+ - 3306
\ No newline at end of file
diff --git a/.laminas-ci/mysql_permissions.sql b/.laminas-ci/mysql_permissions.sql
new file mode 100644
index 0000000..59c1c60
--- /dev/null
+++ b/.laminas-ci/mysql_permissions.sql
@@ -0,0 +1,3 @@
+CREATE USER 'gha'@'%' IDENTIFIED WITH mysql_native_password BY 'password';
+GRANT ALL PRIVILEGES ON *.* TO 'gha'@'%';
+FLUSH PRIVILEGES;
diff --git a/.laminas-ci/phpunit.xml b/.laminas-ci/phpunit.xml
new file mode 100644
index 0000000..c71d028
--- /dev/null
+++ b/.laminas-ci/phpunit.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+ ./test/unit
+
+
+ ./test/integration
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.laminas-ci/pre-install.sh b/.laminas-ci/pre-install.sh
new file mode 100644
index 0000000..a3c153e
--- /dev/null
+++ b/.laminas-ci/pre-install.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+WORKING_DIRECTORY=$2
+JOB=$3
+PHP_VERSION=$(echo "${JOB}" | jq -r '.php')
+
+
+if [ ! -z "$GITHUB_BASE_REF" ] && [[ "$GITHUB_BASE_REF" =~ ^[0-9]+\.[0-9] ]]; then
+ readarray -td. TARGET_BRANCH_VERSION_PARTS <<<"${GITHUB_BASE_REF}.";
+ unset 'TARGET_BRANCH_VERSION_PARTS[-1]';
+ declare -a TARGET_BRANCH_VERSION_PARTS
+ MAJOR_OF_TARGET_BRANCH=${TARGET_BRANCH_VERSION_PARTS[0]}
+ MINOR_OF_TARGET_BRANCH=${TARGET_BRANCH_VERSION_PARTS[1]}
+
+ export COMPOSER_ROOT_VERISON="${MAJOR_OF_TARGET_BRANCH}.${MINOR_OF_TARGET_BRANCH}.99"
+ echo "Exported COMPOSER_ROOT_VERISON as ${COMPOSER_ROOT_VERISON}"
+fi
diff --git a/.laminas-ci/pre-run.sh b/.laminas-ci/pre-run.sh
new file mode 100644
index 0000000..660082a
--- /dev/null
+++ b/.laminas-ci/pre-run.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+set -e
+
+TEST_USER=$1
+WORKSPACE=$2
+JOB=$3
+
+COMMAND=$(echo "${JOB}" | jq -r '.command')
+
+if [[ ! ${COMMAND} =~ phpunit ]]; then
+ exit 0
+fi
+
+PHP_VERSION=$(echo "${JOB}" | jq -r '.php')
+
+# Install CI version of phpunit config
+cp .laminas-ci/phpunit.xml phpunit.xml
+
+# Install lsof (used in integration tests)
+apt update -qq
+apt install -yqq lsof
diff --git a/bin/install-deps.sh b/bin/install-deps.sh
new file mode 100644
index 0000000..df3c3a4
--- /dev/null
+++ b/bin/install-deps.sh
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+/usr/bin/composer validate && \
+/usr/bin/composer --ignore-platform-reqs install \
+ --no-ansi --no-progress --no-scripts \
+ --classmap-authoritative --no-interaction \
+ --quiet
\ No newline at end of file
diff --git a/compose.yml b/compose.yml
new file mode 100644
index 0000000..0bb7326
--- /dev/null
+++ b/compose.yml
@@ -0,0 +1,31 @@
+services:
+ php:
+ build:
+ context: ./
+ dockerfile: docker/php/Dockerfile
+ args:
+ - PHP_VERSION=${PHP_VERSION:-8.3.19}
+ volumes:
+ - ./:/var/www/html
+ depends_on:
+ - mysql
+
+ mysql:
+ build:
+ context: ./
+ dockerfile: docker/databases/mysql/Dockerfile
+ args:
+ - VERSION=${MYSQL_VERSION:-8.0.41}
+ ports:
+ - "3306:3306"
+ environment:
+ - MYSQL_DATABASE=${MYSQL_DATABASE:-laminasdb_test}
+ - MYSQL_USER=${MYSQL_USER:-user}
+ - MYSQL_PASSWORD=${MYSQL_PASSWORD:-password}
+ - MYSQL_RANDOM_ROOT_PASSWORD=${MYSQL_RANDOM_ROOT_PASSWORD:-yes}
+ volumes:
+ - ./test/integration/TestFixtures/mysql.sql:/docker-entrypoint-initdb.d/mysql.sql
+ healthcheck:
+ test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
+ timeout: 20s
+ retries: 10
diff --git a/composer.json b/composer.json
index e649f4c..68135ed 100644
--- a/composer.json
+++ b/composer.json
@@ -1,24 +1,26 @@
{
- "name": "laminas/laminas-db-mysql-adapter",
- "description": "MySQL support for laminas-db",
+ "name": "php-db/phpdb-adapter-mysql",
+ "description": "MySQL support for php-db",
"license": "BSD-3-Clause",
"keywords": [
"laminas",
+ "mezzio",
+ "php-db",
"mysql",
"db"
],
- "homepage": "https://github.com/axleus/laminas-db-mysql/discussions",
+ "homepage": "https://github.com/php-db/phpdb-adapter-mysql/discussions",
"support": {
- "issues": "https://github.com/alxeus/laminas-db-mysql/issues",
- "source": "https://github.com/axleus/laminas-db-mysql",
- "forum": "https://github.com/axleus/laminas-db-mysql/discussions"
+ "issues": "https://github.com/php-db/phpdb-adapter-mysql/issues",
+ "source": "https://github.com/php-db/phpdb-adapter-mysql",
+ "forum": "https://github.com/php-db/phpdb-adapter-mysql/discussions"
},
"minimum-stability": "dev",
"prefer-stable": true,
"config": {
"sort-packages": true,
"platform": {
- "php": "8.1.99"
+ "php": "8.2.99"
},
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
@@ -26,39 +28,35 @@
},
"extra": {
"laminas": {
- "component": "Laminas\\Db\\Mysql",
- "config-provider": "Laminas\\Db\\Mysql\\ConfigProvider"
+ "config-provider": "PhpDb\\Adapter\\Mysql\\ConfigProvider"
}
},
"require": {
"php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
- "laminas/laminas-db": "3.0.x"
+ "php-db/phpdb": "0.0.x-dev"
},
"require-dev": {
"ext-mysqli": "*",
"ext-pdo_mysql": "*",
"laminas/laminas-coding-standard": "^3.0.1",
- "laminas/laminas-eventmanager": "^3.6.0",
- "laminas/laminas-hydrator": "^4.7",
- "laminas/laminas-servicemanager": "^3.19.0",
- "phpunit/phpunit": "^9.5.25",
+ "phpunit/phpunit": "^11.5.15",
"psalm/plugin-phpunit": "^0.19.2",
"vimeo/psalm": "^6.8.8"
},
"suggest": {
"ext-mysqli": "Required for MySQLi support",
"ext-pdo_mysql": "Required for PDO MySQL support",
- "laminas/laminas-servicemanager": "Laminas\\ServiceManager component"
+ "laminas/laminas-eventmanager": "Required for TableGateway events support"
},
"autoload": {
"psr-4": {
- "Laminas\\Db\\Mysql\\": "src/"
+ "PhpDb\\Adapter\\Mysql\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
- "LaminasTest\\Db\\Mysql\\": "test/unit/",
- "LaminasIntegrationTest\\Db\\Mysql\\": "test/integration/"
+ "PhpDbTest\\Adapter\\Mysql\\": "test/unit/",
+ "PhpDbIntegrationTest\\Adapter\\Mysql\\": "test/integration/"
}
},
"scripts": {
@@ -72,9 +70,8 @@
"test-coverage": "phpunit --colors=always --coverage-clover clover.xml",
"test-integration": "phpunit --colors=always --testsuite \"integration test\"",
"static-analysis": "psalm --shepherd --stats",
+ "sa-update-baseline": "psalm --update-baseline",
+ "sa-no-baseline": "psalm --shepherd --stats --ignore-baseline",
"upload-coverage": "coveralls -v"
- },
- "conflict": {
- "laminas/laminas-db": "*"
}
}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..81548d6
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,5577 @@
+{
+ "_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": "3d22ee6c02afd8874611739726e986ea",
+ "packages": [
+ {
+ "name": "brick/varexporter",
+ "version": "0.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/brick/varexporter.git",
+ "reference": "84b2a7a91f69aa5d079aec5a0a7256ebf2dceb6b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/brick/varexporter/zipball/84b2a7a91f69aa5d079aec5a0a7256ebf2dceb6b",
+ "reference": "84b2a7a91f69aa5d079aec5a0a7256ebf2dceb6b",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^5.0",
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "php-coveralls/php-coveralls": "^2.2",
+ "phpunit/phpunit": "^9.3",
+ "psalm/phar": "5.21.1"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Brick\\VarExporter\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A powerful alternative to var_export(), which can export closures and objects without __set_state()",
+ "keywords": [
+ "var_export"
+ ],
+ "support": {
+ "issues": "https://github.com/brick/varexporter/issues",
+ "source": "https://github.com/brick/varexporter/tree/0.5.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/BenMorel",
+ "type": "github"
+ }
+ ],
+ "time": "2024-05-10T17:15:19+00:00"
+ },
+ {
+ "name": "laminas/laminas-servicemanager",
+ "version": "4.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laminas/laminas-servicemanager.git",
+ "reference": "74da44d07e493b834347123242d0047976fb9932"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/74da44d07e493b834347123242d0047976fb9932",
+ "reference": "74da44d07e493b834347123242d0047976fb9932",
+ "shasum": ""
+ },
+ "require": {
+ "brick/varexporter": "^0.3.8 || ^0.4.0 || ^0.5.0",
+ "laminas/laminas-stdlib": "^3.19",
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
+ "psr/container": "^1.1 || ^2.0"
+ },
+ "conflict": {
+ "laminas/laminas-code": "<4.10.0",
+ "zendframework/zend-code": "<3.3.1"
+ },
+ "provide": {
+ "psr/container-implementation": "^1.0 || ^2.0"
+ },
+ "require-dev": {
+ "composer/package-versions-deprecated": "^1.11.99.5",
+ "friendsofphp/proxy-manager-lts": "^1.0.18",
+ "laminas/laminas-cli": "^1.11",
+ "laminas/laminas-coding-standard": "~3.0.1",
+ "laminas/laminas-container-config-test": "^1.0",
+ "mikey179/vfsstream": "^1.6.12",
+ "phpbench/phpbench": "^1.4.0",
+ "phpunit/phpunit": "^10.5.44",
+ "psalm/plugin-phpunit": "^0.19.2",
+ "symfony/console": "^6.4.17 || ^7.0",
+ "vimeo/psalm": "^6.2.0"
+ },
+ "suggest": {
+ "friendsofphp/proxy-manager-lts": "To handle lazy initialization of services",
+ "laminas/laminas-cli": "To consume CLI commands provided by this component"
+ },
+ "type": "library",
+ "extra": {
+ "laminas": {
+ "module": "Laminas\\ServiceManager",
+ "config-provider": "Laminas\\ServiceManager\\ConfigProvider"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Laminas\\ServiceManager\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "Factory-Driven Dependency Injection Container",
+ "homepage": "https://laminas.dev",
+ "keywords": [
+ "PSR-11",
+ "dependency-injection",
+ "di",
+ "dic",
+ "laminas",
+ "service-manager",
+ "servicemanager"
+ ],
+ "support": {
+ "chat": "https://laminas.dev/chat",
+ "forum": "https://discourse.laminas.dev",
+ "issues": "https://github.com/laminas/laminas-servicemanager/issues",
+ "source": "https://github.com/laminas/laminas-servicemanager/tree/4.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://funding.communitybridge.org/projects/laminas-project",
+ "type": "community_bridge"
+ }
+ ],
+ "time": "2025-02-04T06:13:50+00:00"
+ },
+ {
+ "name": "laminas/laminas-stdlib",
+ "version": "3.20.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laminas/laminas-stdlib.git",
+ "reference": "8974a1213be42c3e2f70b2c27b17f910291ab2f4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/8974a1213be42c3e2f70b2c27b17f910291ab2f4",
+ "reference": "8974a1213be42c3e2f70b2c27b17f910291ab2f4",
+ "shasum": ""
+ },
+ "require": {
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
+ },
+ "conflict": {
+ "zendframework/zend-stdlib": "*"
+ },
+ "require-dev": {
+ "laminas/laminas-coding-standard": "^3.0",
+ "phpbench/phpbench": "^1.3.1",
+ "phpunit/phpunit": "^10.5.38",
+ "psalm/plugin-phpunit": "^0.19.0",
+ "vimeo/psalm": "^5.26.1"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Laminas\\Stdlib\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "SPL extensions, array utilities, error handlers, and more",
+ "homepage": "https://laminas.dev",
+ "keywords": [
+ "laminas",
+ "stdlib"
+ ],
+ "support": {
+ "chat": "https://laminas.dev/chat",
+ "docs": "https://docs.laminas.dev/laminas-stdlib/",
+ "forum": "https://discourse.laminas.dev",
+ "issues": "https://github.com/laminas/laminas-stdlib/issues",
+ "rss": "https://github.com/laminas/laminas-stdlib/releases.atom",
+ "source": "https://github.com/laminas/laminas-stdlib"
+ },
+ "funding": [
+ {
+ "url": "https://funding.communitybridge.org/projects/laminas-project",
+ "type": "community_bridge"
+ }
+ ],
+ "time": "2024-10-29T13:46:07+00:00"
+ },
+ {
+ "name": "nikic/php-parser",
+ "version": "v5.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nikic/PHP-Parser.git",
+ "reference": "ae59794362fe85e051a58ad36b289443f57be7a9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9",
+ "reference": "ae59794362fe85e051a58ad36b289443f57be7a9",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "ext-json": "*",
+ "ext-tokenizer": "*",
+ "php": ">=7.4"
+ },
+ "require-dev": {
+ "ircmaxell/php-yacc": "^0.0.7",
+ "phpunit/phpunit": "^9.0"
+ },
+ "bin": [
+ "bin/php-parse"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpParser\\": "lib/PhpParser"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Nikita Popov"
+ }
+ ],
+ "description": "A PHP parser written in PHP",
+ "keywords": [
+ "parser",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/nikic/PHP-Parser/issues",
+ "source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0"
+ },
+ "time": "2025-05-31T08:24:38+00:00"
+ },
+ {
+ "name": "php-db/phpdb",
+ "version": "0.0.x-dev",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-db/phpdb.git",
+ "reference": "82b26147f3aab64c5f19402923a18acbb2991942"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-db/phpdb/zipball/82b26147f3aab64c5f19402923a18acbb2991942",
+ "reference": "82b26147f3aab64c5f19402923a18acbb2991942",
+ "shasum": ""
+ },
+ "require": {
+ "laminas/laminas-servicemanager": "^4.0.0",
+ "laminas/laminas-stdlib": "^3.20.0",
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
+ },
+ "conflict": {
+ "laminas/laminas-db": "*",
+ "zendframework/zend-db": "*"
+ },
+ "require-dev": {
+ "laminas/laminas-coding-standard": "^3.0.1",
+ "laminas/laminas-eventmanager": "^3.14.0",
+ "phpunit/phpunit": "^11.5.12",
+ "psalm/plugin-phpunit": "^0.19.2",
+ "rector/rector": "^2.0",
+ "vimeo/psalm": "^6.8.8"
+ },
+ "suggest": {
+ "laminas/laminas-eventmanager": "Laminas\\EventManager component",
+ "laminas/laminas-hydrator": "(^5.0.0) Laminas\\Hydrator component for using HydratingResultSets"
+ },
+ "default-branch": true,
+ "type": "library",
+ "extra": {
+ "laminas": {
+ "config-provider": "PhpDb\\Container\\ConfigProvider"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpDb\\": "src/",
+ "CustomRule\\PHPUnit\\": "rector/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "Database abstraction layer, SQL abstraction, result set abstraction, and RowDataGateway and TableDataGateway implementations",
+ "homepage": "https://php-db.dev",
+ "keywords": [
+ "db",
+ "laminas",
+ "mezzio",
+ "php-db"
+ ],
+ "support": {
+ "docs": "https://docs.php-db.dev/",
+ "issues": "https://github.com/php-db/phpdb/issues",
+ "source": "https://github.com/php-db/phpdb"
+ },
+ "time": "2025-07-07T04:06:26+00:00"
+ },
+ {
+ "name": "psr/container",
+ "version": "2.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/container.git",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Container\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common Container Interface (PHP FIG PSR-11)",
+ "homepage": "https://github.com/php-fig/container",
+ "keywords": [
+ "PSR-11",
+ "container",
+ "container-interface",
+ "container-interop",
+ "psr"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/container/issues",
+ "source": "https://github.com/php-fig/container/tree/2.0.2"
+ },
+ "time": "2021-11-05T16:47:00+00:00"
+ }
+ ],
+ "packages-dev": [
+ {
+ "name": "amphp/amp",
+ "version": "v3.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/amp.git",
+ "reference": "7cf7fef3d667bfe4b2560bc87e67d5387a7bcde9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/amp/zipball/7cf7fef3d667bfe4b2560bc87e67d5387a7bcde9",
+ "reference": "7cf7fef3d667bfe4b2560bc87e67d5387a7bcde9",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "revolt/event-loop": "^1 || ^0.2"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "5.23.1"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php",
+ "src/Future/functions.php",
+ "src/Internal/functions.php"
+ ],
+ "psr-4": {
+ "Amp\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Bob Weinand",
+ "email": "bobwei9@hotmail.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ },
+ {
+ "name": "Daniel Lowrey",
+ "email": "rdlowrey@php.net"
+ }
+ ],
+ "description": "A non-blocking concurrency framework for PHP applications.",
+ "homepage": "https://amphp.org/amp",
+ "keywords": [
+ "async",
+ "asynchronous",
+ "awaitable",
+ "concurrency",
+ "event",
+ "event-loop",
+ "future",
+ "non-blocking",
+ "promise"
+ ],
+ "support": {
+ "issues": "https://github.com/amphp/amp/issues",
+ "source": "https://github.com/amphp/amp/tree/v3.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2025-01-26T16:07:39+00:00"
+ },
+ {
+ "name": "amphp/byte-stream",
+ "version": "v2.1.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/byte-stream.git",
+ "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/byte-stream/zipball/55a6bd071aec26fa2a3e002618c20c35e3df1b46",
+ "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^3",
+ "amphp/parser": "^1.1",
+ "amphp/pipeline": "^1",
+ "amphp/serialization": "^1",
+ "amphp/sync": "^2",
+ "php": ">=8.1",
+ "revolt/event-loop": "^1 || ^0.2.3"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "amphp/phpunit-util": "^3",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "5.22.1"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php",
+ "src/Internal/functions.php"
+ ],
+ "psr-4": {
+ "Amp\\ByteStream\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ }
+ ],
+ "description": "A stream abstraction to make working with non-blocking I/O simple.",
+ "homepage": "https://amphp.org/byte-stream",
+ "keywords": [
+ "amp",
+ "amphp",
+ "async",
+ "io",
+ "non-blocking",
+ "stream"
+ ],
+ "support": {
+ "issues": "https://github.com/amphp/byte-stream/issues",
+ "source": "https://github.com/amphp/byte-stream/tree/v2.1.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2025-03-16T17:10:27+00:00"
+ },
+ {
+ "name": "amphp/cache",
+ "version": "v2.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/cache.git",
+ "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/cache/zipball/46912e387e6aa94933b61ea1ead9cf7540b7797c",
+ "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^3",
+ "amphp/serialization": "^1",
+ "amphp/sync": "^2",
+ "php": ">=8.1",
+ "revolt/event-loop": "^1 || ^0.2"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "amphp/phpunit-util": "^3",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "^5.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Amp\\Cache\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ },
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Daniel Lowrey",
+ "email": "rdlowrey@php.net"
+ }
+ ],
+ "description": "A fiber-aware cache API based on Amp and Revolt.",
+ "homepage": "https://amphp.org/cache",
+ "support": {
+ "issues": "https://github.com/amphp/cache/issues",
+ "source": "https://github.com/amphp/cache/tree/v2.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2024-04-19T03:38:06+00:00"
+ },
+ {
+ "name": "amphp/dns",
+ "version": "v2.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/dns.git",
+ "reference": "78eb3db5fc69bf2fc0cb503c4fcba667bc223c71"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/dns/zipball/78eb3db5fc69bf2fc0cb503c4fcba667bc223c71",
+ "reference": "78eb3db5fc69bf2fc0cb503c4fcba667bc223c71",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^3",
+ "amphp/byte-stream": "^2",
+ "amphp/cache": "^2",
+ "amphp/parser": "^1",
+ "amphp/process": "^2",
+ "daverandom/libdns": "^2.0.2",
+ "ext-filter": "*",
+ "ext-json": "*",
+ "php": ">=8.1",
+ "revolt/event-loop": "^1 || ^0.2"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "amphp/phpunit-util": "^3",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "5.20"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Amp\\Dns\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Chris Wright",
+ "email": "addr@daverandom.com"
+ },
+ {
+ "name": "Daniel Lowrey",
+ "email": "rdlowrey@php.net"
+ },
+ {
+ "name": "Bob Weinand",
+ "email": "bobwei9@hotmail.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ },
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ }
+ ],
+ "description": "Async DNS resolution for Amp.",
+ "homepage": "https://github.com/amphp/dns",
+ "keywords": [
+ "amp",
+ "amphp",
+ "async",
+ "client",
+ "dns",
+ "resolve"
+ ],
+ "support": {
+ "issues": "https://github.com/amphp/dns/issues",
+ "source": "https://github.com/amphp/dns/tree/v2.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2025-01-19T15:43:40+00:00"
+ },
+ {
+ "name": "amphp/parallel",
+ "version": "v2.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/parallel.git",
+ "reference": "5113111de02796a782f5d90767455e7391cca190"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/parallel/zipball/5113111de02796a782f5d90767455e7391cca190",
+ "reference": "5113111de02796a782f5d90767455e7391cca190",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^3",
+ "amphp/byte-stream": "^2",
+ "amphp/cache": "^2",
+ "amphp/parser": "^1",
+ "amphp/pipeline": "^1",
+ "amphp/process": "^2",
+ "amphp/serialization": "^1",
+ "amphp/socket": "^2",
+ "amphp/sync": "^2",
+ "php": ">=8.1",
+ "revolt/event-loop": "^1"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "amphp/phpunit-util": "^3",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "^5.18"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/Context/functions.php",
+ "src/Context/Internal/functions.php",
+ "src/Ipc/functions.php",
+ "src/Worker/functions.php"
+ ],
+ "psr-4": {
+ "Amp\\Parallel\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ },
+ {
+ "name": "Stephen Coakley",
+ "email": "me@stephencoakley.com"
+ }
+ ],
+ "description": "Parallel processing component for Amp.",
+ "homepage": "https://github.com/amphp/parallel",
+ "keywords": [
+ "async",
+ "asynchronous",
+ "concurrent",
+ "multi-processing",
+ "multi-threading"
+ ],
+ "support": {
+ "issues": "https://github.com/amphp/parallel/issues",
+ "source": "https://github.com/amphp/parallel/tree/v2.3.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2024-12-21T01:56:09+00:00"
+ },
+ {
+ "name": "amphp/parser",
+ "version": "v1.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/parser.git",
+ "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/parser/zipball/3cf1f8b32a0171d4b1bed93d25617637a77cded7",
+ "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.4"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "^5.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Amp\\Parser\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ }
+ ],
+ "description": "A generator parser to make streaming parsers simple.",
+ "homepage": "https://github.com/amphp/parser",
+ "keywords": [
+ "async",
+ "non-blocking",
+ "parser",
+ "stream"
+ ],
+ "support": {
+ "issues": "https://github.com/amphp/parser/issues",
+ "source": "https://github.com/amphp/parser/tree/v1.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-21T19:16:53+00:00"
+ },
+ {
+ "name": "amphp/pipeline",
+ "version": "v1.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/pipeline.git",
+ "reference": "7b52598c2e9105ebcddf247fc523161581930367"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/pipeline/zipball/7b52598c2e9105ebcddf247fc523161581930367",
+ "reference": "7b52598c2e9105ebcddf247fc523161581930367",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^3",
+ "php": ">=8.1",
+ "revolt/event-loop": "^1"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "amphp/phpunit-util": "^3",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "^5.18"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Amp\\Pipeline\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ }
+ ],
+ "description": "Asynchronous iterators and operators.",
+ "homepage": "https://amphp.org/pipeline",
+ "keywords": [
+ "amp",
+ "amphp",
+ "async",
+ "io",
+ "iterator",
+ "non-blocking"
+ ],
+ "support": {
+ "issues": "https://github.com/amphp/pipeline/issues",
+ "source": "https://github.com/amphp/pipeline/tree/v1.2.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2025-03-16T16:33:53+00:00"
+ },
+ {
+ "name": "amphp/process",
+ "version": "v2.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/process.git",
+ "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/process/zipball/52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d",
+ "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^3",
+ "amphp/byte-stream": "^2",
+ "amphp/sync": "^2",
+ "php": ">=8.1",
+ "revolt/event-loop": "^1 || ^0.2"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "amphp/phpunit-util": "^3",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "^5.4"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Amp\\Process\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Bob Weinand",
+ "email": "bobwei9@hotmail.com"
+ },
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ }
+ ],
+ "description": "A fiber-aware process manager based on Amp and Revolt.",
+ "homepage": "https://amphp.org/process",
+ "support": {
+ "issues": "https://github.com/amphp/process/issues",
+ "source": "https://github.com/amphp/process/tree/v2.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2024-04-19T03:13:44+00:00"
+ },
+ {
+ "name": "amphp/serialization",
+ "version": "v1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/serialization.git",
+ "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/serialization/zipball/693e77b2fb0b266c3c7d622317f881de44ae94a1",
+ "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "dev-master",
+ "phpunit/phpunit": "^9 || ^8 || ^7"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Amp\\Serialization\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ }
+ ],
+ "description": "Serialization tools for IPC and data storage in PHP.",
+ "homepage": "https://github.com/amphp/serialization",
+ "keywords": [
+ "async",
+ "asynchronous",
+ "serialization",
+ "serialize"
+ ],
+ "support": {
+ "issues": "https://github.com/amphp/serialization/issues",
+ "source": "https://github.com/amphp/serialization/tree/master"
+ },
+ "time": "2020-03-25T21:39:07+00:00"
+ },
+ {
+ "name": "amphp/socket",
+ "version": "v2.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/socket.git",
+ "reference": "58e0422221825b79681b72c50c47a930be7bf1e1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/socket/zipball/58e0422221825b79681b72c50c47a930be7bf1e1",
+ "reference": "58e0422221825b79681b72c50c47a930be7bf1e1",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^3",
+ "amphp/byte-stream": "^2",
+ "amphp/dns": "^2",
+ "ext-openssl": "*",
+ "kelunik/certificate": "^1.1",
+ "league/uri": "^6.5 | ^7",
+ "league/uri-interfaces": "^2.3 | ^7",
+ "php": ">=8.1",
+ "revolt/event-loop": "^1 || ^0.2"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "amphp/phpunit-util": "^3",
+ "amphp/process": "^2",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "5.20"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php",
+ "src/Internal/functions.php",
+ "src/SocketAddress/functions.php"
+ ],
+ "psr-4": {
+ "Amp\\Socket\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Daniel Lowrey",
+ "email": "rdlowrey@gmail.com"
+ },
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ }
+ ],
+ "description": "Non-blocking socket connection / server implementations based on Amp and Revolt.",
+ "homepage": "https://github.com/amphp/socket",
+ "keywords": [
+ "amp",
+ "async",
+ "encryption",
+ "non-blocking",
+ "sockets",
+ "tcp",
+ "tls"
+ ],
+ "support": {
+ "issues": "https://github.com/amphp/socket/issues",
+ "source": "https://github.com/amphp/socket/tree/v2.3.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2024-04-21T14:33:03+00:00"
+ },
+ {
+ "name": "amphp/sync",
+ "version": "v2.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/amphp/sync.git",
+ "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/amphp/sync/zipball/217097b785130d77cfcc58ff583cf26cd1770bf1",
+ "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^3",
+ "amphp/pipeline": "^1",
+ "amphp/serialization": "^1",
+ "php": ">=8.1",
+ "revolt/event-loop": "^1 || ^0.2"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "amphp/phpunit-util": "^3",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "5.23"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "Amp\\Sync\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ },
+ {
+ "name": "Stephen Coakley",
+ "email": "me@stephencoakley.com"
+ }
+ ],
+ "description": "Non-blocking synchronization primitives for PHP based on Amp and Revolt.",
+ "homepage": "https://github.com/amphp/sync",
+ "keywords": [
+ "async",
+ "asynchronous",
+ "mutex",
+ "semaphore",
+ "synchronization"
+ ],
+ "support": {
+ "issues": "https://github.com/amphp/sync/issues",
+ "source": "https://github.com/amphp/sync/tree/v2.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/amphp",
+ "type": "github"
+ }
+ ],
+ "time": "2024-08-03T19:31:26+00:00"
+ },
+ {
+ "name": "composer/pcre",
+ "version": "3.3.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/pcre.git",
+ "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
+ "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0"
+ },
+ "conflict": {
+ "phpstan/phpstan": "<1.11.10"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.12 || ^2",
+ "phpstan/phpstan-strict-rules": "^1 || ^2",
+ "phpunit/phpunit": "^8 || ^9"
+ },
+ "type": "library",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ },
+ "branch-alias": {
+ "dev-main": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\Pcre\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ }
+ ],
+ "description": "PCRE wrapping library that offers type-safe preg_* replacements.",
+ "keywords": [
+ "PCRE",
+ "preg",
+ "regex",
+ "regular expression"
+ ],
+ "support": {
+ "issues": "https://github.com/composer/pcre/issues",
+ "source": "https://github.com/composer/pcre/tree/3.3.2"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-11-12T16:29:46+00:00"
+ },
+ {
+ "name": "composer/semver",
+ "version": "3.4.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/semver.git",
+ "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12",
+ "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3.2 || ^7.0 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.11",
+ "symfony/phpunit-bridge": "^3 || ^7"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Composer\\Semver\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nils Adermann",
+ "email": "naderman@naderman.de",
+ "homepage": "http://www.naderman.de"
+ },
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ },
+ {
+ "name": "Rob Bast",
+ "email": "rob.bast@gmail.com",
+ "homepage": "http://robbast.nl"
+ }
+ ],
+ "description": "Semver library that offers utilities, version constraint parsing and validation.",
+ "keywords": [
+ "semantic",
+ "semver",
+ "validation",
+ "versioning"
+ ],
+ "support": {
+ "irc": "ircs://irc.libera.chat:6697/composer",
+ "issues": "https://github.com/composer/semver/issues",
+ "source": "https://github.com/composer/semver/tree/3.4.3"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-19T14:15:21+00:00"
+ },
+ {
+ "name": "composer/xdebug-handler",
+ "version": "3.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/composer/xdebug-handler.git",
+ "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef",
+ "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef",
+ "shasum": ""
+ },
+ "require": {
+ "composer/pcre": "^1 || ^2 || ^3",
+ "php": "^7.2.5 || ^8.0",
+ "psr/log": "^1 || ^2 || ^3"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.0",
+ "phpstan/phpstan-strict-rules": "^1.1",
+ "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Composer\\XdebugHandler\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "John Stevenson",
+ "email": "john-stevenson@blueyonder.co.uk"
+ }
+ ],
+ "description": "Restarts a process without Xdebug.",
+ "keywords": [
+ "Xdebug",
+ "performance"
+ ],
+ "support": {
+ "irc": "ircs://irc.libera.chat:6697/composer",
+ "issues": "https://github.com/composer/xdebug-handler/issues",
+ "source": "https://github.com/composer/xdebug-handler/tree/3.0.5"
+ },
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/composer",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-05-06T16:37:16+00:00"
+ },
+ {
+ "name": "danog/advanced-json-rpc",
+ "version": "v3.2.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/danog/php-advanced-json-rpc.git",
+ "reference": "aadb1c4068a88c3d0530cfe324b067920661efcb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/danog/php-advanced-json-rpc/zipball/aadb1c4068a88c3d0530cfe324b067920661efcb",
+ "reference": "aadb1c4068a88c3d0530cfe324b067920661efcb",
+ "shasum": ""
+ },
+ "require": {
+ "netresearch/jsonmapper": "^5",
+ "php": ">=8.1",
+ "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0"
+ },
+ "replace": {
+ "felixfbecker/php-advanced-json-rpc": "^3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "AdvancedJsonRpc\\": "lib/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "ISC"
+ ],
+ "authors": [
+ {
+ "name": "Felix Becker",
+ "email": "felix.b@outlook.com"
+ },
+ {
+ "name": "Daniil Gentili",
+ "email": "daniil@daniil.it"
+ }
+ ],
+ "description": "A more advanced JSONRPC implementation",
+ "support": {
+ "issues": "https://github.com/danog/php-advanced-json-rpc/issues",
+ "source": "https://github.com/danog/php-advanced-json-rpc/tree/v3.2.2"
+ },
+ "time": "2025-02-14T10:55:15+00:00"
+ },
+ {
+ "name": "daverandom/libdns",
+ "version": "v2.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/DaveRandom/LibDNS.git",
+ "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/DaveRandom/LibDNS/zipball/b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a",
+ "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "Required for IDN support"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/functions.php"
+ ],
+ "psr-4": {
+ "LibDNS\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "DNS protocol implementation written in pure PHP",
+ "keywords": [
+ "dns"
+ ],
+ "support": {
+ "issues": "https://github.com/DaveRandom/LibDNS/issues",
+ "source": "https://github.com/DaveRandom/LibDNS/tree/v2.1.0"
+ },
+ "time": "2024-04-12T12:12:48+00:00"
+ },
+ {
+ "name": "dealerdirect/phpcodesniffer-composer-installer",
+ "version": "v1.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCSStandards/composer-installer.git",
+ "reference": "6e0fa428497bf560152ee73ffbb8af5c6a56b0dd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/6e0fa428497bf560152ee73ffbb8af5c6a56b0dd",
+ "reference": "6e0fa428497bf560152ee73ffbb8af5c6a56b0dd",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^2.2",
+ "php": ">=5.4",
+ "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0"
+ },
+ "require-dev": {
+ "composer/composer": "^2.2",
+ "ext-json": "*",
+ "ext-zip": "*",
+ "php-parallel-lint/php-parallel-lint": "^1.4.0",
+ "phpcompatibility/php-compatibility": "^9.0",
+ "yoast/phpunit-polyfills": "^1.0"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Franck Nijhof",
+ "email": "opensource@frenck.dev",
+ "homepage": "https://frenck.dev",
+ "role": "Open source developer"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors"
+ }
+ ],
+ "description": "PHP_CodeSniffer Standards Composer Installer Plugin",
+ "keywords": [
+ "PHPCodeSniffer",
+ "PHP_CodeSniffer",
+ "code quality",
+ "codesniffer",
+ "composer",
+ "installer",
+ "phpcbf",
+ "phpcs",
+ "plugin",
+ "qa",
+ "quality",
+ "standard",
+ "standards",
+ "style guide",
+ "stylecheck",
+ "tests"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPCSStandards/composer-installer/issues",
+ "security": "https://github.com/PHPCSStandards/composer-installer/security/policy",
+ "source": "https://github.com/PHPCSStandards/composer-installer"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/PHPCSStandards",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/jrfnl",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/phpcsstandards",
+ "type": "thanks_dev"
+ }
+ ],
+ "time": "2025-06-27T17:24:01+00:00"
+ },
+ {
+ "name": "dnoegel/php-xdg-base-dir",
+ "version": "v0.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/dnoegel/php-xdg-base-dir.git",
+ "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd",
+ "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "XdgBaseDir\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "implementation of xdg base directory specification for php",
+ "support": {
+ "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues",
+ "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1"
+ },
+ "time": "2019-12-04T15:06:13+00:00"
+ },
+ {
+ "name": "doctrine/deprecations",
+ "version": "1.1.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/deprecations.git",
+ "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38",
+ "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "conflict": {
+ "phpunit/phpunit": "<=7.5 || >=13"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^9 || ^12 || ^13",
+ "phpstan/phpstan": "1.4.10 || 2.1.11",
+ "phpstan/phpstan-phpunit": "^1.0 || ^2",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12",
+ "psr/log": "^1 || ^2 || ^3"
+ },
+ "suggest": {
+ "psr/log": "Allows logging deprecations via PSR-3 logger implementation"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Deprecations\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
+ "homepage": "https://www.doctrine-project.org/",
+ "support": {
+ "issues": "https://github.com/doctrine/deprecations/issues",
+ "source": "https://github.com/doctrine/deprecations/tree/1.1.5"
+ },
+ "time": "2025-04-07T20:06:18+00:00"
+ },
+ {
+ "name": "felixfbecker/language-server-protocol",
+ "version": "v1.5.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/felixfbecker/php-language-server-protocol.git",
+ "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/a9e113dbc7d849e35b8776da39edaf4313b7b6c9",
+ "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "*",
+ "squizlabs/php_codesniffer": "^3.1",
+ "vimeo/psalm": "^4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "LanguageServerProtocol\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "ISC"
+ ],
+ "authors": [
+ {
+ "name": "Felix Becker",
+ "email": "felix.b@outlook.com"
+ }
+ ],
+ "description": "PHP classes for the Language Server Protocol",
+ "keywords": [
+ "language",
+ "microsoft",
+ "php",
+ "server"
+ ],
+ "support": {
+ "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues",
+ "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.3"
+ },
+ "time": "2024-04-30T00:40:11+00:00"
+ },
+ {
+ "name": "fidry/cpu-core-counter",
+ "version": "1.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theofidry/cpu-core-counter.git",
+ "reference": "8520451a140d3f46ac33042715115e290cf5785f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f",
+ "reference": "8520451a140d3f46ac33042715115e290cf5785f",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "fidry/makefile": "^0.2.0",
+ "fidry/php-cs-fixer-config": "^1.1.2",
+ "phpstan/extension-installer": "^1.2.0",
+ "phpstan/phpstan": "^1.9.2",
+ "phpstan/phpstan-deprecation-rules": "^1.0.0",
+ "phpstan/phpstan-phpunit": "^1.2.2",
+ "phpstan/phpstan-strict-rules": "^1.4.4",
+ "phpunit/phpunit": "^8.5.31 || ^9.5.26",
+ "webmozarts/strict-phpunit": "^7.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Fidry\\CpuCoreCounter\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Théo FIDRY",
+ "email": "theo.fidry@gmail.com"
+ }
+ ],
+ "description": "Tiny utility to get the number of CPU cores.",
+ "keywords": [
+ "CPU",
+ "core"
+ ],
+ "support": {
+ "issues": "https://github.com/theofidry/cpu-core-counter/issues",
+ "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theofidry",
+ "type": "github"
+ }
+ ],
+ "time": "2024-08-06T10:04:20+00:00"
+ },
+ {
+ "name": "kelunik/certificate",
+ "version": "v1.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/kelunik/certificate.git",
+ "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/kelunik/certificate/zipball/7e00d498c264d5eb4f78c69f41c8bd6719c0199e",
+ "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e",
+ "shasum": ""
+ },
+ "require": {
+ "ext-openssl": "*",
+ "php": ">=7.0"
+ },
+ "require-dev": {
+ "amphp/php-cs-fixer-config": "^2",
+ "phpunit/phpunit": "^6 | 7 | ^8 | ^9"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Kelunik\\Certificate\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ }
+ ],
+ "description": "Access certificate details and transform between different formats.",
+ "keywords": [
+ "DER",
+ "certificate",
+ "certificates",
+ "openssl",
+ "pem",
+ "x509"
+ ],
+ "support": {
+ "issues": "https://github.com/kelunik/certificate/issues",
+ "source": "https://github.com/kelunik/certificate/tree/v1.1.3"
+ },
+ "time": "2023-02-03T21:26:53+00:00"
+ },
+ {
+ "name": "laminas/laminas-coding-standard",
+ "version": "3.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laminas/laminas-coding-standard.git",
+ "reference": "d4412caba9ed16c93cdcf301759f5ee71f9d9aea"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laminas/laminas-coding-standard/zipball/d4412caba9ed16c93cdcf301759f5ee71f9d9aea",
+ "reference": "d4412caba9ed16c93cdcf301759f5ee71f9d9aea",
+ "shasum": ""
+ },
+ "require": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || ^1.0",
+ "php": "^7.4 || ^8.0",
+ "slevomat/coding-standard": "^8.15.0",
+ "squizlabs/php_codesniffer": "^3.10",
+ "webimpress/coding-standard": "^1.3"
+ },
+ "type": "phpcodesniffer-standard",
+ "autoload": {
+ "psr-4": {
+ "LaminasCodingStandard\\": "src/LaminasCodingStandard/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "Laminas Coding Standard",
+ "homepage": "https://laminas.dev",
+ "keywords": [
+ "Coding Standard",
+ "laminas"
+ ],
+ "support": {
+ "chat": "https://laminas.dev/chat",
+ "docs": "https://docs.laminas.dev/laminas-coding-standard/",
+ "forum": "https://discourse.laminas.dev",
+ "issues": "https://github.com/laminas/laminas-coding-standard/issues",
+ "rss": "https://github.com/laminas/laminas-coding-standard/releases.atom",
+ "source": "https://github.com/laminas/laminas-coding-standard"
+ },
+ "funding": [
+ {
+ "url": "https://funding.communitybridge.org/projects/laminas-project",
+ "type": "community_bridge"
+ }
+ ],
+ "time": "2025-05-13T08:37:04+00:00"
+ },
+ {
+ "name": "league/uri",
+ "version": "7.5.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/uri.git",
+ "reference": "81fb5145d2644324614cc532b28efd0215bda430"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/uri/zipball/81fb5145d2644324614cc532b28efd0215bda430",
+ "reference": "81fb5145d2644324614cc532b28efd0215bda430",
+ "shasum": ""
+ },
+ "require": {
+ "league/uri-interfaces": "^7.5",
+ "php": "^8.1"
+ },
+ "conflict": {
+ "league/uri-schemes": "^1.0"
+ },
+ "suggest": {
+ "ext-bcmath": "to improve IPV4 host parsing",
+ "ext-fileinfo": "to create Data URI from file contennts",
+ "ext-gmp": "to improve IPV4 host parsing",
+ "ext-intl": "to handle IDN host with the best performance",
+ "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain",
+ "league/uri-components": "Needed to easily manipulate URI objects components",
+ "php-64bit": "to improve IPV4 host parsing",
+ "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "7.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\Uri\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ignace Nyamagana Butera",
+ "email": "nyamsprod@gmail.com",
+ "homepage": "https://nyamsprod.com"
+ }
+ ],
+ "description": "URI manipulation library",
+ "homepage": "https://uri.thephpleague.com",
+ "keywords": [
+ "data-uri",
+ "file-uri",
+ "ftp",
+ "hostname",
+ "http",
+ "https",
+ "middleware",
+ "parse_str",
+ "parse_url",
+ "psr-7",
+ "query-string",
+ "querystring",
+ "rfc3986",
+ "rfc3987",
+ "rfc6570",
+ "uri",
+ "uri-template",
+ "url",
+ "ws"
+ ],
+ "support": {
+ "docs": "https://uri.thephpleague.com",
+ "forum": "https://thephpleague.slack.com",
+ "issues": "https://github.com/thephpleague/uri-src/issues",
+ "source": "https://github.com/thephpleague/uri/tree/7.5.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/nyamsprod",
+ "type": "github"
+ }
+ ],
+ "time": "2024-12-08T08:40:02+00:00"
+ },
+ {
+ "name": "league/uri-interfaces",
+ "version": "7.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/uri-interfaces.git",
+ "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/08cfc6c4f3d811584fb09c37e2849e6a7f9b0742",
+ "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742",
+ "shasum": ""
+ },
+ "require": {
+ "ext-filter": "*",
+ "php": "^8.1",
+ "psr/http-factory": "^1",
+ "psr/http-message": "^1.1 || ^2.0"
+ },
+ "suggest": {
+ "ext-bcmath": "to improve IPV4 host parsing",
+ "ext-gmp": "to improve IPV4 host parsing",
+ "ext-intl": "to handle IDN host with the best performance",
+ "php-64bit": "to improve IPV4 host parsing",
+ "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "7.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\Uri\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ignace Nyamagana Butera",
+ "email": "nyamsprod@gmail.com",
+ "homepage": "https://nyamsprod.com"
+ }
+ ],
+ "description": "Common interfaces and classes for URI representation and interaction",
+ "homepage": "https://uri.thephpleague.com",
+ "keywords": [
+ "data-uri",
+ "file-uri",
+ "ftp",
+ "hostname",
+ "http",
+ "https",
+ "parse_str",
+ "parse_url",
+ "psr-7",
+ "query-string",
+ "querystring",
+ "rfc3986",
+ "rfc3987",
+ "rfc6570",
+ "uri",
+ "url",
+ "ws"
+ ],
+ "support": {
+ "docs": "https://uri.thephpleague.com",
+ "forum": "https://thephpleague.slack.com",
+ "issues": "https://github.com/thephpleague/uri-src/issues",
+ "source": "https://github.com/thephpleague/uri-interfaces/tree/7.5.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/nyamsprod",
+ "type": "github"
+ }
+ ],
+ "time": "2024-12-08T08:18:47+00:00"
+ },
+ {
+ "name": "myclabs/deep-copy",
+ "version": "1.13.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/DeepCopy.git",
+ "reference": "faed855a7b5f4d4637717c2b3863e277116beb36"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/faed855a7b5f4d4637717c2b3863e277116beb36",
+ "reference": "faed855a7b5f4d4637717c2b3863e277116beb36",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "conflict": {
+ "doctrine/collections": "<1.6.8",
+ "doctrine/common": "<2.13.3 || >=3 <3.2.2"
+ },
+ "require-dev": {
+ "doctrine/collections": "^1.6.8",
+ "doctrine/common": "^2.13.3 || ^3.2.2",
+ "phpspec/prophecy": "^1.10",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/DeepCopy/deep_copy.php"
+ ],
+ "psr-4": {
+ "DeepCopy\\": "src/DeepCopy/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Create deep copies (clones) of your objects",
+ "keywords": [
+ "clone",
+ "copy",
+ "duplicate",
+ "object",
+ "object graph"
+ ],
+ "support": {
+ "issues": "https://github.com/myclabs/DeepCopy/issues",
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.13.3"
+ },
+ "funding": [
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-07-05T12:25:42+00:00"
+ },
+ {
+ "name": "netresearch/jsonmapper",
+ "version": "v5.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/cweiske/jsonmapper.git",
+ "reference": "8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c",
+ "reference": "8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-pcre": "*",
+ "ext-reflection": "*",
+ "ext-spl": "*",
+ "php": ">=7.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0",
+ "squizlabs/php_codesniffer": "~3.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "JsonMapper": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "OSL-3.0"
+ ],
+ "authors": [
+ {
+ "name": "Christian Weiske",
+ "email": "cweiske@cweiske.de",
+ "homepage": "http://github.com/cweiske/jsonmapper/",
+ "role": "Developer"
+ }
+ ],
+ "description": "Map nested JSON structures onto PHP classes",
+ "support": {
+ "email": "cweiske@cweiske.de",
+ "issues": "https://github.com/cweiske/jsonmapper/issues",
+ "source": "https://github.com/cweiske/jsonmapper/tree/v5.0.0"
+ },
+ "time": "2024-09-08T10:20:00+00:00"
+ },
+ {
+ "name": "phar-io/manifest",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/manifest.git",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-phar": "*",
+ "ext-xmlwriter": "*",
+ "phar-io/version": "^3.0.1",
+ "php": "^7.2 || ^8.0"
+ },
+ "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": "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)",
+ "support": {
+ "issues": "https://github.com/phar-io/manifest/issues",
+ "source": "https://github.com/phar-io/manifest/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-03T12:33:53+00:00"
+ },
+ {
+ "name": "phar-io/version",
+ "version": "3.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/version.git",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "shasum": ""
+ },
+ "require": {
+ "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"
+ },
+ {
+ "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",
+ "support": {
+ "issues": "https://github.com/phar-io/version/issues",
+ "source": "https://github.com/phar-io/version/tree/3.2.1"
+ },
+ "time": "2022-02-21T01:04:05+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"
+ ],
+ "support": {
+ "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues",
+ "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x"
+ },
+ "time": "2020-06-27T09:03:43+00:00"
+ },
+ {
+ "name": "phpdocumentor/reflection-docblock",
+ "version": "5.6.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
+ "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/92dde6a5919e34835c506ac8c523ef095a95ed62",
+ "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/deprecations": "^1.1",
+ "ext-filter": "*",
+ "php": "^7.4 || ^8.0",
+ "phpdocumentor/reflection-common": "^2.2",
+ "phpdocumentor/type-resolver": "^1.7",
+ "phpstan/phpdoc-parser": "^1.7|^2.0",
+ "webmozart/assert": "^1.9.1"
+ },
+ "require-dev": {
+ "mockery/mockery": "~1.3.5 || ~1.6.0",
+ "phpstan/extension-installer": "^1.1",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-mockery": "^1.1",
+ "phpstan/phpstan-webmozart-assert": "^1.2",
+ "phpunit/phpunit": "^9.5",
+ "psalm/phar": "^5.26"
+ },
+ "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": "opensource@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.",
+ "support": {
+ "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
+ "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.2"
+ },
+ "time": "2025-04-13T19:20:35+00:00"
+ },
+ {
+ "name": "phpdocumentor/type-resolver",
+ "version": "1.10.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/TypeResolver.git",
+ "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a",
+ "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/deprecations": "^1.0",
+ "php": "^7.3 || ^8.0",
+ "phpdocumentor/reflection-common": "^2.0",
+ "phpstan/phpdoc-parser": "^1.18|^2.0"
+ },
+ "require-dev": {
+ "ext-tokenizer": "*",
+ "phpbench/phpbench": "^1.2",
+ "phpstan/extension-installer": "^1.1",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-phpunit": "^1.1",
+ "phpunit/phpunit": "^9.5",
+ "rector/rector": "^0.13.9",
+ "vimeo/psalm": "^4.25"
+ },
+ "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",
+ "support": {
+ "issues": "https://github.com/phpDocumentor/TypeResolver/issues",
+ "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0"
+ },
+ "time": "2024-11-09T15:12:26+00:00"
+ },
+ {
+ "name": "phpstan/phpdoc-parser",
+ "version": "2.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpdoc-parser.git",
+ "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/9b30d6fd026b2c132b3985ce6b23bec09ab3aa68",
+ "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/annotations": "^2.0",
+ "nikic/php-parser": "^5.3.0",
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/extension-installer": "^1.0",
+ "phpstan/phpstan": "^2.0",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpstan/phpstan-strict-rules": "^2.0",
+ "phpunit/phpunit": "^9.6",
+ "symfony/process": "^5.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\PhpDocParser\\": [
+ "src/"
+ ]
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPDoc parser with support for nullable, intersection and generic types",
+ "support": {
+ "issues": "https://github.com/phpstan/phpdoc-parser/issues",
+ "source": "https://github.com/phpstan/phpdoc-parser/tree/2.1.0"
+ },
+ "time": "2025-02-19T13:28:12+00:00"
+ },
+ {
+ "name": "phpunit/php-code-coverage",
+ "version": "11.0.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "1a800a7446add2d79cc6b3c01c45381810367d76"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/1a800a7446add2d79cc6b3c01c45381810367d76",
+ "reference": "1a800a7446add2d79cc6b3c01c45381810367d76",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-xmlwriter": "*",
+ "nikic/php-parser": "^5.4.0",
+ "php": ">=8.2",
+ "phpunit/php-file-iterator": "^5.1.0",
+ "phpunit/php-text-template": "^4.0.1",
+ "sebastian/code-unit-reverse-lookup": "^4.0.1",
+ "sebastian/complexity": "^4.0.1",
+ "sebastian/environment": "^7.2.0",
+ "sebastian/lines-of-code": "^3.0.1",
+ "sebastian/version": "^5.0.2",
+ "theseer/tokenizer": "^1.2.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.5.2"
+ },
+ "suggest": {
+ "ext-pcov": "PHP extension that provides line coverage",
+ "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "11.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 provides collection, processing, and rendering functionality for PHP code coverage information.",
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+ "keywords": [
+ "coverage",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+ "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/show"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-06-18T08:56:18+00:00"
+ },
+ {
+ "name": "phpunit/php-file-iterator",
+ "version": "5.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+ "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6",
+ "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.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": "FilterIterator implementation that filters files based on a list of suffixes.",
+ "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+ "keywords": [
+ "filesystem",
+ "iterator"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+ "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-08-27T05:02:59+00:00"
+ },
+ {
+ "name": "phpunit/php-invoker",
+ "version": "5.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-invoker.git",
+ "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2",
+ "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "ext-pcntl": "*",
+ "phpunit/phpunit": "^11.0"
+ },
+ "suggest": {
+ "ext-pcntl": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.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": "Invoke callables with a timeout",
+ "homepage": "https://github.com/sebastianbergmann/php-invoker/",
+ "keywords": [
+ "process"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
+ "security": "https://github.com/sebastianbergmann/php-invoker/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:07:44+00:00"
+ },
+ {
+ "name": "phpunit/php-text-template",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
+ "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964",
+ "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.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": "Simple template engine.",
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+ "keywords": [
+ "template"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+ "security": "https://github.com/sebastianbergmann/php-text-template/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:08:43+00:00"
+ },
+ {
+ "name": "phpunit/php-timer",
+ "version": "7.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
+ "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3",
+ "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "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": "Utility class for timing",
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
+ "keywords": [
+ "timer"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+ "security": "https://github.com/sebastianbergmann/php-timer/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:09:35+00:00"
+ },
+ {
+ "name": "phpunit/phpunit",
+ "version": "11.5.26",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "4ad8fe263a0b55b54a8028c38a18e3c5bef312e0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4ad8fe263a0b55b54a8028c38a18e3c5bef312e0",
+ "reference": "4ad8fe263a0b55b54a8028c38a18e3c5bef312e0",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-xml": "*",
+ "ext-xmlwriter": "*",
+ "myclabs/deep-copy": "^1.13.1",
+ "phar-io/manifest": "^2.0.4",
+ "phar-io/version": "^3.2.1",
+ "php": ">=8.2",
+ "phpunit/php-code-coverage": "^11.0.10",
+ "phpunit/php-file-iterator": "^5.1.0",
+ "phpunit/php-invoker": "^5.0.1",
+ "phpunit/php-text-template": "^4.0.1",
+ "phpunit/php-timer": "^7.0.1",
+ "sebastian/cli-parser": "^3.0.2",
+ "sebastian/code-unit": "^3.0.3",
+ "sebastian/comparator": "^6.3.1",
+ "sebastian/diff": "^6.0.2",
+ "sebastian/environment": "^7.2.1",
+ "sebastian/exporter": "^6.3.0",
+ "sebastian/global-state": "^7.0.2",
+ "sebastian/object-enumerator": "^6.0.1",
+ "sebastian/type": "^5.1.2",
+ "sebastian/version": "^5.0.2",
+ "staabm/side-effects-detector": "^1.0.5"
+ },
+ "suggest": {
+ "ext-soap": "To be able to generate mocks based on WSDL files"
+ },
+ "bin": [
+ "phpunit"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "11.5-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Framework/Assert/Functions.php"
+ ],
+ "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"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+ "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.26"
+ },
+ "funding": [
+ {
+ "url": "https://phpunit.de/sponsors.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-07-04T05:58:21+00:00"
+ },
+ {
+ "name": "psalm/plugin-phpunit",
+ "version": "0.19.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/psalm/psalm-plugin-phpunit.git",
+ "reference": "143f9d5e049fffcdbc0da3fbb99f6149f9d3e2dc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/psalm/psalm-plugin-phpunit/zipball/143f9d5e049fffcdbc0da3fbb99f6149f9d3e2dc",
+ "reference": "143f9d5e049fffcdbc0da3fbb99f6149f9d3e2dc",
+ "shasum": ""
+ },
+ "require": {
+ "ext-simplexml": "*",
+ "php": ">=8.1",
+ "vimeo/psalm": "dev-master || ^6.10.0"
+ },
+ "conflict": {
+ "phpspec/prophecy": "<1.20.0",
+ "phpspec/prophecy-phpunit": "<2.3.0",
+ "phpunit/phpunit": "<8.5.1"
+ },
+ "require-dev": {
+ "php": "^7.3 || ^8.0",
+ "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0",
+ "squizlabs/php_codesniffer": "^3.3.1",
+ "weirdan/prophecy-shim": "^1.0 || ^2.0"
+ },
+ "type": "psalm-plugin",
+ "extra": {
+ "psalm": {
+ "pluginClass": "Psalm\\PhpUnitPlugin\\Plugin"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psalm\\PhpUnitPlugin\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Matt Brown",
+ "email": "github@muglug.com"
+ }
+ ],
+ "description": "Psalm plugin for PHPUnit",
+ "support": {
+ "issues": "https://github.com/psalm/psalm-plugin-phpunit/issues",
+ "source": "https://github.com/psalm/psalm-plugin-phpunit/tree/0.19.5"
+ },
+ "time": "2025-03-31T18:49:55+00:00"
+ },
+ {
+ "name": "psr/http-factory",
+ "version": "1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-factory.git",
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1",
+ "psr/http-message": "^1.0 || ^2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
+ "keywords": [
+ "factory",
+ "http",
+ "message",
+ "psr",
+ "psr-17",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-factory"
+ },
+ "time": "2024-04-15T12:06:14+00:00"
+ },
+ {
+ "name": "psr/http-message",
+ "version": "2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+ "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-message/tree/2.0"
+ },
+ "time": "2023-04-04T09:54:51+00:00"
+ },
+ {
+ "name": "psr/log",
+ "version": "3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
+ "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Log\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for logging libraries",
+ "homepage": "https://github.com/php-fig/log",
+ "keywords": [
+ "log",
+ "psr",
+ "psr-3"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/log/tree/3.0.2"
+ },
+ "time": "2024-09-11T13:17:53+00:00"
+ },
+ {
+ "name": "revolt/event-loop",
+ "version": "v1.0.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/revoltphp/event-loop.git",
+ "reference": "09bf1bf7f7f574453efe43044b06fafe12216eb3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/revoltphp/event-loop/zipball/09bf1bf7f7f574453efe43044b06fafe12216eb3",
+ "reference": "09bf1bf7f7f574453efe43044b06fafe12216eb3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "ext-json": "*",
+ "jetbrains/phpstorm-stubs": "^2019.3",
+ "phpunit/phpunit": "^9",
+ "psalm/phar": "^5.15"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Revolt\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Aaron Piotrowski",
+ "email": "aaron@trowski.com"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "email": "ceesjank@gmail.com"
+ },
+ {
+ "name": "Christian Lück",
+ "email": "christian@clue.engineering"
+ },
+ {
+ "name": "Niklas Keller",
+ "email": "me@kelunik.com"
+ }
+ ],
+ "description": "Rock-solid event loop for concurrent PHP applications.",
+ "keywords": [
+ "async",
+ "asynchronous",
+ "concurrency",
+ "event",
+ "event-loop",
+ "non-blocking",
+ "scheduler"
+ ],
+ "support": {
+ "issues": "https://github.com/revoltphp/event-loop/issues",
+ "source": "https://github.com/revoltphp/event-loop/tree/v1.0.7"
+ },
+ "time": "2025-01-25T19:27:39+00:00"
+ },
+ {
+ "name": "sebastian/cli-parser",
+ "version": "3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/cli-parser.git",
+ "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180",
+ "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.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 for parsing CLI options",
+ "homepage": "https://github.com/sebastianbergmann/cli-parser",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
+ "security": "https://github.com/sebastianbergmann/cli-parser/security/policy",
+ "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:41:36+00:00"
+ },
+ {
+ "name": "sebastian/code-unit",
+ "version": "3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit.git",
+ "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64",
+ "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/code-unit",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit/issues",
+ "security": "https://github.com/sebastianbergmann/code-unit/security/policy",
+ "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-03-19T07:56:08+00:00"
+ },
+ {
+ "name": "sebastian/code-unit-reverse-lookup",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+ "reference": "183a9b2632194febd219bb9246eee421dad8d45e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e",
+ "reference": "183a9b2632194febd219bb9246eee421dad8d45e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-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/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
+ "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy",
+ "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:45:54+00:00"
+ },
+ {
+ "name": "sebastian/comparator",
+ "version": "6.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/comparator.git",
+ "reference": "24b8fbc2c8e201bb1308e7b05148d6ab393b6959"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/24b8fbc2c8e201bb1308e7b05148d6ab393b6959",
+ "reference": "24b8fbc2c8e201bb1308e7b05148d6ab393b6959",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-mbstring": "*",
+ "php": ">=8.2",
+ "sebastian/diff": "^6.0",
+ "sebastian/exporter": "^6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.4"
+ },
+ "suggest": {
+ "ext-bcmath": "For comparing BcMath\\Number objects"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.3-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": "Bernhard Schussek",
+ "email": "bschussek@2bepublished.at"
+ }
+ ],
+ "description": "Provides the functionality to compare PHP values for equality",
+ "homepage": "https://github.com/sebastianbergmann/comparator",
+ "keywords": [
+ "comparator",
+ "compare",
+ "equality"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/comparator/issues",
+ "security": "https://github.com/sebastianbergmann/comparator/security/policy",
+ "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-03-07T06:57:01+00:00"
+ },
+ {
+ "name": "sebastian/complexity",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/complexity.git",
+ "reference": "ee41d384ab1906c68852636b6de493846e13e5a0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0",
+ "reference": "ee41d384ab1906c68852636b6de493846e13e5a0",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^5.0",
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.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 for calculating the complexity of PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/complexity",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/complexity/issues",
+ "security": "https://github.com/sebastianbergmann/complexity/security/policy",
+ "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:49:50+00:00"
+ },
+ {
+ "name": "sebastian/diff",
+ "version": "6.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/diff.git",
+ "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544",
+ "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0",
+ "symfony/process": "^4.2 || ^5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Kore Nordmann",
+ "email": "mail@kore-nordmann.de"
+ }
+ ],
+ "description": "Diff implementation",
+ "homepage": "https://github.com/sebastianbergmann/diff",
+ "keywords": [
+ "diff",
+ "udiff",
+ "unidiff",
+ "unified diff"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/diff/issues",
+ "security": "https://github.com/sebastianbergmann/diff/security/policy",
+ "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:53:05+00:00"
+ },
+ {
+ "name": "sebastian/environment",
+ "version": "7.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/environment.git",
+ "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4",
+ "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.3"
+ },
+ "suggest": {
+ "ext-posix": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.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": "https://github.com/sebastianbergmann/environment",
+ "keywords": [
+ "Xdebug",
+ "environment",
+ "hhvm"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/environment/issues",
+ "security": "https://github.com/sebastianbergmann/environment/security/policy",
+ "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/environment",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-05-21T11:55:47+00:00"
+ },
+ {
+ "name": "sebastian/exporter",
+ "version": "6.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/exporter.git",
+ "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/3473f61172093b2da7de1fb5782e1f24cc036dc3",
+ "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": ">=8.2",
+ "sebastian/recursion-context": "^6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.1-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": "https://www.github.com/sebastianbergmann/exporter",
+ "keywords": [
+ "export",
+ "exporter"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/exporter/issues",
+ "security": "https://github.com/sebastianbergmann/exporter/security/policy",
+ "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-12-05T09:17:50+00:00"
+ },
+ {
+ "name": "sebastian/global-state",
+ "version": "7.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/global-state.git",
+ "reference": "3be331570a721f9a4b5917f4209773de17f747d7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7",
+ "reference": "3be331570a721f9a4b5917f4209773de17f747d7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "sebastian/object-reflector": "^4.0",
+ "sebastian/recursion-context": "^6.0"
+ },
+ "require-dev": {
+ "ext-dom": "*",
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.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": "https://www.github.com/sebastianbergmann/global-state",
+ "keywords": [
+ "global state"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/global-state/issues",
+ "security": "https://github.com/sebastianbergmann/global-state/security/policy",
+ "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:57:36+00:00"
+ },
+ {
+ "name": "sebastian/lines-of-code",
+ "version": "3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+ "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a",
+ "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^5.0",
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.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 for counting the lines of code in PHP source code",
+ "homepage": "https://github.com/sebastianbergmann/lines-of-code",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
+ "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy",
+ "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T04:58:38+00:00"
+ },
+ {
+ "name": "sebastian/object-enumerator",
+ "version": "6.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+ "reference": "f5b498e631a74204185071eb41f33f38d64608aa"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa",
+ "reference": "f5b498e631a74204185071eb41f33f38d64608aa",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "sebastian/object-reflector": "^4.0",
+ "sebastian/recursion-context": "^6.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-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/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+ "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy",
+ "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:00:13+00:00"
+ },
+ {
+ "name": "sebastian/object-reflector",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-reflector.git",
+ "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9",
+ "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-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/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+ "security": "https://github.com/sebastianbergmann/object-reflector/security/policy",
+ "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:01:32+00:00"
+ },
+ {
+ "name": "sebastian/recursion-context",
+ "version": "6.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
+ "reference": "694d156164372abbd149a4b85ccda2e4670c0e16"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/694d156164372abbd149a4b85ccda2e4670c0e16",
+ "reference": "694d156164372abbd149a4b85ccda2e4670c0e16",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-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": "Adam Harvey",
+ "email": "aharvey@php.net"
+ }
+ ],
+ "description": "Provides functionality to recursively process PHP variables",
+ "homepage": "https://github.com/sebastianbergmann/recursion-context",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+ "security": "https://github.com/sebastianbergmann/recursion-context/security/policy",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-07-03T05:10:34+00:00"
+ },
+ {
+ "name": "sebastian/type",
+ "version": "5.1.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/type.git",
+ "reference": "a8a7e30534b0eb0c77cd9d07e82de1a114389f5e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/a8a7e30534b0eb0c77cd9d07e82de1a114389f5e",
+ "reference": "a8a7e30534b0eb0c77cd9d07e82de1a114389f5e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.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",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/type/issues",
+ "security": "https://github.com/sebastianbergmann/type/security/policy",
+ "source": "https://github.com/sebastianbergmann/type/tree/5.1.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-03-18T13:35:50+00:00"
+ },
+ {
+ "name": "sebastian/version",
+ "version": "5.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/version.git",
+ "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874",
+ "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.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 helps with managing the version number of Git-hosted PHP projects",
+ "homepage": "https://github.com/sebastianbergmann/version",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/version/issues",
+ "security": "https://github.com/sebastianbergmann/version/security/policy",
+ "source": "https://github.com/sebastianbergmann/version/tree/5.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-10-09T05:16:32+00:00"
+ },
+ {
+ "name": "slevomat/coding-standard",
+ "version": "8.19.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/slevomat/coding-standard.git",
+ "reference": "458d665acd49009efebd7e0cb385d71ae9ac3220"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/458d665acd49009efebd7e0cb385d71ae9ac3220",
+ "reference": "458d665acd49009efebd7e0cb385d71ae9ac3220",
+ "shasum": ""
+ },
+ "require": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0",
+ "php": "^7.4 || ^8.0",
+ "phpstan/phpdoc-parser": "^2.1.0",
+ "squizlabs/php_codesniffer": "^3.13.0"
+ },
+ "require-dev": {
+ "phing/phing": "3.0.1",
+ "php-parallel-lint/php-parallel-lint": "1.4.0",
+ "phpstan/phpstan": "2.1.17",
+ "phpstan/phpstan-deprecation-rules": "2.0.3",
+ "phpstan/phpstan-phpunit": "2.0.6",
+ "phpstan/phpstan-strict-rules": "2.0.4",
+ "phpunit/phpunit": "9.6.8|10.5.45|11.4.4|11.5.21|12.1.3"
+ },
+ "type": "phpcodesniffer-standard",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "8.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "SlevomatCodingStandard\\": "SlevomatCodingStandard/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
+ "keywords": [
+ "dev",
+ "phpcs"
+ ],
+ "support": {
+ "issues": "https://github.com/slevomat/coding-standard/issues",
+ "source": "https://github.com/slevomat/coding-standard/tree/8.19.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/kukulich",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-06-09T17:53:57+00:00"
+ },
+ {
+ "name": "spatie/array-to-xml",
+ "version": "3.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/spatie/array-to-xml.git",
+ "reference": "7dcfc67d60b0272926dabad1ec01f6b8a5fb5e67"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/7dcfc67d60b0272926dabad1ec01f6b8a5fb5e67",
+ "reference": "7dcfc67d60b0272926dabad1ec01f6b8a5fb5e67",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "php": "^8.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "^1.2",
+ "pestphp/pest": "^1.21",
+ "spatie/pest-plugin-snapshots": "^1.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Spatie\\ArrayToXml\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Freek Van der Herten",
+ "email": "freek@spatie.be",
+ "homepage": "https://freek.dev",
+ "role": "Developer"
+ }
+ ],
+ "description": "Convert an array to xml",
+ "homepage": "https://github.com/spatie/array-to-xml",
+ "keywords": [
+ "array",
+ "convert",
+ "xml"
+ ],
+ "support": {
+ "source": "https://github.com/spatie/array-to-xml/tree/3.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://spatie.be/open-source/support-us",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/spatie",
+ "type": "github"
+ }
+ ],
+ "time": "2024-12-16T12:45:15+00:00"
+ },
+ {
+ "name": "squizlabs/php_codesniffer",
+ "version": "3.13.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
+ "reference": "5b5e3821314f947dd040c70f7992a64eac89025c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/5b5e3821314f947dd040c70f7992a64eac89025c",
+ "reference": "5b5e3821314f947dd040c70f7992a64eac89025c",
+ "shasum": ""
+ },
+ "require": {
+ "ext-simplexml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4"
+ },
+ "bin": [
+ "bin/phpcbf",
+ "bin/phpcs"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Greg Sherwood",
+ "role": "Former lead"
+ },
+ {
+ "name": "Juliette Reinders Folmer",
+ "role": "Current lead"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors"
+ }
+ ],
+ "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
+ "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
+ "keywords": [
+ "phpcs",
+ "standards",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues",
+ "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy",
+ "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
+ "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/PHPCSStandards",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/jrfnl",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/phpcsstandards",
+ "type": "thanks_dev"
+ }
+ ],
+ "time": "2025-06-17T22:17:01+00:00"
+ },
+ {
+ "name": "staabm/side-effects-detector",
+ "version": "1.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/staabm/side-effects-detector.git",
+ "reference": "d8334211a140ce329c13726d4a715adbddd0a163"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163",
+ "reference": "d8334211a140ce329c13726d4a715adbddd0a163",
+ "shasum": ""
+ },
+ "require": {
+ "ext-tokenizer": "*",
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/extension-installer": "^1.4.3",
+ "phpstan/phpstan": "^1.12.6",
+ "phpunit/phpunit": "^9.6.21",
+ "symfony/var-dumper": "^5.4.43",
+ "tomasvotruba/type-coverage": "1.0.0",
+ "tomasvotruba/unused-public": "1.0.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "lib/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A static analysis tool to detect side effects in PHP code",
+ "keywords": [
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/staabm/side-effects-detector/issues",
+ "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/staabm",
+ "type": "github"
+ }
+ ],
+ "time": "2024-10-20T05:08:20+00:00"
+ },
+ {
+ "name": "symfony/console",
+ "version": "v7.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/console.git",
+ "reference": "9e27aecde8f506ba0fd1d9989620c04a87697101"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/console/zipball/9e27aecde8f506ba0fd1d9989620c04a87697101",
+ "reference": "9e27aecde8f506ba0fd1d9989620c04a87697101",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/string": "^7.2"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<6.4",
+ "symfony/dotenv": "<6.4",
+ "symfony/event-dispatcher": "<6.4",
+ "symfony/lock": "<6.4",
+ "symfony/process": "<6.4"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0|2.0|3.0"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^6.4|^7.0",
+ "symfony/dependency-injection": "^6.4|^7.0",
+ "symfony/event-dispatcher": "^6.4|^7.0",
+ "symfony/http-foundation": "^6.4|^7.0",
+ "symfony/http-kernel": "^6.4|^7.0",
+ "symfony/lock": "^6.4|^7.0",
+ "symfony/messenger": "^6.4|^7.0",
+ "symfony/process": "^6.4|^7.0",
+ "symfony/stopwatch": "^6.4|^7.0",
+ "symfony/var-dumper": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Console\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Eases the creation of beautiful and testable command line interfaces",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "cli",
+ "command-line",
+ "console",
+ "terminal"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/console/tree/v7.3.1"
+ },
+ "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": "2025-06-27T19:55:54+00:00"
+ },
+ {
+ "name": "symfony/deprecation-contracts",
+ "version": "v3.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62",
+ "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.6-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "function.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "A generic function and convention to trigger deprecation notices",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0"
+ },
+ "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": "2024-09-25T14:21:43+00:00"
+ },
+ {
+ "name": "symfony/filesystem",
+ "version": "v7.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/filesystem.git",
+ "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb",
+ "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-mbstring": "~1.8"
+ },
+ "require-dev": {
+ "symfony/process": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Filesystem\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides basic utilities for the filesystem",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/filesystem/tree/v7.3.0"
+ },
+ "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": "2024-10-25T15:15:23+00:00"
+ },
+ {
+ "name": "symfony/polyfill-ctype",
+ "version": "v1.32.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-ctype.git",
+ "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
+ "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "provide": {
+ "ext-ctype": "*"
+ },
+ "suggest": {
+ "ext-ctype": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Ctype\\": ""
+ }
+ },
+ "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"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0"
+ },
+ "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": "2024-09-09T11:45:10+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-grapheme",
+ "version": "v1.32.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+ "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe",
+ "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's grapheme_* functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "grapheme",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0"
+ },
+ "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": "2024-09-09T11:45:10+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-normalizer",
+ "version": "v1.32.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+ "reference": "3833d7255cc303546435cb650316bff708a1c75c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c",
+ "reference": "3833d7255cc303546435cb650316bff708a1c75c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's Normalizer class and related functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "intl",
+ "normalizer",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0"
+ },
+ "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": "2024-09-09T11:45:10+00:00"
+ },
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.32.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
+ "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
+ "shasum": ""
+ },
+ "require": {
+ "ext-iconv": "*",
+ "php": ">=7.2"
+ },
+ "provide": {
+ "ext-mbstring": "*"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Mbstring extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0"
+ },
+ "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": "2024-12-23T08:48:59+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php84",
+ "version": "v1.32.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php84.git",
+ "reference": "000df7860439609837bbe28670b0be15783b7fbf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/000df7860439609837bbe28670b0be15783b7fbf",
+ "reference": "000df7860439609837bbe28670b0be15783b7fbf",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php84\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php84/tree/v1.32.0"
+ },
+ "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": "2025-02-20T12:04:08+00:00"
+ },
+ {
+ "name": "symfony/service-contracts",
+ "version": "v3.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/service-contracts.git",
+ "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4",
+ "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/container": "^1.1|^2.0",
+ "symfony/deprecation-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "ext-psr": "<1.1|>=2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.6-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Service\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Test/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to writing services",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/service-contracts/tree/v3.6.0"
+ },
+ "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": "2025-04-25T09:37:31+00:00"
+ },
+ {
+ "name": "symfony/string",
+ "version": "v7.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/string.git",
+ "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/string/zipball/f3570b8c61ca887a9e2938e85cb6458515d2b125",
+ "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-intl-grapheme": "~1.0",
+ "symfony/polyfill-intl-normalizer": "~1.0",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "symfony/translation-contracts": "<2.5"
+ },
+ "require-dev": {
+ "symfony/emoji": "^7.1",
+ "symfony/error-handler": "^6.4|^7.0",
+ "symfony/http-client": "^6.4|^7.0",
+ "symfony/intl": "^6.4|^7.0",
+ "symfony/translation-contracts": "^2.5|^3.0",
+ "symfony/var-exporter": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\String\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "grapheme",
+ "i18n",
+ "string",
+ "unicode",
+ "utf-8",
+ "utf8"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/string/tree/v7.3.0"
+ },
+ "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": "2025-04-20T20:19:01+00:00"
+ },
+ {
+ "name": "theseer/tokenizer",
+ "version": "1.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theseer/tokenizer.git",
+ "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
+ "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
+ "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",
+ "support": {
+ "issues": "https://github.com/theseer/tokenizer/issues",
+ "source": "https://github.com/theseer/tokenizer/tree/1.2.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-03T12:36:25+00:00"
+ },
+ {
+ "name": "vimeo/psalm",
+ "version": "6.12.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/vimeo/psalm.git",
+ "reference": "e71404b0465be25cf7f8a631b298c01c5ddd864f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/vimeo/psalm/zipball/e71404b0465be25cf7f8a631b298c01c5ddd864f",
+ "reference": "e71404b0465be25cf7f8a631b298c01c5ddd864f",
+ "shasum": ""
+ },
+ "require": {
+ "amphp/amp": "^3",
+ "amphp/byte-stream": "^2",
+ "amphp/parallel": "^2.3",
+ "composer-runtime-api": "^2",
+ "composer/semver": "^1.4 || ^2.0 || ^3.0",
+ "composer/xdebug-handler": "^2.0 || ^3.0",
+ "danog/advanced-json-rpc": "^3.1",
+ "dnoegel/php-xdg-base-dir": "^0.1.1",
+ "ext-ctype": "*",
+ "ext-dom": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-simplexml": "*",
+ "ext-tokenizer": "*",
+ "felixfbecker/language-server-protocol": "^1.5.3",
+ "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1 || ^1.0.0",
+ "netresearch/jsonmapper": "^5.0",
+ "nikic/php-parser": "^5.0.0",
+ "php": "~8.1.31 || ~8.2.27 || ~8.3.16 || ~8.4.3",
+ "sebastian/diff": "^4.0 || ^5.0 || ^6.0 || ^7.0",
+ "spatie/array-to-xml": "^2.17.0 || ^3.0",
+ "symfony/console": "^6.0 || ^7.0",
+ "symfony/filesystem": "~6.3.12 || ~6.4.3 || ^7.0.3",
+ "symfony/polyfill-php84": "^1.31.0"
+ },
+ "provide": {
+ "psalm/psalm": "self.version"
+ },
+ "require-dev": {
+ "amphp/phpunit-util": "^3",
+ "bamarni/composer-bin-plugin": "^1.4",
+ "brianium/paratest": "^6.9",
+ "danog/class-finder": "^0.4.8",
+ "dg/bypass-finals": "^1.5",
+ "ext-curl": "*",
+ "mockery/mockery": "^1.5",
+ "nunomaduro/mock-final-classes": "^1.1",
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/phpdoc-parser": "^1.6",
+ "phpunit/phpunit": "^9.6",
+ "psalm/plugin-mockery": "^1.1",
+ "psalm/plugin-phpunit": "^0.19",
+ "slevomat/coding-standard": "^8.4",
+ "squizlabs/php_codesniffer": "^3.6",
+ "symfony/process": "^6.0 || ^7.0"
+ },
+ "suggest": {
+ "ext-curl": "In order to send data to shepherd",
+ "ext-igbinary": "^2.0.5 is required, used to serialize caching data"
+ },
+ "bin": [
+ "psalm",
+ "psalm-language-server",
+ "psalm-plugin",
+ "psalm-refactor",
+ "psalm-review",
+ "psalter"
+ ],
+ "type": "project",
+ "extra": {
+ "branch-alias": {
+ "dev-1.x": "1.x-dev",
+ "dev-2.x": "2.x-dev",
+ "dev-3.x": "3.x-dev",
+ "dev-4.x": "4.x-dev",
+ "dev-5.x": "5.x-dev",
+ "dev-6.x": "6.x-dev",
+ "dev-master": "7.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psalm\\": "src/Psalm/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Matthew Brown"
+ },
+ {
+ "name": "Daniil Gentili",
+ "email": "daniil@daniil.it"
+ }
+ ],
+ "description": "A static analysis tool for finding errors in PHP applications",
+ "keywords": [
+ "code",
+ "inspection",
+ "php",
+ "static analysis"
+ ],
+ "support": {
+ "docs": "https://psalm.dev/docs",
+ "issues": "https://github.com/vimeo/psalm/issues",
+ "source": "https://github.com/vimeo/psalm"
+ },
+ "time": "2025-07-04T09:56:28+00:00"
+ },
+ {
+ "name": "webimpress/coding-standard",
+ "version": "1.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/webimpress/coding-standard.git",
+ "reference": "6f6a1a90bd9e18fc8bee0660dd1d1ce68cf9fc53"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/webimpress/coding-standard/zipball/6f6a1a90bd9e18fc8bee0660dd1d1ce68cf9fc53",
+ "reference": "6f6a1a90bd9e18fc8bee0660dd1d1ce68cf9fc53",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.3 || ^8.0",
+ "squizlabs/php_codesniffer": "^3.10.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.6.15"
+ },
+ "type": "phpcodesniffer-standard",
+ "extra": {
+ "dev-master": "1.2.x-dev",
+ "dev-develop": "1.3.x-dev"
+ },
+ "autoload": {
+ "psr-4": {
+ "WebimpressCodingStandard\\": "src/WebimpressCodingStandard/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-2-Clause"
+ ],
+ "description": "Webimpress Coding Standard",
+ "keywords": [
+ "Coding Standard",
+ "PSR-2",
+ "phpcs",
+ "psr-12",
+ "webimpress"
+ ],
+ "support": {
+ "issues": "https://github.com/webimpress/coding-standard/issues",
+ "source": "https://github.com/webimpress/coding-standard/tree/1.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/michalbundyra",
+ "type": "github"
+ }
+ ],
+ "time": "2024-10-16T06:55:17+00:00"
+ },
+ {
+ "name": "webmozart/assert",
+ "version": "1.11.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/webmozarts/assert.git",
+ "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991",
+ "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "conflict": {
+ "phpstan/phpstan": "<0.12.20",
+ "vimeo/psalm": "<4.6.1 || 4.6.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5.13"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.10-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Webmozart\\Assert\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
+ }
+ ],
+ "description": "Assertions to validate method input/output with nice error messages.",
+ "keywords": [
+ "assert",
+ "check",
+ "validate"
+ ],
+ "support": {
+ "issues": "https://github.com/webmozarts/assert/issues",
+ "source": "https://github.com/webmozarts/assert/tree/1.11.0"
+ },
+ "time": "2022-06-03T18:03:27+00:00"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "dev",
+ "stability-flags": {
+ "php-db/phpdb": 20
+ },
+ "prefer-stable": true,
+ "prefer-lowest": false,
+ "platform": {
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
+ },
+ "platform-dev": {
+ "ext-mysqli": "*",
+ "ext-pdo_mysql": "*"
+ },
+ "platform-overrides": {
+ "php": "8.2.99"
+ },
+ "plugin-api-version": "2.6.0"
+}
diff --git a/docker/databases/mysql/Dockerfile b/docker/databases/mysql/Dockerfile
new file mode 100644
index 0000000..69c3f20
--- /dev/null
+++ b/docker/databases/mysql/Dockerfile
@@ -0,0 +1,3 @@
+ARG VERSION=8.4.5
+
+FROM mysql:${VERSION}-bookworm AS base
\ No newline at end of file
diff --git a/docker/php/Dockerfile b/docker/php/Dockerfile
new file mode 100644
index 0000000..eaaff2e
--- /dev/null
+++ b/docker/php/Dockerfile
@@ -0,0 +1,26 @@
+ARG PHP_VERSION=8.3.19
+
+FROM php:${PHP_VERSION}-apache-bookworm AS base
+
+# Copy Composer from the official Docker Hub repository to the local filesystem
+COPY --from=composer:2.8.6 /usr/bin/composer /usr/bin/
+
+# Install the database extensions for MySQL, PostgreSQL, and SQLite, their
+# dependencies, and any other tools that are required for the environment to be
+# used fully and completely.
+RUN apt-get update \
+ && apt-get install -y git \
+ && docker-php-ext-install mysqli pdo pdo_mysql \
+ && apt-get clean
+
+# Allow the www-data login so that it can run Composer instead of using root
+RUN usermod -s /usr/bin/bash www-data
+
+# Copy all of the files from the context to the current directory setting the
+# correct owner
+COPY --chown=www-data:www-data . .
+
+RUN chmod +x bin/install-deps.sh
+
+# Validate and install PHP's dependencies
+RUN su --preserve-environment www-data --command "bin/install-deps.sh"
\ No newline at end of file
diff --git a/docker/php/conf.d/error_reporting.ini b/docker/php/conf.d/error_reporting.ini
new file mode 100644
index 0000000..d040e65
--- /dev/null
+++ b/docker/php/conf.d/error_reporting.ini
@@ -0,0 +1 @@
+error_reporting=E_ALL
\ No newline at end of file
diff --git a/docker/php/conf.d/xdebug.ini b/docker/php/conf.d/xdebug.ini
new file mode 100644
index 0000000..e29c8bd
--- /dev/null
+++ b/docker/php/conf.d/xdebug.ini
@@ -0,0 +1,6 @@
+zend_extension=xdebug
+
+[xdebug]
+xdebug.mode=develop,debug
+xdebug.client_host=host.docker.internal
+xdebug.start_with_request=yes
\ No newline at end of file
diff --git a/docs/book/adapter.md b/docs/book/adapter.md
new file mode 100644
index 0000000..e69de29
diff --git a/docs/book/index.html b/docs/book/index.html
new file mode 100644
index 0000000..92ba899
--- /dev/null
+++ b/docs/book/index.html
@@ -0,0 +1,9 @@
+
+
+
laminas-db-adapter-mysql
+
+
Provides MySQL support for laminas-db via mysqli or Pdo. This package provides laminas-db as a dependency.
+
+
$ composer require laminas/laminas-db-adapter-mysql
+
+
\ No newline at end of file
diff --git a/docs/book/service-manager.md b/docs/book/service-manager.md
new file mode 100644
index 0000000..f1a5e95
--- /dev/null
+++ b/docs/book/service-manager.md
@@ -0,0 +1,6 @@
+# ServiceManager
+
+As of laminas-db 3.0.0 an AdapterManager has been introduced to manage the
+adapter dependencies and to provide a means to allow the platform packages
+to register their own factories for the required dependencies. This is
+handled by the PhpDb\Adapter\Mysql\Container\AdapterManagerDelegator class.
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
new file mode 100644
index 0000000..38effcf
--- /dev/null
+++ b/phpcs.xml.dist
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ src
+ test
+
+
+
+
+
+
+
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index c79ac12..4522d50 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -13,23 +13,29 @@
failOnNotice="true"
failOnDeprecation="true"
failOnWarning="true">
+
+
+
+
-
- test/unit
+
+ ./test/unit
-
- test/integration
+
+ ./test/integration
-
-
-
+
- src
+ ./src
-
- test/config
- test/resource
-
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/psalm-baseline.xml b/psalm-baseline.xml
new file mode 100644
index 0000000..9f40afc
--- /dev/null
+++ b/psalm-baseline.xml
@@ -0,0 +1,741 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ resource : $resultResource]]>
+
+
+
+
+
+ resource->connect_error]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ resource instanceof \mysqli]]>
+ resource instanceof \mysqli]]>
+
+
+
+ resource->connect_error]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getConnection()->getLastGeneratedValue()]]>
+
+
+
+
+
+ resultPrototype]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ resource->affected_rows]]>
+ resource->num_rows]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ resource->error]]>
+ statementBindValues['keys']]]>
+
+
+ statementBindValues['keys'][$i]]]>
+ statementBindValues['values'][$i]]]>
+
+
+ currentData[$this->statementBindValues['keys'][$i]]]]>
+
+
+ currentData[$this->statementBindValues['keys'][$i]]]]>
+
+
+
+
+
+
+
+
+
+
+
+ name]]>
+
+
+ resource->num_rows]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ resource->error]]>
+ resource->num_rows]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ connectionParameters]]>
+ connectionParameters]]>
+
+
+ resource->getAttribute(PDO::ATTR_DRIVER_NAME)]]>
+
+
+
+
+
+
+
+
+
+ fetchColumn()]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ data['columns'][$schema]]]>
+ data['columns'][$schema][$table]]]>
+ data['constraint_keys'][$schema]]]>
+ data['constraint_names'][$schema]]]>
+ data['constraint_references'][$schema]]]>
+ data['constraints'][$schema]]]>
+ data['constraints'][$schema][$table]]]>
+ data['table_names'][$schema]]]>
+ data['triggers'][$schema]]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ quoteTrustedValue($schema)]]>
+ quoteTrustedValue($schema)]]>
+ quoteTrustedValue($schema)]]>
+ quoteTrustedValue($schema)]]>
+ quoteTrustedValue($schema)]]>
+ quoteTrustedValue($schema)]]>
+ quoteTrustedValue($schema)]]>
+ quoteTrustedValue($table)]]>
+ quoteTrustedValue($table)]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ processInfo['paramPrefix']]]>
+ processInfo['paramPrefix']]]>
+
+
+ limit]]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getConfig()['db']['connection']]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ current()]]>
+ adapter]]>
+
+
+
+
+
+
+
+
+
+
+
+
+ fixtureLoaders]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ fixtureFile)]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mysqliParams['database']]]>
+ mysqliParams['hostname']]]>
+ mysqliParams['password']]]>
+ mysqliParams['username']]]>
+ pdoParams['password']]]>
+ pdoParams['username']]]>
+
+
+ pdoParams['database']]]>
+ pdoParams['hostname']]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/psalm.xml.dist b/psalm.xml.dist
new file mode 100644
index 0000000..a063019
--- /dev/null
+++ b/psalm.xml.dist
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/psr-container.php.stub b/psr-container.php.stub
new file mode 100644
index 0000000..1a59597
--- /dev/null
+++ b/psr-container.php.stub
@@ -0,0 +1,23 @@
+ $id
+ * @psalm-return ($id is class-string ? T : mixed)
+ */
+ public function get(string $id): mixed;
+ }
+}
\ No newline at end of file
diff --git a/src/Adapter.php b/src/Adapter.php
deleted file mode 100644
index f4c7957..0000000
--- a/src/Adapter.php
+++ /dev/null
@@ -1,60 +0,0 @@
-createProfiler($parameters);
- }
- }
-
- $driver->checkEnvironment();
- $this->driver = $driver;
-
- $this->platform = $platform;
- $this->queryResultSetPrototype = $queryResultPrototype ?: new ResultSet\ResultSet();
-
- if ($profiler) {
- $this->setProfiler($profiler);
- }
- }
-}
diff --git a/src/AdapterServiceFactory.php b/src/AdapterServiceFactory.php
deleted file mode 100644
index 963b85f..0000000
--- a/src/AdapterServiceFactory.php
+++ /dev/null
@@ -1,33 +0,0 @@
-get('config');
-
- return new Adapter(
- $config['db'],
- $container->get(DriverInterface::class),
- $container->get(PlatformInterface::class),
- $container->has(ResultSetInterface::class) ? $container->get(ResultSetInterface::class) : null,
- $container->has(ProfilerInterface::class) ? $container->get(ProfilerInterface::class) : null
- );
- }
-}
diff --git a/src/ConfigProvider.php b/src/ConfigProvider.php
index 8c3d700..928bc49 100644
--- a/src/ConfigProvider.php
+++ b/src/ConfigProvider.php
@@ -1,12 +1,10 @@
- [
- PlatformInterface::class => Platform\Mysql::class,
- ],
- 'factories' => [
- AdapterInterface::class => AdapterServiceFactory::class,
- //DriverInterface::class => Driver\Mysqli\DriverFactory::class,
- DriverInterface::class => Driver\Pdo\DriverFactory::class,
- Platform\Mysql::class => InvokableFactory::class,
+ 'delegators' => [
+ AdapterManager::class => [
+ Container\AdapterManagerDelegator::class,
+ ],
],
];
}
-}
\ No newline at end of file
+}
diff --git a/src/Container/AdapterFactory.php b/src/Container/AdapterFactory.php
new file mode 100644
index 0000000..b6b9b16
--- /dev/null
+++ b/src/Container/AdapterFactory.php
@@ -0,0 +1,83 @@
+get(AdapterManager::class);
+
+ /** @var array $config */
+ $config = $container->get('config');
+
+ /** @var array $dbConfig */
+ $dbConfig = $config['db'] ?? [];
+
+ if (! isset($dbConfig['driver'])) {
+ throw new RuntimeException('Database driver configuration is missing.');
+ }
+
+ /** @var string $driver */
+ $driver = $dbConfig['driver'];
+
+ if (! $adapterManager->has($driver)) {
+ throw new ServiceNotFoundException(sprintf(
+ 'Database driver "%s" is not registered in the adapter manager.',
+ $driver
+ ));
+ }
+
+ /** @var DriverInterface|PdoDriverInterface $driverInstance */
+ $driverInstance = $adapterManager->get($driver);
+
+ if (! $adapterManager->has(PlatformInterface::class)) {
+ throw new ServiceNotFoundException(sprintf(
+ 'Database platform "%s" is not registered in the adapter manager.',
+ PlatformInterface::class
+ ));
+ }
+
+ /** @var PlatformInterface $platformInstance */
+ $platformInstance = $adapterManager->get(PlatformInterface::class);
+
+ if (! $adapterManager->has(ResultSetInterface::class)) {
+ throw new ServiceNotFoundException(sprintf(
+ 'ResultSet "%s" is not registered in the adapter manager.',
+ ResultSetInterface::class
+ ));
+ }
+
+ /** @var ResultSetInterface $resultSetInstance */
+ $resultSetInstance = $adapterManager->get(ResultSetInterface::class);
+
+ /** @var ProfilerInterface|null $profilerInstanceOrNull */
+ $profilerInstanceOrNull = $adapterManager->has(ProfilerInterface::class)
+ ? $adapterManager->get(ProfilerInterface::class)
+ : null;
+
+ return new Adapter(
+ $driverInstance,
+ $platformInstance,
+ $resultSetInstance,
+ $profilerInstanceOrNull
+ );
+ }
+}
diff --git a/src/Container/AdapterManagerDelegator.php b/src/Container/AdapterManagerDelegator.php
new file mode 100644
index 0000000..271d0ba
--- /dev/null
+++ b/src/Container/AdapterManagerDelegator.php
@@ -0,0 +1,67 @@
+configure([
+ 'aliases' => [
+ 'MySqli' => Driver\Mysqli\Mysqli::class,
+ 'MySQLi' => Driver\Mysqli\Mysqli::class,
+ 'Mysqli' => Driver\Mysqli\Mysqli::class,
+ 'mysqli' => Driver\Mysqli\Mysqli::class,
+ 'PDO_MySQL' => Driver\Pdo\Pdo::class,
+ 'Pdo_MySQL' => Driver\Pdo\Pdo::class,
+ 'Pdo_Mysql' => Driver\Pdo\Pdo::class,
+ 'pdo_mysql' => Driver\Pdo\Pdo::class,
+ 'pdomysql' => Driver\Pdo\Pdo::class,
+ 'pdodriver' => Driver\Pdo\Pdo::class,
+ 'pdo' => Driver\Pdo\Pdo::class,
+ DriverInterface::class => Driver\Mysqli\Mysqli::class,
+ PdoDriverInterface::class => Driver\Pdo\Pdo::class,
+ Profiler\ProfilerInterface::class => Profiler\Profiler::class,
+ ResultSet\ResultSetInterface::class => ResultSet\ResultSet::class,
+ ],
+ 'factories' => [
+ AdapterInterface::class => AdapterFactory::class,
+ Driver\Mysqli\Mysqli::class => MysqliDriverFactory::class,
+ Driver\Mysqli\Connection::class => MysqliConnectionFactory::class,
+ Driver\Mysqli\Result::class => MysqliResultFactory::class,
+ Driver\Mysqli\Statement::class => MysqliStatementFactory::class,
+ Driver\Pdo\Pdo::class => PdoDriverFactory::class,
+ Driver\Pdo\Connection::class => PdoConnectionFactory::class,
+ Result::class => PdoResultFactory::class,
+ PdoStatement::class => PdoStatementFactory::class,
+ PlatformInterface::class => PlatformInterfaceFactory::class,
+ Profiler\Profiler::class => InvokableFactory::class,
+ ResultSet\ResultSet::class => InvokableFactory::class,
+ ],
+ ]);
+
+ return $adapterManager;
+ }
+}
diff --git a/src/Container/MysqliConnectionFactory.php b/src/Container/MysqliConnectionFactory.php
new file mode 100644
index 0000000..8c084ce
--- /dev/null
+++ b/src/Container/MysqliConnectionFactory.php
@@ -0,0 +1,26 @@
+get('config');
+
+ /** @var array $dbConfig */
+ $dbConfig = $config['db'] ?? [];
+
+ /** @var array $connectionConfig */
+ $connectionConfig = $dbConfig['connection'] ?? [];
+
+ return new Connection($connectionConfig);
+ }
+}
diff --git a/src/Container/MysqliDriverFactory.php b/src/Container/MysqliDriverFactory.php
new file mode 100644
index 0000000..310bd3a
--- /dev/null
+++ b/src/Container/MysqliDriverFactory.php
@@ -0,0 +1,44 @@
+get(AdapterManager::class);
+
+ /** @var array $config */
+ $config = $container->get('config');
+
+ /** @var array $dbConfig */
+ $dbConfig = $config['db'] ?? [];
+
+ /** @var array $options */
+ $options = $dbConfig['options'] ?? [];
+
+ /** @var Driver\ConnectionInterface&Mysqli\Connection $connectionInstance */
+ $connectionInstance = $adapterManager->get(Mysqli\Connection::class);
+
+ /** @var Driver\StatementInterface&Mysqli\Statement $statementInstance */
+ $statementInstance = $adapterManager->get(Mysqli\Statement::class);
+
+ /** @var Driver\ResultInterface&Mysqli\Result $resultInstance */
+ $resultInstance = $adapterManager->get(Mysqli\Result::class);
+
+ return new Mysqli\Mysqli(
+ $connectionInstance,
+ $statementInstance,
+ $resultInstance,
+ $options
+ );
+ }
+}
diff --git a/src/Container/MysqliResultFactory.php b/src/Container/MysqliResultFactory.php
new file mode 100644
index 0000000..bb1f9cb
--- /dev/null
+++ b/src/Container/MysqliResultFactory.php
@@ -0,0 +1,17 @@
+get('config');
+
+ /** @var array $dbConfig */
+ $dbConfig = $config['db'] ?? [];
+
+ /** @var array $options */
+ $options = $dbConfig['options'] ?? [];
+
+ /** @var bool $bufferResults */
+ $bufferResults = $options['buffer_results'] ?? false;
+
+ return new Statement(bufferResults: $bufferResults);
+ }
+}
diff --git a/src/Container/PdoConnectionFactory.php b/src/Container/PdoConnectionFactory.php
new file mode 100644
index 0000000..5b07458
--- /dev/null
+++ b/src/Container/PdoConnectionFactory.php
@@ -0,0 +1,26 @@
+get('config');
+
+ /** @var array $dbConfig */
+ $dbConfig = $config['db'] ?? [];
+
+ /** @var array $connectionConfig */
+ $connectionConfig = $dbConfig['connection'] ?? [];
+
+ return new Connection($connectionConfig);
+ }
+}
diff --git a/src/Container/PdoDriverFactory.php b/src/Container/PdoDriverFactory.php
new file mode 100644
index 0000000..2d5420b
--- /dev/null
+++ b/src/Container/PdoDriverFactory.php
@@ -0,0 +1,40 @@
+get(AdapterManager::class);
+
+ /** @var ConnectionInterface&Connection $connectionInstance */
+ $connectionInstance = $adapterManager->get(Connection::class);
+
+ /** @var StatementInterface&Statement $statementInstance */
+ $statementInstance = $adapterManager->get(Statement::class);
+
+ /** @var ResultInterface&Result $resultInstance */
+ $resultInstance = $adapterManager->get(Result::class);
+
+ return new PdoDriver(
+ $connectionInstance,
+ $statementInstance,
+ $resultInstance
+ );
+ }
+}
diff --git a/src/Container/PdoResultFactory.php b/src/Container/PdoResultFactory.php
new file mode 100644
index 0000000..86f1ea3
--- /dev/null
+++ b/src/Container/PdoResultFactory.php
@@ -0,0 +1,17 @@
+get('config');
+
+ /** @var array $dbConfig */
+ $dbConfig = $config['db'] ?? [];
+
+ /** @var array $options */
+ $options = $dbConfig['options'] ?? [];
+
+ return new Statement(options: $options);
+ }
+}
diff --git a/src/Container/PlatformInterfaceFactory.php b/src/Container/PlatformInterfaceFactory.php
new file mode 100644
index 0000000..fdac197
--- /dev/null
+++ b/src/Container/PlatformInterfaceFactory.php
@@ -0,0 +1,36 @@
+get(AdapterManager::class);
+
+ /** @var array $config */
+ $config = $container->get('config');
+
+ /** @var array $dbConfig */
+ $dbConfig = $config['db'] ?? [];
+
+ /** @var string $driver */
+ $driver = $dbConfig['driver'];
+
+ /** @var DriverInterface|mysqli|PDO $driverInstance */
+ $driverInstance = $adapterManager->get($driver);
+
+ return new Mysql($driverInstance);
+ }
+}
diff --git a/src/DatabasePlatformNameTrait.php b/src/DatabasePlatformNameTrait.php
new file mode 100644
index 0000000..72d9ccc
--- /dev/null
+++ b/src/DatabasePlatformNameTrait.php
@@ -0,0 +1,30 @@
+setConnectionParameters($connectionInfo);
} elseif ($connectionInfo instanceof \mysqli) {
@@ -44,20 +49,16 @@ public function __construct($connectionInfo = null)
}
}
- /**
- * @return $this Provides a fluent interface
- */
- public function setDriver(DriverInterface $driver)
+ public function setDriver(DriverInterface $driver): DriverAwareInterface
{
$this->driver = $driver;
return $this;
}
- /**
- * {@inheritDoc}
- */
- public function getCurrentSchema()
+ /** @inheritDoc */
+ #[Override]
+ public function getCurrentSchema(): string|bool
{
if (! $this->isConnected()) {
$this->connect();
@@ -74,27 +75,27 @@ public function getCurrentSchema()
*
* @return $this Provides a fluent interface
*/
- public function setResource(\mysqli $resource)
+ public function setResource(\mysqli $resource): static
{
$this->resource = $resource;
return $this;
}
- /**
- * {@inheritDoc}
- */
- public function connect()
+ /** @inheritDoc */
+ #[Override]
+ public function connect(): ConnectionInterface
{
if ($this->resource instanceof \mysqli) {
return $this;
}
- // localize
+ /** @var array $p */
$p = $this->connectionParameters;
// given a list of key names, test for existence in $p
- $findParameterValue = function (array $names) use ($p) {
+ /** @var string[] $names */
+ $findParameterValue = function (array $names) use ($p): string|null {
foreach ($names as $name) {
if (isset($p[$name])) {
return $p[$name];
@@ -104,12 +105,15 @@ public function connect()
return null;
};
+ /** @var string|null $hostname */
$hostname = $findParameterValue(['hostname', 'host']);
$username = $findParameterValue(['username', 'user']);
$password = $findParameterValue(['password', 'passwd', 'pw']);
$database = $findParameterValue(['database', 'dbname', 'db', 'schema']);
- $port = isset($p['port']) ? (int) $p['port'] : null;
- $socket = $p['socket'] ?? null;
+ /** @var int|null $port */
+ $port = isset($p['port']) ? (int) $p['port'] : null;
+ /** @var string|null $socket */
+ $socket = $p['socket'] ?? null;
// phpcs:ignore WebimpressCodingStandard.NamingConventions.ValidVariableName.NotCamelCaps
$useSSL = $p['use_ssl'] ?? 0;
@@ -145,8 +149,7 @@ public function connect()
$this->resource->ssl_set($clientKey, $clientCert, $caCert, $caPath, $cipher);
//MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT is not valid option, needs to be set as flag
if (
- isset($p['driver_options'])
- && isset($p['driver_options'][MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT])
+ isset($p['driver_options'][MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT])
) {
$flags |= MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT;
}
@@ -156,7 +159,7 @@ public function connect()
$flags === null
? $this->resource->real_connect($hostname, $username, $password, $database, $port, $socket)
: $this->resource->real_connect($hostname, $username, $password, $database, $port, $socket, $flags);
- } catch (GenericException $e) {
+ } catch (GenericException) {
throw new Exception\RuntimeException(
'Connection error',
$this->resource->connect_errno,
@@ -179,29 +182,26 @@ public function connect()
return $this;
}
- /**
- * {@inheritDoc}
- */
- public function isConnected()
+ /** @inheritDoc */
+ public function isConnected(): bool
{
return $this->resource instanceof \mysqli;
}
- /**
- * {@inheritDoc}
- */
- public function disconnect()
+ /** @inheritDoc */
+ #[Override]
+ public function disconnect(): ConnectionInterface
{
if ($this->resource instanceof \mysqli) {
$this->resource->close();
}
$this->resource = null;
+ return $this;
}
- /**
- * {@inheritDoc}
- */
- public function beginTransaction()
+ /** @inheritDoc */
+ #[Override]
+ public function beginTransaction(): ConnectionInterface
{
if (! $this->isConnected()) {
$this->connect();
@@ -213,10 +213,9 @@ public function beginTransaction()
return $this;
}
- /**
- * {@inheritDoc}
- */
- public function commit()
+ /** @inheritDoc */
+ #[Override]
+ public function commit(): ConnectionInterface
{
if (! $this->isConnected()) {
$this->connect();
@@ -229,10 +228,9 @@ public function commit()
return $this;
}
- /**
- * {@inheritDoc}
- */
- public function rollback()
+ /** @inheritDoc */
+ #[Override]
+ public function rollback(): ConnectionInterface
{
if (! $this->isConnected()) {
throw new Exception\RuntimeException('Must be connected before you can rollback.');
@@ -254,21 +252,18 @@ public function rollback()
*
* @throws Exception\InvalidQueryException
*/
- public function execute($sql)
+ #[Override]
+ public function execute($sql): ?ResultInterface
{
if (! $this->isConnected()) {
$this->connect();
}
- if ($this->profiler) {
- $this->profiler->profilerStart($sql);
- }
+ $this->profiler?->profilerStart($sql);
$resultResource = $this->resource->query($sql);
- if ($this->profiler) {
- $this->profiler->profilerFinish($sql);
- }
+ $this->profiler?->profilerFinish($sql);
// if the returnValue is something other than a mysqli_result, bypass wrapping it
if ($resultResource === false) {
@@ -278,10 +273,9 @@ public function execute($sql)
return $this->driver->createResult($resultResource === true ? $this->resource : $resultResource);
}
- /**
- * {@inheritDoc}
- */
- public function getLastGeneratedValue($name = null)
+ /** @inheritDoc */
+ #[Override]
+ public function getLastGeneratedValue($name = null): string|int|bool|null
{
return $this->resource->insert_id;
}
@@ -289,6 +283,8 @@ public function getLastGeneratedValue($name = null)
/**
* Create a new mysqli resource
*
+ * todo: why do we have this random method here?
+ *
* @return \mysqli
*/
protected function createResource()
diff --git a/src/Driver/Mysqli/Driver.php b/src/Driver/Mysqli/Driver.php
deleted file mode 100644
index 7d3c8de..0000000
--- a/src/Driver/Mysqli/Driver.php
+++ /dev/null
@@ -1,230 +0,0 @@
- false,
- ];
-
- /**
- * Constructor
- *
- * @param array|Connection|\mysqli $connection
- * @param array $options
- */
- public function __construct(
- $connection,
- ?Statement $statementPrototype = null,
- ?Result $resultPrototype = null,
- array $options = []
- ) {
- if (! $connection instanceof Connection) {
- $connection = new Connection($connection);
- }
-
- $options = array_intersect_key(array_merge($this->options, $options), $this->options);
-
- $this->registerConnection($connection);
- $this->registerStatementPrototype($statementPrototype ?: new Statement($options['buffer_results']));
- $this->registerResultPrototype($resultPrototype ?: new Result());
- }
-
- /**
- * @return $this Provides a fluent interface
- */
- public function setProfiler(Profiler\ProfilerInterface $profiler)
- {
- $this->profiler = $profiler;
- if ($this->connection instanceof Profiler\ProfilerAwareInterface) {
- $this->connection->setProfiler($profiler);
- }
- if ($this->statementPrototype instanceof Profiler\ProfilerAwareInterface) {
- $this->statementPrototype->setProfiler($profiler);
- }
- return $this;
- }
-
- /**
- * @return null|Profiler\ProfilerInterface
- */
- public function getProfiler()
- {
- return $this->profiler;
- }
-
- /**
- * Register connection
- *
- * @return $this Provides a fluent interface
- */
- public function registerConnection(Connection $connection)
- {
- $this->connection = $connection;
- $this->connection->setDriver($this); // needs access to driver to createStatement()
- return $this;
- }
-
- /**
- * Register statement prototype
- */
- public function registerStatementPrototype(Statement $statementPrototype)
- {
- $this->statementPrototype = $statementPrototype;
- $this->statementPrototype->setDriver($this); // needs access to driver to createResult()
- }
-
- /**
- * Get statement prototype
- *
- * @return null|Statement
- */
- public function getStatementPrototype()
- {
- return $this->statementPrototype;
- }
-
- /**
- * Register result prototype
- */
- public function registerResultPrototype(Result $resultPrototype)
- {
- $this->resultPrototype = $resultPrototype;
- }
-
- /**
- * @return null|Result
- */
- public function getResultPrototype()
- {
- return $this->resultPrototype;
- }
-
- /**
- * Check environment
- *
- * @throws Exception\RuntimeException
- * @return void
- */
- public function checkEnvironment()
- {
- if (! extension_loaded('mysqli')) {
- throw new Exception\RuntimeException(
- 'The Mysqli extension is required for this adapter but the extension is not loaded'
- );
- }
- }
-
- /**
- * Get connection
- *
- * @return Connection
- */
- public function getConnection()
- {
- return $this->connection;
- }
-
- /**
- * Create statement
- *
- * @param string $sqlOrResource
- * @return Statement
- */
- public function createStatement($sqlOrResource = null)
- {
- /**
- * @todo Resource tracking
- * if (is_resource($sqlOrResource) && !in_array($sqlOrResource, $this->resources, true)) {
- * $this->resources[] = $sqlOrResource;
- * }
- */
-
- $statement = clone $this->statementPrototype;
- if ($sqlOrResource instanceof mysqli_stmt) {
- $statement->setResource($sqlOrResource);
- } else {
- if (is_string($sqlOrResource)) {
- $statement->setSql($sqlOrResource);
- }
- if (! $this->connection->isConnected()) {
- $this->connection->connect();
- }
- $statement->initialize($this->connection->getResource());
- }
- return $statement;
- }
-
- /**
- * Create result
- *
- * @param resource $resource
- * @param null|bool $isBuffered
- * @return Result
- */
- public function createResult($resource, $isBuffered = null)
- {
- $result = clone $this->resultPrototype;
- $result->initialize($resource, $this->connection->getLastGeneratedValue(), $isBuffered);
- return $result;
- }
-
- /**
- * Get prepare type
- *
- * @return string
- */
- public function getPrepareType()
- {
- return self::PARAMETERIZATION_POSITIONAL;
- }
-
- /**
- * Format parameter name
- *
- * @param string $name
- * @param mixed $type
- * @return string
- */
- public function formatParameterName($name, $type = null)
- {
- return '?';
- }
-
- /**
- * Get last generated value
- *
- * @return mixed
- */
- public function getLastGeneratedValue()
- {
- return $this->getConnection()->getLastGeneratedValue();
- }
-}
diff --git a/src/Driver/Mysqli/DriverFactory.php b/src/Driver/Mysqli/DriverFactory.php
deleted file mode 100644
index e3adb9d..0000000
--- a/src/Driver/Mysqli/DriverFactory.php
+++ /dev/null
@@ -1,24 +0,0 @@
-get('config')['db'];
- $options = [];
- if (isset($dbConfig['options'])) {
- $options = (array) $dbConfig['options'];
- unset($dbConfig['options']);
- }
- return new Driver($dbConfig, null, null, $options);
- }
-}
diff --git a/src/Driver/Mysqli/Mysqli.php b/src/Driver/Mysqli/Mysqli.php
new file mode 100644
index 0000000..12cc05a
--- /dev/null
+++ b/src/Driver/Mysqli/Mysqli.php
@@ -0,0 +1,164 @@
+ false,
+ ];
+
+ public function __construct(
+ protected readonly ConnectionInterface&Connection $connection,
+ protected readonly StatementInterface&Statement $statementPrototype,
+ protected readonly ResultInterface $resultPrototype,
+ array $options = []
+ ) {
+ $this->checkEnvironment();
+
+ $options = array_intersect_key(array_merge($this->options, $options), $this->options);
+
+ if ($this->connection instanceof DriverAwareInterface) {
+ $this->connection->setDriver($this);
+ }
+ if ($this->statementPrototype instanceof DriverAwareInterface) {
+ $this->statementPrototype->setDriver($this);
+ }
+ }
+
+ public function setProfiler(ProfilerInterface $profiler): ProfilerAwareInterface
+ {
+ $this->profiler = $profiler;
+ if ($this->connection instanceof ProfilerAwareInterface) {
+ $this->connection->setProfiler($profiler);
+ }
+ if ($this->statementPrototype instanceof ProfilerAwareInterface) {
+ $this->statementPrototype->setProfiler($profiler);
+ }
+ return $this;
+ }
+
+ public function getProfiler(): ?ProfilerInterface
+ {
+ return $this->profiler;
+ }
+
+ /**
+ * Get statement prototype
+ */
+ public function getStatementPrototype(): StatementInterface&Statement
+ {
+ return $this->statementPrototype;
+ }
+
+ public function getResultPrototype(): ResultInterface&Result
+ {
+ return $this->resultPrototype;
+ }
+
+ public function checkEnvironment(): bool
+ {
+ if (! extension_loaded('mysqli')) {
+ throw new Exception\RuntimeException(
+ 'The Mysqli extension is required for this adapter but the extension is not loaded'
+ );
+ }
+ return true;
+ }
+
+ public function getConnection(): ConnectionInterface&Connection
+ {
+ return $this->connection;
+ }
+
+ /**
+ * Create statement
+ *
+ * @param \mysqli|mysqli_stmt|string $sqlOrResource
+ */
+ public function createStatement($sqlOrResource = null): StatementInterface&Statement
+ {
+ /**
+ * @todo Resource tracking
+ * if (is_resource($sqlOrResource) && !in_array($sqlOrResource, $this->resources, true)) {
+ * $this->resources[] = $sqlOrResource;
+ *}
+ */
+
+ $statement = clone $this->statementPrototype;
+ if ($sqlOrResource instanceof mysqli_stmt) {
+ $statement->setResource($sqlOrResource);
+ } else {
+ if (is_string($sqlOrResource)) {
+ $statement->setSql($sqlOrResource);
+ }
+ if (! $this->connection->isConnected()) {
+ $this->connection->connect();
+ }
+ /** @var \mysqli $resource */
+ $resource = $this->connection->getResource();
+ $statement->initialize($resource);
+ }
+ return $statement;
+ }
+
+ /**
+ * Create result
+ *
+ * @param \mysqli|mysqli_result|mysqli_stmt $resource
+ */
+ public function createResult($resource, ?bool $isBuffered = null): ResultInterface&Result
+ {
+ /** @var Result $result */
+ $result = clone $this->resultPrototype;
+ $result->initialize($resource, $this->connection->getLastGeneratedValue(), $isBuffered);
+ return $result;
+ }
+
+ /**
+ * Get prepare type
+ */
+ public function getPrepareType(): string
+ {
+ return self::PARAMETERIZATION_POSITIONAL;
+ }
+
+ /**
+ * Format parameter name
+ */
+ public function formatParameterName(string $name, ?string $type = null): string
+ {
+ return '?';
+ }
+
+ /**
+ * Get last generated value
+ */
+ public function getLastGeneratedValue(): int|string|null|false
+ {
+ return $this->getConnection()->getLastGeneratedValue();
+ }
+}
diff --git a/src/Driver/Mysqli/Result.php b/src/Driver/Mysqli/Result.php
index 19c513c..3efedec 100644
--- a/src/Driver/Mysqli/Result.php
+++ b/src/Driver/Mysqli/Result.php
@@ -1,13 +1,16 @@
null, 'values' => []];
+ protected array $statementBindValues = ['keys' => null, 'values' => []];
- /** @var mixed */
- protected $generatedValue;
+ protected mixed $generatedValue;
/**
* Initialize
*
- * @param mixed $resource
- * @param mixed $generatedValue
- * @param bool|null $isBuffered
- * @return $this Provides a fluent interface
* @throws Exception\InvalidArgumentException
+ * psalm-suppress PossiblyUnusedMethod
*/
- public function initialize($resource, $generatedValue, $isBuffered = null)
- {
+ public function initialize(
+ mysqli|mysqli_result|mysqli_stmt $resource,
+ mixed $generatedValue,
+ ?bool $isBuffered = null
+ ): ResultInterface {
if (
! $resource instanceof mysqli
&& ! $resource instanceof mysqli_result
@@ -77,6 +61,9 @@ public function initialize($resource, $generatedValue, $isBuffered = null)
throw new Exception\InvalidArgumentException('Invalid resource provided.');
}
+ /**
+ * todo: examine this closely to see if this is the correct behavior
+ */
if ($isBuffered !== null) {
$this->isBuffered = $isBuffered;
} else {
@@ -94,11 +81,12 @@ public function initialize($resource, $generatedValue, $isBuffered = null)
}
/**
- * Force buffering
+ * {@inheritDoc}
*
* @throws Exception\RuntimeException
*/
- public function buffer()
+ #[Override]
+ public function buffer(): void
{
if ($this->resource instanceof mysqli_stmt && $this->isBuffered !== true) {
if ($this->position > 0) {
@@ -110,41 +98,37 @@ public function buffer()
}
/**
- * Check if is buffered
- *
- * @return bool|null
+ * {@inheritDoc}
*/
- public function isBuffered()
+ #[Override]
+ public function isBuffered(): ?bool
{
return $this->isBuffered;
}
/**
- * Return the resource
- *
- * @return mixed
+ * {@inheritDoc}
*/
- public function getResource()
+ #[Override]
+ public function getResource(): mysqli|mysqli_result|mysqli_stmt
{
return $this->resource;
}
/**
- * Is query result?
- *
- * @return bool
+ * {@inheritDoc}
*/
- public function isQueryResult()
+ #[Override]
+ public function isQueryResult(): bool
{
return $this->resource->field_count > 0;
}
/**
- * Get affected rows
- *
- * @return int
+ * {@inheritDoc}
*/
- public function getAffectedRows()
+ #[Override]
+ public function getAffectedRows(): int
{
if ($this->resource instanceof mysqli || $this->resource instanceof mysqli_stmt) {
return $this->resource->affected_rows;
@@ -159,6 +143,7 @@ public function getAffectedRows()
* @return mixed
*/
#[ReturnTypeWillChange]
+ #[Override]
public function current()
{
if ($this->currentComplete) {
@@ -183,9 +168,8 @@ public function current()
* @see http://php.net/manual/en/mysqli-stmt.bind-result.php
*
* @throws Exception\RuntimeException
- * @return bool
*/
- protected function loadDataFromMysqliStatement()
+ protected function loadDataFromMysqliStatement(): bool
{
// build the default reference based bind structure, if it does not already exist
if ($this->statementBindValues['keys'] === null) {
@@ -223,10 +207,8 @@ protected function loadDataFromMysqliStatement()
/**
* Load from mysqli result
- *
- * @return bool
*/
- protected function loadFromMysqliResult()
+ protected function loadFromMysqliResult(): bool
{
$this->currentData = null;
@@ -248,6 +230,7 @@ protected function loadFromMysqliResult()
* @return void
*/
#[ReturnTypeWillChange]
+ #[Override]
public function next()
{
$this->currentComplete = false;
@@ -265,6 +248,7 @@ public function next()
* @return mixed
*/
#[ReturnTypeWillChange]
+ #[Override]
public function key()
{
return $this->position;
@@ -277,6 +261,7 @@ public function key()
* @return void
*/
#[ReturnTypeWillChange]
+ #[Override]
public function rewind()
{
if (0 !== $this->position && false === $this->isBuffered) {
@@ -294,6 +279,7 @@ public function rewind()
* @return bool
*/
#[ReturnTypeWillChange]
+ #[Override]
public function valid()
{
if ($this->currentComplete) {
@@ -314,6 +300,7 @@ public function valid()
* @return int
*/
#[ReturnTypeWillChange]
+ #[Override]
public function count()
{
if ($this->isBuffered === false) {
@@ -323,11 +310,10 @@ public function count()
}
/**
- * Get field count
- *
- * @return int
+ * {@inheritDoc}
*/
- public function getFieldCount()
+ #[Override]
+ public function getFieldCount(): int
{
return $this->resource->field_count;
}
@@ -337,6 +323,7 @@ public function getFieldCount()
*
* @return mixed|null
*/
+ #[Override]
public function getGeneratedValue()
{
return $this->generatedValue;
diff --git a/src/Driver/Mysqli/Statement.php b/src/Driver/Mysqli/Statement.php
index d9500c7..a04c50c 100644
--- a/src/Driver/Mysqli/Statement.php
+++ b/src/Driver/Mysqli/Statement.php
@@ -1,182 +1,118 @@
bufferResults = (bool) $bufferResults;
+ public function __construct(
+ protected ParameterContainer $parameterContainer = new ParameterContainer(),
+ protected bool $bufferResults = false
+ ) {
}
- /**
- * Set driver
- *
- * @return $this Provides a fluent interface
- */
- public function setDriver(Mysqli $driver)
+ #[Override]
+ public function setDriver(DriverInterface $driver): DriverAwareInterface
{
$this->driver = $driver;
return $this;
}
- /**
- * @return $this Provides a fluent interface
- */
- public function setProfiler(Profiler\ProfilerInterface $profiler)
+ #[Override]
+ public function setProfiler(ProfilerInterface $profiler): ProfilerAwareInterface
{
$this->profiler = $profiler;
return $this;
}
- /**
- * @return null|Profiler\ProfilerInterface
- */
- public function getProfiler()
+ public function getProfiler(): ?ProfilerInterface
{
return $this->profiler;
}
- /**
- * Initialize
- *
- * @return $this Provides a fluent interface
- */
- public function initialize(\mysqli $mysqli)
+ public function initialize(\mysqli $mysqli): static
{
$this->mysqli = $mysqli;
return $this;
}
- /**
- * Set sql
- *
- * @param string $sql
- * @return $this Provides a fluent interface
- */
- public function setSql($sql)
+ #[Override]
+ public function getSql(): ?string
+ {
+ return $this->sql;
+ }
+
+ #[Override]
+ public function setSql(?string $sql): StatementContainerInterface
{
$this->sql = $sql;
return $this;
}
- /**
- * Set Parameter container
- *
- * @return $this Provides a fluent interface
- */
- public function setParameterContainer(ParameterContainer $parameterContainer)
- {
+ #[Override]
+ public function setParameterContainer(
+ ParameterContainer $parameterContainer
+ ): StatementContainerInterface {
$this->parameterContainer = $parameterContainer;
return $this;
}
- /**
- * Get resource
- *
- * @return mixed
- */
- public function getResource()
+ #[Override]
+ public function getResource(): mysqli_stmt
{
return $this->resource;
}
- /**
- * Set resource
- *
- * @return $this Provides a fluent interface
- */
- public function setResource(mysqli_stmt $mysqliStatement)
+ public function setResource(mysqli_stmt $mysqliStatement): StatementInterface
{
$this->resource = $mysqliStatement;
$this->isPrepared = true;
return $this;
}
- /**
- * Get sql
- *
- * @return string
- */
- public function getSql()
- {
- return $this->sql;
- }
-
- /**
- * Get parameter count
- *
- * @return ParameterContainer
- */
- public function getParameterContainer()
+ #[Override]
+ public function getParameterContainer(): ?ParameterContainer
{
return $this->parameterContainer;
}
- /**
- * Is prepared
- *
- * @return bool
- */
- public function isPrepared()
+ #[Override]
+ public function isPrepared(): bool
{
return $this->isPrepared;
}
- /**
- * Prepare
- *
- * @param string $sql
- * @return $this Provides a fluent interface
- * @throws Exception\InvalidQueryException
- * @throws Exception\RuntimeException
- */
- public function prepare($sql = null)
+ #[Override]
+ public function prepare(?string $sql = null): StatementInterface
{
if ($this->isPrepared) {
throw new Exception\RuntimeException('This statement has already been prepared');
@@ -200,11 +136,10 @@ public function prepare($sql = null)
/**
* Execute
*
- * @param null|array|ParameterContainer $parameters
* @throws Exception\RuntimeException
- * @return mixed
*/
- public function execute($parameters = null)
+ #[Override]
+ public function execute(ParameterContainer|array|null $parameters = null): ?ResultInterface
{
if (! $this->isPrepared) {
$this->prepare();
@@ -229,15 +164,11 @@ public function execute($parameters = null)
}
/** END Standard ParameterContainer Merging Block */
- if ($this->profiler) {
- $this->profiler->profilerStart($this);
- }
+ $this->profiler?->profilerStart($this);
$return = $this->resource->execute();
- if ($this->profiler) {
- $this->profiler->profilerFinish();
- }
+ $this->profiler?->profilerFinish();
if ($return === false) {
throw new Exception\RuntimeException($this->resource->error);
@@ -256,10 +187,8 @@ public function execute($parameters = null)
/**
* Bind parameters from container
- *
- * @return void
*/
- protected function bindParametersFromContainer()
+ protected function bindParametersFromContainer(): void
{
$parameters = $this->parameterContainer->getNamedArray();
$type = '';
diff --git a/src/Driver/Pdo/Connection.php b/src/Driver/Pdo/Connection.php
index ab2992a..27033e7 100644
--- a/src/Driver/Pdo/Connection.php
+++ b/src/Driver/Pdo/Connection.php
@@ -1,129 +1,35 @@
setConnectionParameters($connectionParameters);
- } elseif ($connectionParameters instanceof \PDO) {
- $this->setResource($connectionParameters);
- } elseif (null !== $connectionParameters) {
- throw new Exception\InvalidArgumentException(
- '$connection must be an array of parameters, a PDO object or null'
- );
- }
- }
-
- /**
- * Set driver
- *
- * @return $this Provides a fluent interface
- */
- public function setDriver(DriverInterface $driver)
- {
- $this->driver = $driver;
-
- return $this;
- }
-
/**
* {@inheritDoc}
*/
- public function setConnectionParameters(array $connectionParameters)
- {
- $this->connectionParameters = $connectionParameters;
- if (isset($connectionParameters['dsn'])) {
- $this->driverName = substr(
- $connectionParameters['dsn'],
- 0,
- strpos($connectionParameters['dsn'], ':')
- );
- } elseif (isset($connectionParameters['pdodriver'])) {
- $this->driverName = strtolower($connectionParameters['pdodriver']);
- } elseif (isset($connectionParameters['driver'])) {
- $this->driverName = strtolower(substr(
- str_replace(['-', '_', ' '], '', $connectionParameters['driver']),
- 3
- ));
- }
- }
-
- /**
- * Get the dsn string for this connection
- *
- * @throws RunTimeException
- * @return string
- */
- public function getDsn()
- {
- if (! $this->dsn) {
- throw new Exception\RuntimeException(
- 'The DSN has not been set or constructed from parameters in connect() for this Connection'
- );
- }
-
- return $this->dsn;
- }
-
- /**
- * {@inheritDoc}
- */
- public function getCurrentSchema()
+ #[Override]
+ public function getCurrentSchema(): string|bool
{
if (! $this->isConnected()) {
$this->connect();
}
- // switch ($this->driverName) {
- // case 'mysql':
- // $sql = 'SELECT DATABASE()';
- // break;
- // case 'sqlite':
- // return 'main';
- // case 'sqlsrv':
- // case 'dblib':
- // $sql = 'SELECT SCHEMA_NAME()';
- // break;
- // case 'pgsql':
- // default:
- // $sql = 'SELECT CURRENT_SCHEMA';
- // break;
- // }
-
/** @var PDOStatement $result */
$result = $this->resource->query('SELECT DATABASE()');
if ($result instanceof PDOStatement) {
@@ -133,26 +39,14 @@ public function getCurrentSchema()
return false;
}
- /**
- * Set resource
- *
- * @return $this Provides a fluent interface
- */
- public function setResource(\PDO $resource)
- {
- $this->resource = $resource;
- $this->driverName = strtolower($this->resource->getAttribute(\PDO::ATTR_DRIVER_NAME));
-
- return $this;
- }
-
/**
* {@inheritDoc}
*
* @throws Exception\InvalidConnectionParametersException
* @throws Exception\RuntimeException
*/
- public function connect()
+ #[Override]
+ public function connect(): ConnectionInterface
{
if ($this->resource) {
return $this;
@@ -163,17 +57,7 @@ public function connect()
foreach ($this->connectionParameters as $key => $value) {
switch (strtolower($key)) {
case 'dsn':
- $dsn = $value;
- break;
- case 'driver':
- $value = strtolower((string) $value);
- if (strpos($value, 'pdo') === 0) {
- $pdoDriver = str_replace(['-', '_', ' '], '', $value);
- $pdoDriver = substr($pdoDriver, 3) ?: '';
- }
- break;
- case 'pdodriver':
- $pdoDriver = (string) $value;
+ $dsn = (string) $value;
break;
case 'user':
case 'username':
@@ -204,7 +88,6 @@ public function connect()
$version = (string) $value;
break;
case 'driver_options':
- case 'options':
$value = (array) $value;
$options = array_diff_key($options, $value) + $value;
break;
@@ -221,43 +104,30 @@ public function connect()
);
}
- if (! isset($dsn) && isset($pdoDriver)) {
+ if (! isset($dsn)) {
$dsn = [];
- switch ($pdoDriver) {
- case 'sqlite':
- $dsn[] = $database;
- break;
- case 'sqlsrv':
- if (isset($database)) {
- $dsn[] = "database={$database}";
- }
- if (isset($hostname)) {
- $dsn[] = "server={$hostname}";
- }
- break;
- default:
- if (isset($database)) {
- $dsn[] = "dbname={$database}";
- }
- if (isset($hostname)) {
- $dsn[] = "host={$hostname}";
- }
- if (isset($port)) {
- $dsn[] = "port={$port}";
- }
- if (isset($charset) && $pdoDriver !== 'pgsql') {
- $dsn[] = "charset={$charset}";
- }
- if (isset($unixSocket)) {
- $dsn[] = "unix_socket={$unixSocket}";
- }
- if (isset($version)) {
- $dsn[] = "version={$version}";
- }
- break;
+ if (isset($database)) {
+ $dsn[] = "dbname={$database}";
+ }
+ if (isset($hostname)) {
+ $dsn[] = "host={$hostname}";
+ }
+ if (isset($port)) {
+ $dsn[] = "port={$port}";
}
- $dsn = $pdoDriver . ':' . implode(';', $dsn);
- } elseif (! isset($dsn)) {
+ if (isset($charset)) {
+ $dsn[] = "charset={$charset}";
+ }
+ if (isset($unixSocket)) {
+ $dsn[] = "unix_socket={$unixSocket}";
+ }
+ if (isset($version)) {
+ $dsn[] = "version={$version}";
+ }
+ $dsn = 'mysql:' . implode(';', $dsn);
+ }
+
+ if (! is_string($dsn)) {
throw new Exception\InvalidConnectionParametersException(
'A dsn was not provided or could not be constructed from your parameters',
$this->connectionParameters
@@ -267,12 +137,9 @@ public function connect()
$this->dsn = $dsn;
try {
- $this->resource = new \PDO($dsn, $username, $password, $options);
- $this->resource->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
- if (isset($charset) && $pdoDriver === 'pgsql') {
- $this->resource->exec('SET NAMES ' . $this->resource->quote($charset));
- }
- $this->driverName = strtolower($this->resource->getAttribute(\PDO::ATTR_DRIVER_NAME));
+ $this->resource = new PDO($dsn, $username, $password, $options);
+ $this->resource->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+ $this->driverName = strtolower($this->resource->getAttribute(PDO::ATTR_DRIVER_NAME));
} catch (PDOException $e) {
$code = $e->getCode();
if (! is_int($code)) {
@@ -284,143 +151,17 @@ public function connect()
return $this;
}
- /**
- * {@inheritDoc}
- */
- public function isConnected()
- {
- return $this->resource instanceof \PDO;
- }
-
- /**
- * {@inheritDoc}
- */
- public function beginTransaction()
- {
- if (! $this->isConnected()) {
- $this->connect();
- }
-
- if (0 === $this->nestedTransactionsCount) {
- $this->resource->beginTransaction();
- $this->inTransaction = true;
- }
-
- $this->nestedTransactionsCount++;
-
- return $this;
- }
-
- /**
- * {@inheritDoc}
- */
- public function commit()
- {
- if (! $this->isConnected()) {
- $this->connect();
- }
-
- if ($this->inTransaction) {
- $this->nestedTransactionsCount -= 1;
- }
-
- /*
- * This shouldn't check for being in a transaction since
- * after issuing a SET autocommit=0; we have to commit too.
- */
- if (0 === $this->nestedTransactionsCount) {
- $this->resource->commit();
- $this->inTransaction = false;
- }
-
- return $this;
- }
-
- /**
- * {@inheritDoc}
- *
- * @throws Exception\RuntimeException
- */
- public function rollback()
- {
- if (! $this->isConnected()) {
- throw new Exception\RuntimeException('Must be connected before you can rollback');
- }
-
- if (! $this->inTransaction()) {
- throw new Exception\RuntimeException('Must call beginTransaction() before you can rollback');
- }
-
- $this->resource->rollBack();
-
- $this->inTransaction = false;
- $this->nestedTransactionsCount = 0;
-
- return $this;
- }
-
/**
* {@inheritDoc}
*
- * @throws Exception\InvalidQueryException
+ * @param string $name
*/
- public function execute($sql)
+ #[Override]
+ public function getLastGeneratedValue($name = null): string|int|bool|null
{
- if (! $this->isConnected()) {
- $this->connect();
- }
-
- if ($this->profiler) {
- $this->profiler->profilerStart($sql);
- }
-
- $resultResource = $this->resource->query($sql);
-
- if ($this->profiler) {
- $this->profiler->profilerFinish($sql);
- }
-
- if ($resultResource === false) {
- $errorInfo = $this->resource->errorInfo();
- throw new Exception\InvalidQueryException($errorInfo[2]);
- }
-
- return $this->driver->createResult($resultResource, $sql);
- }
-
- /**
- * Prepare
- *
- * @param string $sql
- * @return Statement
- */
- public function prepare($sql)
- {
- if (! $this->isConnected()) {
- $this->connect();
- }
-
- return $this->driver->createStatement($sql);
- }
-
- /**
- * {@inheritDoc}
- *
- * @param string $name
- * @return string|null|false
- */
- public function getLastGeneratedValue($name = null)
- {
- if (
- $name === null
- && ($this->driverName === 'pgsql' || $this->driverName === 'firebird')
- ) {
- return;
- }
-
try {
return $this->resource->lastInsertId($name);
- } catch (\Exception $e) {
+ } catch (\Exception) {
// do nothing
}
diff --git a/src/Driver/Pdo/Driver.php b/src/Driver/Pdo/Driver.php
deleted file mode 100644
index 2236e0e..0000000
--- a/src/Driver/Pdo/Driver.php
+++ /dev/null
@@ -1,302 +0,0 @@
-registerConnection($connection);
- $this->registerStatementPrototype($statementPrototype ?: new Statement());
- $this->registerResultPrototype($resultPrototype ?: new Result());
- if (is_array($features)) {
- foreach ($features as $name => $feature) {
- $this->addFeature($name, $feature);
- }
- } elseif ($features instanceof AbstractFeature) {
- $this->addFeature($features->getName(), $features);
- } elseif ($features === self::FEATURES_DEFAULT) {
- $this->setupDefaultFeatures();
- }
- }
-
- /**
- * @return $this Provides a fluent interface
- */
- public function setProfiler(Profiler\ProfilerInterface $profiler)
- {
- $this->profiler = $profiler;
- if ($this->connection instanceof Profiler\ProfilerAwareInterface) {
- $this->connection->setProfiler($profiler);
- }
- if ($this->statementPrototype instanceof Profiler\ProfilerAwareInterface) {
- $this->statementPrototype->setProfiler($profiler);
- }
- return $this;
- }
-
- /**
- * @return null|Profiler\ProfilerInterface
- */
- public function getProfiler()
- {
- return $this->profiler;
- }
-
- /**
- * Register connection
- *
- * @return $this Provides a fluent interface
- */
- public function registerConnection(Connection $connection)
- {
- $this->connection = $connection;
- $this->connection->setDriver($this);
- return $this;
- }
-
- /**
- * Register statement prototype
- */
- public function registerStatementPrototype(Statement $statementPrototype)
- {
- $this->statementPrototype = $statementPrototype;
- $this->statementPrototype->setDriver($this);
- }
-
- /**
- * Register result prototype
- */
- public function registerResultPrototype(Result $resultPrototype)
- {
- $this->resultPrototype = $resultPrototype;
- }
-
- /**
- * Add feature
- *
- * @param string $name
- * @param AbstractFeature $feature
- * @return $this Provides a fluent interface
- */
- public function addFeature($name, $feature)
- {
- if ($feature instanceof AbstractFeature) {
- $name = $feature->getName(); // overwrite the name, just in case
- $feature->setDriver($this);
- }
- $this->features[$name] = $feature;
- return $this;
- }
-
- /**
- * Setup the default features for Pdo
- *
- * @return $this Provides a fluent interface
- */
- public function setupDefaultFeatures()
- {
- $driverName = $this->connection->getDriverName();
- if ($driverName === 'sqlite') {
- $this->addFeature(null, new Feature\SqliteRowCounter());
- return $this;
- }
-
- if ($driverName === 'oci') {
- $this->addFeature(null, new Feature\OracleRowCounter());
- return $this;
- }
-
- return $this;
- }
-
- /**
- * Get feature
- *
- * @param string $name
- * @return AbstractFeature|false
- */
- public function getFeature($name)
- {
- if (isset($this->features[$name])) {
- return $this->features[$name];
- }
- return false;
- }
-
- /**
- * Check environment
- */
- public function checkEnvironment()
- {
- if (! extension_loaded('PDO')) {
- throw new Exception\RuntimeException(
- 'The PDO extension is required for this adapter but the extension is not loaded'
- );
- }
- }
-
- /**
- * @return Connection
- */
- public function getConnection()
- {
- return $this->connection;
- }
-
- /**
- * @param string|PDOStatement $sqlOrResource
- * @return Statement
- */
- public function createStatement($sqlOrResource = null)
- {
- $statement = clone $this->statementPrototype;
- if ($sqlOrResource instanceof PDOStatement) {
- $statement->setResource($sqlOrResource);
- } else {
- if (is_string($sqlOrResource)) {
- $statement->setSql($sqlOrResource);
- }
- if (! $this->connection->isConnected()) {
- $this->connection->connect();
- }
- $statement->initialize($this->connection->getResource());
- }
- return $statement;
- }
-
- /**
- * @param resource $resource
- * @param mixed $context
- * @return Result
- */
- public function createResult($resource, $context = null)
- {
- $result = clone $this->resultPrototype;
- $rowCount = null;
-
- // special feature, sqlite PDO counter
- // if (
- // $this->connection->getDriverName() === 'sqlite'
- // && ($sqliteRowCounter = $this->getFeature('SqliteRowCounter'))
- // && $resource->columnCount() > 0
- // ) {
- // $rowCount = $sqliteRowCounter->getRowCountClosure($context);
- // }
-
- // special feature, oracle PDO counter
- // if (
- // $this->connection->getDriverName() === 'oci'
- // && ($oracleRowCounter = $this->getFeature('OracleRowCounter'))
- // && $resource->columnCount() > 0
- // ) {
- // $rowCount = $oracleRowCounter->getRowCountClosure($context);
- // }
-
- $result->initialize($resource, $this->connection->getLastGeneratedValue(), $rowCount);
- return $result;
- }
-
- /**
- * @return Result
- */
- public function getResultPrototype()
- {
- return $this->resultPrototype;
- }
-
- /**
- * @return string
- */
- public function getPrepareType()
- {
- return self::PARAMETERIZATION_NAMED;
- }
-
- /**
- * @param string $name
- * @param string|null $type
- * @return string
- */
- public function formatParameterName($name, $type = null)
- {
- if ($type === null && ! is_numeric($name) || $type === self::PARAMETERIZATION_NAMED) {
- $name = ltrim($name, ':');
- // @see https://bugs.php.net/bug.php?id=43130
- if (preg_match('/[^a-zA-Z0-9_]/', $name)) {
- throw new Exception\RuntimeException(sprintf(
- 'The PDO param %s contains invalid characters.'
- . ' Only alphabetic characters, digits, and underscores (_)'
- . ' are allowed.',
- $name
- ));
- }
- return ':' . $name;
- }
-
- return '?';
- }
-
- /**
- * @param string|null $name
- * @return string|null|false
- */
- public function getLastGeneratedValue($name = null)
- {
- return $this->connection->getLastGeneratedValue($name);
- }
-}
diff --git a/src/Driver/Pdo/DriverFactory.php b/src/Driver/Pdo/DriverFactory.php
deleted file mode 100644
index d15903b..0000000
--- a/src/Driver/Pdo/DriverFactory.php
+++ /dev/null
@@ -1,17 +0,0 @@
-get('config')['db']);
- }
-}
diff --git a/src/Driver/Pdo/Pdo.php b/src/Driver/Pdo/Pdo.php
new file mode 100644
index 0000000..fa89fe7
--- /dev/null
+++ b/src/Driver/Pdo/Pdo.php
@@ -0,0 +1,36 @@
+resultPrototype;
+
+ /** @var null $rowCount */
+ $rowCount = null;
+
+ /** @var string|int|bool|null $lastGeneratedValue */
+ $lastGeneratedValue = $this->getLastGeneratedValue();
+
+ $result->initialize($resource, $lastGeneratedValue, $rowCount);
+ return $result;
+ }
+}
diff --git a/src/Driver/Pdo/Result.php b/src/Driver/Pdo/Result.php
deleted file mode 100644
index 04b53c4..0000000
--- a/src/Driver/Pdo/Result.php
+++ /dev/null
@@ -1,276 +0,0 @@
-resource = $resource;
- $this->generatedValue = $generatedValue;
- $this->rowCount = $rowCount;
-
- return $this;
- }
-
- /**
- * @return void
- */
- public function buffer()
- {
- }
-
- /**
- * @return bool|null
- */
- public function isBuffered()
- {
- return false;
- }
-
- /**
- * @param int $fetchMode
- * @throws Exception\InvalidArgumentException On invalid fetch mode.
- */
- public function setFetchMode($fetchMode)
- {
- if (! in_array($fetchMode, self::VALID_FETCH_MODES, true)) {
- throw new Exception\InvalidArgumentException(
- 'The fetch mode must be one of the PDO::FETCH_* constants.'
- );
- }
-
- $this->fetchMode = (int) $fetchMode;
- }
-
- /**
- * @return int
- */
- public function getFetchMode()
- {
- return $this->fetchMode;
- }
-
- /**
- * Get resource
- *
- * @return mixed
- */
- public function getResource()
- {
- return $this->resource;
- }
-
- /**
- * Get the data
- *
- * @return mixed
- */
- #[ReturnTypeWillChange]
- public function current()
- {
- if ($this->currentComplete) {
- return $this->currentData;
- }
-
- $this->currentData = $this->resource->fetch($this->fetchMode);
- $this->currentComplete = true;
- return $this->currentData;
- }
-
- /**
- * Next
- *
- * @return mixed
- */
- #[ReturnTypeWillChange]
- public function next()
- {
- $this->currentData = $this->resource->fetch($this->fetchMode);
- $this->currentComplete = true;
- $this->position++;
- return $this->currentData;
- }
-
- /**
- * Key
- *
- * @return mixed
- */
- #[ReturnTypeWillChange]
- public function key()
- {
- return $this->position;
- }
-
- /**
- * @throws Exception\RuntimeException
- * @return void
- */
- #[ReturnTypeWillChange]
- public function rewind()
- {
- if ($this->statementMode === self::STATEMENT_MODE_FORWARD && $this->position > 0) {
- throw new Exception\RuntimeException(
- 'This result is a forward only result set, calling rewind() after moving forward is not supported'
- );
- }
- if (! $this->currentComplete) {
- $this->currentData = $this->resource->fetch($this->fetchMode);
- $this->currentComplete = true;
- }
- $this->position = 0;
- }
-
- /**
- * Valid
- *
- * @return bool
- */
- #[ReturnTypeWillChange]
- public function valid()
- {
- return $this->currentData !== false;
- }
-
- /**
- * Count
- *
- * @return int
- */
- #[ReturnTypeWillChange]
- public function count()
- {
- if (is_int($this->rowCount)) {
- return $this->rowCount;
- }
- if ($this->rowCount instanceof Closure) {
- $this->rowCount = (int) call_user_func($this->rowCount);
- } else {
- $this->rowCount = (int) $this->resource->rowCount();
- }
- return $this->rowCount;
- }
-
- /**
- * @return int
- */
- public function getFieldCount()
- {
- return $this->resource->columnCount();
- }
-
- /**
- * Is query result
- *
- * @return bool
- */
- public function isQueryResult()
- {
- return $this->resource->columnCount() > 0;
- }
-
- /**
- * Get affected rows
- *
- * @return int
- */
- public function getAffectedRows()
- {
- return $this->resource->rowCount();
- }
-
- /**
- * @return mixed|null
- */
- public function getGeneratedValue()
- {
- return $this->generatedValue;
- }
-}
diff --git a/src/Driver/Pdo/Statement.php b/src/Driver/Pdo/Statement.php
deleted file mode 100644
index 50968f0..0000000
--- a/src/Driver/Pdo/Statement.php
+++ /dev/null
@@ -1,290 +0,0 @@
-driver = $driver;
- return $this;
- }
-
- /**
- * @return $this Provides a fluent interface
- */
- public function setProfiler(Profiler\ProfilerInterface $profiler)
- {
- $this->profiler = $profiler;
- return $this;
- }
-
- /**
- * @return null|Profiler\ProfilerInterface
- */
- public function getProfiler()
- {
- return $this->profiler;
- }
-
- /**
- * Initialize
- *
- * @return $this Provides a fluent interface
- */
- public function initialize(\PDO $connectionResource)
- {
- $this->pdo = $connectionResource;
- return $this;
- }
-
- /**
- * Set resource
- *
- * @return $this Provides a fluent interface
- */
- public function setResource(PDOStatement $pdoStatement)
- {
- $this->resource = $pdoStatement;
- return $this;
- }
-
- /**
- * Get resource
- *
- * @return mixed
- */
- public function getResource()
- {
- return $this->resource;
- }
-
- /**
- * Set sql
- *
- * @param string $sql
- * @return $this Provides a fluent interface
- */
- public function setSql($sql)
- {
- $this->sql = $sql;
- return $this;
- }
-
- /**
- * Get sql
- *
- * @return string
- */
- public function getSql()
- {
- return $this->sql;
- }
-
- /**
- * @return $this Provides a fluent interface
- */
- public function setParameterContainer(ParameterContainer $parameterContainer)
- {
- $this->parameterContainer = $parameterContainer;
- return $this;
- }
-
- /**
- * @return ParameterContainer
- */
- public function getParameterContainer()
- {
- return $this->parameterContainer;
- }
-
- /**
- * @param string $sql
- * @throws Exception\RuntimeException
- */
- public function prepare($sql = null)
- {
- if ($this->isPrepared) {
- throw new Exception\RuntimeException('This statement has been prepared already');
- }
-
- if ($sql === null) {
- $sql = $this->sql;
- }
-
- $this->resource = $this->pdo->prepare($sql);
-
- if ($this->resource === false) {
- $error = $this->pdo->errorInfo();
- throw new Exception\RuntimeException($error[2]);
- }
-
- $this->isPrepared = true;
- }
-
- /**
- * @return bool
- */
- public function isPrepared()
- {
- return $this->isPrepared;
- }
-
- /**
- * @param null|array|ParameterContainer $parameters
- * @throws Exception\InvalidQueryException
- * @return Result
- */
- public function execute($parameters = null)
- {
- if (! $this->isPrepared) {
- $this->prepare();
- }
-
- /** START Standard ParameterContainer Merging Block */
- if (! $this->parameterContainer instanceof ParameterContainer) {
- if ($parameters instanceof ParameterContainer) {
- $this->parameterContainer = $parameters;
- $parameters = null;
- } else {
- $this->parameterContainer = new ParameterContainer();
- }
- }
-
- if (is_array($parameters)) {
- $this->parameterContainer->setFromArray($parameters);
- }
-
- if ($this->parameterContainer->count() > 0) {
- $this->bindParametersFromContainer();
- }
- /** END Standard ParameterContainer Merging Block */
-
- if ($this->profiler) {
- $this->profiler->profilerStart($this);
- }
-
- try {
- $this->resource->execute();
- } catch (PDOException $e) {
- if ($this->profiler) {
- $this->profiler->profilerFinish();
- }
-
- $code = $e->getCode();
- if (! is_int($code)) {
- $code = 0;
- }
-
- throw new Exception\InvalidQueryException(
- 'Statement could not be executed (' . implode(' - ', $this->resource->errorInfo()) . ')',
- $code,
- $e
- );
- }
-
- if ($this->profiler) {
- $this->profiler->profilerFinish();
- }
-
- return $this->driver->createResult($this->resource, $this);
- }
-
- /**
- * Bind parameters from container
- */
- protected function bindParametersFromContainer()
- {
- if ($this->parametersBound) {
- return;
- }
-
- $parameters = $this->parameterContainer->getNamedArray();
- foreach ($parameters as $name => &$value) {
- if (is_bool($value)) {
- $type = \PDO::PARAM_BOOL;
- } elseif (is_int($value)) {
- $type = \PDO::PARAM_INT;
- } else {
- $type = \PDO::PARAM_STR;
- }
- if ($this->parameterContainer->offsetHasErrata($name)) {
- switch ($this->parameterContainer->offsetGetErrata($name)) {
- case ParameterContainer::TYPE_INTEGER:
- $type = \PDO::PARAM_INT;
- break;
- case ParameterContainer::TYPE_NULL:
- $type = \PDO::PARAM_NULL;
- break;
- case ParameterContainer::TYPE_LOB:
- $type = \PDO::PARAM_LOB;
- break;
- }
- }
-
- // parameter is named or positional, value is reference
- $parameter = is_int($name) ? $name + 1 : $this->driver->formatParameterName($name);
- $this->resource->bindParam($parameter, $value, $type);
- }
- }
-
- /**
- * Perform a deep clone
- *
- * @return void
- */
- public function __clone()
- {
- $this->isPrepared = false;
- $this->parametersBound = false;
- $this->resource = null;
- if ($this->parameterContainer) {
- $this->parameterContainer = clone $this->parameterContainer;
- }
- }
-}
diff --git a/src/Metadata/Source/MysqlMetadata.php b/src/Metadata/Source/MysqlMetadata.php
new file mode 100644
index 0000000..f19bf36
--- /dev/null
+++ b/src/Metadata/Source/MysqlMetadata.php
@@ -0,0 +1,518 @@
+data['schemas'])) {
+ return;
+ }
+ $this->prepareDataHierarchy('schemas');
+
+ $p = $this->adapter->getPlatform();
+
+ $sql = 'SELECT ' . $p->quoteIdentifier('SCHEMA_NAME')
+ . ' FROM ' . $p->quoteIdentifierChain(['INFORMATION_SCHEMA', 'SCHEMATA'])
+ . ' WHERE ' . $p->quoteIdentifier('SCHEMA_NAME')
+ . ' != \'INFORMATION_SCHEMA\'';
+
+ $results = $this->adapter->query($sql, AdapterInterface::QUERY_MODE_EXECUTE);
+
+ $schemas = [];
+ foreach ($results->toArray() as $row) {
+ $schemas[] = $row['SCHEMA_NAME'];
+ }
+
+ $this->data['schemas'] = $schemas;
+ }
+
+ protected function loadTableNameData(string $schema): void
+ {
+ if (isset($this->data['table_names'][$schema])) {
+ return;
+ }
+ $this->prepareDataHierarchy('table_names', $schema);
+
+ $p = $this->adapter->getPlatform();
+
+ $isColumns = [
+ ['T', 'TABLE_NAME'],
+ ['T', 'TABLE_TYPE'],
+ ['V', 'VIEW_DEFINITION'],
+ ['V', 'CHECK_OPTION'],
+ ['V', 'IS_UPDATABLE'],
+ ];
+
+ array_walk($isColumns, function (&$c) use ($p) {
+ $c = $p->quoteIdentifierChain($c);
+ });
+
+ $sql = 'SELECT ' . implode(', ', $isColumns)
+ . ' FROM ' . $p->quoteIdentifierChain(['INFORMATION_SCHEMA', 'TABLES']) . 'T'
+
+ . ' LEFT JOIN ' . $p->quoteIdentifierChain(['INFORMATION_SCHEMA', 'VIEWS']) . ' V'
+ . ' ON ' . $p->quoteIdentifierChain(['T', 'TABLE_SCHEMA'])
+ . ' = ' . $p->quoteIdentifierChain(['V', 'TABLE_SCHEMA'])
+ . ' AND ' . $p->quoteIdentifierChain(['T', 'TABLE_NAME'])
+ . ' = ' . $p->quoteIdentifierChain(['V', 'TABLE_NAME'])
+
+ . ' WHERE ' . $p->quoteIdentifierChain(['T', 'TABLE_TYPE'])
+ . ' IN (\'BASE TABLE\', \'VIEW\')';
+
+ if ($schema !== self::DEFAULT_SCHEMA) {
+ $sql .= ' AND ' . $p->quoteIdentifierChain(['T', 'TABLE_SCHEMA'])
+ . ' = ' . $p->quoteTrustedValue($schema);
+ } else {
+ $sql .= ' AND ' . $p->quoteIdentifierChain(['T', 'TABLE_SCHEMA'])
+ . ' != \'INFORMATION_SCHEMA\'';
+ }
+
+ $results = $this->adapter->query($sql, AdapterInterface::QUERY_MODE_EXECUTE);
+
+ $tables = [];
+ foreach ($results->toArray() as $row) {
+ $tables[$row['TABLE_NAME']] = [
+ 'table_type' => $row['TABLE_TYPE'],
+ 'view_definition' => $row['VIEW_DEFINITION'],
+ 'check_option' => $row['CHECK_OPTION'],
+ 'is_updatable' => 'YES' === $row['IS_UPDATABLE'],
+ ];
+ }
+
+ $this->data['table_names'][$schema] = $tables;
+ }
+
+ protected function loadColumnData(string $table, string $schema): void
+ {
+ if (isset($this->data['columns'][$schema][$table])) {
+ return;
+ }
+ $this->prepareDataHierarchy('columns', $schema, $table);
+ $p = $this->adapter->getPlatform();
+
+ $isColumns = [
+ ['C', 'ORDINAL_POSITION'],
+ ['C', 'COLUMN_DEFAULT'],
+ ['C', 'IS_NULLABLE'],
+ ['C', 'DATA_TYPE'],
+ ['C', 'CHARACTER_MAXIMUM_LENGTH'],
+ ['C', 'CHARACTER_OCTET_LENGTH'],
+ ['C', 'NUMERIC_PRECISION'],
+ ['C', 'NUMERIC_SCALE'],
+ ['C', 'COLUMN_NAME'],
+ ['C', 'COLUMN_TYPE'],
+ ];
+
+ array_walk($isColumns, function (&$c) use ($p) {
+ $c = $p->quoteIdentifierChain($c);
+ });
+
+ $sql = 'SELECT ' . implode(', ', $isColumns)
+ . ' FROM ' . $p->quoteIdentifierChain(['INFORMATION_SCHEMA', 'TABLES']) . 'T'
+ . ' INNER JOIN ' . $p->quoteIdentifierChain(['INFORMATION_SCHEMA', 'COLUMNS']) . 'C'
+ . ' ON ' . $p->quoteIdentifierChain(['T', 'TABLE_SCHEMA'])
+ . ' = ' . $p->quoteIdentifierChain(['C', 'TABLE_SCHEMA'])
+ . ' AND ' . $p->quoteIdentifierChain(['T', 'TABLE_NAME'])
+ . ' = ' . $p->quoteIdentifierChain(['C', 'TABLE_NAME'])
+ . ' WHERE ' . $p->quoteIdentifierChain(['T', 'TABLE_TYPE'])
+ . ' IN (\'BASE TABLE\', \'VIEW\')'
+ . ' AND ' . $p->quoteIdentifierChain(['T', 'TABLE_NAME'])
+ . ' = ' . $p->quoteTrustedValue($table);
+
+ if ($schema !== self::DEFAULT_SCHEMA) {
+ $sql .= ' AND ' . $p->quoteIdentifierChain(['T', 'TABLE_SCHEMA'])
+ . ' = ' . $p->quoteTrustedValue($schema);
+ } else {
+ $sql .= ' AND ' . $p->quoteIdentifierChain(['T', 'TABLE_SCHEMA'])
+ . ' != \'INFORMATION_SCHEMA\'';
+ }
+
+ $results = $this->adapter->query($sql, AdapterInterface::QUERY_MODE_EXECUTE);
+ $columns = [];
+ foreach ($results->toArray() as $row) {
+ $erratas = [];
+ $matches = [];
+ if (preg_match('/^(?:enum|set)\((.+)\)$/i', $row['COLUMN_TYPE'], $matches)) {
+ $permittedValues = $matches[1];
+ if (
+ preg_match_all(
+ "/\\s*'((?:[^']++|'')*+)'\\s*(?:,|\$)/",
+ $permittedValues,
+ $matches,
+ PREG_PATTERN_ORDER
+ )
+ ) {
+ $permittedValues = str_replace("''", "'", $matches[1]);
+ } else {
+ $permittedValues = [$permittedValues];
+ }
+ $erratas['permitted_values'] = $permittedValues;
+ }
+ $columns[$row['COLUMN_NAME']] = [
+ 'ordinal_position' => $row['ORDINAL_POSITION'],
+ 'column_default' => $row['COLUMN_DEFAULT'],
+ 'is_nullable' => 'YES' === $row['IS_NULLABLE'],
+ 'data_type' => $row['DATA_TYPE'],
+ 'character_maximum_length' => $row['CHARACTER_MAXIMUM_LENGTH'],
+ 'character_octet_length' => $row['CHARACTER_OCTET_LENGTH'],
+ 'numeric_precision' => $row['NUMERIC_PRECISION'],
+ 'numeric_scale' => $row['NUMERIC_SCALE'],
+ 'numeric_unsigned' => str_contains($row['COLUMN_TYPE'], 'unsigned'),
+ 'erratas' => $erratas,
+ ];
+ }
+
+ $this->data['columns'][$schema][$table] = $columns;
+ }
+
+ protected function loadConstraintData(string $table, string $schema): void
+ {
+ // phpcs:disable WebimpressCodingStandard.NamingConventions.ValidVariableName.NotCamelCaps
+ if (isset($this->data['constraints'][$schema][$table])) {
+ return;
+ }
+
+ $this->prepareDataHierarchy('constraints', $schema, $table);
+
+ $isColumns = [
+ ['T', 'TABLE_NAME'],
+ ['TC', 'CONSTRAINT_NAME'],
+ ['TC', 'CONSTRAINT_TYPE'],
+ ['KCU', 'COLUMN_NAME'],
+ ['RC', 'MATCH_OPTION'],
+ ['RC', 'UPDATE_RULE'],
+ ['RC', 'DELETE_RULE'],
+ ['KCU', 'REFERENCED_TABLE_SCHEMA'],
+ ['KCU', 'REFERENCED_TABLE_NAME'],
+ ['KCU', 'REFERENCED_COLUMN_NAME'],
+ ];
+
+ $p = $this->adapter->getPlatform();
+
+ array_walk($isColumns, function (&$c) use ($p) {
+ $c = $p->quoteIdentifierChain($c);
+ });
+
+ $sql = 'SELECT ' . implode(', ', $isColumns)
+ . ' FROM ' . $p->quoteIdentifierChain(['INFORMATION_SCHEMA', 'TABLES']) . ' T'
+
+ . ' INNER JOIN ' . $p->quoteIdentifierChain(['INFORMATION_SCHEMA', 'TABLE_CONSTRAINTS']) . ' TC'
+ . ' ON ' . $p->quoteIdentifierChain(['T', 'TABLE_SCHEMA'])
+ . ' = ' . $p->quoteIdentifierChain(['TC', 'TABLE_SCHEMA'])
+ . ' AND ' . $p->quoteIdentifierChain(['T', 'TABLE_NAME'])
+ . ' = ' . $p->quoteIdentifierChain(['TC', 'TABLE_NAME'])
+
+ . ' LEFT JOIN ' . $p->quoteIdentifierChain(['INFORMATION_SCHEMA', 'KEY_COLUMN_USAGE']) . ' KCU'
+ . ' ON ' . $p->quoteIdentifierChain(['TC', 'TABLE_SCHEMA'])
+ . ' = ' . $p->quoteIdentifierChain(['KCU', 'TABLE_SCHEMA'])
+ . ' AND ' . $p->quoteIdentifierChain(['TC', 'TABLE_NAME'])
+ . ' = ' . $p->quoteIdentifierChain(['KCU', 'TABLE_NAME'])
+ . ' AND ' . $p->quoteIdentifierChain(['TC', 'CONSTRAINT_NAME'])
+ . ' = ' . $p->quoteIdentifierChain(['KCU', 'CONSTRAINT_NAME'])
+
+ . ' LEFT JOIN ' . $p->quoteIdentifierChain(['INFORMATION_SCHEMA', 'REFERENTIAL_CONSTRAINTS']) . ' RC'
+ . ' ON ' . $p->quoteIdentifierChain(['TC', 'CONSTRAINT_SCHEMA'])
+ . ' = ' . $p->quoteIdentifierChain(['RC', 'CONSTRAINT_SCHEMA'])
+ . ' AND ' . $p->quoteIdentifierChain(['TC', 'CONSTRAINT_NAME'])
+ . ' = ' . $p->quoteIdentifierChain(['RC', 'CONSTRAINT_NAME'])
+
+ . ' WHERE ' . $p->quoteIdentifierChain(['T', 'TABLE_NAME'])
+ . ' = ' . $p->quoteTrustedValue($table)
+ . ' AND ' . $p->quoteIdentifierChain(['T', 'TABLE_TYPE'])
+ . ' IN (\'BASE TABLE\', \'VIEW\')';
+
+ if ($schema !== self::DEFAULT_SCHEMA) {
+ $sql .= ' AND ' . $p->quoteIdentifierChain(['T', 'TABLE_SCHEMA'])
+ . ' = ' . $p->quoteTrustedValue($schema);
+ } else {
+ $sql .= ' AND ' . $p->quoteIdentifierChain(['T', 'TABLE_SCHEMA'])
+ . ' != \'INFORMATION_SCHEMA\'';
+ }
+
+ $sql .= ' ORDER BY CASE ' . $p->quoteIdentifierChain(['TC', 'CONSTRAINT_TYPE'])
+ . " WHEN 'PRIMARY KEY' THEN 1"
+ . " WHEN 'UNIQUE' THEN 2"
+ . " WHEN 'FOREIGN KEY' THEN 3"
+ . " ELSE 4 END"
+
+ . ', ' . $p->quoteIdentifierChain(['TC', 'CONSTRAINT_NAME'])
+ . ', ' . $p->quoteIdentifierChain(['KCU', 'ORDINAL_POSITION']);
+
+ $results = $this->adapter->query($sql, AdapterInterface::QUERY_MODE_EXECUTE);
+
+ $realName = null;
+ $constraints = [];
+ foreach ($results->toArray() as $row) {
+ if ($row['CONSTRAINT_NAME'] !== $realName) {
+ $realName = $row['CONSTRAINT_NAME'];
+ $isFK = 'FOREIGN KEY' === $row['CONSTRAINT_TYPE'];
+ if ($isFK) {
+ $name = $realName;
+ } else {
+ $name = '_laminas_' . $row['TABLE_NAME'] . '_' . $realName;
+ }
+ $constraints[$name] = [
+ 'constraint_name' => $name,
+ 'constraint_type' => $row['CONSTRAINT_TYPE'],
+ 'table_name' => $row['TABLE_NAME'],
+ 'columns' => [],
+ ];
+ if ($isFK) {
+ $constraints[$name]['referenced_table_schema'] = $row['REFERENCED_TABLE_SCHEMA'];
+ $constraints[$name]['referenced_table_name'] = $row['REFERENCED_TABLE_NAME'];
+ $constraints[$name]['referenced_columns'] = [];
+ $constraints[$name]['match_option'] = $row['MATCH_OPTION'];
+ $constraints[$name]['update_rule'] = $row['UPDATE_RULE'];
+ $constraints[$name]['delete_rule'] = $row['DELETE_RULE'];
+ }
+ }
+ $constraints[$name]['columns'][] = $row['COLUMN_NAME'];
+ if ($isFK) {
+ $constraints[$name]['referenced_columns'][] = $row['REFERENCED_COLUMN_NAME'];
+ }
+ }
+
+ $this->data['constraints'][$schema][$table] = $constraints;
+ // phpcs:enable WebimpressCodingStandard.NamingConventions.ValidVariableName.NotCamelCaps
+ }
+
+ protected function loadConstraintDataNames(string $schema): void
+ {
+ if (isset($this->data['constraint_names'][$schema])) {
+ return;
+ }
+
+ $this->prepareDataHierarchy('constraint_names', $schema);
+
+ $p = $this->adapter->getPlatform();
+
+ $isColumns = [
+ ['TC', 'TABLE_NAME'],
+ ['TC', 'CONSTRAINT_NAME'],
+ ['TC', 'CONSTRAINT_TYPE'],
+ ];
+
+ array_walk($isColumns, function (&$c) use ($p) {
+ $c = $p->quoteIdentifierChain($c);
+ });
+
+ $sql = 'SELECT ' . implode(', ', $isColumns)
+ . ' FROM ' . $p->quoteIdentifierChain(['INFORMATION_SCHEMA', 'TABLES']) . 'T'
+ . ' INNER JOIN ' . $p->quoteIdentifierChain(['INFORMATION_SCHEMA', 'TABLE_CONSTRAINTS']) . 'TC'
+ . ' ON ' . $p->quoteIdentifierChain(['T', 'TABLE_SCHEMA'])
+ . ' = ' . $p->quoteIdentifierChain(['TC', 'TABLE_SCHEMA'])
+ . ' AND ' . $p->quoteIdentifierChain(['T', 'TABLE_NAME'])
+ . ' = ' . $p->quoteIdentifierChain(['TC', 'TABLE_NAME'])
+ . ' WHERE ' . $p->quoteIdentifierChain(['T', 'TABLE_TYPE'])
+ . ' IN (\'BASE TABLE\', \'VIEW\')';
+
+ if ($schema !== self::DEFAULT_SCHEMA) {
+ $sql .= ' AND ' . $p->quoteIdentifierChain(['T', 'TABLE_SCHEMA'])
+ . ' = ' . $p->quoteTrustedValue($schema);
+ } else {
+ $sql .= ' AND ' . $p->quoteIdentifierChain(['T', 'TABLE_SCHEMA'])
+ . ' != \'INFORMATION_SCHEMA\'';
+ }
+
+ $results = $this->adapter->query($sql, AdapterInterface::QUERY_MODE_EXECUTE);
+
+ $data = [];
+ foreach ($results->toArray() as $row) {
+ $data[] = array_change_key_case($row, CASE_LOWER);
+ }
+
+ $this->data['constraint_names'][$schema] = $data;
+ }
+
+ protected function loadConstraintDataKeys(string $schema): void
+ {
+ if (isset($this->data['constraint_keys'][$schema])) {
+ return;
+ }
+
+ $this->prepareDataHierarchy('constraint_keys', $schema);
+
+ $p = $this->adapter->getPlatform();
+
+ $isColumns = [
+ ['T', 'TABLE_NAME'],
+ ['KCU', 'CONSTRAINT_NAME'],
+ ['KCU', 'COLUMN_NAME'],
+ ['KCU', 'ORDINAL_POSITION'],
+ ];
+
+ array_walk($isColumns, function (&$c) use ($p) {
+ $c = $p->quoteIdentifierChain($c);
+ });
+
+ $sql = 'SELECT ' . implode(', ', $isColumns)
+ . ' FROM ' . $p->quoteIdentifierChain(['INFORMATION_SCHEMA', 'TABLES']) . 'T'
+
+ . ' INNER JOIN ' . $p->quoteIdentifierChain(['INFORMATION_SCHEMA', 'KEY_COLUMN_USAGE']) . 'KCU'
+ . ' ON ' . $p->quoteIdentifierChain(['T', 'TABLE_SCHEMA'])
+ . ' = ' . $p->quoteIdentifierChain(['KCU', 'TABLE_SCHEMA'])
+ . ' AND ' . $p->quoteIdentifierChain(['T', 'TABLE_NAME'])
+ . ' = ' . $p->quoteIdentifierChain(['KCU', 'TABLE_NAME'])
+
+ . ' WHERE ' . $p->quoteIdentifierChain(['T', 'TABLE_TYPE'])
+ . ' IN (\'BASE TABLE\', \'VIEW\')';
+
+ if ($schema !== self::DEFAULT_SCHEMA) {
+ $sql .= ' AND ' . $p->quoteIdentifierChain(['T', 'TABLE_SCHEMA'])
+ . ' = ' . $p->quoteTrustedValue($schema);
+ } else {
+ $sql .= ' AND ' . $p->quoteIdentifierChain(['T', 'TABLE_SCHEMA'])
+ . ' != \'INFORMATION_SCHEMA\'';
+ }
+
+ $results = $this->adapter->query($sql, AdapterInterface::QUERY_MODE_EXECUTE);
+
+ $data = [];
+ foreach ($results->toArray() as $row) {
+ $data[] = array_change_key_case($row, CASE_LOWER);
+ }
+
+ $this->data['constraint_keys'][$schema] = $data;
+ }
+
+ protected function loadConstraintReferences(string $table, string $schema): void
+ {
+ parent::loadConstraintReferences($table, $schema);
+
+ $p = $this->adapter->getPlatform();
+
+ $isColumns = [
+ ['RC', 'TABLE_NAME'],
+ ['RC', 'CONSTRAINT_NAME'],
+ ['RC', 'UPDATE_RULE'],
+ ['RC', 'DELETE_RULE'],
+ ['KCU', 'REFERENCED_TABLE_SCHEMA'],
+ ['KCU', 'REFERENCED_TABLE_NAME'],
+ ['KCU', 'REFERENCED_COLUMN_NAME'],
+ ];
+
+ array_walk($isColumns, function (&$c) use ($p) {
+ $c = $p->quoteIdentifierChain($c);
+ });
+
+ $sql = 'SELECT ' . implode(', ', $isColumns)
+ . 'FROM ' . $p->quoteIdentifierChain(['INFORMATION_SCHEMA', 'TABLES']) . 'T'
+
+ . ' INNER JOIN ' . $p->quoteIdentifierChain(['INFORMATION_SCHEMA', 'REFERENTIAL_CONSTRAINTS']) . 'RC'
+ . ' ON ' . $p->quoteIdentifierChain(['T', 'TABLE_SCHEMA'])
+ . ' = ' . $p->quoteIdentifierChain(['RC', 'CONSTRAINT_SCHEMA'])
+ . ' AND ' . $p->quoteIdentifierChain(['T', 'TABLE_NAME'])
+ . ' = ' . $p->quoteIdentifierChain(['RC', 'TABLE_NAME'])
+
+ . ' INNER JOIN ' . $p->quoteIdentifierChain(['INFORMATION_SCHEMA', 'KEY_COLUMN_USAGE']) . 'KCU'
+ . ' ON ' . $p->quoteIdentifierChain(['RC', 'CONSTRAINT_SCHEMA'])
+ . ' = ' . $p->quoteIdentifierChain(['KCU', 'TABLE_SCHEMA'])
+ . ' AND ' . $p->quoteIdentifierChain(['RC', 'TABLE_NAME'])
+ . ' = ' . $p->quoteIdentifierChain(['KCU', 'TABLE_NAME'])
+ . ' AND ' . $p->quoteIdentifierChain(['RC', 'CONSTRAINT_NAME'])
+ . ' = ' . $p->quoteIdentifierChain(['KCU', 'CONSTRAINT_NAME'])
+
+ . 'WHERE ' . $p->quoteIdentifierChain(['T', 'TABLE_TYPE'])
+ . ' IN (\'BASE TABLE\', \'VIEW\')';
+
+ if ($schema !== self::DEFAULT_SCHEMA) {
+ $sql .= ' AND ' . $p->quoteIdentifierChain(['T', 'TABLE_SCHEMA'])
+ . ' = ' . $p->quoteTrustedValue($schema);
+ } else {
+ $sql .= ' AND ' . $p->quoteIdentifierChain(['T', 'TABLE_SCHEMA'])
+ . ' != \'INFORMATION_SCHEMA\'';
+ }
+
+ $results = $this->adapter->query($sql, AdapterInterface::QUERY_MODE_EXECUTE);
+
+ $data = [];
+ foreach ($results->toArray() as $row) {
+ $data[] = array_change_key_case($row, CASE_LOWER);
+ }
+
+ $this->data['constraint_references'][$schema] = $data;
+ }
+
+ protected function loadTriggerData(string $schema): void
+ {
+ if (isset($this->data['triggers'][$schema])) {
+ return;
+ }
+
+ $this->prepareDataHierarchy('triggers', $schema);
+
+ $p = $this->adapter->getPlatform();
+
+ $isColumns = [
+ // 'TRIGGER_CATALOG',
+ // 'TRIGGER_SCHEMA',
+ 'TRIGGER_NAME',
+ 'EVENT_MANIPULATION',
+ 'EVENT_OBJECT_CATALOG',
+ 'EVENT_OBJECT_SCHEMA',
+ 'EVENT_OBJECT_TABLE',
+ 'ACTION_ORDER',
+ 'ACTION_CONDITION',
+ 'ACTION_STATEMENT',
+ 'ACTION_ORIENTATION',
+ 'ACTION_TIMING',
+ 'ACTION_REFERENCE_OLD_TABLE',
+ 'ACTION_REFERENCE_NEW_TABLE',
+ 'ACTION_REFERENCE_OLD_ROW',
+ 'ACTION_REFERENCE_NEW_ROW',
+ 'CREATED',
+ ];
+
+ array_walk($isColumns, function (&$c) use ($p) {
+ $c = $p->quoteIdentifier($c);
+ });
+
+ $sql = 'SELECT ' . implode(', ', $isColumns)
+ . ' FROM ' . $p->quoteIdentifierChain(['INFORMATION_SCHEMA', 'TRIGGERS'])
+ . ' WHERE ';
+
+ if ($schema !== self::DEFAULT_SCHEMA) {
+ $sql .= $p->quoteIdentifier('TRIGGER_SCHEMA')
+ . ' = ' . $p->quoteTrustedValue($schema);
+ } else {
+ $sql .= $p->quoteIdentifier('TRIGGER_SCHEMA')
+ . ' != \'INFORMATION_SCHEMA\'';
+ }
+
+ $results = $this->adapter->query($sql, AdapterInterface::QUERY_MODE_EXECUTE);
+
+ $data = [];
+ foreach ($results->toArray() as $row) {
+ $row = array_change_key_case($row, CASE_LOWER);
+ if (null !== $row['created']) {
+ $row['created'] = new DateTime($row['created']);
+ }
+ $data[$row['trigger_name']] = $row;
+ }
+
+ $this->data['triggers'][$schema] = $data;
+ }
+}
diff --git a/src/Module.php b/src/Module.php
deleted file mode 100644
index 57b4f71..0000000
--- a/src/Module.php
+++ /dev/null
@@ -1,15 +0,0 @@
- (new ConfigProvider())->getDependencyConfig(),
- ];
- }
-}
diff --git a/src/Platform/Mysql.php b/src/Platform/Mysql.php
index 6967a50..c87a870 100644
--- a/src/Platform/Mysql.php
+++ b/src/Platform/Mysql.php
@@ -1,19 +1,24 @@
setDriver($driver);
- }
+ public function __construct(
+ protected readonly DriverInterface|mysqli|PDO $driver
+ ) {
}
/**
- * @param \Laminas\Db\Adapter\Driver\Mysqli\Mysqli|\Laminas\Db\Adapter\Driver\Pdo\Pdo|\mysqli|\PDO $driver
- * @return $this Provides a fluent interface
- * @throws InvalidArgumentException
+ * {@inheritDoc}
*/
- public function setDriver($driver)
+ #[Override]
+ public function getName(): string
{
- // handle Laminas\Db drivers
- if (
- $driver instanceof Mysqli\Mysqli
- || ($driver instanceof Pdo\Pdo && $driver->getDatabasePlatformName() === 'Mysql')
- || $driver instanceof \mysqli
- || ($driver instanceof \PDO && $driver->getAttribute(\PDO::ATTR_DRIVER_NAME) === 'mysql')
- ) {
- $this->driver = $driver;
- return $this;
- }
-
- throw new Exception\InvalidArgumentException(
- '$driver must be a Mysqli or Mysql PDO Laminas\Db\Adapter\Driver, Mysqli instance or MySQL PDO instance'
- );
+ return self::PLATFORM_NAME;
}
/**
- * todo: if needed return Backed Enum
* {@inheritDoc}
*/
- public function getName()
+ #[Override]
+ public function getSqlPlatformDecorator(): PlatformDecoratorInterface
{
- return 'MySQL';
+ return new SqlPlatform();
}
/**
* {@inheritDoc}
*/
- public function quoteIdentifierChain($identifierChain)
+ #[Override]
+ public function quoteIdentifierChain(array|string $identifierChain): string
{
return '`' . implode('`.`', (array) str_replace('`', '``', $identifierChain)) . '`';
}
@@ -84,7 +71,8 @@ public function quoteIdentifierChain($identifierChain)
/**
* {@inheritDoc}
*/
- public function quoteValue($value)
+ #[Override]
+ public function quoteValue(string $value): string
{
$quotedViaDriverValue = $this->quoteViaDriver($value);
@@ -94,29 +82,28 @@ public function quoteValue($value)
/**
* {@inheritDoc}
*/
- public function quoteTrustedValue($value)
+ #[Override]
+ public function quoteTrustedValue(int|float|string|bool $value): ?string
{
$quotedViaDriverValue = $this->quoteViaDriver($value);
return $quotedViaDriverValue ?? parent::quoteTrustedValue($value);
}
- /**
- * @param string $value
- * @return string|null
- */
- protected function quoteViaDriver($value)
+ protected function quoteViaDriver(string $value): ?string
{
if ($this->driver instanceof DriverInterface) {
+ // todo: verify this can not return a PDOStatement instance
$resource = $this->driver->getConnection()->getResource();
} else {
$resource = $this->driver;
}
- if ($resource instanceof \mysqli) {
+ if ($resource instanceof mysqli) {
return '\'' . $resource->real_escape_string($value) . '\'';
}
- if ($resource instanceof \PDO) {
+
+ if ($resource instanceof PDO) {
return $resource->quote($value);
}
diff --git a/src/Platform/PlatformFactory.php b/src/Platform/PlatformFactory.php
deleted file mode 100644
index 9606bc6..0000000
--- a/src/Platform/PlatformFactory.php
+++ /dev/null
@@ -1,17 +0,0 @@
- 0,
'zerofill' => 1,
diff --git a/src/Sql/Platform/Ddl/CreateTableDecorator.php b/src/Sql/Platform/Mysql/Ddl/CreateTableDecorator.php
similarity index 96%
rename from src/Sql/Platform/Ddl/CreateTableDecorator.php
rename to src/Sql/Platform/Mysql/Ddl/CreateTableDecorator.php
index b36ac7b..07985cf 100644
--- a/src/Sql/Platform/Ddl/CreateTableDecorator.php
+++ b/src/Sql/Platform/Mysql/Ddl/CreateTableDecorator.php
@@ -1,10 +1,12 @@
container);
+ self::assertInstanceOf(AdapterInterface::class, $adapter);
+ }
+}
diff --git a/test/integration/Container/MysqliConnectionFactoryTest.php b/test/integration/Container/MysqliConnectionFactoryTest.php
new file mode 100644
index 0000000..f345089
--- /dev/null
+++ b/test/integration/Container/MysqliConnectionFactoryTest.php
@@ -0,0 +1,36 @@
+getAdapter([
+ 'db' => [
+ 'driver' => 'Mysqli',
+ ],
+ ]);
+
+ $factory = new MysqliConnectionFactory();
+ $connection = $factory($this->container);
+
+ self::assertInstanceOf(ConnectionInterface::class, $connection);
+ self::assertInstanceOf(Connection::class, $connection);
+ }
+}
diff --git a/test/integration/Container/MysqliDriverFactoryTest.php b/test/integration/Container/MysqliDriverFactoryTest.php
new file mode 100644
index 0000000..4bcf801
--- /dev/null
+++ b/test/integration/Container/MysqliDriverFactoryTest.php
@@ -0,0 +1,34 @@
+getAdapter([
+ 'db' => [
+ 'driver' => 'Mysqli',
+ ],
+ ]);
+ $factory = new MysqliDriverFactory();
+ $driver = $factory($this->container);
+ self::assertInstanceOf(DriverInterface::class, $driver);
+ $this->assertInstanceOf(Mysqli::class, $driver);
+ }
+}
diff --git a/test/integration/Container/MysqliResultFactoryTest.php b/test/integration/Container/MysqliResultFactoryTest.php
new file mode 100644
index 0000000..30ed58a
--- /dev/null
+++ b/test/integration/Container/MysqliResultFactoryTest.php
@@ -0,0 +1,30 @@
+container);
+
+ self::assertInstanceOf(ResultInterface::class, $result);
+ self::assertInstanceOf(Result::class, $result);
+ }
+}
diff --git a/test/integration/Container/MysqliStatementFactoryTest.php b/test/integration/Container/MysqliStatementFactoryTest.php
new file mode 100644
index 0000000..419a0c1
--- /dev/null
+++ b/test/integration/Container/MysqliStatementFactoryTest.php
@@ -0,0 +1,39 @@
+getAdapter([
+ 'db' => [
+ 'driver' => 'Mysqli',
+ 'options' => [
+ 'buffer_results' => false,
+ ],
+ ],
+ ]);
+
+ $factory = new MysqliStatementFactory();
+ $statement = $factory($this->container);
+
+ self::assertInstanceOf(StatementInterface::class, $statement);
+ self::assertInstanceOf(Statement::class, $statement);
+ }
+}
diff --git a/test/integration/Container/PdoConnectionFactoryTest.php b/test/integration/Container/PdoConnectionFactoryTest.php
new file mode 100644
index 0000000..463e463
--- /dev/null
+++ b/test/integration/Container/PdoConnectionFactoryTest.php
@@ -0,0 +1,32 @@
+container);
+ self::assertInstanceOf(ConnectionInterface::class, $instance);
+ self::assertInstanceOf(PdoConnectionInterface::class, $instance);
+ self::assertInstanceOf(Connection::class, $instance);
+ }
+}
diff --git a/test/integration/Container/PdoDriverFactoryTest.php b/test/integration/Container/PdoDriverFactoryTest.php
new file mode 100644
index 0000000..49ad463
--- /dev/null
+++ b/test/integration/Container/PdoDriverFactoryTest.php
@@ -0,0 +1,31 @@
+container);
+
+ self::assertInstanceOf(PdoDriverInterface::class, $instance);
+ self::assertInstanceOf(Pdo::class, $instance);
+ }
+}
diff --git a/test/integration/Container/PdoResultFactoryTest.php b/test/integration/Container/PdoResultFactoryTest.php
new file mode 100644
index 0000000..81d3177
--- /dev/null
+++ b/test/integration/Container/PdoResultFactoryTest.php
@@ -0,0 +1,31 @@
+container);
+
+ self::assertInstanceOf(ResultInterface::class, $result);
+ self::assertInstanceOf(Result::class, $result);
+ }
+}
diff --git a/test/integration/Container/PdoStatementFactoryTest.php b/test/integration/Container/PdoStatementFactoryTest.php
new file mode 100644
index 0000000..21e8712
--- /dev/null
+++ b/test/integration/Container/PdoStatementFactoryTest.php
@@ -0,0 +1,30 @@
+container);
+ self::assertInstanceOf(StatementInterface::class, $statement);
+ self::assertInstanceOf(Statement::class, $statement);
+ }
+}
diff --git a/test/integration/Container/PlatformInterfaceFactoryTest.php b/test/integration/Container/PlatformInterfaceFactoryTest.php
new file mode 100644
index 0000000..a5e1cf5
--- /dev/null
+++ b/test/integration/Container/PlatformInterfaceFactoryTest.php
@@ -0,0 +1,30 @@
+container);
+ self::assertInstanceOf(PlatformInterface::class, $instance);
+ self::assertInstanceOf(Mysql::class, $instance);
+ }
+}
diff --git a/test/integration/Container/TestAsset/SetupTrait.php b/test/integration/Container/TestAsset/SetupTrait.php
new file mode 100644
index 0000000..3b728b3
--- /dev/null
+++ b/test/integration/Container/TestAsset/SetupTrait.php
@@ -0,0 +1,108 @@
+ []];
+
+ protected ?AdapterInterface $adapter;
+
+ protected AdapterManager $adapterManager;
+
+ protected ContainerInterface $container;
+
+ protected DriverInterface|string|null $driver = null;
+
+ protected function setUp(): void
+ {
+ $this->getAdapter();
+ parent::setUp();
+ }
+
+ protected function getAdapter(array $config = []): AdapterInterface&Adapter
+ {
+ $connectionConfig = [
+ 'db' => [
+ 'driver' => $this->driver ?? Pdo::class,
+ 'connection' => [
+ 'hostname' => (string) getenv('TESTS_PHPDB_ADAPTER_MYSQL_HOSTNAME') ?: 'localhost',
+ 'username' => (string) getenv('TESTS_PHPDB_ADAPTER_MYSQL_USERNAME'),
+ 'password' => (string) getenv('TESTS_PHPDB_ADAPTER_MYSQL_PASSWORD'),
+ 'database' => (string) getenv('TESTS_PHPDB_ADAPTER_MYSQL_DATABASE'),
+ 'port' => (string) getenv('TESTS_PHPDB_ADAPTER_MYSQL_PORT') ?: '3306',
+ 'charset' => 'utf8',
+ 'driver_options' => [],
+ ],
+ 'options' => [
+ 'buffer_results' => false,
+ ],
+ ],
+ ];
+
+ // merge service config from both PhpDb and PhpDb\Adapter\Mysql
+ $serviceManagerConfig = ArrayUtils::merge(
+ (new LaminasDbConfigProvider())()['dependencies'],
+ (new ConfigProvider())()['dependencies']
+ );
+
+ $serviceManagerConfig = ArrayUtils::merge(
+ $serviceManagerConfig,
+ $connectionConfig
+ );
+
+ // prefer passed config over environment variables
+ if ($config !== []) {
+ $serviceManagerConfig = ArrayUtils::merge($serviceManagerConfig, $config);
+ }
+
+ $serviceManagerConfig = ArrayUtils::merge(
+ $serviceManagerConfig,
+ [
+ 'services' => [
+ 'config' => $serviceManagerConfig,
+ ],
+ ]
+ );
+
+ $this->config = $serviceManagerConfig;
+ $this->container = new ServiceManager($this->config);
+ $this->adapterManager = $this->container->get(AdapterManager::class);
+ $this->adapter = $this->adapterManager->get(AdapterInterface::class);
+
+ return $this->adapter;
+ }
+
+ protected function getConfig(): array
+ {
+ return $this->config;
+ }
+
+ protected function getHostname(): string
+ {
+ return $this->getConfig()['db']['connection']['hostname'];
+ }
+}
diff --git a/test/integration/Driver/Mysqli/ConnectionTest.php b/test/integration/Driver/Mysqli/ConnectionTest.php
new file mode 100644
index 0000000..7d679b3
--- /dev/null
+++ b/test/integration/Driver/Mysqli/ConnectionTest.php
@@ -0,0 +1,33 @@
+ ['driver' => 'Mysqli']];
+ /** @var Connection $connection */
+ $connection = $this->getAdapter($config)->getDriver()->getConnection();
+ $connection->connect();
+ self::assertTrue($connection->isConnected());
+ $connection->disconnect();
+ self::assertFalse($connection->isConnected());
+ }
+}
diff --git a/test/integration/Driver/Mysqli/TableGatewayTest.php b/test/integration/Driver/Mysqli/TableGatewayTest.php
new file mode 100644
index 0000000..f7686eb
--- /dev/null
+++ b/test/integration/Driver/Mysqli/TableGatewayTest.php
@@ -0,0 +1,70 @@
+getAdapter([
+ 'db' => [
+ 'driver' => Mysqli::class,
+ 'options' => [
+ 'buffer_results' => true,
+ ],
+ ],
+ ]);
+
+ $tableGateway = new TableGateway('test', $adapter);
+ /** @var AbstractResultSet $rowset */
+ $rowset = $tableGateway->select('id = 0');
+ $this->assertEquals(true, $rowset->isBuffered());
+
+ $this->assertNull($rowset->current());
+
+ $adapter->getDriver()->getConnection()->disconnect();
+ }
+
+ /**
+ * @see https://github.com/zendframework/zend-db/issues/330
+ */
+ public function testSelectWithEmptyCurrentWithoutBufferResult(): void
+ {
+ /** @var AdapterInterface $adapter */
+ $adapter = $this->getAdapter([
+ 'db' => [
+ 'driver' => 'mysqli',
+ 'options' => [
+ 'buffer_results' => false,
+ ],
+ ],
+ ]);
+ $tableGateway = new TableGateway('test', $adapter);
+ /** @var AbstractResultSet $rowset */
+ $rowset = $tableGateway->select('id = 0');
+ $this->assertEquals(false, $rowset->isBuffered());
+
+ $this->assertNull($rowset->current());
+
+ $adapter->getDriver()->getConnection()->disconnect();
+ }
+}
diff --git a/test/integration/Driver/Pdo/AbstractAdapterTestCase.php b/test/integration/Driver/Pdo/AbstractAdapterTestCase.php
new file mode 100644
index 0000000..71a2ba6
--- /dev/null
+++ b/test/integration/Driver/Pdo/AbstractAdapterTestCase.php
@@ -0,0 +1,85 @@
+getAdapter()->getDriver()->getConnection();
+ $this->assertInstanceOf(ConnectionInterface::class, $connection);
+ }
+
+ public function testGetCurrentSchema(): void
+ {
+ /** @var AdapterInterface&SchemaAwareInterface $adapter */
+ $adapter = $this->getAdapter();
+ $schema = $adapter->getCurrentSchema();
+ self::assertIsString($schema);
+ self::assertNotEmpty($schema);
+ }
+
+ public function testDriverDisconnectAfterQuoteWithPlatform(): void
+ {
+ $isTcpConnection = $this->isTcpConnection();
+
+ /** @var AdapterInterface $adapter */
+ $adapter = $this->getAdapter([
+ 'db' => [
+ 'driver' => Pdo::class,
+ ],
+ ]);
+ $adapter->getDriver()->getConnection()->connect();
+ self::assertTrue($adapter->getDriver()->getConnection()->isConnected());
+ if ($isTcpConnection) {
+ self::assertTrue($adapter->getDriver()->getConnection()->isConnected());
+ }
+
+ $adapter->getDriver()->getConnection()->disconnect();
+ self::assertFalse($adapter->getDriver()->getConnection()->isConnected());
+ if ($isTcpConnection) {
+ self::assertFalse($adapter->getDriver()->getConnection()->isConnected());
+ }
+
+ $adapter->getDriver()->getConnection()->connect();
+ self::assertTrue($adapter->getDriver()->getConnection()->isConnected());
+ if ($isTcpConnection) {
+ self::assertTrue($adapter->getDriver()->getConnection()->isConnected());
+ }
+
+ $adapter->getPlatform()->quoteValue('test');
+
+ $adapter->getDriver()->getConnection()->disconnect();
+
+ self::assertFalse($adapter->getDriver()->getConnection()->isConnected());
+ if ($isTcpConnection) {
+ self::assertFalse($adapter->getDriver()->getConnection()->isConnected());
+ }
+ }
+
+ protected function isTcpConnection(): bool
+ {
+ $hostName = $this->getHostname();
+ return $hostName !== 'localhost' && $hostName !== '127.0.0.1';
+ }
+}
diff --git a/test/integration/Driver/Pdo/AdapterTest.php b/test/integration/Driver/Pdo/AdapterTest.php
new file mode 100644
index 0000000..1b5c571
--- /dev/null
+++ b/test/integration/Driver/Pdo/AdapterTest.php
@@ -0,0 +1,15 @@
+getAdapter()->getDriver()->getConnection();
+ self::assertInstanceOf(PDO::class, $connection->getResource());
+ }
+
+ public function testExecute(): void
+ {
+ $connection = $this->getAdapter()->getDriver()->getConnection();
+ /** @var ResultInterface&Result $result */
+ $result = $connection->execute('SELECT \'foo\'');
+ self::assertInstanceOf(ResultInterface::class, $result);
+ self::assertInstanceOf(Result::class, $result);
+ }
+
+ public function testPrepare(): void
+ {
+ /** @var ConnectionInterface&PdoConnectionInterface&AbstractConnection&AbstractPdoConnection&Connection $connection */
+ $connection = $this->getAdapter()->getDriver()->getConnection();
+ /** @var StatementInterface&Statement $statement */
+ $statement = $connection->prepare('SELECT \'foo\'');
+ self::assertInstanceOf(StatementInterface::class, $statement);
+ self::assertInstanceOf(Statement::class, $statement);
+ }
+
+ public function testGetLastGeneratedValue(): void
+ {
+ /** @var ConnectionInterface&PdoConnectionInterface&AbstractConnection&AbstractPdoConnection&Connection $connection */
+ $connection = $this->getAdapter()->getDriver()->getConnection();
+ $connection->connect();
+ $lastId = (int) $connection->getLastGeneratedValue();
+ self::assertIsInt($lastId);
+ $connection->disconnect();
+ }
+
+ public function testConnectMethodReturnsConnectionInterface(): void
+ {
+ /** @var ConnectionInterface&PdoConnectionInterface&AbstractConnection&AbstractPdoConnection&Connection $connection */
+ $connection = $this->getAdapter()->getDriver()->getConnection();
+ self::assertInstanceOf(ConnectionInterface::class, $connection->connect());
+ $connection->disconnect();
+ }
+
+ /**
+ * @todo Implement testBeginTransaction().
+ */
+ public function testBeginTransaction(): never
+ {
+ // Remove the following lines when you implement this test.
+ $this->markTestIncomplete(
+ 'This test has not been implemented yet.'
+ );
+ }
+
+ /**
+ * @todo Implement testCommit().
+ */
+ public function testCommit(): never
+ {
+ // Remove the following lines when you implement this test.
+ $this->markTestIncomplete(
+ 'This test has not been implemented yet.'
+ );
+ }
+
+ /**
+ * @todo Implement testRollback().
+ */
+ public function testRollback(): never
+ {
+ // Remove the following lines when you implement this test.
+ $this->markTestIncomplete(
+ 'This test has not been implemented yet.'
+ );
+ }
+}
diff --git a/test/integration/Driver/Pdo/QueryTest.php b/test/integration/Driver/Pdo/QueryTest.php
new file mode 100644
index 0000000..9cc2c26
--- /dev/null
+++ b/test/integration/Driver/Pdo/QueryTest.php
@@ -0,0 +1,125 @@
+,
+ * 2: array
+ * }>
+ */
+ public static function getQueriesWithRowResult(): array
+ {
+ return [
+ ['SELECT * FROM test WHERE id = ?', [1], ['id' => 1, 'name' => 'foo', 'value' => 'bar']],
+ ['SELECT * FROM test WHERE id = :id', [':id' => 1], ['id' => 1, 'name' => 'foo', 'value' => 'bar']],
+ ['SELECT * FROM test WHERE id = :id', ['id' => 1], ['id' => 1, 'name' => 'foo', 'value' => 'bar']],
+ ['SELECT * FROM test WHERE name = ?', ['123'], ['id' => '4', 'name' => '123', 'value' => 'bar']],
+ [
+ // name is string, but given parameter is int, can lead to unexpected result
+ 'SELECT * FROM test WHERE name = ?',
+ [123],
+ ['id' => '3', 'name' => '123a', 'value' => 'bar'],
+ ],
+ ];
+ }
+
+ /**
+ * @throws Exception
+ */
+ #[DataProvider('getQueriesWithRowResult')]
+ public function testQuery(string $query, array $params, array $expected): void
+ {
+ /** @todo Have AdapterInterface implement query */
+ $result = $this->getAdapter()->query($query, $params);
+ $this->assertInstanceOf(ResultSet::class, $result);
+ $current = $result->current();
+ // test as array value
+ $this->assertEquals($expected, (array) $current);
+ // test as object value
+ /** @var string $value */
+ foreach ($expected as $key => $value) {
+ $this->assertEquals($value, $current->$key);
+ }
+ }
+
+ /**
+ * @see https://github.com/zendframework/zend-db/issues/288
+ *
+ * @throws Exception
+ */
+ public function testSetSessionTimeZone(): void
+ {
+ $result = $this->getAdapter()->query('SET @@session.time_zone = :tz', [':tz' => 'SYSTEM']);
+ $this->assertInstanceOf(PdoResult::class, $result);
+ }
+
+ /**
+ * @throws Exception
+ */
+ public function testSelectWithNotPermittedBindParamName(): void
+ {
+ $this->expectException(RuntimeException::class);
+ $this->getAdapter()->query('SET @@session.time_zone = :tz$', [':tz$' => 'SYSTEM']);
+ }
+
+ /**
+ * @see https://github.com/laminas/laminas-db/issues/47
+ */
+ public function testNamedParameters(): void
+ {
+ $this->assertNotNull($this->adapter);
+ $sql = new Sql($this->adapter);
+
+ $insert = $sql->update('test');
+ $insert->set([
+ 'name' => ':name',
+ 'value' => ':value',
+ ])->where(['id' => ':id']);
+ /** @var StatementInterface $stmt */
+ $stmt = $sql->prepareStatementForSqlObject($insert);
+ $this->assertInstanceOf(StatementInterface::class, $stmt);
+
+ //positional parameters
+ $stmt->execute([
+ 'foo',
+ 'bar',
+ 1,
+ ]);
+
+ //"mapped" named parameters
+ $stmt->execute([
+ 'c_0' => 'foo',
+ 'c_1' => 'bar',
+ 'where1' => 1,
+ ]);
+
+ //real named parameters
+ $stmt->execute([
+ 'id' => 1,
+ 'name' => 'foo',
+ 'value' => 'bar',
+ ]);
+ }
+}
diff --git a/test/integration/Driver/Pdo/TableGatewayAndAdapterTest.php b/test/integration/Driver/Pdo/TableGatewayAndAdapterTest.php
new file mode 100644
index 0000000..97cab1b
--- /dev/null
+++ b/test/integration/Driver/Pdo/TableGatewayAndAdapterTest.php
@@ -0,0 +1,59 @@
+getAdapter();
+ $adapter->query('SELECT VERSION();');
+ $table = new TableGateway(
+ 'test',
+ $this->adapter
+ );
+ $select = $table->getSql()->select()->where(['name' => 'foo']);
+ /** @var AbstractResultSet $result */
+ $result = $table->selectWith($select);
+ self::assertCount(3, $result->current());
+ }
+
+ protected function tearDown(): void
+ {
+ if ($this->adapter->getDriver()->getConnection()->isConnected()) {
+ $this->adapter->getDriver()->getConnection()->disconnect();
+ }
+ $this->adapter = null;
+ }
+
+ public static function connections(): array
+ {
+ return array_fill(0, 200, []);
+ }
+}
diff --git a/test/integration/Driver/Pdo/TableGatewayTest.php b/test/integration/Driver/Pdo/TableGatewayTest.php
new file mode 100644
index 0000000..47ef2c6
--- /dev/null
+++ b/test/integration/Driver/Pdo/TableGatewayTest.php
@@ -0,0 +1,137 @@
+getAdapter();
+ $tableGateway = new TableGateway('test', $adapter);
+ $this->assertInstanceOf(TableGateway::class, $tableGateway);
+ }
+
+ public function testSelect(): void
+ {
+ $tableGateway = new TableGateway('test', $this->getAdapter());
+ /** @var ResultSet $rowset */
+ $rowset = $tableGateway->select();
+ $this->assertTrue(count($rowset) > 0);
+ /** @var ArrayObject $row */
+ foreach ($rowset as $row) {
+ $this->assertTrue(isset($row->id));
+ $this->assertNotEmpty(isset($row->name));
+ $this->assertNotEmpty(isset($row->value));
+ }
+ }
+
+ public function testInsert(): void
+ {
+ $tableGateway = new TableGateway('test', $this->getAdapter());
+
+ $tableGateway->select();
+ $data = [
+ 'name' => 'test_name',
+ 'value' => 'test_value',
+ ];
+ $affectedRows = $tableGateway->insert($data);
+ $this->assertEquals(1, $affectedRows);
+ /** @var ResultSet $rowSet */
+ $rowSet = $tableGateway->select(['id' => $tableGateway->getLastInsertValue()]);
+ /** @var ArrayObject $row */
+ $row = $rowSet->current();
+
+ foreach ($data as $key => $value) {
+ $this->assertEquals($row->$key, $value);
+ }
+ }
+
+ /**
+ * @see https://github.com/zendframework/zend-db/issues/35
+ * @see https://github.com/zendframework/zend-db/pull/178
+ */
+ public function testInsertWithExtendedCharsetFieldName(): int|string
+ {
+ $tableGateway = new TableGateway('test_charset', $this->getAdapter());
+
+ $affectedRows = $tableGateway->insert([
+ 'field$' => 'test_value1',
+ 'field_' => 'test_value2',
+ ]);
+ $this->assertEquals(1, $affectedRows);
+
+ return $tableGateway->getLastInsertValue();
+ }
+
+ #[Depends('testInsertWithExtendedCharsetFieldName')]
+ public function testUpdateWithExtendedCharsetFieldName(mixed $id): void
+ {
+ $tableGateway = new TableGateway('test_charset', $this->getAdapter());
+
+ $data = [
+ 'field$' => 'test_value3',
+ 'field_' => 'test_value4',
+ ];
+ $affectedRows = $tableGateway->update($data, ['id' => $id]);
+ $this->assertEquals(1, $affectedRows);
+ /** @var ResultSet $rowSet */
+ $rowSet = $tableGateway->select(['id' => $id]);
+ /** @var ArrayObject $row */
+ $row = $rowSet->current();
+
+ foreach ($data as $key => $value) {
+ $this->assertEquals($row->$key, $value);
+ }
+ }
+
+ #[DataProvider('tableProvider')]
+ public function testTableGatewayWithMetadataFeature(array|string|TableIdentifier $table): void
+ {
+ /** @var AdapterInterface $adapter */
+ $adapter = $this->getAdapter(['db' => ['driver' => 'pdo']]);
+ $tableGateway = new TableGateway(
+ $table,
+ $adapter,
+ new MetadataFeature(
+ new MysqlMetadata($adapter),
+ )
+ );
+
+ self::assertInstanceOf(TableGateway::class, $tableGateway);
+ self::assertSame($table, $tableGateway->getTable());
+ }
+
+ /** @psalm-return array */
+ public static function tableProvider(): array
+ {
+ return [
+ 'string' => ['test'],
+ 'aliased string' => [['foo' => 'test']],
+ 'TableIdentifier' => [new TableIdentifier('test')],
+ 'aliased TableIdentifier' => [['foo' => new TableIdentifier('test')]],
+ ];
+ }
+}
diff --git a/test/integration/Extension/IntegrationTestStartedListener.php b/test/integration/Extension/IntegrationTestStartedListener.php
new file mode 100644
index 0000000..c542b52
--- /dev/null
+++ b/test/integration/Extension/IntegrationTestStartedListener.php
@@ -0,0 +1,44 @@
+testSuite()->name() !== 'integration test') {
+ return;
+ }
+
+ if (getenv('TESTS_PHPDB_ADAPTER_MYSQL')) {
+ $this->fixtureLoaders[] = new MysqlFixtureLoader();
+ }
+
+ if (empty($this->fixtureLoaders)) {
+ return;
+ }
+
+ printf("\nIntegration test started.\n");
+
+ foreach ($this->fixtureLoaders as $fixtureLoader) {
+ $fixtureLoader->createDatabase();
+ }
+ }
+}
diff --git a/test/integration/Extension/IntegrationTestStoppedListener.php b/test/integration/Extension/IntegrationTestStoppedListener.php
new file mode 100644
index 0000000..3038083
--- /dev/null
+++ b/test/integration/Extension/IntegrationTestStoppedListener.php
@@ -0,0 +1,33 @@
+testSuite()->name() !== 'integration test'
+ || empty($this->fixtureLoaders)
+ ) {
+ return;
+ }
+
+ printf("\nIntegration test ended.\n");
+
+ foreach ($this->fixtureLoaders as $fixtureLoader) {
+ $fixtureLoader->dropDatabase();
+ }
+ }
+}
diff --git a/test/integration/Extension/ListenerExtension.php b/test/integration/Extension/ListenerExtension.php
new file mode 100644
index 0000000..3ebd817
--- /dev/null
+++ b/test/integration/Extension/ListenerExtension.php
@@ -0,0 +1,24 @@
+registerSubscribers(
+ new IntegrationTestStartedListener(),
+ new IntegrationTestStoppedListener(),
+ );
+ }
+}
diff --git a/test/integration/FixtureLoader/FixtureLoader.php b/test/integration/FixtureLoader/FixtureLoader.php
new file mode 100644
index 0000000..be39bbf
--- /dev/null
+++ b/test/integration/FixtureLoader/FixtureLoader.php
@@ -0,0 +1,13 @@
+connect();
+
+ if (
+ false === $this->pdo->exec(sprintf(
+ "CREATE DATABASE IF NOT EXISTS %s",
+ getenv('TESTS_PHPDB_ADAPTER_MYSQL_DATABASE')
+ ))
+ ) {
+ throw new Exception(sprintf(
+ "I cannot create the MySQL %s test database: %s",
+ getenv('TESTS_PHPDB_ADAPTER_MYSQL_DATABASE'),
+ print_r($this->pdo->errorInfo(), true)
+ ));
+ }
+
+ $this->pdo->exec('USE ' . getenv('TESTS_PHPDB_ADAPTER_MYSQL_DATABASE'));
+
+ if (false === $this->pdo->exec(file_get_contents($this->fixtureFile))) {
+ throw new Exception(sprintf(
+ "I cannot create the table for %s database. Check the %s file. %s ",
+ getenv('TESTS_PHPDB_ADAPTER_MYSQL_DATABASE'),
+ $this->fixtureFile,
+ print_r($this->pdo->errorInfo(), true)
+ ));
+ }
+
+ $this->disconnect();
+ }
+
+ public function dropDatabase(): void
+ {
+ $this->connect();
+
+ $this->pdo->exec(sprintf(
+ "DROP DATABASE IF EXISTS %s",
+ getenv('TESTS_PHPDB_ADAPTER_MYSQL_DATABASE')
+ ));
+
+ $this->disconnect();
+ }
+
+ protected function connect(): void
+ {
+ $dsn = 'mysql:host=' . getenv('TESTS_PHPDB_ADAPTER_MYSQL_HOSTNAME');
+ if (getenv('TESTS_PHPDB_ADAPTER_MYSQL_PORT')) {
+ $dsn .= ';port=' . getenv('TESTS_PHPDB_ADAPTER_MYSQL_PORT');
+ }
+
+ $this->pdo = new PDO(
+ $dsn,
+ getenv('TESTS_PHPDB_ADAPTER_MYSQL_USERNAME'),
+ getenv('TESTS_PHPDB_ADAPTER_MYSQL_PASSWORD')
+ );
+ }
+
+ protected function disconnect(): void
+ {
+ $this->pdo = null;
+ }
+}
diff --git a/test/integration/Platform/MysqlTest.php b/test/integration/Platform/MysqlTest.php
new file mode 100644
index 0000000..d79aba7
--- /dev/null
+++ b/test/integration/Platform/MysqlTest.php
@@ -0,0 +1,115 @@
+ */
+ public array $adapters = [];
+
+ protected array $mysqliParams;
+
+ protected array $pdoParams;
+
+ #[Override]
+ protected function setUp(): void
+ {
+ //$this->markTestSkipped(self::class . ' test need refactored');
+
+ if (extension_loaded('mysqli')) {
+ $this->mysqliParams = [
+ 'hostname' => getenv('TESTS_PHPDB_ADAPTER_MYSQL_HOSTNAME'),
+ 'username' => getenv('TESTS_PHPDB_ADAPTER_MYSQL_USERNAME'),
+ 'password' => getenv('TESTS_PHPDB_ADAPTER_MYSQL_PASSWORD'),
+ 'database' => getenv('TESTS_PHPDB_ADAPTER_MYSQL_DATABASE'),
+ ];
+
+ $this->adapters['mysqli'] = new \mysqli(
+ $this->mysqliParams['hostname'],
+ $this->mysqliParams['username'],
+ $this->mysqliParams['password'],
+ $this->mysqliParams['database']
+ );
+ }
+
+ if (extension_loaded('pdo')) {
+ $this->pdoParams = [
+ 'hostname' => getenv('TESTS_PHPDB_ADAPTER_MYSQL_HOSTNAME'),
+ 'username' => getenv('TESTS_PHPDB_ADAPTER_MYSQL_USERNAME'),
+ 'password' => getenv('TESTS_PHPDB_ADAPTER_MYSQL_PASSWORD'),
+ 'database' => getenv('TESTS_PHPDB_ADAPTER_MYSQL_DATABASE'),
+ ];
+
+ $this->adapters['pdo_mysql'] = new \PDO(
+ 'mysql:host=' . $this->pdoParams['hostname'] . ';dbname='
+ . $this->pdoParams['database'],
+ $this->pdoParams['username'],
+ $this->pdoParams['password']
+ );
+ }
+ }
+
+ /**
+ * @return void
+ */
+ public function testQuoteValueWithMysqli()
+ {
+ if (! $this->adapters['mysqli'] instanceof \Mysqli) {
+ $this->markTestSkipped('MySQL (Mysqli) not configured in unit test configuration file');
+ }
+ $mysql = new Mysql($this->adapters['mysqli']);
+ $value = $mysql->quoteValue('value');
+ self::assertEquals('\'value\'', $value);
+
+ $mysql = new Mysql(
+ new Mysqli\Mysqli(
+ new Mysqli\Connection($this->mysqliParams),
+ new Mysqli\Statement(),
+ new Mysqli\Result()
+ )
+ );
+ $value = $mysql->quoteValue('value');
+ self::assertEquals('\'value\'', $value);
+ }
+
+ /**
+ * @return void
+ */
+ public function testQuoteValueWithPdoMysql()
+ {
+ if (! $this->adapters['pdo_mysql'] instanceof \PDO) {
+ $this->markTestSkipped('MySQL (PDO_Mysql) not configured in unit test configuration file');
+ }
+ $mysql = new Mysql($this->adapters['pdo_mysql']);
+ $value = $mysql->quoteValue('value');
+ self::assertEquals('\'value\'', $value);
+
+ $mysql = new Mysql(
+ new PdoDriver(
+ new Connection($this->pdoParams),
+ new Pdo\Statement(),
+ new Pdo\Result()
+ )
+ );
+ $value = $mysql->quoteValue('value');
+ self::assertEquals('\'value\'', $value);
+ }
+}
diff --git a/test/integration/TestFixtures/mysql.sql b/test/integration/TestFixtures/mysql.sql
new file mode 100644
index 0000000..ddddd7e
--- /dev/null
+++ b/test/integration/TestFixtures/mysql.sql
@@ -0,0 +1,55 @@
+DROP TABLE IF EXISTS test;
+CREATE TABLE IF NOT EXISTS test (
+ id INT NOT NULL AUTO_INCREMENT,
+ name VARCHAR(255) NOT NULL,
+ value VARCHAR(255) NOT NULL,
+ PRIMARY KEY (id)
+);
+
+INSERT INTO test (name, value) VALUES
+('foo', 'bar'),
+('bar', 'baz'),
+('123a', 'bar'),
+('123', 'bar');
+
+DROP TABLE IF EXISTS test_charset;
+CREATE TABLE IF NOT EXISTS test_charset (
+ id INT NOT NULL AUTO_INCREMENT,
+ field$ VARCHAR(255) NOT NULL,
+ field_ VARCHAR(255) NOT NULL,
+ PRIMARY KEY (id)
+);
+
+INSERT INTO test_charset (field$, field_) VALUES
+('foo', 'bar'),
+('bar', 'baz');
+
+DROP TABLE IF EXISTS test_audit_trail;
+CREATE TABLE IF NOT EXISTS test_audit_trail (
+ id INT NOT NULL AUTO_INCREMENT,
+ test_id INT NOT NULL,
+ test_value_old VARCHAR(255) NOT NULL,
+ test_value_new VARCHAR(255) NOT NULL,
+ changed TIMESTAMP,
+ PRIMARY KEY (id)
+);
+
+DROP VIEW IF EXISTS test_view;
+CREATE VIEW test_view
+AS
+SELECT
+ name AS v_name,
+ value AS v_value
+FROM
+ test;
+
+DROP TRIGGER IF EXISTS after_test_update;
+CREATE TRIGGER after_test_update
+ AFTER UPDATE ON test
+ FOR EACH ROW
+ INSERT INTO test_audit_trail
+ SET
+ test_id = OLD.id,
+ test_value_old = OLD.value,
+ test_value_new = NEW.value,
+ changed = NOW();
diff --git a/test/unit/Driver/Mysqli/ConnectionTest.php b/test/unit/Driver/Mysqli/ConnectionTest.php
new file mode 100644
index 0000000..3774a3b
--- /dev/null
+++ b/test/unit/Driver/Mysqli/ConnectionTest.php
@@ -0,0 +1,201 @@
+markTestSkipped('Mysqli test disabled');
+ // }
+ $this->connection = new Connection([]);
+ }
+
+ /**
+ * Tears down the fixture, for example, closes a network connection.
+ * This method is called after a test is executed.
+ */
+ protected function tearDown(): void
+ {
+ }
+
+ public function testSetDriver(): void
+ {
+ $this->markTestIncomplete('This test needs refactored');
+ //self::assertEquals($this->connection, $this->connection->setDriver(new Mysqli([])));
+ }
+
+ public function testSetConnectionParameters(): void
+ {
+ self::assertEquals($this->connection, $this->connection->setConnectionParameters([]));
+ }
+
+ public function testGetConnectionParameters(): void
+ {
+ $this->connection->setConnectionParameters(['foo' => 'bar']);
+ self::assertEquals(['foo' => 'bar'], $this->connection->getConnectionParameters());
+ }
+
+ public function testNonSecureConnection(): void
+ {
+ $mysqli = $this->createMockMysqli(0);
+ /** @var Connection $connection */
+ $connection = $this->createMockConnection(
+ $mysqli,
+ [
+ 'hostname' => 'localhost',
+ 'username' => 'superuser',
+ 'password' => '1234',
+ 'database' => 'main',
+ 'port' => 123,
+ ]
+ );
+
+ $connection->connect();
+ }
+
+ public function testSslConnection(): void
+ {
+ $mysqli = $this->createMockMysqli(MYSQLI_CLIENT_SSL);
+ /** @var Connection $connection */
+ $connection = $this->createMockConnection(
+ $mysqli,
+ [
+ 'hostname' => 'localhost',
+ 'username' => 'superuser',
+ 'password' => '1234',
+ 'database' => 'main',
+ 'port' => 123,
+ 'use_ssl' => true,
+ ]
+ );
+
+ $connection->connect();
+ }
+
+ public function testSslConnectionNoVerify(): void
+ {
+ $mysqli = $this->createMockMysqli(MYSQLI_CLIENT_SSL | MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT);
+ /** @var Connection $connection */
+ $connection = $this->createMockConnection(
+ $mysqli,
+ [
+ 'hostname' => 'localhost',
+ 'username' => 'superuser',
+ 'password' => '1234',
+ 'database' => 'main',
+ 'port' => 123,
+ 'use_ssl' => true,
+ 'driver_options' => [
+ MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT => true,
+ ],
+ ]
+ );
+
+ $connection->connect();
+ }
+
+ public function testConnectionFails(): void
+ {
+ $connection = new Connection([]);
+
+ $this->expectException(RuntimeException::class);
+ $this->expectExceptionMessage('Connection error');
+ $connection->connect();
+ }
+
+ /**
+ * Create a mock mysqli
+ *
+ * @param int $flags Expected flags to real_connect
+ */
+ protected function createMockMysqli(int $flags): MockObject
+ {
+ $mysqli = $this->getMockBuilder(mysqli::class)->getMock();
+ $mysqli->expects($flags ? $this->once() : $this->never())
+ ->method('ssl_set')
+ ->with(
+ $this->equalTo(''),
+ $this->equalTo(''),
+ $this->equalTo(''),
+ $this->equalTo(''),
+ $this->equalTo('')
+ );
+
+ if ($flags === 0) {
+ // Do not pass $flags argument if invalid flags provided
+ $mysqli->expects($this->once())
+ ->method('real_connect')
+ ->with(
+ $this->equalTo('localhost'),
+ $this->equalTo('superuser'),
+ $this->equalTo('1234'),
+ $this->equalTo('main'),
+ $this->equalTo(123),
+ $this->equalTo('')
+ )
+ ->willReturn(true);
+ return $mysqli;
+ }
+
+ $mysqli->expects($this->once())
+ ->method('real_connect')
+ ->with(
+ $this->equalTo('localhost'),
+ $this->equalTo('superuser'),
+ $this->equalTo('1234'),
+ $this->equalTo('main'),
+ $this->equalTo(123),
+ $this->equalTo(''),
+ $this->equalTo($flags)
+ )
+ ->willReturn(true);
+
+ return $mysqli;
+ }
+
+ /**
+ * Create a mock connection
+ *
+ * @param MockObject $mysqli Mock mysqli object
+ * @param array $params Connection params
+ */
+ protected function createMockConnection(MockObject $mysqli, array $params): MockObject
+ {
+ $connection = $this->getMockBuilder(Connection::class)
+ ->onlyMethods(['createResource'])
+ ->setConstructorArgs([$params])
+ ->getMock();
+ $connection->expects($this->once())
+ ->method('createResource')
+ ->willReturn($mysqli);
+
+ return $connection;
+ }
+}
diff --git a/test/unit/Driver/Pdo/ConnectionTest.php b/test/unit/Driver/Pdo/ConnectionTest.php
new file mode 100644
index 0000000..10635bc
--- /dev/null
+++ b/test/unit/Driver/Pdo/ConnectionTest.php
@@ -0,0 +1,98 @@
+connection = new Connection();
+ }
+
+ /**
+ * Test getResource method tries to connect to the database, it should never return null
+ */
+ public function testResource(): void
+ {
+ $this->expectException(RuntimeException::class);
+ $this->connection->getResource();
+ }
+
+ /**
+ * Test getConnectedDsn returns a DSN string if it has been set
+ */
+ public function testGetDsn(): void
+ {
+ $dsn = "mysql:";
+ $this->connection->setConnectionParameters(['dsn' => $dsn]);
+ try {
+ $this->connection->connect();
+ } catch (Exception) {
+ }
+ $responseString = $this->connection->getDsn();
+
+ self::assertEquals($dsn, $responseString);
+ }
+
+ #[Group('2622')]
+ public function testArrayOfConnectionParametersCreatesCorrectDsn(): void
+ {
+ $this->connection->setConnectionParameters([
+ 'driver' => 'pdo_mysql',
+ 'charset' => 'utf8',
+ 'dbname' => 'foo',
+ 'port' => '3306',
+ 'unix_socket' => '/var/run/mysqld/mysqld.sock',
+ ]);
+ try {
+ $this->connection->connect();
+ } catch (Exception) {
+ }
+ $responseString = $this->connection->getDsn();
+
+ self::assertStringStartsWith('mysql:', $responseString);
+ self::assertStringContainsString('charset=utf8', $responseString);
+ self::assertStringContainsString('dbname=foo', $responseString);
+ self::assertStringContainsString('port=3306', $responseString);
+ self::assertStringContainsString('unix_socket=/var/run/mysqld/mysqld.sock', $responseString);
+ }
+
+ public function testHostnameAndUnixSocketThrowsInvalidConnectionParametersException(): void
+ {
+ $this->expectException(InvalidConnectionParametersException::class);
+ $this->expectExceptionMessage(
+ 'Ambiguous connection parameters, both hostname and unix_socket parameters were set'
+ );
+
+ $this->connection->setConnectionParameters([
+ 'driver' => 'pdo_mysql',
+ 'host' => '127.0.0.1',
+ 'dbname' => 'foo',
+ 'port' => '3306',
+ 'unix_socket' => '/var/run/mysqld/mysqld.sock',
+ ]);
+ $this->connection->connect();
+ }
+}
diff --git a/test/unit/Driver/Pdo/ConnectionTransactionsTest.php b/test/unit/Driver/Pdo/ConnectionTransactionsTest.php
new file mode 100644
index 0000000..efc6f94
--- /dev/null
+++ b/test/unit/Driver/Pdo/ConnectionTransactionsTest.php
@@ -0,0 +1,161 @@
+wrapper = new ConnectionWrapper();
+ }
+
+ public function testBeginTransactionReturnsInstanceOfConnection(): void
+ {
+ self::assertInstanceOf(Connection::class, $this->wrapper->beginTransaction());
+ }
+
+ public function testBeginTransactionSetsInTransactionAtTrue(): void
+ {
+ $this->wrapper->beginTransaction();
+ self::assertTrue($this->wrapper->inTransaction());
+ }
+
+ public function testCommitReturnsInstanceOfConnection(): void
+ {
+ $this->wrapper->beginTransaction();
+ self::assertInstanceOf(Connection::class, $this->wrapper->commit());
+ }
+
+ public function testCommitSetsInTransactionAtFalse(): void
+ {
+ $this->wrapper->beginTransaction();
+ $this->wrapper->commit();
+ self::assertFalse($this->wrapper->inTransaction());
+ }
+
+ /**
+ * Standalone commit after a SET autocommit=0;
+ */
+ public function testCommitWithoutBeginReturnsInstanceOfConnection(): void
+ {
+ self::assertInstanceOf(Connection::class, $this->wrapper->commit());
+ }
+
+ public function testNestedTransactionsCommit(): void
+ {
+ $nested = 0;
+
+ self::assertFalse($this->wrapper->inTransaction());
+
+ // 1st transaction
+ $this->wrapper->beginTransaction();
+ self::assertTrue($this->wrapper->inTransaction());
+ self::assertSame(++$nested, $this->wrapper->getNestedTransactionsCount());
+
+ // 2nd transaction
+ $this->wrapper->beginTransaction();
+ self::assertTrue($this->wrapper->inTransaction());
+ self::assertSame(++$nested, $this->wrapper->getNestedTransactionsCount());
+
+ // 1st commit
+ $this->wrapper->commit();
+ self::assertTrue($this->wrapper->inTransaction());
+ self::assertSame(--$nested, $this->wrapper->getNestedTransactionsCount());
+
+ // 2nd commit
+ $this->wrapper->commit();
+ self::assertFalse($this->wrapper->inTransaction());
+ self::assertSame(--$nested, $this->wrapper->getNestedTransactionsCount());
+ }
+
+ public function testNestedTransactionsRollback(): void
+ {
+ $nested = 0;
+
+ self::assertFalse($this->wrapper->inTransaction());
+
+ // 1st transaction
+ $this->wrapper->beginTransaction();
+ self::assertTrue($this->wrapper->inTransaction());
+ self::assertSame(++$nested, $this->wrapper->getNestedTransactionsCount());
+
+ // 2nd transaction
+ $this->wrapper->beginTransaction();
+ self::assertTrue($this->wrapper->inTransaction());
+ self::assertSame(++$nested, $this->wrapper->getNestedTransactionsCount());
+
+ // Rollback
+ $this->wrapper->rollback();
+ self::assertFalse($this->wrapper->inTransaction());
+ self::assertSame(0, $this->wrapper->getNestedTransactionsCount());
+ }
+
+ public function testRollbackDisconnectedThrowsException(): void
+ {
+ $this->wrapper->disconnect();
+
+ $this->expectException(RuntimeException::class);
+ $this->expectExceptionMessage('Must be connected before you can rollback');
+ $this->wrapper->rollback();
+ }
+
+ public function testRollbackReturnsInstanceOfConnection(): void
+ {
+ $this->wrapper->beginTransaction();
+ self::assertInstanceOf(Connection::class, $this->wrapper->rollback());
+ }
+
+ public function testRollbackSetsInTransactionAtFalse(): void
+ {
+ $this->wrapper->beginTransaction();
+ $this->wrapper->rollback();
+ self::assertFalse($this->wrapper->inTransaction());
+ }
+
+ public function testRollbackWithoutBeginThrowsException(): void
+ {
+ $this->expectException(RuntimeException::class);
+ $this->expectExceptionMessage('Must call beginTransaction() before you can rollback');
+ $this->wrapper->rollback();
+ }
+
+ /**
+ * Standalone commit after a SET autocommit=0;
+ */
+ public function testStandaloneCommit(): void
+ {
+ self::assertFalse($this->wrapper->inTransaction());
+ self::assertSame(0, $this->wrapper->getNestedTransactionsCount());
+
+ $this->wrapper->commit();
+
+ self::assertFalse($this->wrapper->inTransaction());
+ self::assertSame(0, $this->wrapper->getNestedTransactionsCount());
+ }
+}
diff --git a/test/unit/Driver/Pdo/PdoTest.php b/test/unit/Driver/Pdo/PdoTest.php
new file mode 100644
index 0000000..cb45b81
--- /dev/null
+++ b/test/unit/Driver/Pdo/PdoTest.php
@@ -0,0 +1,97 @@
+createMock(Pdo\Connection::class);
+ $statement = $this->createMock(Statement::class);
+ $result = $this->createMock(Result::class);
+ $this->pdo = new Pdo\Pdo(
+ $connection,
+ $statement,
+ $result
+ );
+ }
+
+ public function testGetDatabasePlatformName(): void
+ {
+ // Test platform name for SqlServer
+ //$this->pdo->getConnection()->setConnectionParameters(['driver' => 'pdo_mysql']);
+ self::assertEquals('Mysql', $this->pdo->getDatabasePlatformName());
+ self::assertEquals('MySQL', $this->pdo->getDatabasePlatformName(DriverInterface::NAME_FORMAT_NATURAL));
+ }
+
+ /** @psalm-return array */
+ public static function getParamsAndType(): array
+ {
+ return [
+ ['foo', null, ':foo'],
+ ['foo_bar', null, ':foo_bar'],
+ ['123foo', null, ':123foo'],
+ [1, null, '?'],
+ ['1', null, '?'],
+ ['foo', DriverInterface::PARAMETERIZATION_NAMED, ':foo'],
+ ['foo_bar', DriverInterface::PARAMETERIZATION_NAMED, ':foo_bar'],
+ ['123foo', DriverInterface::PARAMETERIZATION_NAMED, ':123foo'],
+ [1, DriverInterface::PARAMETERIZATION_NAMED, ':1'],
+ ['1', DriverInterface::PARAMETERIZATION_NAMED, ':1'],
+ [':foo', null, ':foo'],
+ ];
+ }
+
+ #[DataProvider('getParamsAndType')]
+ public function testFormatParameterName(int|string $name, ?string $type, string $expected): void
+ {
+ $result = $this->pdo->formatParameterName($name, $type);
+ $this->assertEquals($expected, $result);
+ }
+
+ /** @psalm-return array */
+ public static function getInvalidParamName(): array
+ {
+ return [
+ ['foo%'],
+ ['foo-'],
+ ['foo$'],
+ ['foo0!'],
+ ];
+ }
+
+ #[DataProvider('getInvalidParamName')]
+ public function testFormatParameterNameWithInvalidCharacters(string $name): void
+ {
+ $this->expectException(RuntimeException::class);
+ $this->pdo->formatParameterName($name);
+ }
+
+ public function testGetResultPrototype(): void
+ {
+ $resultPrototype = $this->pdo->getResultPrototype();
+
+ self::assertInstanceOf(Result::class, $resultPrototype);
+ }
+}
diff --git a/test/unit/Driver/Pdo/ResultTest.php b/test/unit/Driver/Pdo/ResultTest.php
new file mode 100644
index 0000000..a29a4b8
--- /dev/null
+++ b/test/unit/Driver/Pdo/ResultTest.php
@@ -0,0 +1,111 @@
+getMockBuilder(PDOStatement::class)->getMock();
+ $stub->expects($this->any())
+ ->method('fetch')
+ ->willReturnCallback(fn() => uniqid());
+
+ $result = new Result();
+ $result->initialize($stub, null);
+
+ self::assertEquals($result->current(), $result->current());
+ }
+
+ public function testFetchModeException(): void
+ {
+ $result = new Result();
+
+ $this->expectException(InvalidArgumentException::class);
+ $result->setFetchMode(13);
+ }
+
+ /**
+ * Tests whether the fetch mode was set properly and
+ */
+ public function testFetchModeAnonymousObject(): void
+ {
+ $stub = $this->getMockBuilder(PDOStatement::class)->getMock();
+ $stub->expects($this->any())
+ ->method('fetch')
+ ->willReturnCallback(fn() => new stdClass());
+
+ $result = new Result();
+ $result->initialize($stub, null);
+ $result->setFetchMode(PDO::FETCH_OBJ);
+
+ self::assertEquals(5, $result->getFetchMode());
+ self::assertInstanceOf('stdClass', $result->current());
+ }
+
+ /**
+ * Tests whether the fetch mode has a broader range
+ */
+ public function testFetchModeRange(): void
+ {
+ $stub = $this->getMockBuilder(PDOStatement::class)->getMock();
+ $stub->expects($this->any())
+ ->method('fetch')
+ ->willReturnCallback(fn() => new stdClass());
+ $result = new Result();
+ $result->initialize($stub, null);
+ $result->setFetchMode(PDO::FETCH_NAMED);
+ self::assertEquals(11, $result->getFetchMode());
+ self::assertInstanceOf('stdClass', $result->current());
+ }
+
+ public function testMultipleRewind(): void
+ {
+ $data = [
+ ['test' => 1],
+ ['test' => 2],
+ ];
+ $position = 0;
+
+ $stub = $this->getMockBuilder(PDOStatement::class)->getMock();
+ assert($stub instanceof PDOStatement); // to suppress IDE type warnings
+ $stub->expects($this->any())
+ ->method('fetch')
+ ->willReturnCallback(function () use ($data, &$position) {
+ return $data[$position++];
+ });
+ $result = new Result();
+ $result->initialize($stub, null);
+
+ $result->rewind();
+ $result->rewind();
+
+ $this->assertEquals(0, $result->key());
+ $this->assertEquals(1, $position);
+ $this->assertEquals($data[0], $result->current());
+
+ $result->next();
+ $this->assertEquals(1, $result->key());
+ $this->assertEquals(2, $position);
+ $this->assertEquals($data[1], $result->current());
+ }
+}
diff --git a/test/unit/Driver/Pdo/StatementIntegrationTest.php b/test/unit/Driver/Pdo/StatementIntegrationTest.php
new file mode 100644
index 0000000..b2f28aa
--- /dev/null
+++ b/test/unit/Driver/Pdo/StatementIntegrationTest.php
@@ -0,0 +1,93 @@
+getMockBuilder(PdoDriver::class)
+ ->onlyMethods(['createResult'])
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->statement = new Statement();
+ $this->statement->setDriver($driver);
+ $this->statement->initialize(new TestAsset\CtorlessPdo(
+ $this->pdoStatementMock = $this->getMockBuilder(PDOStatement::class)
+ ->onlyMethods(['execute', 'bindParam'])
+ ->getMock()
+ ));
+ }
+
+ /**
+ * Tears down the fixture, for example, closes a network connection.
+ * This method is called after a test is executed.
+ */
+ protected function tearDown(): void
+ {
+ }
+
+ public function testStatementExecuteWillConvertPhpBoolToPdoBoolWhenBinding(): void
+ {
+ $this->pdoStatementMock->expects($this->any())->method('bindParam')->with(
+ $this->equalTo(':foo'),
+ $this->equalTo(false),
+ $this->equalTo(PDO::PARAM_BOOL)
+ );
+ $this->statement->execute(['foo' => false]);
+ }
+
+ public function testStatementExecuteWillUsePdoStrByDefaultWhenBinding(): void
+ {
+ $this->pdoStatementMock->expects($this->any())->method('bindParam')->with(
+ $this->equalTo(':foo'),
+ $this->equalTo('bar'),
+ $this->equalTo(PDO::PARAM_STR)
+ );
+ $this->statement->execute(['foo' => 'bar']);
+ }
+
+ public function testStatementExecuteWillUsePdoStrForStringIntegerWhenBinding(): void
+ {
+ $this->pdoStatementMock->expects($this->any())->method('bindParam')->with(
+ $this->equalTo(':foo'),
+ $this->equalTo('123'),
+ $this->equalTo(PDO::PARAM_STR)
+ );
+ $this->statement->execute(['foo' => '123']);
+ }
+
+ public function testStatementExecuteWillUsePdoIntForIntWhenBinding(): void
+ {
+ $this->pdoStatementMock->expects($this->any())->method('bindParam')->with(
+ $this->equalTo(':foo'),
+ $this->equalTo(123),
+ $this->equalTo(PDO::PARAM_INT)
+ );
+ $this->statement->execute(['foo' => 123]);
+ }
+}
diff --git a/test/unit/Driver/Pdo/StatementTest.php b/test/unit/Driver/Pdo/StatementTest.php
new file mode 100644
index 0000000..ae59589
--- /dev/null
+++ b/test/unit/Driver/Pdo/StatementTest.php
@@ -0,0 +1,123 @@
+statement = new Statement();
+ $this->pdo = new Pdo(
+ $this->createMock(Connection::class),
+ $this->statement,
+ $this->createMock(Result::class),
+ );
+ }
+
+ /**
+ * Tears down the fixture, for example, closes a network connection.
+ * This method is called after a test is executed.
+ */
+ protected function tearDown(): void
+ {
+ }
+
+ public function testSetDriver(): void
+ {
+ self::assertInstanceOf(PdoDriverInterface::class, $this->pdo);
+ self::assertEquals($this->statement, $this->statement->setDriver($this->pdo));
+ }
+
+ public function testSetParameterContainer(): void
+ {
+ self::assertSame($this->statement, $this->statement->setParameterContainer(new ParameterContainer()));
+ }
+
+ /**
+ * @todo Implement testGetParameterContainer().
+ */
+ public function testGetParameterContainer(): void
+ {
+ $container = new ParameterContainer();
+ $this->statement->setParameterContainer($container);
+ self::assertSame($container, $this->statement->getParameterContainer());
+ }
+
+ public function testGetResource(): void
+ {
+ $stmt = $this->createMock(PDOStatement::class);
+ $this->statement->setResource($stmt);
+
+ self::assertSame($stmt, $this->statement->getResource());
+ }
+
+ public function testSetSql(): void
+ {
+ $this->statement->setSql('SELECT 1');
+ self::assertEquals('SELECT 1', $this->statement->getSql());
+ }
+
+ public function testGetSql(): void
+ {
+ $this->statement->setSql('SELECT 1');
+ self::assertEquals('SELECT 1', $this->statement->getSql());
+ }
+
+ /**
+ * @todo Implement testPrepare().
+ */
+ public function testPrepare(): void
+ {
+ $this->markTestSkipped('Needs to be covered by integration group');
+ // $this->statement->initialize(new TestAsset\SqliteMemoryPdo());
+ // self::assertNull($this->statement->prepare('SELECT 1'));
+ }
+
+ public function testIsPrepared(): void
+ {
+ $this->markTestSkipped('Needs to be covered by integration group');
+ // self::assertFalse($this->statement->isPrepared());
+ // $this->statement->initialize(new TestAsset\SqliteMemoryPdo());
+ // $this->statement->prepare('SELECT 1');
+ // self::assertTrue($this->statement->isPrepared());
+ }
+
+ public function testExecute(): void
+ {
+ $this->markTestSkipped('Needs to be covered by integration group');
+ // $this->statement->setDriver(new Pdo(new Connection($pdo = new TestAsset\SqliteMemoryPdo())));
+ // $this->statement->initialize($pdo);
+ // $this->statement->prepare('SELECT 1');
+ // self::assertInstanceOf(Result::class, $this->statement->execute());
+ }
+}
diff --git a/test/unit/Driver/Pdo/TestAsset/CtorlessPdo.php b/test/unit/Driver/Pdo/TestAsset/CtorlessPdo.php
new file mode 100644
index 0000000..f0cc479
--- /dev/null
+++ b/test/unit/Driver/Pdo/TestAsset/CtorlessPdo.php
@@ -0,0 +1,26 @@
+ $options
+ */
+ #[Override]
+ public function prepare(string $query, $options = null): PDOStatement
+ {
+ return $this->mockStatement;
+ }
+}
diff --git a/test/unit/Driver/TestAsset/PdoMock.php b/test/unit/Driver/TestAsset/PdoMock.php
new file mode 100644
index 0000000..5bf2a82
--- /dev/null
+++ b/test/unit/Driver/TestAsset/PdoMock.php
@@ -0,0 +1,32 @@
+createMock(Pdo\Connection::class),
+ $this->createMock(Statement::class),
+ $this->createMock(Result::class),
+ );
+ $this->platform = new Mysql($pdo);
+ }
+
+ public function testGetName(): void
+ {
+ self::assertEquals('MySQL', $this->platform->getName());
+ }
+
+ public function testGetQuoteIdentifierSymbol(): void
+ {
+ self::assertEquals('`', $this->platform->getQuoteIdentifierSymbol());
+ }
+
+ public function testQuoteIdentifier(): void
+ {
+ self::assertEquals('`identifier`', $this->platform->quoteIdentifier('identifier'));
+ self::assertEquals('`ident``ifier`', $this->platform->quoteIdentifier('ident`ifier'));
+ self::assertEquals('`namespace:$identifier`', $this->platform->quoteIdentifier('namespace:$identifier'));
+ }
+
+ public function testQuoteIdentifierChain(): void
+ {
+ self::assertEquals('`identifier`', $this->platform->quoteIdentifierChain('identifier'));
+ self::assertEquals('`identifier`', $this->platform->quoteIdentifierChain(['identifier']));
+ self::assertEquals('`schema`.`identifier`', $this->platform->quoteIdentifierChain(['schema', 'identifier']));
+
+ self::assertEquals('`ident``ifier`', $this->platform->quoteIdentifierChain('ident`ifier'));
+ self::assertEquals('`ident``ifier`', $this->platform->quoteIdentifierChain(['ident`ifier']));
+ self::assertEquals(
+ '`schema`.`ident``ifier`',
+ $this->platform->quoteIdentifierChain(['schema', 'ident`ifier'])
+ );
+ }
+
+ public function testGetQuoteValueSymbol(): void
+ {
+ self::assertEquals("'", $this->platform->getQuoteValueSymbol());
+ }
+
+ public function testQuoteValueRaisesNoticeWithoutPlatformSupport(): void
+ {
+ /**
+ * todo: Determine if vulnerability warning is required during unit testing
+ *
+ * todo: This testing needs expanded to cover all possible driver types
+ * since using \PDO currently causes a TypeError to be raised due to the
+ * underlying quoteViaDriver method returning false instead of ?string
+ */
+ //$this->expectNotice();
+ //$this->expectExceptionMessage(
+ // 'Attempting to quote a value in PhpDb\Adapter\Platform\Mysql without extension/driver support can '
+ // . 'introduce security vulnerabilities in a production environment'
+ //);
+ $this->expectNotToPerformAssertions();
+ $this->platform->quoteValue('value');
+ }
+
+ public function testQuoteValue(): void
+ {
+ self::assertEquals("'value'", @$this->platform->quoteValue('value'));
+ self::assertEquals("'Foo O\\'Bar'", @$this->platform->quoteValue("Foo O'Bar"));
+ self::assertEquals(
+ '\'\\\'; DELETE FROM some_table; -- \'',
+ @$this->platform->quoteValue('\'; DELETE FROM some_table; -- ')
+ );
+ self::assertEquals(
+ "'\\\\\\'; DELETE FROM some_table; -- '",
+ @$this->platform->quoteValue('\\\'; DELETE FROM some_table; -- ')
+ );
+ }
+
+ public function testQuoteTrustedValue(): void
+ {
+ self::assertEquals("'value'", $this->platform->quoteTrustedValue('value'));
+ self::assertEquals("'Foo O\\'Bar'", $this->platform->quoteTrustedValue("Foo O'Bar"));
+ self::assertEquals(
+ '\'\\\'; DELETE FROM some_table; -- \'',
+ $this->platform->quoteTrustedValue('\'; DELETE FROM some_table; -- ')
+ );
+
+ // '\\\'; DELETE FROM some_table; -- ' <- actual below
+ self::assertEquals(
+ "'\\\\\\'; DELETE FROM some_table; -- '",
+ $this->platform->quoteTrustedValue('\\\'; DELETE FROM some_table; -- ')
+ );
+ }
+
+ public function testQuoteValueList(): void
+ {
+ /**
+ * @todo Determine if vulnerability warning is required during unit testing
+ */
+ //$this->expectError();
+ //$this->expectExceptionMessage(
+ // 'Attempting to quote a value in PhpDb\Adapter\Platform\Mysql without extension/driver support can '
+ // . 'introduce security vulnerabilities in a production environment'
+ //);
+ self::assertEquals("'Foo O\\'Bar'", $this->platform->quoteValueList("Foo O'Bar"));
+ }
+
+ public function testGetIdentifierSeparator(): void
+ {
+ self::assertEquals('.', $this->platform->getIdentifierSeparator());
+ }
+
+ public function testQuoteIdentifierInFragment(): void
+ {
+ self::assertEquals('`foo`.`bar`', $this->platform->quoteIdentifierInFragment('foo.bar'));
+ self::assertEquals('`foo` as `bar`', $this->platform->quoteIdentifierInFragment('foo as bar'));
+ self::assertEquals('`$TableName`.`bar`', $this->platform->quoteIdentifierInFragment('$TableName.bar'));
+ self::assertEquals(
+ '`cmis:$TableName` as `cmis:TableAlias`',
+ $this->platform->quoteIdentifierInFragment('cmis:$TableName as cmis:TableAlias')
+ );
+
+ $this->assertEquals(
+ '`foo-bar`.`bar-foo`',
+ $this->platform->quoteIdentifierInFragment('foo-bar.bar-foo')
+ );
+ $this->assertEquals(
+ '`foo-bar` as `bar-foo`',
+ $this->platform->quoteIdentifierInFragment('foo-bar as bar-foo')
+ );
+ $this->assertEquals(
+ '`$TableName-$ColumnName`.`bar-foo`',
+ $this->platform->quoteIdentifierInFragment('$TableName-$ColumnName.bar-foo')
+ );
+ $this->assertEquals(
+ '`cmis:$TableName-$ColumnName` as `cmis:TableAlias-ColumnAlias`',
+ $this->platform->quoteIdentifierInFragment('cmis:$TableName-$ColumnName as cmis:TableAlias-ColumnAlias')
+ );
+
+ // single char words
+ self::assertEquals(
+ '(`foo`.`bar` = `boo`.`baz`)',
+ $this->platform->quoteIdentifierInFragment('(foo.bar = boo.baz)', ['(', ')', '='])
+ );
+ self::assertEquals(
+ '(`foo`.`bar`=`boo`.`baz`)',
+ $this->platform->quoteIdentifierInFragment('(foo.bar=boo.baz)', ['(', ')', '='])
+ );
+ self::assertEquals('`foo`=`bar`', $this->platform->quoteIdentifierInFragment('foo=bar', ['=']));
+
+ $this->assertEquals(
+ '(`foo-bar`.`bar-foo` = `boo-baz`.`baz-boo`)',
+ $this->platform->quoteIdentifierInFragment('(foo-bar.bar-foo = boo-baz.baz-boo)', ['(', ')', '='])
+ );
+ $this->assertEquals(
+ '(`foo-bar`.`bar-foo`=`boo-baz`.`baz-boo`)',
+ $this->platform->quoteIdentifierInFragment('(foo-bar.bar-foo=boo-baz.baz-boo)', ['(', ')', '='])
+ );
+ $this->assertEquals(
+ '`foo-bar`=`bar-foo`',
+ $this->platform->quoteIdentifierInFragment('foo-bar=bar-foo', ['='])
+ );
+
+ // case insensitive safe words
+ self::assertEquals(
+ '(`foo`.`bar` = `boo`.`baz`) AND (`foo`.`baz` = `boo`.`baz`)',
+ $this->platform->quoteIdentifierInFragment(
+ '(foo.bar = boo.baz) AND (foo.baz = boo.baz)',
+ ['(', ')', '=', 'and']
+ )
+ );
+
+ $this->assertEquals(
+ '(`foo-bar`.`bar-foo` = `boo-baz`.`baz-boo`) AND (`foo-baz`.`baz-foo` = `boo-baz`.`baz-boo`)',
+ $this->platform->quoteIdentifierInFragment(
+ '(foo-bar.bar-foo = boo-baz.baz-boo) AND (foo-baz.baz-foo = boo-baz.baz-boo)',
+ ['(', ')', '=', 'and']
+ )
+ );
+
+ // case insensitive safe words in field
+ self::assertEquals(
+ '(`foo`.`bar` = `boo`.baz) AND (`foo`.baz = `boo`.baz)',
+ $this->platform->quoteIdentifierInFragment(
+ '(foo.bar = boo.baz) AND (foo.baz = boo.baz)',
+ ['(', ')', '=', 'and', 'bAz']
+ )
+ );
+
+ // case insensitive safe words in field
+ $this->assertEquals(
+ '(`foo-bar`.`bar-foo` = `boo-baz`.baz-boo) AND (`foo-baz`.`baz-foo` = `boo-baz`.baz-boo)',
+ $this->platform->quoteIdentifierInFragment(
+ '(foo-bar.bar-foo = boo-baz.baz-boo) AND (foo-baz.baz-foo = boo-baz.baz-boo)',
+ ['(', ')', '=', 'and', 'bAz-BOo']
+ )
+ );
+ }
+}
diff --git a/test/unit/TestAsset/ConnectionWrapper.php b/test/unit/TestAsset/ConnectionWrapper.php
new file mode 100644
index 0000000..26fe813
--- /dev/null
+++ b/test/unit/TestAsset/ConnectionWrapper.php
@@ -0,0 +1,23 @@
+resource = new PdoStubDriver('foo', 'bar', 'baz');
+ }
+
+ public function getNestedTransactionsCount(): int
+ {
+ return $this->nestedTransactionsCount;
+ }
+}
diff --git a/test/unit/TestAsset/PdoStubDriver.php b/test/unit/TestAsset/PdoStubDriver.php
new file mode 100644
index 0000000..b1047ec
--- /dev/null
+++ b/test/unit/TestAsset/PdoStubDriver.php
@@ -0,0 +1,31 @@
+