diff --git a/composer.json b/composer.json index a45bc60f..571b84ce 100644 --- a/composer.json +++ b/composer.json @@ -34,8 +34,8 @@ "require": { "php": "~8.1.0 || ~8.2.0 || ~8.3.0", "ext-mbstring": "*", - "laminas/laminas-servicemanager": "^3.21.0", - "laminas/laminas-stdlib": "^3.13.0" + "laminas/laminas-servicemanager": "^4.1.0", + "laminas/laminas-stdlib": "^3.19.0" }, "require-dev": { "laminas/laminas-coding-standard": "~2.5.0", diff --git a/composer.lock b/composer.lock index d8f54751..14f056fb 100644 --- a/composer.lock +++ b/composer.lock @@ -4,63 +4,111 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "dc7ab9e282128061526953709d8bd29f", + "content-hash": "494a41a79c1b61ce078ae5eb5e230184", "packages": [ + { + "name": "brick/varexporter", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/brick/varexporter.git", + "reference": "2fd038f7c9d12d468130c6e1b3ce06e4160a7dbb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/varexporter/zipball/2fd038f7c9d12d468130c6e1b3ce06e4160a7dbb", + "reference": "2fd038f7c9d12d468130c6e1b3ce06e4160a7dbb", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.0", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.2", + "phpunit/phpunit": "^8.5 || ^9.0", + "vimeo/psalm": "5.15.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\VarExporter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A powerful alternative to var_export(), which can export closures and objects without __set_state()", + "keywords": [ + "var_export" + ], + "support": { + "issues": "https://github.com/brick/varexporter/issues", + "source": "https://github.com/brick/varexporter/tree/0.4.0" + }, + "funding": [ + { + "url": "https://github.com/BenMorel", + "type": "github" + } + ], + "time": "2023-09-01T21:10:07+00:00" + }, { "name": "laminas/laminas-servicemanager", - "version": "3.22.1", + "version": "4.1.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-servicemanager.git", - "reference": "de98d297d4743956a0558a6d71616979ff779328" + "reference": "ae01570ecdf4559aef2f15d4e79e8c36874f7416" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/de98d297d4743956a0558a6d71616979ff779328", - "reference": "de98d297d4743956a0558a6d71616979ff779328", + "url": "https://api.github.com/repos/laminas/laminas-servicemanager/zipball/ae01570ecdf4559aef2f15d4e79e8c36874f7416", + "reference": "ae01570ecdf4559aef2f15d4e79e8c36874f7416", "shasum": "" }, "require": { + "brick/varexporter": "^0.3.8 || ^0.4.0", "laminas/laminas-stdlib": "^3.17", "php": "~8.1.0 || ~8.2.0 || ~8.3.0", - "psr/container": "^1.0" + "psr/container": "^1.1 || ^2.0" }, "conflict": { - "ext-psr": "*", "laminas/laminas-code": "<4.10.0", - "zendframework/zend-code": "<3.3.1", - "zendframework/zend-servicemanager": "*" + "zendframework/zend-code": "<3.3.1" }, "provide": { - "psr/container-implementation": "^1.0" - }, - "replace": { - "container-interop/container-interop": "^1.2.0" + "psr/container-implementation": "^1.0 || ^2.0" }, "require-dev": { + "boesing/psalm-plugin-stringf": "^1.4", "composer/package-versions-deprecated": "^1.11.99.5", - "friendsofphp/proxy-manager-lts": "^1.0.14", - "laminas/laminas-code": "^4.10.0", + "friendsofphp/proxy-manager-lts": "^1", + "laminas/laminas-cli": "^1.8", "laminas/laminas-coding-standard": "~2.5.0", - "laminas/laminas-container-config-test": "^0.8", - "mikey179/vfsstream": "^1.6.11", - "phpbench/phpbench": "^1.2.9", + "laminas/laminas-container-config-test": "^1.0", + "lctrs/psalm-psr-container-plugin": "^1.9", + "mikey179/vfsstream": "^1.6.11@alpha", + "phpbench/phpbench": "^1.2.7", "phpunit/phpunit": "^10.4", "psalm/plugin-phpunit": "^0.18.4", - "vimeo/psalm": "^5.8.0" + "symfony/console": "^6.0", + "vimeo/psalm": "^5.22" }, "suggest": { - "friendsofphp/proxy-manager-lts": "ProxyManager ^2.1.1 to handle lazy initialization of services" + "friendsofphp/proxy-manager-lts": "To handle lazy initialization of services", + "laminas/laminas-cli": "To consume CLI commands provided by this component" }, - "bin": [ - "bin/generate-deps-for-config-factory", - "bin/generate-factory-for-class" - ], "type": "library", + "extra": { + "laminas": { + "config-provider": "Laminas\\ServiceManager\\ConfigProvider", + "module": "Laminas\\ServiceManager" + } + }, "autoload": { - "files": [ - "src/autoload.php" - ], "psr-4": { "Laminas\\ServiceManager\\": "src/" } @@ -82,11 +130,9 @@ ], "support": { "chat": "https://laminas.dev/chat", - "docs": "https://docs.laminas.dev/laminas-servicemanager/", "forum": "https://discourse.laminas.dev", "issues": "https://github.com/laminas/laminas-servicemanager/issues", - "rss": "https://github.com/laminas/laminas-servicemanager/releases.atom", - "source": "https://github.com/laminas/laminas-servicemanager" + "source": "https://github.com/laminas/laminas-servicemanager/tree/4.1.0" }, "funding": [ { @@ -94,7 +140,7 @@ "type": "community_bridge" } ], - "time": "2023-10-24T11:19:47+00:00" + "time": "2024-04-03T20:39:36+00:00" }, { "name": "laminas/laminas-stdlib", @@ -155,24 +201,85 @@ ], "time": "2024-01-19T12:39:49+00:00" }, + { + "name": "nikic/php-parser", + "version": "v4.19.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4e1b88d21c69391150ace211e9eaf05810858d0b", + "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.1" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.1" + }, + "time": "2024-03-17T08:10:35+00:00" + }, { "name": "psr/container", - "version": "1.1.2", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "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/" @@ -199,9 +306,9 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.2" + "source": "https://github.com/php-fig/container/tree/2.0.2" }, - "time": "2021-11-05T16:50:12+00:00" + "time": "2021-11-05T16:47:00+00:00" } ], "packages-dev": [ @@ -1102,62 +1209,6 @@ }, "time": "2024-01-31T06:18:54+00:00" }, - { - "name": "nikic/php-parser", - "version": "v4.19.1", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4e1b88d21c69391150ace211e9eaf05810858d0b", - "reference": "4e1b88d21c69391150ace211e9eaf05810858d0b", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.1" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.1" - }, - "time": "2024-03-17T08:10:35+00:00" - }, { "name": "pear/archive_tar", "version": "1.5.0", @@ -3476,16 +3527,16 @@ }, { "name": "symfony/console", - "version": "v6.4.4", + "version": "v6.4.6", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "0d9e4eb5ad413075624378f474c4167ea202de78" + "reference": "a2708a5da5c87d1d0d52937bdeac625df659e11f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/0d9e4eb5ad413075624378f474c4167ea202de78", - "reference": "0d9e4eb5ad413075624378f474c4167ea202de78", + "url": "https://api.github.com/repos/symfony/console/zipball/a2708a5da5c87d1d0d52937bdeac625df659e11f", + "reference": "a2708a5da5c87d1d0d52937bdeac625df659e11f", "shasum": "" }, "require": { @@ -3550,7 +3601,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.4" + "source": "https://github.com/symfony/console/tree/v6.4.6" }, "funding": [ { @@ -3566,7 +3617,7 @@ "type": "tidelift" } ], - "time": "2024-02-22T20:27:10+00:00" + "time": "2024-03-29T19:07:53+00:00" }, { "name": "symfony/deprecation-contracts", @@ -3637,16 +3688,16 @@ }, { "name": "symfony/filesystem", - "version": "v6.4.3", + "version": "v6.4.6", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb" + "reference": "9919b5509ada52cc7f66f9a35c86a4a29955c9d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb", - "reference": "7f3b1755eb49297a0827a7575d5d2b2fd11cc9fb", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/9919b5509ada52cc7f66f9a35c86a4a29955c9d3", + "reference": "9919b5509ada52cc7f66f9a35c86a4a29955c9d3", "shasum": "" }, "require": { @@ -3680,7 +3731,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.4.3" + "source": "https://github.com/symfony/filesystem/tree/v6.4.6" }, "funding": [ { @@ -3696,7 +3747,7 @@ "type": "tidelift" } ], - "time": "2024-01-23T14:51:35+00:00" + "time": "2024-03-21T19:36:20+00:00" }, { "name": "symfony/polyfill-ctype", @@ -4018,16 +4069,16 @@ }, { "name": "symfony/service-contracts", - "version": "v3.4.1", + "version": "v3.4.2", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0" + "reference": "11bbf19a0fb7b36345861e85c5768844c552906e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/fe07cbc8d837f60caf7018068e350cc5163681a0", - "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/11bbf19a0fb7b36345861e85c5768844c552906e", + "reference": "11bbf19a0fb7b36345861e85c5768844c552906e", "shasum": "" }, "require": { @@ -4080,7 +4131,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.4.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.4.2" }, "funding": [ { @@ -4096,7 +4147,7 @@ "type": "tidelift" } ], - "time": "2023-12-26T14:02:43+00:00" + "time": "2023-12-19T21:51:00+00:00" }, { "name": "symfony/string", diff --git a/psalm-baseline.xml b/psalm-baseline.xml index f6f10e24..5e0205e4 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -512,34 +512,10 @@ - - - - - - InvokableFactory::class]]> - InvokableFactory::class]]> - - - - - - - - - - - - - - - - - @@ -616,6 +592,7 @@ + @@ -623,6 +600,8 @@ + getPluginManager()->get($rule)]]> + getPluginManager()->get($rule)]]> rules[$spec]]]> rules[$spec][$index]]]> @@ -655,9 +634,6 @@ - - - @@ -1040,6 +1016,17 @@ + + + + + + + + + + + diff --git a/src/ConfigProvider.php b/src/ConfigProvider.php index bf00a070..254c31e3 100644 --- a/src/ConfigProvider.php +++ b/src/ConfigProvider.php @@ -4,19 +4,19 @@ namespace Laminas\Filter; -use Laminas\ServiceManager\ConfigInterface; +use Laminas\ServiceManager\ServiceManager; /** - * @psalm-import-type ServiceManagerConfigurationType from ConfigInterface + * @psalm-import-type ServiceManagerConfiguration from ServiceManager */ -class ConfigProvider +final class ConfigProvider { /** * Return configuration for this component. * - * @return array + * @return array{dependencies: ServiceManagerConfiguration} */ - public function __invoke() + public function __invoke(): array { return [ 'dependencies' => $this->getDependencyConfig(), @@ -26,16 +26,13 @@ public function __invoke() /** * Return dependency mappings for this component. * - * @return ServiceManagerConfigurationType + * @return ServiceManagerConfiguration */ public function getDependencyConfig() { return [ 'aliases' => [ 'FilterManager' => FilterPluginManager::class, - - // Legacy Zend Framework aliases - 'Zend\Filter\FilterPluginManager' => FilterPluginManager::class, ], 'factories' => [ FilterPluginManager::class => FilterPluginManagerFactory::class, diff --git a/src/FilterChain.php b/src/FilterChain.php index 2e35ce5f..2a12972c 100644 --- a/src/FilterChain.php +++ b/src/FilterChain.php @@ -8,7 +8,6 @@ use IteratorAggregate; use Laminas\ServiceManager\ServiceManager; use Laminas\Stdlib\PriorityQueue; -use ReturnTypeWillChange; use Traversable; use function call_user_func; @@ -21,6 +20,7 @@ use function strtolower; /** + * @psalm-type InstanceType = FilterInterface|callable(mixed): mixed * @psalm-type FilterChainConfiguration = array{ * filters?: list, @@ -33,7 +33,7 @@ * }> * } * @extends AbstractFilter - * @implements IteratorAggregate + * @implements IteratorAggregate */ final class FilterChain extends AbstractFilter implements Countable, IteratorAggregate { @@ -48,7 +48,7 @@ final class FilterChain extends AbstractFilter implements Countable, IteratorAgg /** * Filter chain * - * @var PriorityQueue + * @var PriorityQueue */ protected $filters; @@ -110,23 +110,14 @@ public function setOptions($options) return $this; } - /** - * Return the count of attached filters - * - * @return int - */ - #[ReturnTypeWillChange] - public function count() + /** Return the count of attached filters */ + public function count(): int { return count($this->filters); } - /** - * Get plugin manager instance - * - * @return FilterPluginManager - */ - public function getPluginManager() + /** Get plugin manager instance */ + public function getPluginManager(): FilterPluginManager { $plugins = $this->plugins; if (! $plugins instanceof FilterPluginManager) { @@ -151,24 +142,24 @@ public function setPluginManager(FilterPluginManager $plugins) /** * Retrieve a filter plugin by name * - * @param string $name - * @return FilterInterface|callable(mixed): mixed + * @template T of FilterInterface + * @param class-string|string $name + * @return ($name is class-string ? T : InstanceType) */ - public function plugin($name, array $options = []) + public function plugin(string $name, array $options = []) { - $plugins = $this->getPluginManager(); - return $plugins->get($name, $options); + return $this->getPluginManager()->build($name, $options); } /** * Attach a filter to the chain * - * @param callable(mixed): mixed|FilterInterface $callback A Filter implementation or valid PHP callback + * @param InstanceType $callback A Filter implementation or valid PHP callback * @param int $priority Priority at which to enqueue filter; defaults to 1000 (higher executes earlier) * @throws Exception\InvalidArgumentException * @return self */ - public function attach($callback, $priority = self::DEFAULT_PRIORITY) + public function attach(FilterInterface|callable $callback, int $priority = self::DEFAULT_PRIORITY) { if (! is_callable($callback)) { if (! $callback instanceof FilterInterface) { @@ -189,19 +180,13 @@ public function attach($callback, $priority = self::DEFAULT_PRIORITY) * Retrieves the filter from the attached plugin manager, and then calls attach() * with the retrieved instance. * - * @param string $name + * @param class-string|string $name * @param int $priority Priority at which to enqueue filter; defaults to 1000 (higher executes earlier) * @return self */ - public function attachByName($name, mixed $options = [], $priority = self::DEFAULT_PRIORITY) + public function attachByName(string $name, array $options = [], int $priority = self::DEFAULT_PRIORITY) { - if (! is_array($options)) { - $options = (array) $options; - } elseif (empty($options)) { - $options = null; - } - $filter = $this->getPluginManager()->get($name, $options); - return $this->attach($filter, $priority); + return $this->attach($this->plugin($name, $options), $priority); } /** diff --git a/src/FilterPluginManager.php b/src/FilterPluginManager.php index c21b3aee..15aa6e04 100644 --- a/src/FilterPluginManager.php +++ b/src/FilterPluginManager.php @@ -4,401 +4,250 @@ namespace Laminas\Filter; -use Laminas\Filter\Exception\RuntimeException; use Laminas\ServiceManager\AbstractPluginManager; use Laminas\ServiceManager\Exception\InvalidServiceException; use Laminas\ServiceManager\Factory\InvokableFactory; +use Laminas\ServiceManager\ServiceManager; +use Psr\Container\ContainerInterface; +use function array_replace_recursive; use function get_debug_type; use function is_callable; use function sprintf; /** - * Plugin manager implementation for the filter chain. + * Plugin manager implementation for filters * - * Enforces that filters retrieved are either callbacks or instances of - * FilterInterface. Additionally, it registers a number of default filters - * available, as well as aliases for them. + * Enforces that filters retrieved are either callbacks or instances of FilterInterface. * - * @extends AbstractPluginManager + * @psalm-type InstanceType = FilterInterface|callable(mixed): mixed + * @extends AbstractPluginManager + * @psalm-import-type ServiceManagerConfiguration from ServiceManager */ final class FilterPluginManager extends AbstractPluginManager { - protected $aliases = [ - // @codingStandardsIgnoreStart - // For the future - 'int' => ToInt::class, - 'Int' => ToInt::class, - 'null' => ToNull::class, - 'Null' => ToNull::class, + private const CONFIGURATION = [ + 'factories' => [ + AllowList::class => InvokableFactory::class, + BaseName::class => InvokableFactory::class, + Boolean::class => InvokableFactory::class, + Callback::class => InvokableFactory::class, + Compress::class => InvokableFactory::class, + DataUnitFormatter::class => InvokableFactory::class, + DateSelect::class => InvokableFactory::class, + DateTimeFormatter::class => InvokableFactory::class, + DateTimeSelect::class => InvokableFactory::class, + Decompress::class => InvokableFactory::class, + DenyList::class => InvokableFactory::class, + Digits::class => InvokableFactory::class, + Dir::class => InvokableFactory::class, + File\LowerCase::class => InvokableFactory::class, + File\Rename::class => InvokableFactory::class, + File\RenameUpload::class => InvokableFactory::class, + File\UpperCase::class => InvokableFactory::class, + HtmlEntities::class => InvokableFactory::class, + Inflector::class => InvokableFactory::class, + ToFloat::class => InvokableFactory::class, + MonthSelect::class => InvokableFactory::class, + UpperCaseWords::class => InvokableFactory::class, + PregReplace::class => InvokableFactory::class, + RealPath::class => InvokableFactory::class, + StringPrefix::class => InvokableFactory::class, + StringSuffix::class => InvokableFactory::class, + StringToLower::class => InvokableFactory::class, + StringToUpper::class => InvokableFactory::class, + StringTrim::class => InvokableFactory::class, + StripNewlines::class => InvokableFactory::class, + StripTags::class => InvokableFactory::class, + ToInt::class => InvokableFactory::class, + ToNull::class => InvokableFactory::class, + Word\CamelCaseToDash::class => InvokableFactory::class, + Word\CamelCaseToSeparator::class => InvokableFactory::class, + Word\CamelCaseToUnderscore::class => InvokableFactory::class, + Word\DashToCamelCase::class => InvokableFactory::class, + Word\DashToSeparator::class => InvokableFactory::class, + Word\DashToUnderscore::class => InvokableFactory::class, + Word\SeparatorToCamelCase::class => InvokableFactory::class, + Word\SeparatorToDash::class => InvokableFactory::class, + Word\SeparatorToSeparator::class => Word\Service\SeparatorToSeparatorFactory::class, + Word\UnderscoreToCamelCase::class => InvokableFactory::class, + Word\UnderscoreToStudlyCase::class => InvokableFactory::class, + Word\UnderscoreToDash::class => InvokableFactory::class, + Word\UnderscoreToSeparator::class => InvokableFactory::class, + ], + 'aliases' => [ + // For the future + 'int' => ToInt::class, + 'Int' => ToInt::class, + 'null' => ToNull::class, + 'Null' => ToNull::class, - // Standard filters - 'allowlist' => AllowList::class, - 'allowList' => AllowList::class, - 'AllowList' => AllowList::class, - 'basename' => BaseName::class, - 'Basename' => BaseName::class, - 'boolean' => Boolean::class, - 'Boolean' => Boolean::class, - 'callback' => Callback::class, - 'Callback' => Callback::class, - 'compress' => Compress::class, - 'Compress' => Compress::class, - 'dataunitformatter' => DataUnitFormatter::class, - 'dataUnitFormatter' => DataUnitFormatter::class, - 'DataUnitFormatter' => DataUnitFormatter::class, - 'dateselect' => DateSelect::class, - 'dateSelect' => DateSelect::class, - 'DateSelect' => DateSelect::class, - 'datetimeformatter' => DateTimeFormatter::class, - 'datetimeFormatter' => DateTimeFormatter::class, - 'DatetimeFormatter' => DateTimeFormatter::class, - 'dateTimeFormatter' => DateTimeFormatter::class, - 'DateTimeFormatter' => DateTimeFormatter::class, - 'datetimeselect' => DateTimeSelect::class, - 'datetimeSelect' => DateTimeSelect::class, - 'DatetimeSelect' => DateTimeSelect::class, - 'dateTimeSelect' => DateTimeSelect::class, - 'DateTimeSelect' => DateTimeSelect::class, - 'decompress' => Decompress::class, - 'Decompress' => Decompress::class, - 'denylist' => DenyList::class, - 'denyList' => DenyList::class, - 'DenyList' => DenyList::class, - 'digits' => Digits::class, - 'Digits' => Digits::class, - 'dir' => Dir::class, - 'Dir' => Dir::class, - 'filelowercase' => File\LowerCase::class, - 'fileLowercase' => File\LowerCase::class, - 'FileLowercase' => File\LowerCase::class, - 'fileLowerCase' => File\LowerCase::class, - 'FileLowerCase' => File\LowerCase::class, - 'filerename' => File\Rename::class, - 'fileRename' => File\Rename::class, - 'FileRename' => File\Rename::class, - 'filerenameupload' => File\RenameUpload::class, - 'fileRenameUpload' => File\RenameUpload::class, - 'FileRenameUpload' => File\RenameUpload::class, - 'fileuppercase' => File\UpperCase::class, - 'fileUppercase' => File\UpperCase::class, - 'FileUppercase' => File\UpperCase::class, - 'fileUpperCase' => File\UpperCase::class, - 'FileUpperCase' => File\UpperCase::class, - 'htmlentities' => HtmlEntities::class, - 'htmlEntities' => HtmlEntities::class, - 'HtmlEntities' => HtmlEntities::class, - 'inflector' => Inflector::class, - 'Inflector' => Inflector::class, - 'monthselect' => MonthSelect::class, - 'monthSelect' => MonthSelect::class, - 'MonthSelect' => MonthSelect::class, - 'pregreplace' => PregReplace::class, - 'pregReplace' => PregReplace::class, - 'PregReplace' => PregReplace::class, - 'realpath' => RealPath::class, - 'realPath' => RealPath::class, - 'RealPath' => RealPath::class, - 'stringprefix' => StringPrefix::class, - 'stringPrefix' => StringPrefix::class, - 'StringPrefix' => StringPrefix::class, - 'stringsuffix' => StringSuffix::class, - 'stringSuffix' => StringSuffix::class, - 'StringSuffix' => StringSuffix::class, - 'stringtolower' => StringToLower::class, - 'stringToLower' => StringToLower::class, - 'StringToLower' => StringToLower::class, - 'stringtoupper' => StringToUpper::class, - 'stringToUpper' => StringToUpper::class, - 'StringToUpper' => StringToUpper::class, - 'stringtrim' => StringTrim::class, - 'stringTrim' => StringTrim::class, - 'StringTrim' => StringTrim::class, - 'stripnewlines' => StripNewlines::class, - 'stripNewlines' => StripNewlines::class, - 'StripNewlines' => StripNewlines::class, - 'striptags' => StripTags::class, - 'stripTags' => StripTags::class, - 'StripTags' => StripTags::class, - 'toint' => ToInt::class, - 'toInt' => ToInt::class, - 'ToInt' => ToInt::class, - 'tofloat' => ToFloat::class, - 'toFloat' => ToFloat::class, - 'ToFloat' => ToFloat::class, - 'tonull' => ToNull::class, - 'toNull' => ToNull::class, - 'ToNull' => ToNull::class, - 'uppercasewords' => UpperCaseWords::class, - 'upperCaseWords' => UpperCaseWords::class, - 'UpperCaseWords' => UpperCaseWords::class, - 'wordcamelcasetodash' => Word\CamelCaseToDash::class, - 'wordCamelCaseToDash' => Word\CamelCaseToDash::class, - 'WordCamelCaseToDash' => Word\CamelCaseToDash::class, - 'wordcamelcasetoseparator' => Word\CamelCaseToSeparator::class, - 'wordCamelCaseToSeparator' => Word\CamelCaseToSeparator::class, - 'WordCamelCaseToSeparator' => Word\CamelCaseToSeparator::class, - 'wordcamelcasetounderscore' => Word\CamelCaseToUnderscore::class, - 'wordCamelCaseToUnderscore' => Word\CamelCaseToUnderscore::class, - 'WordCamelCaseToUnderscore' => Word\CamelCaseToUnderscore::class, - 'worddashtocamelcase' => Word\DashToCamelCase::class, - 'wordDashToCamelCase' => Word\DashToCamelCase::class, - 'WordDashToCamelCase' => Word\DashToCamelCase::class, - 'worddashtoseparator' => Word\DashToSeparator::class, - 'wordDashToSeparator' => Word\DashToSeparator::class, - 'WordDashToSeparator' => Word\DashToSeparator::class, - 'worddashtounderscore' => Word\DashToUnderscore::class, - 'wordDashToUnderscore' => Word\DashToUnderscore::class, - 'WordDashToUnderscore' => Word\DashToUnderscore::class, - 'wordseparatortocamelcase' => Word\SeparatorToCamelCase::class, - 'wordSeparatorToCamelCase' => Word\SeparatorToCamelCase::class, - 'WordSeparatorToCamelCase' => Word\SeparatorToCamelCase::class, - 'wordseparatortodash' => Word\SeparatorToDash::class, - 'wordSeparatorToDash' => Word\SeparatorToDash::class, - 'WordSeparatorToDash' => Word\SeparatorToDash::class, - 'wordseparatortoseparator' => Word\SeparatorToSeparator::class, - 'wordSeparatorToSeparator' => Word\SeparatorToSeparator::class, - 'WordSeparatorToSeparator' => Word\SeparatorToSeparator::class, - 'wordunderscoretocamelcase' => Word\UnderscoreToCamelCase::class, - 'wordUnderscoreToCamelCase' => Word\UnderscoreToCamelCase::class, - 'WordUnderscoreToCamelCase' => Word\UnderscoreToCamelCase::class, - 'wordunderscoretostudlycase' => Word\UnderscoreToStudlyCase::class, - 'wordUnderscoreToStudlyCase' => Word\UnderscoreToStudlyCase::class, - 'WordUnderscoreToStudlyCase' => Word\UnderscoreToStudlyCase::class, - 'wordunderscoretodash' => Word\UnderscoreToDash::class, - 'wordUnderscoreToDash' => Word\UnderscoreToDash::class, - 'WordUnderscoreToDash' => Word\UnderscoreToDash::class, - 'wordunderscoretoseparator' => Word\UnderscoreToSeparator::class, - 'wordUnderscoreToSeparator' => Word\UnderscoreToSeparator::class, - 'WordUnderscoreToSeparator' => Word\UnderscoreToSeparator::class, - - // Legacy Zend Framework aliases - 'Zend\Filter\BaseName' => BaseName::class, - 'Zend\Filter\Boolean' => Boolean::class, - 'Zend\Filter\Callback' => Callback::class, - 'Zend\Filter\Compress' => Compress::class, - 'Zend\Filter\DataUnitFormatter' => DataUnitFormatter::class, - 'Zend\Filter\DateSelect' => DateSelect::class, - 'Zend\Filter\DateTimeFormatter' => DateTimeFormatter::class, - 'Zend\Filter\DateTimeSelect' => DateTimeSelect::class, - 'Zend\Filter\Decompress' => Decompress::class, - 'Zend\Filter\Digits' => Digits::class, - 'Zend\Filter\Dir' => Dir::class, - 'Zend\Filter\File\LowerCase' => File\LowerCase::class, - 'Zend\Filter\File\Rename' => File\Rename::class, - 'Zend\Filter\File\RenameUpload' => File\RenameUpload::class, - 'Zend\Filter\File\UpperCase' => File\UpperCase::class, - 'Zend\Filter\HtmlEntities' => HtmlEntities::class, - 'Zend\Filter\Inflector' => Inflector::class, - 'Zend\Filter\ToInt' => ToInt::class, - 'Zend\Filter\ToFloat' => ToFloat::class, - 'Zend\Filter\MonthSelect' => MonthSelect::class, - 'Zend\Filter\ToNull' => ToNull::class, - 'Zend\Filter\UpperCaseWords' => UpperCaseWords::class, - 'Zend\Filter\PregReplace' => PregReplace::class, - 'Zend\Filter\RealPath' => RealPath::class, - 'Zend\Filter\StringPrefix' => StringPrefix::class, - 'Zend\Filter\StringSuffix' => StringSuffix::class, - 'Zend\Filter\StringToLower' => StringToLower::class, - 'Zend\Filter\StringToUpper' => StringToUpper::class, - 'Zend\Filter\StringTrim' => StringTrim::class, - 'Zend\Filter\StripNewlines' => StripNewlines::class, - 'Zend\Filter\StripTags' => StripTags::class, - 'Zend\Filter\Word\CamelCaseToDash' => Word\CamelCaseToDash::class, - 'Zend\Filter\Word\CamelCaseToSeparator' => Word\CamelCaseToSeparator::class, - 'Zend\Filter\Word\CamelCaseToUnderscore' => Word\CamelCaseToUnderscore::class, - 'Zend\Filter\Word\DashToCamelCase' => Word\DashToCamelCase::class, - 'Zend\Filter\Word\DashToSeparator' => Word\DashToSeparator::class, - 'Zend\Filter\Word\DashToUnderscore' => Word\DashToUnderscore::class, - 'Zend\Filter\Word\SeparatorToCamelCase' => Word\SeparatorToCamelCase::class, - 'Zend\Filter\Word\SeparatorToDash' => Word\SeparatorToDash::class, - 'Zend\Filter\Word\SeparatorToSeparator' => Word\SeparatorToSeparator::class, - 'Zend\Filter\Word\UnderscoreToCamelCase' => Word\UnderscoreToCamelCase::class, - 'Zend\Filter\Word\UnderscoreToStudlyCase' => Word\UnderscoreToStudlyCase::class, - 'Zend\Filter\Word\UnderscoreToDash' => Word\UnderscoreToDash::class, - 'Zend\Filter\Word\UnderscoreToSeparator' => Word\UnderscoreToSeparator::class, - - // v2 normalized FQCNs - 'zendfiltertoint' => ToInt::class, - 'zendfiltertofloat' => ToFloat::class, - 'zendfiltertonull' => ToNull::class, - 'zendfilterbasename' => BaseName::class, - 'zendfilterboolean' => Boolean::class, - 'zendfiltercallback' => Callback::class, - 'zendfiltercompress' => Compress::class, - 'zendfilterdataunitformatter' => DataUnitFormatter::class, - 'zendfilterdateselect' => DateSelect::class, - 'zendfilterdatetimeformatter' => DateTimeFormatter::class, - 'zendfilterdatetimeselect' => DateTimeSelect::class, - 'zendfilterdecompress' => Decompress::class, - 'zendfilterdigits' => Digits::class, - 'zendfilterdir' => Dir::class, - 'zendfilterfilelowercase' => File\LowerCase::class, - 'zendfilterfilerename' => File\Rename::class, - 'zendfilterfilerenameupload' => File\RenameUpload::class, - 'zendfilterfileuppercase' => File\UpperCase::class, - 'zendfilterhtmlentities' => HtmlEntities::class, - 'zendfilterinflector' => Inflector::class, - 'zendfiltermonthselect' => MonthSelect::class, - 'zendfilterpregreplace' => PregReplace::class, - 'zendfilterrealpath' => RealPath::class, - 'zendfilterstringprefix' => StringPrefix::class, - 'zendfilterstringsuffix' => StringSuffix::class, - 'zendfilterstringtolower' => StringToLower::class, - 'zendfilterstringtoupper' => StringToUpper::class, - 'zendfilterstringtrim' => StringTrim::class, - 'zendfilterstripnewlines' => StripNewlines::class, - 'zendfilterstriptags' => StripTags::class, - 'zendfilteruppercasewords' => UpperCaseWords::class, - 'zendfilterwordcamelcasetodash' => Word\CamelCaseToDash::class, - 'zendfilterwordcamelcasetoseparator' => Word\CamelCaseToSeparator::class, - 'zendfilterwordcamelcasetounderscore' => Word\CamelCaseToUnderscore::class, - 'zendfilterworddashtocamelcase' => Word\DashToCamelCase::class, - 'zendfilterworddashtoseparator' => Word\DashToSeparator::class, - 'zendfilterworddashtounderscore' => Word\DashToUnderscore::class, - 'zendfilterwordseparatortocamelcase' => Word\SeparatorToCamelCase::class, - 'zendfilterwordseparatortodash' => Word\SeparatorToDash::class, - 'zendfilterwordseparatortoseparator' => Word\SeparatorToSeparator::class, - 'zendfilterwordunderscoretocamelcase' => Word\UnderscoreToCamelCase::class, - 'zendfilterwordunderscoretostudlycase' => Word\UnderscoreToStudlyCase::class, - 'zendfilterwordunderscoretodash' => Word\UnderscoreToDash::class, - 'zendfilterwordunderscoretoseparator' => Word\UnderscoreToSeparator::class, - // @codingStandardsIgnoreEnd + // Standard filters + 'allowlist' => AllowList::class, + 'allowList' => AllowList::class, + 'AllowList' => AllowList::class, + 'basename' => BaseName::class, + 'Basename' => BaseName::class, + 'boolean' => Boolean::class, + 'Boolean' => Boolean::class, + 'callback' => Callback::class, + 'Callback' => Callback::class, + 'compress' => Compress::class, + 'Compress' => Compress::class, + 'dataunitformatter' => DataUnitFormatter::class, + 'dataUnitFormatter' => DataUnitFormatter::class, + 'DataUnitFormatter' => DataUnitFormatter::class, + 'dateselect' => DateSelect::class, + 'dateSelect' => DateSelect::class, + 'DateSelect' => DateSelect::class, + 'datetimeformatter' => DateTimeFormatter::class, + 'datetimeFormatter' => DateTimeFormatter::class, + 'DatetimeFormatter' => DateTimeFormatter::class, + 'dateTimeFormatter' => DateTimeFormatter::class, + 'DateTimeFormatter' => DateTimeFormatter::class, + 'datetimeselect' => DateTimeSelect::class, + 'datetimeSelect' => DateTimeSelect::class, + 'DatetimeSelect' => DateTimeSelect::class, + 'dateTimeSelect' => DateTimeSelect::class, + 'DateTimeSelect' => DateTimeSelect::class, + 'decompress' => Decompress::class, + 'Decompress' => Decompress::class, + 'denylist' => DenyList::class, + 'denyList' => DenyList::class, + 'DenyList' => DenyList::class, + 'digits' => Digits::class, + 'Digits' => Digits::class, + 'dir' => Dir::class, + 'Dir' => Dir::class, + 'filelowercase' => File\LowerCase::class, + 'fileLowercase' => File\LowerCase::class, + 'FileLowercase' => File\LowerCase::class, + 'fileLowerCase' => File\LowerCase::class, + 'FileLowerCase' => File\LowerCase::class, + 'filerename' => File\Rename::class, + 'fileRename' => File\Rename::class, + 'FileRename' => File\Rename::class, + 'filerenameupload' => File\RenameUpload::class, + 'fileRenameUpload' => File\RenameUpload::class, + 'FileRenameUpload' => File\RenameUpload::class, + 'fileuppercase' => File\UpperCase::class, + 'fileUppercase' => File\UpperCase::class, + 'FileUppercase' => File\UpperCase::class, + 'fileUpperCase' => File\UpperCase::class, + 'FileUpperCase' => File\UpperCase::class, + 'htmlentities' => HtmlEntities::class, + 'htmlEntities' => HtmlEntities::class, + 'HtmlEntities' => HtmlEntities::class, + 'inflector' => Inflector::class, + 'Inflector' => Inflector::class, + 'monthselect' => MonthSelect::class, + 'monthSelect' => MonthSelect::class, + 'MonthSelect' => MonthSelect::class, + 'pregreplace' => PregReplace::class, + 'pregReplace' => PregReplace::class, + 'PregReplace' => PregReplace::class, + 'realpath' => RealPath::class, + 'realPath' => RealPath::class, + 'RealPath' => RealPath::class, + 'stringprefix' => StringPrefix::class, + 'stringPrefix' => StringPrefix::class, + 'StringPrefix' => StringPrefix::class, + 'stringsuffix' => StringSuffix::class, + 'stringSuffix' => StringSuffix::class, + 'StringSuffix' => StringSuffix::class, + 'stringtolower' => StringToLower::class, + 'stringToLower' => StringToLower::class, + 'StringToLower' => StringToLower::class, + 'stringtoupper' => StringToUpper::class, + 'stringToUpper' => StringToUpper::class, + 'StringToUpper' => StringToUpper::class, + 'stringtrim' => StringTrim::class, + 'stringTrim' => StringTrim::class, + 'StringTrim' => StringTrim::class, + 'stripnewlines' => StripNewlines::class, + 'stripNewlines' => StripNewlines::class, + 'StripNewlines' => StripNewlines::class, + 'striptags' => StripTags::class, + 'stripTags' => StripTags::class, + 'StripTags' => StripTags::class, + 'toint' => ToInt::class, + 'toInt' => ToInt::class, + 'ToInt' => ToInt::class, + 'tofloat' => ToFloat::class, + 'toFloat' => ToFloat::class, + 'ToFloat' => ToFloat::class, + 'tonull' => ToNull::class, + 'toNull' => ToNull::class, + 'ToNull' => ToNull::class, + 'uppercasewords' => UpperCaseWords::class, + 'upperCaseWords' => UpperCaseWords::class, + 'UpperCaseWords' => UpperCaseWords::class, + 'wordcamelcasetodash' => Word\CamelCaseToDash::class, + 'wordCamelCaseToDash' => Word\CamelCaseToDash::class, + 'WordCamelCaseToDash' => Word\CamelCaseToDash::class, + 'wordcamelcasetoseparator' => Word\CamelCaseToSeparator::class, + 'wordCamelCaseToSeparator' => Word\CamelCaseToSeparator::class, + 'WordCamelCaseToSeparator' => Word\CamelCaseToSeparator::class, + 'wordcamelcasetounderscore' => Word\CamelCaseToUnderscore::class, + 'wordCamelCaseToUnderscore' => Word\CamelCaseToUnderscore::class, + 'WordCamelCaseToUnderscore' => Word\CamelCaseToUnderscore::class, + 'worddashtocamelcase' => Word\DashToCamelCase::class, + 'wordDashToCamelCase' => Word\DashToCamelCase::class, + 'WordDashToCamelCase' => Word\DashToCamelCase::class, + 'worddashtoseparator' => Word\DashToSeparator::class, + 'wordDashToSeparator' => Word\DashToSeparator::class, + 'WordDashToSeparator' => Word\DashToSeparator::class, + 'worddashtounderscore' => Word\DashToUnderscore::class, + 'wordDashToUnderscore' => Word\DashToUnderscore::class, + 'WordDashToUnderscore' => Word\DashToUnderscore::class, + 'wordseparatortocamelcase' => Word\SeparatorToCamelCase::class, + 'wordSeparatorToCamelCase' => Word\SeparatorToCamelCase::class, + 'WordSeparatorToCamelCase' => Word\SeparatorToCamelCase::class, + 'wordseparatortodash' => Word\SeparatorToDash::class, + 'wordSeparatorToDash' => Word\SeparatorToDash::class, + 'WordSeparatorToDash' => Word\SeparatorToDash::class, + 'wordseparatortoseparator' => Word\SeparatorToSeparator::class, + 'wordSeparatorToSeparator' => Word\SeparatorToSeparator::class, + 'WordSeparatorToSeparator' => Word\SeparatorToSeparator::class, + 'wordunderscoretocamelcase' => Word\UnderscoreToCamelCase::class, + 'wordUnderscoreToCamelCase' => Word\UnderscoreToCamelCase::class, + 'WordUnderscoreToCamelCase' => Word\UnderscoreToCamelCase::class, + 'wordunderscoretostudlycase' => Word\UnderscoreToStudlyCase::class, + 'wordUnderscoreToStudlyCase' => Word\UnderscoreToStudlyCase::class, + 'WordUnderscoreToStudlyCase' => Word\UnderscoreToStudlyCase::class, + 'wordunderscoretodash' => Word\UnderscoreToDash::class, + 'wordUnderscoreToDash' => Word\UnderscoreToDash::class, + 'WordUnderscoreToDash' => Word\UnderscoreToDash::class, + 'wordunderscoretoseparator' => Word\UnderscoreToSeparator::class, + 'wordUnderscoreToSeparator' => Word\UnderscoreToSeparator::class, + 'WordUnderscoreToSeparator' => Word\UnderscoreToSeparator::class, + ], ]; - /** - * Default set of plugins factories - * - * @var array - */ - protected $factories = [ - // Standard filters - AllowList::class => InvokableFactory::class, - BaseName::class => InvokableFactory::class, - Boolean::class => InvokableFactory::class, - Callback::class => InvokableFactory::class, - Compress::class => InvokableFactory::class, - DataUnitFormatter::class => InvokableFactory::class, - DateSelect::class => InvokableFactory::class, - DateTimeFormatter::class => InvokableFactory::class, - DateTimeSelect::class => InvokableFactory::class, - Decompress::class => InvokableFactory::class, - DenyList::class => InvokableFactory::class, - Digits::class => InvokableFactory::class, - Dir::class => InvokableFactory::class, - File\LowerCase::class => InvokableFactory::class, - File\Rename::class => InvokableFactory::class, - File\RenameUpload::class => InvokableFactory::class, - File\UpperCase::class => InvokableFactory::class, - HtmlEntities::class => InvokableFactory::class, - Inflector::class => InvokableFactory::class, - ToInt::class => InvokableFactory::class, - ToFloat::class => InvokableFactory::class, - MonthSelect::class => InvokableFactory::class, - ToNull::class => InvokableFactory::class, - UpperCaseWords::class => InvokableFactory::class, - PregReplace::class => InvokableFactory::class, - RealPath::class => InvokableFactory::class, - StringPrefix::class => InvokableFactory::class, - StringSuffix::class => InvokableFactory::class, - StringToLower::class => InvokableFactory::class, - StringToUpper::class => InvokableFactory::class, - StringTrim::class => InvokableFactory::class, - StripNewlines::class => InvokableFactory::class, - StripTags::class => InvokableFactory::class, - ToInt::class => InvokableFactory::class, - ToNull::class => InvokableFactory::class, - Word\CamelCaseToDash::class => InvokableFactory::class, - Word\CamelCaseToSeparator::class => InvokableFactory::class, - Word\CamelCaseToUnderscore::class => InvokableFactory::class, - Word\DashToCamelCase::class => InvokableFactory::class, - Word\DashToSeparator::class => InvokableFactory::class, - Word\DashToUnderscore::class => InvokableFactory::class, - Word\SeparatorToCamelCase::class => InvokableFactory::class, - Word\SeparatorToDash::class => InvokableFactory::class, - Word\SeparatorToSeparator::class => Word\Service\SeparatorToSeparatorFactory::class, - Word\UnderscoreToCamelCase::class => InvokableFactory::class, - Word\UnderscoreToStudlyCase::class => InvokableFactory::class, - Word\UnderscoreToDash::class => InvokableFactory::class, - Word\UnderscoreToSeparator::class => InvokableFactory::class, - - // v2 canonical FQCNs - 'laminasfiltertoint' => InvokableFactory::class, - 'laminasfiltertofloat' => InvokableFactory::class, - 'laminasfiltertonull' => InvokableFactory::class, - 'laminasfilterbasename' => InvokableFactory::class, - 'laminasfilterboolean' => InvokableFactory::class, - 'laminasfiltercallback' => InvokableFactory::class, - 'laminasfiltercompress' => InvokableFactory::class, - 'laminasfilterdataunitformatter' => InvokableFactory::class, - 'laminasfilterdateselect' => InvokableFactory::class, - 'laminasfilterdatetimeformatter' => InvokableFactory::class, - 'laminasfilterdatetimeselect' => InvokableFactory::class, - 'laminasfilterdecompress' => InvokableFactory::class, - 'laminasfilterdigits' => InvokableFactory::class, - 'laminasfilterdir' => InvokableFactory::class, - 'laminasfilterfilelowercase' => InvokableFactory::class, - 'laminasfilterfilerename' => InvokableFactory::class, - 'laminasfilterfilerenameupload' => InvokableFactory::class, - 'laminasfilterfileuppercase' => InvokableFactory::class, - 'laminasfilterhtmlentities' => InvokableFactory::class, - 'laminasfilterinflector' => InvokableFactory::class, - 'laminasfiltermonthselect' => InvokableFactory::class, - 'laminasfilterpregreplace' => InvokableFactory::class, - 'laminasfilterrealpath' => InvokableFactory::class, - 'laminasfilterstringprefix' => InvokableFactory::class, - 'laminasfilterstringsuffix' => InvokableFactory::class, - 'laminasfilterstringtolower' => InvokableFactory::class, - 'laminasfilterstringtoupper' => InvokableFactory::class, - 'laminasfilterstringtrim' => InvokableFactory::class, - 'laminasfilterstripnewlines' => InvokableFactory::class, - 'laminasfilterstriptags' => InvokableFactory::class, - 'laminasfilteruppercasewords' => InvokableFactory::class, - 'laminasfilterwordcamelcasetodash' => InvokableFactory::class, - 'laminasfilterwordcamelcasetoseparator' => InvokableFactory::class, - 'laminasfilterwordcamelcasetounderscore' => InvokableFactory::class, - 'laminasfilterworddashtocamelcase' => InvokableFactory::class, - 'laminasfilterworddashtoseparator' => InvokableFactory::class, - 'laminasfilterworddashtounderscore' => InvokableFactory::class, - 'laminasfilterwordseparatortocamelcase' => InvokableFactory::class, - 'laminasfilterwordseparatortodash' => InvokableFactory::class, - 'laminasfilterwordseparatortoseparator' => Word\Service\SeparatorToSeparatorFactory::class, - 'laminasfilterwordunderscoretocamelcase' => InvokableFactory::class, - 'laminasfilterwordunderscoretostudlycase' => InvokableFactory::class, - 'laminasfilterwordunderscoretodash' => InvokableFactory::class, - 'laminasfilterwordunderscoretoseparator' => InvokableFactory::class, - ]; + /** Filter instances are never shared */ + protected bool $sharedByDefault = false; - protected $instanceOf = FilterInterface::class; + /** Generally speaking, filters can be constructed without arguments */ + protected bool $autoAddInvokableClass = true; /** - * Whether or not to share by default; default to false (v2) - * - * @var bool + * @param ServiceManagerConfiguration $config */ - protected $shareByDefault = false; - - /** - * Whether or not to share by default; default to false (v3) - * - * @var bool - */ - protected $sharedByDefault = false; + public function __construct(ContainerInterface $creationContext, array $config = []) + { + /** @var ServiceManagerConfiguration $config */ + $config = array_replace_recursive(self::CONFIGURATION, $config); + parent::__construct($creationContext, $config); + } - /** - * {@inheritdoc} - * - * @psalm-assert FilterInterface|callable(mixed): mixed $instance - */ - public function validate(mixed $instance) + /** @inheritDoc */ + public function validate(mixed $instance): void { - if ($instance instanceof $this->instanceOf) { - // we're okay + if ($instance instanceof FilterInterface) { return; } if (is_callable($instance)) { - // also okay return; } @@ -408,47 +257,4 @@ public function validate(mixed $instance) __NAMESPACE__ )); } - - /** - * Validate the plugin (v2) - * - * Checks that the filter loaded is either a valid callback or an instance - * of FilterInterface. - * - * @return void - * @throws RuntimeException If invalid. - */ - public function validatePlugin(mixed $plugin) - { - try { - $this->validate($plugin); - } catch (InvalidServiceException $e) { - throw new RuntimeException($e->getMessage(), $e->getCode(), $e); - } - } - - /** - * @inheritDoc - * @template InstanceType of FilterInterface - * @param class-string|string $name Service name of plugin to retrieve. - * @param null|array $options Options to use when creating the instance. - * @return InstanceType|callable(mixed): mixed - * @psalm-return ($name is class-string ? InstanceType : callable(mixed): mixed) - */ - public function get($name, ?array $options = null) - { - /** @psalm-suppress MixedReturnStatement */ - return parent::get($name, $options); - } - - /** - * @param string $name - * @param FilterInterface|callable(mixed): mixed $service - * @return void - * @psalm-suppress MoreSpecificImplementedParamType - */ - public function setService($name, $service) - { - parent::setService($name, $service); - } } diff --git a/src/FilterProviderInterface.php b/src/FilterProviderInterface.php index 0a075cc4..72978d28 100644 --- a/src/FilterProviderInterface.php +++ b/src/FilterProviderInterface.php @@ -4,16 +4,20 @@ namespace Laminas\Filter; +use Laminas\ServiceManager\ServiceManager; + /** * Implement this interface within Module classes to indicate that your module * provides filter configuration for the FilterPluginManager. + * + * @psalm-import-type ServiceManagerConfiguration from ServiceManager */ interface FilterProviderInterface { /** * Provide plugin manager configuration for filters. * - * @return array + * @return ServiceManagerConfiguration */ - public function getFilterConfig(); + public function getFilterConfig(): array; } diff --git a/src/Module.php b/src/Module.php index a03522ca..4bb95a51 100644 --- a/src/Module.php +++ b/src/Module.php @@ -5,13 +5,19 @@ namespace Laminas\Filter; use Laminas\ModuleManager\ModuleManager; +use Laminas\ServiceManager\ServiceManager; +/** + * @psalm-import-type ServiceManagerConfiguration from ServiceManager + */ class Module { /** * Return default laminas-filter configuration for laminas-mvc applications. + * + * @return array{service_manager: ServiceManagerConfiguration} */ - public function getConfig() + public function getConfig(): array { $provider = new ConfigProvider(); @@ -24,9 +30,8 @@ public function getConfig() * Register a specification for the FilterManager with the ServiceListener. * * @param ModuleManager $moduleManager - * @return void */ - public function init($moduleManager) + public function init($moduleManager): void { $event = $moduleManager->getEvent(); $container = $event->getParam('ServiceManager'); diff --git a/test/AllowListTest.php b/test/AllowListTest.php index d22c3581..c19ce1fa 100644 --- a/test/AllowListTest.php +++ b/test/AllowListTest.php @@ -5,8 +5,6 @@ namespace LaminasTest\Filter; use Laminas\Filter\AllowList as AllowListFilter; -use Laminas\Filter\FilterPluginManager; -use Laminas\ServiceManager\ServiceManager; use Laminas\Stdlib\ArrayObject; use Laminas\Stdlib\Exception; use PHPUnit\Framework\Attributes\DataProvider; @@ -39,7 +37,7 @@ public function testConstructorDefaults(): void public function testWithPluginManager(): void { - $pluginManager = new FilterPluginManager(new ServiceManager()); + $pluginManager = CreatePluginManager::withDefaults(); $filter = $pluginManager->get('AllowList'); self::assertInstanceOf(AllowListFilter::class, $filter); diff --git a/test/CreatePluginManager.php b/test/CreatePluginManager.php new file mode 100644 index 00000000..6984e904 --- /dev/null +++ b/test/CreatePluginManager.php @@ -0,0 +1,19 @@ +get('DenyList'); self::assertInstanceOf(DenyListFilter::class, $filter); diff --git a/test/FilterChainTest.php b/test/FilterChainTest.php index 33b3df7f..649d1e34 100644 --- a/test/FilterChainTest.php +++ b/test/FilterChainTest.php @@ -62,7 +62,7 @@ public function testAllowsConnectingArbitraryCallbacks(): void public function testAllowsConnectingViaClassShortName(): void { $chain = new FilterChain(); - $chain->attachByName(StringTrim::class, null, 100) + $chain->attachByName(StringTrim::class, [], 100) ->attachByName(StripTags::class) ->attachByName(StringToLower::class, ['encoding' => 'utf-8'], 900); diff --git a/test/FilterPluginManagerCompatibilityTest.php b/test/FilterPluginManagerCompatibilityTest.php index 99d9cba5..9df0e2fa 100644 --- a/test/FilterPluginManagerCompatibilityTest.php +++ b/test/FilterPluginManagerCompatibilityTest.php @@ -5,53 +5,84 @@ namespace LaminasTest\Filter; use Generator; -use Laminas\Filter\Exception\RuntimeException; -use Laminas\Filter\FilterInterface; use Laminas\Filter\FilterPluginManager; -use Laminas\ServiceManager\ServiceManager; -use Laminas\ServiceManager\Test\CommonPluginManagerTrait; +use Laminas\ServiceManager\Exception\InvalidServiceException; use PHPUnit\Framework\TestCase; -use ReflectionProperty; +use ReflectionClass; +use stdClass; +use Throwable; +use function assert; +use function class_exists; +use function is_string; use function strpos; class FilterPluginManagerCompatibilityTest extends TestCase { - use CommonPluginManagerTrait; - protected static function getPluginManager(): FilterPluginManager { - return new FilterPluginManager(new ServiceManager()); - } - - protected function getV2InvalidPluginException(): string - { - return RuntimeException::class; - } - - protected function getInstanceOf(): string - { - return FilterInterface::class; + return CreatePluginManager::withDefaults(); } - /** @return Generator */ + /** @return Generator */ public static function aliasProvider(): Generator { - $pluginManager = self::getPluginManager(); - $r = new ReflectionProperty($pluginManager, 'aliases'); - $aliases = $r->getValue($pluginManager); - self::assertIsArray($aliases); + $class = new ReflectionClass(FilterPluginManager::class); + $config = $class->getConstant('CONFIGURATION'); + self::assertIsArray($config); + self::assertArrayHasKey('aliases', $config); + self::assertIsArray($config['aliases']); - foreach ($aliases as $alias => $target) { - self::assertIsString($alias); - self::assertIsString($target); + foreach ($config['aliases'] as $alias => $target) { + assert(is_string($alias) && is_string($target)); // Skipping as it has required options if (strpos($target, 'DataUnitFormatter') !== false) { continue; } + assert(class_exists($target)); + yield $alias => [$alias, $target]; } } + + /** + * @param class-string $expected + * @dataProvider aliasProvider + */ + public function testPluginAliasesResolve(string $alias, string $expected): void + { + self::assertInstanceOf( + $expected, + self::getPluginManager()->get($alias), + "Alias '$alias' does not resolve'", + ); + } + + public function testLoadingInvalidElementRaisesException(): void + { + $manager = self::getPluginManager(); + $manager->configure([ + 'factories' => [ + 'test' => static fn(): stdClass => new stdClass(), + ], + ]); + $this->expectException($this->getServiceNotFoundException()); + $manager->get('test'); + } + + /** @return class-string */ + protected function getServiceNotFoundException(): string + { + return InvalidServiceException::class; + } + + public function testRegisteringInvalidElementRaisesException(): void + { + $manager = self::getPluginManager(); + $this->expectException($this->getServiceNotFoundException()); + /** @psalm-suppress InvalidArgument - Because we are testing an invalid argument */ + $manager->setService('test', new stdClass()); + } } diff --git a/test/FilterPluginManagerFactoryTest.php b/test/FilterPluginManagerFactoryTest.php index 7079d40d..9ed3537a 100644 --- a/test/FilterPluginManagerFactoryTest.php +++ b/test/FilterPluginManagerFactoryTest.php @@ -8,10 +8,10 @@ use Laminas\Filter\FilterInterface; use Laminas\Filter\FilterPluginManager; use Laminas\Filter\FilterPluginManagerFactory; +use Laminas\ServiceManager\Factory\InvokableFactory; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; -use ReflectionObject; class FilterPluginManagerFactoryTest extends TestCase { @@ -21,10 +21,6 @@ public function testFactoryReturnsPluginManager(): void $factory = new FilterPluginManagerFactory(); $filters = $factory($container); self::assertInstanceOf(FilterPluginManager::class, $filters); - - $r = new ReflectionObject($filters); - $p = $r->getProperty('creationContext'); - self::assertSame($container, $p->getValue($filters)); } public function testConfiguresFilterServicesWhenFound(): void @@ -36,7 +32,8 @@ public function testConfiguresFilterServicesWhenFound(): void 'test' => Boolean::class, ], 'factories' => [ - 'test-too' => static fn(): MockObject&FilterInterface => $filter, + Boolean::class => InvokableFactory::class, + 'test-too' => static fn(): MockObject&FilterInterface => $filter, ], ], ]; diff --git a/test/FilterPluginManagerTest.php b/test/FilterPluginManagerTest.php index 0480e99e..cfac4f60 100644 --- a/test/FilterPluginManagerTest.php +++ b/test/FilterPluginManagerTest.php @@ -9,7 +9,6 @@ use Laminas\Filter\ToInt; use Laminas\Filter\Word\SeparatorToSeparator; use Laminas\ServiceManager\Exception\InvalidServiceException; -use Laminas\ServiceManager\ServiceManager; use LaminasTest\Filter\TestAsset\NotAValidFilter; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\TestCase; @@ -23,7 +22,7 @@ class FilterPluginManagerTest extends TestCase public function setUp(): void { - $this->filters = new FilterPluginManager(new ServiceManager()); + $this->filters = CreatePluginManager::withDefaults(); } public function testFilterSuccessfullyRetrieved(): void @@ -57,7 +56,7 @@ public function testFilterSuccessfullyConstructed(): void 'replacement_separator' => $replacementSeparator, ]; - $filter = $this->filters->get('wordseparatortoseparator', $options); + $filter = $this->filters->build('wordseparatortoseparator', $options); self::assertInstanceOf(SeparatorToSeparator::class, $filter); self::assertSame($searchSeparator, $filter->getSearchSeparator()); @@ -67,7 +66,7 @@ public function testFilterSuccessfullyConstructed(): void #[Group('7169')] public function testFiltersConstructedAreDifferent(): void { - $filterOne = $this->filters->get( + $filterOne = $this->filters->build( 'wordseparatortoseparator', [ 'search_separator' => ';', @@ -75,7 +74,7 @@ public function testFiltersConstructedAreDifferent(): void ] ); - $filterTwo = $this->filters->get( + $filterTwo = $this->filters->build( 'wordseparatortoseparator', [ 'search_separator' => '.', diff --git a/test/StaticAnalysis/PluginRetrieval.php b/test/StaticAnalysis/PluginRetrieval.php index 1778fde3..affcb520 100644 --- a/test/StaticAnalysis/PluginRetrieval.php +++ b/test/StaticAnalysis/PluginRetrieval.php @@ -4,6 +4,7 @@ namespace LaminasTest\Filter\StaticAnalysis; +use Laminas\Filter\FilterInterface; use Laminas\Filter\FilterPluginManager; use Laminas\Filter\StringToUpper; @@ -14,6 +15,21 @@ public function __construct(private FilterPluginManager $pluginManager) { } + public function fqcnPluginTypesAreInferredInGet(): StringToUpper + { + return $this->pluginManager->get(StringToUpper::class); + } + + public function fqcnPluginTypesAreInferredInBuild(): StringToUpper + { + return $this->pluginManager->build(StringToUpper::class); + } + + public function expectCallableOrFilterInterfaceForArbitraryString(): FilterInterface|callable + { + return $this->pluginManager->get('something'); + } + public function filterSomethingWithAKnownFilterClass(string $value): string { $plugin = $this->pluginManager->get(StringToUpper::class);