From 3cd7da48b615e36a886d5317fa73ec4b57be68e6 Mon Sep 17 00:00:00 2001 From: Colin Viebrock Date: Fri, 3 Oct 2025 14:03:39 -0400 Subject: [PATCH 01/18] Set up for unit tests, html code coverage report, AI-generated tests for one class --- .gitignore | 6 +- composer.json | 12 +- composer.lock | 2501 ++++++++++++++--- phpunit.dist.xml | 24 + .../SwatI18N/SwatI18NNumberFormatTest.php | 72 + 5 files changed, 2212 insertions(+), 403 deletions(-) create mode 100644 phpunit.dist.xml create mode 100644 tests/Unit/SwatI18N/SwatI18NNumberFormatTest.php diff --git a/.gitignore b/.gitignore index f022ec4b..043f162c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,10 +4,14 @@ vendor/ node_modules/ +# testing and code styling output +/build/ +.php-cs-fixer.cache +.phpunit.result.cache + # misc .DS_Store .env .idea/ -.php-cs-fixer.cache *.swp diff --git a/composer.json b/composer.json index 3441a818..0c728b54 100644 --- a/composer.json +++ b/composer.json @@ -62,7 +62,8 @@ }, "require-dev": { "friendsofphp/php-cs-fixer": "3.88.2", - "phpstan/phpstan": "^2.1" + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^11.5" }, "autoload": { "classmap": [ @@ -71,13 +72,20 @@ "SwatI18N/" ] }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } + }, "scripts": { "phpcs": "./vendor/bin/php-cs-fixer check -v", "phpcs:ci": "./vendor/bin/php-cs-fixer check --config=.php-cs-fixer.php --no-interaction --show-progress=none --diff --using-cache=no -vvv", "phpcs:write": "./vendor/bin/php-cs-fixer fix -v", "phpstan": "./vendor/bin/phpstan analyze", "phpstan:ci": "./vendor/bin/phpstan analyze -vvv --no-progress --memory-limit 2G", - "phpstan:baseline": "./vendor/bin/phpstan analyze --generate-baseline" + "phpstan:baseline": "./vendor/bin/phpstan analyze --generate-baseline", + "phpunit": "./vendor/bin/phpunit --no-coverage", + "phpunit:coverage": "XDEBUG_MODE=coverage ./vendor/bin/phpunit && npx serve build/coverage" }, "config": { "sort-packages": true diff --git a/composer.lock b/composer.lock index a80d08cc..3e2f2a97 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "51e2a3810f6355a8bd17a0a471f5f321", + "content-hash": "a2e9e812c640f1301fea8f72446a2dd7", "packages": [ { "name": "league/climate", @@ -1314,717 +1314,2325 @@ "time": "2025-09-27T00:24:15+00:00" }, { - "name": "phpstan/phpstan", - "version": "2.1.16", + "name": "myclabs/deep-copy", + "version": "1.13.4", "source": { "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9" + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9", - "reference": "b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { - "php": "^7.4|^8.0" + "php": "^7.1 || ^8.0" }, "conflict": { - "phpstan/phpstan-shim": "*" + "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" }, - "bin": [ - "phpstan", - "phpstan.phar" - ], "type": "library", "autoload": { "files": [ - "bootstrap.php" - ] + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "PHPStan - PHP Static Analysis Tool", + "description": "Create deep copies (clones) of your objects", "keywords": [ - "dev", - "static analysis" + "clone", + "copy", + "duplicate", + "object", + "object graph" ], "support": { - "docs": "https://phpstan.org/user-guide/getting-started", - "forum": "https://github.com/phpstan/phpstan/discussions", - "issues": "https://github.com/phpstan/phpstan/issues", - "security": "https://github.com/phpstan/phpstan/security/policy", - "source": "https://github.com/phpstan/phpstan-src" + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { - "url": "https://github.com/ondrejmirtes", - "type": "github" - }, - { - "url": "https://github.com/phpstan", - "type": "github" + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" } ], - "time": "2025-05-16T09:40:10+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { - "name": "psr/container", - "version": "2.0.2", + "name": "nikic/php-parser", + "version": "v5.6.1", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", "shasum": "" }, "require": { - "php": ">=7.4.0" + "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": "2.0.x-dev" + "dev-master": "5.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Container\\": "src/" + "PhpParser\\": "lib/PhpParser" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Nikita Popov" } ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", + "description": "A PHP parser written in PHP", "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" + "parser", + "php" ], "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/2.0.2" + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1" }, - "time": "2021-11-05T16:47:00+00:00" + "time": "2025-08-13T20:13:15+00:00" }, { - "name": "psr/event-dispatcher", - "version": "1.0.0", + "name": "phar-io/manifest", + "version": "2.0.4", "source": { "type": "git", - "url": "https://github.com/php-fig/event-dispatcher.git", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", "shasum": "" }, "require": { - "php": ">=7.2.0" + "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": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { - "psr-4": { - "Psr\\EventDispatcher\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "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": "Standard interfaces for event handling.", - "keywords": [ - "events", - "psr", - "psr-14" - ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { - "issues": "https://github.com/php-fig/event-dispatcher/issues", - "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" }, - "time": "2019-01-08T18:20:26+00:00" + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" }, { - "name": "react/cache", - "version": "v1.2.0", + "name": "phar-io/version", + "version": "3.2.1", "source": { "type": "git", - "url": "https://github.com/reactphp/cache.git", - "reference": "d47c472b64aa5608225f47965a484b75c7817d5b" + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b", - "reference": "d47c472b64aa5608225f47965a484b75c7817d5b", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "shasum": "" }, "require": { - "php": ">=5.3.0", - "react/promise": "^3.0 || ^2.0 || ^1.1" - }, - "require-dev": { - "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" + "php": "^7.2 || ^8.0" }, "type": "library", "autoload": { - "psr-4": { - "React\\Cache\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" }, { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" }, { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" } ], - "description": "Async, Promise-based cache interface for ReactPHP", - "keywords": [ - "cache", - "caching", - "promise", - "reactphp" - ], + "description": "Library for handling version information and constraints", "support": { - "issues": "https://github.com/reactphp/cache/issues", - "source": "https://github.com/reactphp/cache/tree/v1.2.0" + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" }, - "funding": [ - { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" - } - ], - "time": "2022-11-30T15:59:55+00:00" + "time": "2022-02-21T01:04:05+00:00" }, { - "name": "react/child-process", - "version": "v0.6.6", + "name": "phpstan/phpstan", + "version": "2.1.16", "source": { "type": "git", - "url": "https://github.com/reactphp/child-process.git", - "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159" + "url": "https://github.com/phpstan/phpstan.git", + "reference": "b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/child-process/zipball/1721e2b93d89b745664353b9cfc8f155ba8a6159", - "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9", + "reference": "b8c1cf533cba0c305d91c6ccd23f3dd0566ba5f9", "shasum": "" }, "require": { - "evenement/evenement": "^3.0 || ^2.0 || ^1.0", - "php": ">=5.3.0", - "react/event-loop": "^1.2", - "react/stream": "^1.4" + "php": "^7.4|^8.0" }, - "require-dev": { - "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", - "react/socket": "^1.16", - "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0" + "conflict": { + "phpstan/phpstan-shim": "*" }, + "bin": [ + "phpstan", + "phpstan.phar" + ], "type": "library", "autoload": { - "psr-4": { - "React\\ChildProcess\\": "src/" - } + "files": [ + "bootstrap.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "Event-driven library for executing child processes with ReactPHP.", + "description": "PHPStan - PHP Static Analysis Tool", "keywords": [ - "event-driven", - "process", - "reactphp" + "dev", + "static analysis" ], "support": { - "issues": "https://github.com/reactphp/child-process/issues", - "source": "https://github.com/reactphp/child-process/tree/v0.6.6" + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" }, "funding": [ { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" } ], - "time": "2025-01-01T16:37:48+00:00" + "time": "2025-05-16T09:40:10+00:00" }, { - "name": "react/dns", - "version": "v1.13.0", + "name": "phpunit/php-code-coverage", + "version": "11.0.11", "source": { "type": "git", - "url": "https://github.com/reactphp/dns.git", - "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5" + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/dns/zipball/eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", - "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", + "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", "shasum": "" }, "require": { - "php": ">=5.3.0", - "react/cache": "^1.0 || ^0.6 || ^0.5", - "react/event-loop": "^1.2", - "react/promise": "^3.2 || ^2.7 || ^1.2.1" + "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": "^9.6 || ^5.7 || ^4.8.36", - "react/async": "^4.3 || ^3 || ^2", - "react/promise-timer": "^1.11" + "phpunit/phpunit": "^11.5.2" }, - "type": "library", - "autoload": { - "psr-4": { - "React\\Dns\\": "src/" + "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": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" + "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/11.0.11" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" }, { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" }, { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" }, { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" } ], - "description": "Async DNS resolver for ReactPHP", + "time": "2025-08-27T14:37:49+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": [ - "async", - "dns", - "dns-resolver", - "reactphp" + "filesystem", + "iterator" ], "support": { - "issues": "https://github.com/reactphp/dns/issues", - "source": "https://github.com/reactphp/dns/tree/v1.13.0" + "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://opencollective.com/reactphp", - "type": "open_collective" + "url": "https://github.com/sebastianbergmann", + "type": "github" } ], - "time": "2024-06-13T14:18:03+00:00" + "time": "2024-08-27T05:02:59+00:00" }, { - "name": "react/event-loop", - "version": "v1.5.0", + "name": "phpunit/php-invoker", + "version": "5.0.1", "source": { "type": "git", - "url": "https://github.com/reactphp/event-loop.git", - "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354" + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", - "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + "ext-pcntl": "*", + "phpunit/phpunit": "^11.0" }, "suggest": { - "ext-pcntl": "For signal handling support when using the StreamSelectLoop" + "ext-pcntl": "*" }, "type": "library", - "autoload": { - "psr-4": { - "React\\EventLoop\\": "src/" + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, + "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": [ { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, + "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": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", "keywords": [ - "asynchronous", - "event-loop" + "template" ], "support": { - "issues": "https://github.com/reactphp/event-loop/issues", - "source": "https://github.com/reactphp/event-loop/tree/v1.5.0" + "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://opencollective.com/reactphp", - "type": "open_collective" + "url": "https://github.com/sebastianbergmann", + "type": "github" } ], - "time": "2023-11-13T13:48:05+00:00" + "time": "2024-07-03T05:08:43+00:00" }, { - "name": "react/promise", - "version": "v3.3.0", + "name": "phpunit/php-timer", + "version": "7.0.1", "source": { "type": "git", - "url": "https://github.com/reactphp/promise.git", - "reference": "23444f53a813a3296c1368bb104793ce8d88f04a" + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a", - "reference": "23444f53a813a3296c1368bb104793ce8d88f04a", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", "shasum": "" }, "require": { - "php": ">=7.1.0" + "php": ">=8.2" }, "require-dev": { - "phpstan/phpstan": "1.12.28 || 1.4.10", - "phpunit/phpunit": "^9.6 || ^7.5" + "phpunit/phpunit": "^11.0" }, "type": "library", - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "React\\Promise\\": "src/" + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, + "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": [ { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:09:35+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "11.5.42", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "1c6cb5dfe412af3d0dfd414cfd110e3b9cfdbc3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1c6cb5dfe412af3d0dfd414cfd110e3b9cfdbc3c", + "reference": "1c6cb5dfe412af3d0dfd414cfd110e3b9cfdbc3c", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.2", + "phpunit/php-code-coverage": "^11.0.11", + "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.2", + "sebastian/diff": "^6.0.2", + "sebastian/environment": "^7.2.1", + "sebastian/exporter": "^6.3.2", + "sebastian/global-state": "^7.0.2", + "sebastian/object-enumerator": "^6.0.1", + "sebastian/type": "^5.1.3", + "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.42" + }, + "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-09-28T12:09:13+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" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "react/cache", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/cache.git", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/promise": "^3.0 || ^2.0 || ^1.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async, Promise-based cache interface for ReactPHP", + "keywords": [ + "cache", + "caching", + "promise", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/cache/issues", + "source": "https://github.com/reactphp/cache/tree/v1.2.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2022-11-30T15:59:55+00:00" + }, + { + "name": "react/child-process", + "version": "v0.6.6", + "source": { + "type": "git", + "url": "https://github.com/reactphp/child-process.git", + "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/child-process/zipball/1721e2b93d89b745664353b9cfc8f155ba8a6159", + "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/event-loop": "^1.2", + "react/stream": "^1.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/socket": "^1.16", + "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\ChildProcess\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, { "name": "Cees-Jan Kiewiet", "email": "reactphp@ceesjankiewiet.nl", "homepage": "https://wyrihaximus.net/" }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, { "name": "Chris Boden", "email": "cboden@gmail.com", "homepage": "https://cboden.dev/" } ], - "description": "A lightweight implementation of CommonJS Promises/A for PHP", - "keywords": [ - "promise", - "promises" + "description": "Event-driven library for executing child processes with ReactPHP.", + "keywords": [ + "event-driven", + "process", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/child-process/issues", + "source": "https://github.com/reactphp/child-process/tree/v0.6.6" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-01-01T16:37:48+00:00" + }, + { + "name": "react/dns", + "version": "v1.13.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/dns.git", + "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/dns/zipball/eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", + "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/cache": "^1.0 || ^0.6 || ^0.5", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.7 || ^1.2.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3 || ^2", + "react/promise-timer": "^1.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Dns\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async DNS resolver for ReactPHP", + "keywords": [ + "async", + "dns", + "dns-resolver", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/dns/issues", + "source": "https://github.com/reactphp/dns/tree/v1.13.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-06-13T14:18:03+00:00" + }, + { + "name": "react/event-loop", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/event-loop.git", + "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", + "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "suggest": { + "ext-pcntl": "For signal handling support when using the StreamSelectLoop" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\EventLoop\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", + "keywords": [ + "asynchronous", + "event-loop" + ], + "support": { + "issues": "https://github.com/reactphp/event-loop/issues", + "source": "https://github.com/reactphp/event-loop/tree/v1.5.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2023-11-13T13:48:05+00:00" + }, + { + "name": "react/promise", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a", + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a", + "shasum": "" + }, + "require": { + "php": ">=7.1.0" + }, + "require-dev": { + "phpstan/phpstan": "1.12.28 || 1.4.10", + "phpunit/phpunit": "^9.6 || ^7.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "React\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "support": { + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v3.3.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-08-19T18:57:03+00:00" + }, + { + "name": "react/socket", + "version": "v1.16.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/socket.git", + "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/socket/zipball/23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", + "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/dns": "^1.13", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.6 || ^1.2.1", + "react/stream": "^1.4" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3.3 || ^2", + "react/promise-stream": "^1.4", + "react/promise-timer": "^1.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Socket\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", + "keywords": [ + "Connection", + "Socket", + "async", + "reactphp", + "stream" + ], + "support": { + "issues": "https://github.com/reactphp/socket/issues", + "source": "https://github.com/reactphp/socket/tree/v1.16.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-07-26T10:38:09+00:00" + }, + { + "name": "react/stream", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/stream.git", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.8", + "react/event-loop": "^1.2" + }, + "require-dev": { + "clue/stream-filter": "~1.2", + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Stream\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", + "keywords": [ + "event-driven", + "io", + "non-blocking", + "pipe", + "reactphp", + "readable", + "stream", + "writable" + ], + "support": { + "issues": "https://github.com/reactphp/stream/issues", + "source": "https://github.com/reactphp/stream/tree/v1.4.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-06-11T12:45:25+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.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/85c77556683e6eee4323e4c5468641ca0237e2e8", + "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8", + "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.2" + }, + "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/comparator", + "type": "tidelift" + } + ], + "time": "2025-08-10T08:07:46+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.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74", + "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.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": "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.2" + }, + "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/exporter", + "type": "tidelift" + } + ], + "time": "2025-09-24T06:12:51+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/reactphp/promise/issues", - "source": "https://github.com/reactphp/promise/tree/v3.3.0" + "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://opencollective.com/reactphp", - "type": "open_collective" + "url": "https://github.com/sebastianbergmann", + "type": "github" } ], - "time": "2025-08-19T18:57:03+00:00" + "time": "2024-07-03T05:01:32+00:00" }, { - "name": "react/socket", - "version": "v1.16.0", + "name": "sebastian/recursion-context", + "version": "6.0.3", "source": { "type": "git", - "url": "https://github.com/reactphp/socket.git", - "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1" + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/socket/zipball/23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", - "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", "shasum": "" }, "require": { - "evenement/evenement": "^3.0 || ^2.0 || ^1.0", - "php": ">=5.3.0", - "react/dns": "^1.13", - "react/event-loop": "^1.2", - "react/promise": "^3.2 || ^2.6 || ^1.2.1", - "react/stream": "^1.4" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", - "react/async": "^4.3 || ^3.3 || ^2", - "react/promise-stream": "^1.4", - "react/promise-timer": "^1.11" + "phpunit/phpunit": "^11.3" }, "type": "library", - "autoload": { - "psr-4": { - "React\\Socket\\": "src/" + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" }, { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" }, { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" + "name": "Adam Harvey", + "email": "aharvey@php.net" } ], - "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", - "keywords": [ - "Connection", - "Socket", - "async", - "reactphp", - "stream" - ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { - "issues": "https://github.com/reactphp/socket/issues", - "source": "https://github.com/reactphp/socket/tree/v1.16.0" + "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.3" }, "funding": [ { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" + "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/recursion-context", + "type": "tidelift" } ], - "time": "2024-07-26T10:38:09+00:00" + "time": "2025-08-13T04:42:22+00:00" }, { - "name": "react/stream", - "version": "v1.4.0", + "name": "sebastian/type", + "version": "5.1.3", "source": { "type": "git", - "url": "https://github.com/reactphp/stream.git", - "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d", - "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449", "shasum": "" }, "require": { - "evenement/evenement": "^3.0 || ^2.0 || ^1.0", - "php": ">=5.3.8", - "react/event-loop": "^1.2" + "php": ">=8.2" }, "require-dev": { - "clue/stream-filter": "~1.2", - "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + "phpunit/phpunit": "^11.3" }, "type": "library", - "autoload": { - "psr-4": { - "React\\Stream\\": "src/" + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", - "keywords": [ - "event-driven", - "io", - "non-blocking", - "pipe", - "reactphp", - "readable", - "stream", - "writable" - ], + "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/reactphp/stream/issues", - "source": "https://github.com/reactphp/stream/tree/v1.4.0" + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/5.1.3" }, "funding": [ { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" + "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/type", + "type": "tidelift" } ], - "time": "2024-06-11T12:45:25+00:00" + "time": "2025-08-09T06:55:48+00:00" }, { - "name": "sebastian/diff", - "version": "6.0.2", + "name": "sebastian/version", + "version": "5.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", - "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", "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" + "dev-main": "5.0-dev" } }, "autoload": { @@ -2039,33 +3647,76 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, + "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": [ { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" + "url": "https://github.com/sebastianbergmann", + "type": "github" } ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", + "time": "2024-10-09T05:16:32+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": [ - "diff", - "udiff", - "unidiff", - "unified diff" + "static analysis" ], "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" + "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/sebastianbergmann", + "url": "https://github.com/staabm", "type": "github" } ], - "time": "2024-07-03T04:53:05+00:00" + "time": "2024-10-20T05:08:20+00:00" }, { "name": "symfony/console", @@ -3293,6 +4944,56 @@ } ], "time": "2025-04-20T20:18:16+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" } ], "aliases": [], diff --git a/phpunit.dist.xml b/phpunit.dist.xml new file mode 100644 index 00000000..61cd92f9 --- /dev/null +++ b/phpunit.dist.xml @@ -0,0 +1,24 @@ + + + + + tests/Unit + + + + + Swat + SwatDB + SwatI18N + + + + + + + + diff --git a/tests/Unit/SwatI18N/SwatI18NNumberFormatTest.php b/tests/Unit/SwatI18N/SwatI18NNumberFormatTest.php new file mode 100644 index 00000000..71410eca --- /dev/null +++ b/tests/Unit/SwatI18N/SwatI18NNumberFormatTest.php @@ -0,0 +1,72 @@ +format = new \SwatI18NNumberFormat(); + $this->format->decimal_separator = '.'; + $this->format->thousands_separator = ','; + $this->format->grouping = [3]; + } + + public function testOverrideValidProperties() + { + $newFormat = $this->format->override([ + 'decimal_separator' => ',', + 'thousands_separator' => '.', + ]); + + $this->assertNotSame($this->format, $newFormat); + $this->assertEquals(',', $newFormat->decimal_separator); + $this->assertEquals('.', $newFormat->thousands_separator); + $this->assertEquals( + [3], + $newFormat->grouping + ); + } + + public function testOverrideInvalidPropertyThrowsException() + { + $this->expectException(\SwatException::class); + $this->format->override(['invalid_property' => 'value']); + } + + public function testOverrideNullValueDoesNotChangeProperty() + { + $newFormat = $this->format->override([ + 'decimal_separator' => null, + ]); + $this->assertEquals( + '.', + $newFormat->decimal_separator + ); + } + + public function testToString() + { + $expected = "decimal_separator => .\nthousands_separator => ,\ngrouping => 3\n"; + $this->assertEquals( + $expected, + (string) $this->format + ); + } + + public function testToStringWithArrayGrouping() + { + $newFormat = $this->format->override([ + 'grouping' => 3, + ]); + $expected = "decimal_separator => .\nthousands_separator => ,\ngrouping => 3\n"; + $this->assertEquals( + $expected, + (string) $newFormat + ); + } +} From fcda84fbb3fc34e6ffaabb1c632365761a3254f3 Mon Sep 17 00:00:00 2001 From: Colin Viebrock Date: Fri, 3 Oct 2025 14:51:23 -0400 Subject: [PATCH 02/18] Fix some phpstan level 1 issues --- Swat/SwatWidgetCellRenderer.php | 2 ++ Swat/exceptions/SwatException.php | 7 +++---- SwatDB/SwatDBDataObject.php | 2 +- SwatDB/SwatDBRecordsetWrapper.php | 1 + phpstan.dist.neon | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Swat/SwatWidgetCellRenderer.php b/Swat/SwatWidgetCellRenderer.php index c10ed0bf..ff0dcd68 100644 --- a/Swat/SwatWidgetCellRenderer.php +++ b/Swat/SwatWidgetCellRenderer.php @@ -71,6 +71,8 @@ public function __construct() /** * Fulfills SwatUIParent::addChild(). * + * @param SwatWidget $child + * * @throws SwatException */ public function addChild(SwatObject $child) diff --git a/Swat/exceptions/SwatException.php b/Swat/exceptions/SwatException.php index 65d8c0de..8b0e51fc 100644 --- a/Swat/exceptions/SwatException.php +++ b/Swat/exceptions/SwatException.php @@ -295,8 +295,8 @@ public function toString() } printf( - "%s. In file '%s' on line %s.\n%sMethod: %s%s%s(%s)\n", - str_pad(--$count, 6, ' ', STR_PAD_LEFT), + "%6d. In file '%s' on line %s.\n%sMethod: %s%s%s(%s)\n", + --$count, array_key_exists('file', $entry) ? $entry['file'] : 'unknown', array_key_exists('line', $entry) ? $entry['line'] : 'unknown', str_repeat(' ', 8), @@ -355,8 +355,7 @@ public function toXHTML() if (array_key_exists('args', $entry)) { $arguments = htmlspecialchars( $this->getArguments($entry['args'], $function, $class), - null, - 'UTF-8', + encoding: 'UTF-8', ); } else { $arguments = ''; diff --git a/SwatDB/SwatDBDataObject.php b/SwatDB/SwatDBDataObject.php index 0429818b..9b8f14df 100644 --- a/SwatDB/SwatDBDataObject.php +++ b/SwatDB/SwatDBDataObject.php @@ -54,7 +54,7 @@ class SwatDBDataObject extends SwatObject implements Serializable, SwatDBRecorda private $deprecated_properties = []; /** - * @var MDB2 + * @var MDB2_Driver_Common */ protected $db; diff --git a/SwatDB/SwatDBRecordsetWrapper.php b/SwatDB/SwatDBRecordsetWrapper.php index 5cae4344..5fdb9ccc 100644 --- a/SwatDB/SwatDBRecordsetWrapper.php +++ b/SwatDB/SwatDBRecordsetWrapper.php @@ -130,6 +130,7 @@ public function initializeFromResultSet(MDB2_Result_Common $rs) $this->setDatabase($rs->db); do { + /** @var stdClass $row */ $row = $rs->fetchRow(MDB2_FETCHMODE_OBJECT); while ($row) { $object = $this->instantiateRowWrapperObject($row); diff --git a/phpstan.dist.neon b/phpstan.dist.neon index 50993529..ab31c533 100644 --- a/phpstan.dist.neon +++ b/phpstan.dist.neon @@ -17,4 +17,4 @@ parameters: # Level 3 checkPhpDocMethodSignatures: true # Level 5 -# checkFunctionArgumentTypes: true + checkFunctionArgumentTypes: true From 1925ad477f9fa97e42cce9426dd45544813f9e68 Mon Sep 17 00:00:00 2001 From: Colin Viebrock Date: Fri, 3 Oct 2025 14:58:25 -0400 Subject: [PATCH 03/18] Add clover reporting --- phpunit.dist.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/phpunit.dist.xml b/phpunit.dist.xml index 61cd92f9..5a0c5020 100644 --- a/phpunit.dist.xml +++ b/phpunit.dist.xml @@ -19,6 +19,7 @@ + From 43deb59e8bc798f00a2339e73aad541dc6122b1b Mon Sep 17 00:00:00 2001 From: Colin Viebrock Date: Fri, 3 Oct 2025 15:49:19 -0400 Subject: [PATCH 04/18] Add unit tests to CI --- .github/workflows/pull-requests.yml | 3 +- Jenkinsfile | 6 ++++ tests/Unit/Swat/SwatStringTest.php | 44 +++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 tests/Unit/Swat/SwatStringTest.php diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml index 4c6d8be8..de08d0b5 100644 --- a/.github/workflows/pull-requests.yml +++ b/.github/workflows/pull-requests.yml @@ -40,6 +40,7 @@ jobs: - name: Run tests timeout-minutes: 5 run: | - pnpm prettier composer run phpcs:ci composer run phpstan:ci + composer run phpunit + pnpm prettier diff --git a/Jenkinsfile b/Jenkinsfile index 136d7683..c8af7cb9 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -30,6 +30,12 @@ pipeline { } } + stage('Run Test Suite') { + steps { + sh 'composer run phpunit' + } + } + stage('Check Formating') { steps { sh 'n -d exec engine pnpm prettier' diff --git a/tests/Unit/Swat/SwatStringTest.php b/tests/Unit/Swat/SwatStringTest.php new file mode 100644 index 00000000..3b402fd1 --- /dev/null +++ b/tests/Unit/Swat/SwatStringTest.php @@ -0,0 +1,44 @@ +assertEquals('foo', $result); + } + + public function testToListTwoItems() + { + $result = \SwatString::toList(['foo', 'bar']); + $this->assertEquals('foo and bar', $result); + } + + public function testToListThreeItems() + { + $result = \SwatString::toList(['foo', 'bar', 'baz']); + $this->assertEquals('foo, bar, and baz', $result); + } + + public function testToListCustomConjunctionAndDelimiter() + { + $result = \SwatString::toList(['a', 'b', 'c'], 'or', '; ', false); + $this->assertEquals('a; b or c', $result); + } + + public function testToListWithIterator() + { + $result = \SwatString::toList(new \ArrayIterator(['x', 'y'])); + $this->assertEquals('x and y', $result); + } + + public function testToListWithNonIterator() + { + $this->expectException(\SwatException::class); + \SwatString::toList('a string'); + } +} From 5bd0d90f7822f88adea0f04b36491c07be7f03b3 Mon Sep 17 00:00:00 2001 From: Colin Viebrock Date: Fri, 3 Oct 2025 15:49:36 -0400 Subject: [PATCH 05/18] Fix some issues and refactor SwatString::toList() (fixes counting iterator issue) --- Swat/SwatString.php | 54 ++++++++++++++++++---------------------- Swat/SwatTimeEntry.php | 8 +++--- Swat/SwatTreeFlydown.php | 2 +- Swat/SwatTreeNode.php | 2 +- 4 files changed, 30 insertions(+), 36 deletions(-) diff --git a/Swat/SwatString.php b/Swat/SwatString.php index cc5bc92e..c3703d5c 100644 --- a/Swat/SwatString.php +++ b/Swat/SwatString.php @@ -1439,7 +1439,7 @@ public static function toFloat($string) * Convert an iterable object or array into a human-readable, delimited * list. * - * @param array|Iterator $iterator the object to convert to a list + * @param array|Iterator $array the object to convert to a list * @param string $conjunction the list's conjunction. Usually 'and' or * 'or'. * @param string $delimiter the list delimiter. If list items should @@ -1457,46 +1457,40 @@ public static function toFloat($string) * different locales. */ public static function toList( - $iterator, + $array, $conjunction = 'and', $delimiter = ', ', $display_final_delimiter = true, ) { - if (is_array($iterator)) { - $iterator = new ArrayIterator($iterator); + if ($array instanceof Iterator) { + $array = iterator_to_array($array); } - if (!$iterator instanceof Iterator) { + if (!is_array($array)) { throw new SwatException('Value is not an Iterator or array'); } - if (count($iterator) === 1) { - $iterator->rewind(); - $list = $iterator->current(); - } else { - $count = 0; - $list = ''; - - foreach ($iterator as $value) { - if ($count != 0) { - if ($count == count($iterator) - 1) { - $list - .= $display_final_delimiter && count($iterator) > 2 - ? $delimiter - : ' '; - - if ($conjunction != '') { - $list .= $conjunction . ' '; - } - } else { - $list .= $delimiter; - } - } + $count = count($array); - $list .= $value; - $count++; - } + if ($count === 0) { + return ''; + } + + if ($count === 1) { + return reset($array); + } + + $last = array_pop($array); + $list = implode($delimiter, $array); + if ($display_final_delimiter && $count > 2) { + $list .= $delimiter; + } else { + $list .= ' '; + } + if ($conjunction !== '') { + $list .= $conjunction . ' '; } + $list .= $last; return $list; } diff --git a/Swat/SwatTimeEntry.php b/Swat/SwatTimeEntry.php index caed5111..7341661e 100644 --- a/Swat/SwatTimeEntry.php +++ b/Swat/SwatTimeEntry.php @@ -408,7 +408,7 @@ public function getState() /** * Sets the current state of this time entry widget. * - * @param bool $state the new state of this time entry widget + * @param mixed $state the new state of this time entry widget * * @see SwatState::setState() */ @@ -616,7 +616,7 @@ protected function createCompositeWidgets() /** * Creates the hour flydown for this time entry. * - * @return the hour flydown for this time entry + * @return SwatFlydown the hour flydown for this time entry */ private function createHourFlydown() { @@ -647,7 +647,7 @@ private function createMinuteFlydown() $flydown->classes = ['swat-time-entry-minute']; for ($i = 0; $i <= 59; $i++) { - $flydown->addOption($i, str_pad($i, 2, '0', STR_PAD_LEFT)); + $flydown->addOption($i, sprintf('%02d', $i)); } return $flydown; @@ -664,7 +664,7 @@ private function createSecondFlydown() $flydown->classes = ['swat-time-entry-second']; for ($i = 0; $i <= 59; $i++) { - $flydown->addOption($i, str_pad($i, 2, '0', STR_PAD_LEFT)); + $flydown->addOption($i, sprintf('%02d', $i)); } return $flydown; diff --git a/Swat/SwatTreeFlydown.php b/Swat/SwatTreeFlydown.php index f8f29c17..d90368cc 100644 --- a/Swat/SwatTreeFlydown.php +++ b/Swat/SwatTreeFlydown.php @@ -159,7 +159,7 @@ public function process() if ($this->value === null) { $this->path = []; } else { - $this->path = $this->value; + $this->path = [$this->value]; $this->value = end($this->path); } } diff --git a/Swat/SwatTreeNode.php b/Swat/SwatTreeNode.php index 1f374ea6..c15ff076 100644 --- a/Swat/SwatTreeNode.php +++ b/Swat/SwatTreeNode.php @@ -8,7 +8,7 @@ * @copyright 2005-2016 silverorange * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1 * - * @template T of SwatTreeNode + * @template T of static * * @implements RecursiveIterator */ From 1fceeba2c53366b5bf05aae295028f67145040ea Mon Sep 17 00:00:00 2001 From: Colin Viebrock Date: Fri, 3 Oct 2025 15:52:53 -0400 Subject: [PATCH 06/18] phpcs --- tests/Unit/Swat/SwatStringTest.php | 5 +++++ tests/Unit/SwatI18N/SwatI18NNumberFormatTest.php | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/tests/Unit/Swat/SwatStringTest.php b/tests/Unit/Swat/SwatStringTest.php index 3b402fd1..2cc77e41 100644 --- a/tests/Unit/Swat/SwatStringTest.php +++ b/tests/Unit/Swat/SwatStringTest.php @@ -4,6 +4,11 @@ use PHPUnit\Framework\TestCase; +/** + * @internal + * + * @coversNothing + */ class SwatStringTest extends TestCase { public function testToListSingleItem() diff --git a/tests/Unit/SwatI18N/SwatI18NNumberFormatTest.php b/tests/Unit/SwatI18N/SwatI18NNumberFormatTest.php index 71410eca..6d14633c 100644 --- a/tests/Unit/SwatI18N/SwatI18NNumberFormatTest.php +++ b/tests/Unit/SwatI18N/SwatI18NNumberFormatTest.php @@ -4,6 +4,11 @@ use PHPUnit\Framework\TestCase; +/** + * @internal + * + * @coversNothing + */ class SwatI18NNumberFormatTest extends TestCase { protected \SwatI18NNumberFormat $format; From 010505d6124ed7252f59a8f164f054158a672774 Mon Sep 17 00:00:00 2001 From: Colin Viebrock Date: Fri, 3 Oct 2025 17:20:21 -0400 Subject: [PATCH 07/18] Reorganize directory structure/namespace for tests (HTML report still not perfect) --- composer.json | 8 ++++---- phpunit.dist.xml | 6 +++--- tests/{Unit => unit}/Swat/SwatStringTest.php | 20 +++++++++---------- .../SwatI18N/SwatI18NNumberFormatTest.php | 12 +++++------ 4 files changed, 21 insertions(+), 25 deletions(-) rename tests/{Unit => unit}/Swat/SwatStringTest.php (59%) rename tests/{Unit => unit}/SwatI18N/SwatI18NNumberFormatTest.php (88%) diff --git a/composer.json b/composer.json index 0c728b54..843ab823 100644 --- a/composer.json +++ b/composer.json @@ -73,9 +73,9 @@ ] }, "autoload-dev": { - "psr-4": { - "Tests\\": "tests/" - } + "classmap": [ + "tests/" + ] }, "scripts": { "phpcs": "./vendor/bin/php-cs-fixer check -v", @@ -85,7 +85,7 @@ "phpstan:ci": "./vendor/bin/phpstan analyze -vvv --no-progress --memory-limit 2G", "phpstan:baseline": "./vendor/bin/phpstan analyze --generate-baseline", "phpunit": "./vendor/bin/phpunit --no-coverage", - "phpunit:coverage": "XDEBUG_MODE=coverage ./vendor/bin/phpunit && npx serve build/coverage" + "phpunit:coverage": "rm -rf ./build && XDEBUG_MODE=coverage ./vendor/bin/phpunit && npx serve build/coverage" }, "config": { "sort-packages": true diff --git a/phpunit.dist.xml b/phpunit.dist.xml index 5a0c5020..d26b39a8 100644 --- a/phpunit.dist.xml +++ b/phpunit.dist.xml @@ -11,9 +11,9 @@ - Swat - SwatDB - SwatI18N + . + SwatDB + SwatI18N diff --git a/tests/Unit/Swat/SwatStringTest.php b/tests/unit/Swat/SwatStringTest.php similarity index 59% rename from tests/Unit/Swat/SwatStringTest.php rename to tests/unit/Swat/SwatStringTest.php index 2cc77e41..d1bff1c4 100644 --- a/tests/Unit/Swat/SwatStringTest.php +++ b/tests/unit/Swat/SwatStringTest.php @@ -1,49 +1,47 @@ assertEquals('foo', $result); } public function testToListTwoItems() { - $result = \SwatString::toList(['foo', 'bar']); + $result = SwatString::toList(['foo', 'bar']); $this->assertEquals('foo and bar', $result); } public function testToListThreeItems() { - $result = \SwatString::toList(['foo', 'bar', 'baz']); + $result = SwatString::toList(['foo', 'bar', 'baz']); $this->assertEquals('foo, bar, and baz', $result); } public function testToListCustomConjunctionAndDelimiter() { - $result = \SwatString::toList(['a', 'b', 'c'], 'or', '; ', false); + $result = SwatString::toList(['a', 'b', 'c'], 'or', '; ', false); $this->assertEquals('a; b or c', $result); } public function testToListWithIterator() { - $result = \SwatString::toList(new \ArrayIterator(['x', 'y'])); + $result = SwatString::toList(new ArrayIterator(['x', 'y'])); $this->assertEquals('x and y', $result); } public function testToListWithNonIterator() { - $this->expectException(\SwatException::class); - \SwatString::toList('a string'); + $this->expectException(SwatException::class); + SwatString::toList('a string'); } } diff --git a/tests/Unit/SwatI18N/SwatI18NNumberFormatTest.php b/tests/unit/SwatI18N/SwatI18NNumberFormatTest.php similarity index 88% rename from tests/Unit/SwatI18N/SwatI18NNumberFormatTest.php rename to tests/unit/SwatI18N/SwatI18NNumberFormatTest.php index 6d14633c..0a534fab 100644 --- a/tests/Unit/SwatI18N/SwatI18NNumberFormatTest.php +++ b/tests/unit/SwatI18N/SwatI18NNumberFormatTest.php @@ -1,21 +1,19 @@ format = new \SwatI18NNumberFormat(); + $this->format = new SwatI18NNumberFormat(); $this->format->decimal_separator = '.'; $this->format->thousands_separator = ','; $this->format->grouping = [3]; @@ -39,7 +37,7 @@ public function testOverrideValidProperties() public function testOverrideInvalidPropertyThrowsException() { - $this->expectException(\SwatException::class); + $this->expectException(SwatException::class); $this->format->override(['invalid_property' => 'value']); } From 5e5780dd4fa14aca42f44587ecad986199548a0e Mon Sep 17 00:00:00 2001 From: Colin Viebrock Date: Fri, 3 Oct 2025 17:55:52 -0400 Subject: [PATCH 08/18] Fix level 1 issues --- Swat/SwatByteCellRenderer.php | 2 +- Swat/SwatCellRendererSet.php | 2 +- Swat/SwatCheckboxTree.php | 4 ++-- Swat/SwatContainer.php | 2 +- Swat/SwatDate.php | 6 +++--- Swat/SwatDateEntry.php | 4 ++-- Swat/SwatEntry.php | 2 +- Swat/SwatError.php | 7 +++---- Swat/SwatGroupedFlydown.php | 2 +- Swat/SwatInputCell.php | 10 +++++----- Swat/SwatNumber.php | 2 +- Swat/SwatNumericEntry.php | 4 ++-- Swat/SwatPercentageEntry.php | 2 +- Swat/SwatRatingCellRenderer.php | 4 ++-- Swat/SwatRemoveInputCell.php | 2 +- Swat/SwatString.php | 17 ++++++++-------- Swat/SwatUI.php | 20 ++++++++----------- .../SwatObjectNotFoundException.php | 8 ++++---- 18 files changed, 47 insertions(+), 53 deletions(-) diff --git a/Swat/SwatByteCellRenderer.php b/Swat/SwatByteCellRenderer.php index 33103f73..5fd87fd6 100644 --- a/Swat/SwatByteCellRenderer.php +++ b/Swat/SwatByteCellRenderer.php @@ -14,7 +14,7 @@ class SwatByteCellRenderer extends SwatCellRenderer /** * Value in bytes. * - * @var float + * @var int */ public $value; diff --git a/Swat/SwatCellRendererSet.php b/Swat/SwatCellRendererSet.php index 68cadd4c..246ecd0c 100644 --- a/Swat/SwatCellRendererSet.php +++ b/Swat/SwatCellRendererSet.php @@ -212,7 +212,7 @@ public function getRendererByPosition($position = 0) throw new SwatObjectNotFoundException( 'Set does not contain that many renderers.', 0, - $position, + (string) $position, ); } diff --git a/Swat/SwatCheckboxTree.php b/Swat/SwatCheckboxTree.php index 765a2db4..7bb67fbe 100644 --- a/Swat/SwatCheckboxTree.php +++ b/Swat/SwatCheckboxTree.php @@ -170,7 +170,7 @@ protected function validate(SwatDataTreeNode $node, $is_parent_selected) : $is_parent_selected && !$is_selected; return array_reduce( - $node->getChildren(), + iterator_to_array($node->getChildren()), function ($carry, $child) use ($is_selected) { return $carry && $this->validate($child, $is_selected); }, @@ -269,7 +269,7 @@ private function displayNode( // display children $child_nodes = $node->getChildren(); - if (count($child_nodes) > 0) { + if (iterator_count($child_nodes) > 0) { echo '
    '; foreach ($child_nodes as $child_node) { $nodes = $this->displayNode($child_node, $nodes, $index); diff --git a/Swat/SwatContainer.php b/Swat/SwatContainer.php index ea42619f..7de13262 100644 --- a/Swat/SwatContainer.php +++ b/Swat/SwatContainer.php @@ -481,7 +481,7 @@ public function hasMessage() * called elsewhere. To add a widget to a container use * {@link SwatContainer::add()}. * - * @param SwatWidget $child a reference to the child object to add + * @param SwatObject $child a reference to the child object to add * * @throws SwatInvalidClassException */ diff --git a/Swat/SwatDate.php b/Swat/SwatDate.php index 494cce18..708da168 100644 --- a/Swat/SwatDate.php +++ b/Swat/SwatDate.php @@ -905,7 +905,7 @@ public function getMinute(): int * * This method is provided for backwards compatibility with PEAR::Date. * - * @return float the second of this date + * @return int the second of this date */ public function getSecond() { @@ -1143,7 +1143,7 @@ public function convertTZById($time_zone_name): DateTime */ public function setTZ(DateTimeZone $time_zone): DateTime { - return $this->addSeconds($this->format('Z')) + return $this->addSeconds((float) $this->format('Z')) ->setTimezone($time_zone) ->subtractSeconds($this->format('Z')); } @@ -1482,7 +1482,7 @@ public function setMinute($minute): DateTime */ public function setSecond($second): DateTime { - return $this->setTime($this->getHour(), $this->getMinute(), $second); + return $this->setTime($this->getHour(), $this->getMinute(), (int) $second); } /** diff --git a/Swat/SwatDateEntry.php b/Swat/SwatDateEntry.php index 13d369ed..75ec909e 100644 --- a/Swat/SwatDateEntry.php +++ b/Swat/SwatDateEntry.php @@ -393,7 +393,7 @@ public function getState() /** * Sets the current state of this date entry widget. * - * @param bool $state the new state of this date entry widget + * @param mixed $state the new state of this date entry widget * * @see SwatState::setState() */ @@ -720,7 +720,7 @@ protected function getMonthOptionText($month) $text = ''; if ($this->show_month_number) { - $text .= str_pad($month, 2, '0', STR_PAD_LEFT) . ' - '; + $text .= sprintf('%02d', $month) . ' - '; } $date = new SwatDate('2010-' . $month . '-01'); diff --git a/Swat/SwatEntry.php b/Swat/SwatEntry.php index 2fbbab37..a7a0378b 100644 --- a/Swat/SwatEntry.php +++ b/Swat/SwatEntry.php @@ -350,7 +350,7 @@ protected function getCSSClassNames() protected function getNonce() { if ($this->nonce === null) { - $this->nonce = 'n' . md5(rand()); + $this->nonce = 'n' . md5((string) rand()); } return $this->nonce; diff --git a/Swat/SwatError.php b/Swat/SwatError.php index 38c94fb2..76096089 100644 --- a/Swat/SwatError.php +++ b/Swat/SwatError.php @@ -323,8 +323,8 @@ public function toString() } printf( - "%s. In file '%s' on line %s.\n%sMethod: %s%s%s(%s)\n", - str_pad(--$count, 6, ' ', STR_PAD_LEFT), + "%6d. In file '%s' on line %s.\n%sMethod: %s%s%s(%s)\n", + --$count, array_key_exists('file', $entry) ? $entry['file'] : 'unknown', array_key_exists('line', $entry) ? $entry['line'] : 'unknown', str_repeat(' ', 8), @@ -380,8 +380,7 @@ public function toXHTML() if (array_key_exists('args', $entry)) { $arguments = htmlspecialchars( $this->getArguments($entry['args'], $function, $class), - null, - 'UTF-8', + encoding: 'UTF-8', ); } else { $arguments = ''; diff --git a/Swat/SwatGroupedFlydown.php b/Swat/SwatGroupedFlydown.php index 667b26e8..ab08b170 100644 --- a/Swat/SwatGroupedFlydown.php +++ b/Swat/SwatGroupedFlydown.php @@ -119,7 +119,7 @@ protected function displayNode( if ( $level == 1 - && count($children) > 0 + && iterator_count($children) > 0 && end($flydown_option->value) === null && !($flydown_option instanceof SwatFlydownDivider) ) { diff --git a/Swat/SwatInputCell.php b/Swat/SwatInputCell.php index 7d3a429f..a1095755 100644 --- a/Swat/SwatInputCell.php +++ b/Swat/SwatInputCell.php @@ -469,11 +469,11 @@ protected function getInputRow() /** * Gets a cloned widget given a unique identifier. * - * @param string $replicator_id the unique identifier of the new cloned - * widget. The actual cloned widget id is - * constructed from this identifier and from - * the input row that this input cell belongs - * to. + * @param int $replicator_id the unique identifier of the new cloned + * widget. The actual cloned widget id is + * constructed from this identifier and from + * the input row that this input cell belongs + * to. * * @return SwatWidget the new cloned widget or the cloned widget retrieved * from the {@link SwatInputCell::$clones} array diff --git a/Swat/SwatNumber.php b/Swat/SwatNumber.php index 07445b76..23f39100 100644 --- a/Swat/SwatNumber.php +++ b/Swat/SwatNumber.php @@ -84,7 +84,7 @@ public static function ordinal($value) if (extension_loaded('intl')) { // get current locale - $locale = setlocale(LC_ALL, 0); + $locale = setlocale(LC_ALL, '0'); static $formatters = []; if (!isset($formatter[$locale])) { diff --git a/Swat/SwatNumericEntry.php b/Swat/SwatNumericEntry.php index c5257bd8..2f09c4e3 100644 --- a/Swat/SwatNumericEntry.php +++ b/Swat/SwatNumericEntry.php @@ -74,7 +74,7 @@ public function process() $minimum_value = str_replace( '%', '%%', - $this->getDisplayValue($this->minimum_value), + $this->getDisplayValue((string) $this->minimum_value), ); $message->primary_content = sprintf( @@ -92,7 +92,7 @@ public function process() $maximum_value = str_replace( '%', '%%', - $this->getDisplayValue($this->maximum_value), + $this->getDisplayValue((string) $this->maximum_value), ); $message->primary_content = sprintf( diff --git a/Swat/SwatPercentageEntry.php b/Swat/SwatPercentageEntry.php index ca23c254..b00a8660 100644 --- a/Swat/SwatPercentageEntry.php +++ b/Swat/SwatPercentageEntry.php @@ -22,7 +22,7 @@ protected function getDisplayValue($value) { if (is_numeric($value)) { $value = $value * 100; - $value = parent::getDisplayValue($value); + $value = parent::getDisplayValue((string) $value); $value = $value . '%'; } else { $value = parent::getDisplayValue($value); diff --git a/Swat/SwatRatingCellRenderer.php b/Swat/SwatRatingCellRenderer.php index 6ca13352..95564ec5 100644 --- a/Swat/SwatRatingCellRenderer.php +++ b/Swat/SwatRatingCellRenderer.php @@ -60,9 +60,9 @@ public function render() $outer_span->class = 'rating ' . $rating_class; $outer_span->open(); - $content = str_repeat('★', ceil($value)); + $content = str_repeat('★', (int) ceil($value)); if ($difference > 0) { - $content .= str_repeat('☆', floor($difference)); + $content .= str_repeat('☆', (int) floor($difference)); } $value_tag = new SwatHtmlTag('span'); diff --git a/Swat/SwatRemoveInputCell.php b/Swat/SwatRemoveInputCell.php index 1e3857aa..eff2556a 100644 --- a/Swat/SwatRemoveInputCell.php +++ b/Swat/SwatRemoveInputCell.php @@ -73,7 +73,7 @@ public function display($replicator_id) { $widget = $this->getClonedWidget($replicator_id); // substitute the replicator_id into the content block's contents - $widget->content = str_replace('%s', $replicator_id, $widget->content); + $widget->content = str_replace('%s', (string) $replicator_id, $widget->content); $widget->display(); } diff --git a/Swat/SwatString.php b/Swat/SwatString.php index c3703d5c..d722c70f 100644 --- a/Swat/SwatString.php +++ b/Swat/SwatString.php @@ -959,7 +959,7 @@ public static function moneyFormat( } if ($locale !== null) { - $old_locale = setlocale(LC_ALL, 0); + $old_locale = setlocale(LC_ALL, '0'); if (setlocale(LC_ALL, $locale) === false) { throw new SwatException( sprintf( @@ -1023,7 +1023,7 @@ public static function moneyFormat( public static function getInternationalCurrencySymbol($locale = null) { if ($locale !== null) { - $old_locale = setlocale(LC_MONETARY, 0); + $old_locale = setlocale(LC_MONETARY, '0'); if (setlocale(LC_MONETARY, $locale) === false) { throw new SwatException( sprintf( @@ -1104,7 +1104,7 @@ public static function numberFormat( ); if ($locale !== null) { - $old_locale = setlocale(LC_ALL, 0); + $old_locale = setlocale(LC_ALL, '0'); if (setlocale(LC_ALL, $locale) === false) { throw new SwatException( sprintf( @@ -1290,7 +1290,6 @@ public static function pad( $pad_string = ' ', $pad_type = STR_PAD_RIGHT, ) { - $output = ''; $length = $pad_length - mb_strlen($input); if ($pad_string === null || mb_strlen($pad_string) === 0) { @@ -1302,17 +1301,17 @@ public static function pad( case STR_PAD_LEFT: $padding = str_repeat( $pad_string, - ceil($length / mb_strlen($pad_string)), + (int) ceil($length / mb_strlen($pad_string)), ); $output = mb_substr($padding, 0, $length) . $input; break; case STR_PAD_BOTH: - $left_length = floor($length / 2); - $right_length = ceil($length / 2); + $left_length = (int) floor($length / 2); + $right_length = (int) ceil($length / 2); $padding = str_repeat( $pad_string, - ceil($right_length / mb_strlen($pad_string)), + (int) ceil($right_length / mb_strlen($pad_string)), ); $output = mb_substr($padding, 0, $left_length) @@ -1325,7 +1324,7 @@ public static function pad( default: $padding = str_repeat( $pad_string, - ceil($length / mb_strlen($pad_string)), + (int) ceil($length / mb_strlen($pad_string)), ); $output = $input . mb_substr($padding, 0, $length); } diff --git a/Swat/SwatUI.php b/Swat/SwatUI.php index 6354570b..5806e5dc 100644 --- a/Swat/SwatUI.php +++ b/Swat/SwatUI.php @@ -192,12 +192,16 @@ public function loadFromXML($filename, $container = null, $validate = null) $validate = self::$validate_mode; } - $xml_file = null; - - if (file_exists($filename)) { - $xml_file = $filename; + if (!file_exists($filename)) { + throw new SwatFileNotFoundException( + "SwatML file not found: '{$filename}'.", + 0, + $filename, + ); } + $xml_file = $filename; + // try to guess the translation callback based on the // filename of the xml $class_map_reversed = array_reverse(self::$class_map, true); @@ -219,14 +223,6 @@ public function loadFromXML($filename, $container = null, $validate = null) $this->translation_callback = 'gettext'; } - if ($xml_file === null) { - throw new SwatFileNotFoundException( - "SwatML file not found: '{$filename}'.", - 0, - $xml_file, - ); - } - $errors = libxml_use_internal_errors(true); // Use PHP's file loader rather than libxml so it will work with diff --git a/Swat/exceptions/SwatObjectNotFoundException.php b/Swat/exceptions/SwatObjectNotFoundException.php index 58b81a7d..7e288ab9 100644 --- a/Swat/exceptions/SwatObjectNotFoundException.php +++ b/Swat/exceptions/SwatObjectNotFoundException.php @@ -11,16 +11,16 @@ class SwatObjectNotFoundException extends SwatException /** * The object id that was searched for. * - * @var string + * @var ?string */ protected $object_id; /** * Creates a new object not found exception. * - * @param string $message the message of the exception - * @param int $code the code of the exception - * @param string $object_id the object id that was searched for + * @param string $message the message of the exception + * @param int $code the code of the exception + * @param ?string $object_id the object id that was searched for */ public function __construct($message = null, $code = 0, $object_id = null) { From 90a5bfcd4f0ccd1b805e1a3fbc1e7259ff21b886 Mon Sep 17 00:00:00 2001 From: Colin Viebrock Date: Fri, 3 Oct 2025 18:18:38 -0400 Subject: [PATCH 09/18] Fix more level 1 issues --- Swat/SwatCheckboxCellRenderer.php | 2 +- Swat/SwatDate.php | 6 ++--- Swat/SwatDateEntry.php | 10 +++------ Swat/SwatHtmlHeadEntrySetDisplayer.php | 8 +++---- Swat/SwatNumber.php | 7 +++--- Swat/SwatRadioButtonCellRenderer.php | 2 +- Swat/SwatRatingCellRenderer.php | 31 +++++++------------------- Swat/SwatTimeEntry.php | 8 ++----- SwatDB/SwatDB.php | 13 ++++++++--- phpstan.dist.neon | 2 +- 10 files changed, 36 insertions(+), 53 deletions(-) diff --git a/Swat/SwatCheckboxCellRenderer.php b/Swat/SwatCheckboxCellRenderer.php index a9100642..cc373db6 100644 --- a/Swat/SwatCheckboxCellRenderer.php +++ b/Swat/SwatCheckboxCellRenderer.php @@ -153,7 +153,7 @@ public function render() echo ''; echo ''; - if ($this->title !== null) { + if (isset($label_tag)) { $label_tag->displayContent(); $label_tag->close(); } diff --git a/Swat/SwatDate.php b/Swat/SwatDate.php index 708da168..3405ecbe 100644 --- a/Swat/SwatDate.php +++ b/Swat/SwatDate.php @@ -692,8 +692,6 @@ public static function getFormatLikeIntlById($id): string */ public static function getTimeZoneAbbreviations(): array { - static $shortnames = null; - if (self::$tz_abbreviations === null) { self::$tz_abbreviations = []; @@ -737,10 +735,10 @@ public static function getTimeZoneAbbreviation( $key = $time_zone->getName(); if (array_key_exists($key, $abbreviations)) { - $abbreviation = $abbreviations[$key]; + return $abbreviations[$key]; } - return $abbreviation; + return []; } /** diff --git a/Swat/SwatDateEntry.php b/Swat/SwatDateEntry.php index 75ec909e..1a90e85e 100644 --- a/Swat/SwatDateEntry.php +++ b/Swat/SwatDateEntry.php @@ -530,11 +530,7 @@ protected function isStartDateValid() { $this->valid_range_start->setTZById('UTC'); - return SwatDate::compare( - $this->value, - $this->valid_range_start, - true, - ) >= 0; + return SwatDate::compare($this->value, $this->valid_range_start) >= 0; } /** @@ -548,7 +544,7 @@ protected function isEndDateValid() { $this->valid_range_end->setTZById('UTC'); - return SwatDate::compare($this->value, $this->valid_range_end, true) + return SwatDate::compare($this->value, $this->valid_range_end) < 0; } @@ -764,7 +760,7 @@ protected function createDayFlydown() for ($i = $start_day; $i <= $end_day; $i++) { $flydown->addOption($i, $i); } - } elseif (SwatDate::compare($end_check, $range_end, true) != -1) { + } elseif (SwatDate::compare($end_check, $range_end) != -1) { // extra days at the beginning of the next month allowed $days_in_month = $this->valid_range_start->getDaysInMonth(); diff --git a/Swat/SwatHtmlHeadEntrySetDisplayer.php b/Swat/SwatHtmlHeadEntrySetDisplayer.php index f7563754..ae7209d9 100644 --- a/Swat/SwatHtmlHeadEntrySetDisplayer.php +++ b/Swat/SwatHtmlHeadEntrySetDisplayer.php @@ -139,13 +139,13 @@ protected function getCombinedEntries(array $entries) // add combines to set of entries foreach ($info['combines'] as $combine) { if (mb_substr($combine, -4) === '.css') { - $class_name = 'SwatStyleSheetHtmlHeadEntry'; + $class_name = SwatStyleSheetHtmlHeadEntry::class; } elseif (mb_substr($combine, -5) === '.less') { - $class_name = 'SwatLessStyleSheetHtmlHeadEntry'; + $class_name = SwatLessStyleSheetHtmlHeadEntry::class; } else { - $class_name = 'SwatJavaScriptHtmlHeadEntry'; + $class_name = SwatJavaScriptHtmlHeadEntry::class; } - $entries[$combine] = new $class_name($combine, '__combine__'); + $entries[$combine] = new $class_name($combine); } // remove files included in combines diff --git a/Swat/SwatNumber.php b/Swat/SwatNumber.php index 23f39100..61888a42 100644 --- a/Swat/SwatNumber.php +++ b/Swat/SwatNumber.php @@ -87,15 +87,16 @@ public static function ordinal($value) $locale = setlocale(LC_ALL, '0'); static $formatters = []; - if (!isset($formatter[$locale])) { - $formatter[$locale] = new NumberFormatter( + + if (!isset($formatters[$locale])) { + $formatters[$locale] = new NumberFormatter( $locale, NumberFormatter::ORDINAL, ); } // format ordinal - $ordinal_value = $formatter[$locale]->format($value); + $ordinal_value = $formatters[$locale]->format($value); // decompose to latin-1 characters (removes superscripts) $ordinal_value = Normalizer::normalize( diff --git a/Swat/SwatRadioButtonCellRenderer.php b/Swat/SwatRadioButtonCellRenderer.php index fa75effe..447fcb19 100644 --- a/Swat/SwatRadioButtonCellRenderer.php +++ b/Swat/SwatRadioButtonCellRenderer.php @@ -144,7 +144,7 @@ public function render() echo ''; echo ''; - if ($this->title !== null) { + if (isset($label_tag)) { $label_tag->displayContent(); $label_tag->close(); } diff --git a/Swat/SwatRatingCellRenderer.php b/Swat/SwatRatingCellRenderer.php index 95564ec5..26726ab1 100644 --- a/Swat/SwatRatingCellRenderer.php +++ b/Swat/SwatRatingCellRenderer.php @@ -83,28 +83,13 @@ public function render() public function getDisplayValue() { - switch ($this->round_mode) { - case self::ROUND_FLOOR: - $value = floor($this->value); - break; - - case self::ROUND_CEIL: - $value = ceil($this->value); - break; - - case self::ROUND_UP: - $value = round($this->value, $this->precision); - break; - - case self::ROUND_NONE: - $value = $this->value; - break; - - case self::ROUND_HALF: - $value = round($this->value * 2) / 2; - break; - } - - return $value; + return match ($this->round_mode) { + self::ROUND_FLOOR => floor($this->value), + self::ROUND_CEIL => ceil($this->value), + self::ROUND_UP => round($this->value, $this->precision), + self::ROUND_NONE => $this->value, + self::ROUND_HALF => round($this->value * 2) / 2, + default => throw new SwatException('Invalid round mode.'), + }; } } diff --git a/Swat/SwatTimeEntry.php b/Swat/SwatTimeEntry.php index 7341661e..d94a23dd 100644 --- a/Swat/SwatTimeEntry.php +++ b/Swat/SwatTimeEntry.php @@ -552,11 +552,7 @@ protected function isStartTimeValid() $this->valid_range_start->setDay(self::$date_day); $this->valid_range_start->setTZById('UTC'); - return SwatDate::compare( - $this->value, - $this->valid_range_start, - true, - ) >= 0; + return SwatDate::compare($this->value, $this->valid_range_start) >= 0; } /** @@ -573,7 +569,7 @@ protected function isEndTimeValid() $this->valid_range_end->setDay(self::$date_day); $this->valid_range_end->setTZById('UTC'); - return SwatDate::compare($this->value, $this->valid_range_end, true) + return SwatDate::compare($this->value, $this->valid_range_end) <= 0; } diff --git a/SwatDB/SwatDB.php b/SwatDB/SwatDB.php index bf6df4b2..1b694eea 100644 --- a/SwatDB/SwatDB.php +++ b/SwatDB/SwatDB.php @@ -1033,9 +1033,11 @@ public static function getGroupedOptionArray( $current_group = $row->group_id; } - $current_parent->addChild( - new SwatDataTreeNode($row->id, $row->title), - ); + if (isset($current_parent)) { + $current_parent->addChild( + new SwatDataTreeNode($row->id, $row->title), + ); + } $row = $rs->fetchRow(MDB2_FETCHMODE_OBJECT); } @@ -1300,6 +1302,11 @@ private static function debugStart($message) } } + if (!isset($entry)) { + // fallback if all entries are in SwatDB package + $entry = end($trace); + } + $class = array_key_exists('class', $entry) ? $entry['class'] : null; $function = array_key_exists('function', $entry) diff --git a/phpstan.dist.neon b/phpstan.dist.neon index ab31c533..efc4dcfc 100644 --- a/phpstan.dist.neon +++ b/phpstan.dist.neon @@ -2,7 +2,7 @@ includes: - phpstan-baseline.neon parameters: - level: 0 + level: 1 paths: - Swat - SwatDB From 5336520890e8c9bd543980889d2801110bd4f5e1 Mon Sep 17 00:00:00 2001 From: Colin Viebrock Date: Mon, 6 Oct 2025 10:41:57 -0400 Subject: [PATCH 10/18] Fix issue with SwatUIParent not being known --- Swat/SwatUI.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Swat/SwatUI.php b/Swat/SwatUI.php index 5806e5dc..11487d8a 100644 --- a/Swat/SwatUI.php +++ b/Swat/SwatUI.php @@ -518,25 +518,25 @@ private function checkParsedObject( /** * Attaches a widget to a parent widget in the widget tree. * - * @param SwatUIObject $object the object to attach - * @param SwatUIParent $parent the parent to attach the widget to + * @param SwatUIObject $object the widget to attach + * @param SwatUIObject $parent the parent to which to attach the widget * * @throws SwatDoesNotImplementException */ - private function attachToParent(SwatUIObject $object, SwatUIParent $parent) + private function attachToParent(SwatUIObject $object, SwatUIObject $parent) { if ($parent instanceof SwatUIParent) { $parent->addChild($object); - } else { - $class_name = get_class($parent); - throw new SwatDoesNotImplementException( - "Can not add object to parent. '{$class_name}' does not " - . 'implement SwatUIParent.', - 0, - $parent, - ); + return; } + $class_name = $parent::class; + + throw new SwatDoesNotImplementException( + "Can not add object to parent. '{$class_name}' does not implement SwatUIParent.", + 0, + $parent, + ); } /** From 282e8ba7415324d7a552a37ad2236ec634df0f16 Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Mon, 6 Oct 2025 12:04:34 -0300 Subject: [PATCH 11/18] When using named arguments, use them for all arguments --- Swat/SwatError.php | 6 +++++- Swat/exceptions/SwatException.php | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Swat/SwatError.php b/Swat/SwatError.php index 76096089..38feb8ed 100644 --- a/Swat/SwatError.php +++ b/Swat/SwatError.php @@ -379,7 +379,11 @@ public function toXHTML() if (array_key_exists('args', $entry)) { $arguments = htmlspecialchars( - $this->getArguments($entry['args'], $function, $class), + string: $this->getArguments( + $entry['args'], + $function, + $class + ), encoding: 'UTF-8', ); } else { diff --git a/Swat/exceptions/SwatException.php b/Swat/exceptions/SwatException.php index 8b0e51fc..b4a9faa1 100644 --- a/Swat/exceptions/SwatException.php +++ b/Swat/exceptions/SwatException.php @@ -354,7 +354,11 @@ public function toXHTML() if (array_key_exists('args', $entry)) { $arguments = htmlspecialchars( - $this->getArguments($entry['args'], $function, $class), + string: $this->getArguments( + $entry['args'], + $function, + $class + ), encoding: 'UTF-8', ); } else { From 24a15c07dff11ec9857d194a994709e57de43eed Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Mon, 6 Oct 2025 12:07:35 -0300 Subject: [PATCH 12/18] Allow microseconds with setSeconds as per documentation --- Swat/SwatDate.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Swat/SwatDate.php b/Swat/SwatDate.php index 3405ecbe..d0ba1b1f 100644 --- a/Swat/SwatDate.php +++ b/Swat/SwatDate.php @@ -1480,7 +1480,15 @@ public function setMinute($minute): DateTime */ public function setSecond($second): DateTime { - return $this->setTime($this->getHour(), $this->getMinute(), (int) $second); + $whole_second = (int) $second; + $microsecond = (int) (abs($second - $whole_second) * 1_000_000); + + return $this->setTime( + $this->getHour(), + $this->getMinute(), + $whole_second, + $microsecond + ); } /** From b232d181c771b417b6ca40aeeb7110775842fedc Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Mon, 6 Oct 2025 12:18:44 -0300 Subject: [PATCH 13/18] Clean up more types in input cell for row identifiers --- Swat/SwatInputCell.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Swat/SwatInputCell.php b/Swat/SwatInputCell.php index a1095755..cac50a08 100644 --- a/Swat/SwatInputCell.php +++ b/Swat/SwatInputCell.php @@ -124,7 +124,7 @@ public function process($row_identifier) * This creates a cloned widget for the given numeric identifier and then * displays the widget. * - * @param mixed $row_identifier + * @param int $row_identifier */ public function display($row_identifier) { @@ -228,9 +228,8 @@ public function getWidget($row_identifier, $widget_id = null) * * This is useful if you are deleting a row from an input row. * - * @param int replicator_id the replicator id of the cloned widget to - * unset - * @param mixed $replicator_id + * @param int $replicator_id the replicator id of the cloned widget to + * unset * * @see SwatTableViewInputRow::removeReplicatedRow() */ From 4eb3958b61cac50be5547f1163301ce442ea513a Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Mon, 6 Oct 2025 12:20:28 -0300 Subject: [PATCH 14/18] Move rounding to a protected method and make round results always return a float --- Swat/SwatRatingCellRenderer.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Swat/SwatRatingCellRenderer.php b/Swat/SwatRatingCellRenderer.php index 26726ab1..4cbdeb9c 100644 --- a/Swat/SwatRatingCellRenderer.php +++ b/Swat/SwatRatingCellRenderer.php @@ -50,7 +50,7 @@ public function render() } elseif ($this->value !== null) { $locale = SwatI18NLocale::get(); - $value = $this->getDisplayValue(); + $value = $this->getRoundedValue(); $difference = $this->maximum_value - $value; $rating_class = floor(10 * min($value, $this->maximum_value)); @@ -82,13 +82,18 @@ public function render() } public function getDisplayValue() + { + return (string) $this->getRoundedValue(); + } + + protected function getRoundedValue() { return match ($this->round_mode) { self::ROUND_FLOOR => floor($this->value), self::ROUND_CEIL => ceil($this->value), self::ROUND_UP => round($this->value, $this->precision), self::ROUND_NONE => $this->value, - self::ROUND_HALF => round($this->value * 2) / 2, + self::ROUND_HALF => round($this->value * 2) / 2.0, default => throw new SwatException('Invalid round mode.'), }; } From 32a6d56bddc7982dfb13faa2ad09a43fc078857e Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Mon, 6 Oct 2025 14:10:22 -0300 Subject: [PATCH 15/18] If no suitable input column is found, use td attributes of first column --- Swat/SwatTableViewInputRow.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Swat/SwatTableViewInputRow.php b/Swat/SwatTableViewInputRow.php index 9c48e8be..0d5f151d 100644 --- a/Swat/SwatTableViewInputRow.php +++ b/Swat/SwatTableViewInputRow.php @@ -661,6 +661,10 @@ private function displayEnterAnotherRow() $colspan += $column->getXhtmlColspan(); } + if (!isset($column)) { + $column = reset($columns); + } + $close_length = $this->parent->getXhtmlColspan() - $position - 1; $tr_tag = new SwatHtmlTag('tr'); From faa05a7fdc50e09ffe97d0e3d804c5364c29e4da Mon Sep 17 00:00:00 2001 From: Michael Gauthier Date: Mon, 6 Oct 2025 14:12:32 -0300 Subject: [PATCH 16/18] If there are no columns, do not display enter-another row --- Swat/SwatTableViewInputRow.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Swat/SwatTableViewInputRow.php b/Swat/SwatTableViewInputRow.php index 0d5f151d..69bce943 100644 --- a/Swat/SwatTableViewInputRow.php +++ b/Swat/SwatTableViewInputRow.php @@ -639,6 +639,12 @@ private function displayEnterAnotherRow() { $columns = $this->parent->getVisibleColumns(); + // If there are no columns in the table view, we cannot display an + // enter-another row. + if (count($columns) === 0) { + return; + } + $this->createEmbeddedWidgets(); /* From 34798b62336952441254442e48dbcadf8ae18f37 Mon Sep 17 00:00:00 2001 From: Colin Viebrock Date: Mon, 6 Oct 2025 13:26:38 -0400 Subject: [PATCH 17/18] Pass an empty string as the title, as we do in other places for "non-options" --- Swat/SwatCascadeFlydown.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Swat/SwatCascadeFlydown.php b/Swat/SwatCascadeFlydown.php index 8870c56b..5e127b57 100644 --- a/Swat/SwatCascadeFlydown.php +++ b/Swat/SwatCascadeFlydown.php @@ -166,7 +166,7 @@ protected function &getOptions() // if the options array doesn't exist for this parent_value, then // assume that means we don't want any values in this flydown for // that option. - $options = [new SwatOption(null, null)]; + $options = [new SwatOption(null, '')]; } return $options; From 8818ba9bc4c12ddf2657153bfdb19b771abd5583 Mon Sep 17 00:00:00 2001 From: Colin Viebrock Date: Mon, 6 Oct 2025 13:57:05 -0400 Subject: [PATCH 18/18] Fix directory casing --- phpunit.dist.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpunit.dist.xml b/phpunit.dist.xml index d26b39a8..a7581bff 100644 --- a/phpunit.dist.xml +++ b/phpunit.dist.xml @@ -6,7 +6,7 @@ > - tests/Unit + tests/unit