diff --git a/CHANGELOG.md b/CHANGELOG.md index c41f41b6..92321442 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,14 +10,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Discovery lives in its own package, [`pollora/discovery`](https://github.com/Pollora/discovery), which the framework requires: the engine, its contracts, `DiscoveryManager`, the cache and the `#[SkipDiscovery]` attribute, with their tests and benchmarks. Class names do not change. The framework keeps what needs Laravel: the service provider, `discovery:run` / `discovery:clear`, `DiscoveryRegistrar`, `ServiceProviderDiscovery`, the Laravel adapters, and `HasConfiguringSupport`, which builds post types and taxonomies - Discovery can run without Laravel's application, the first step towards `pollora/discovery`: `new DiscoveryEngine()` works with no argument. Two contracts let an environment plug in — `InstanceResolverInterface` builds the classes discovery needs (Laravel's container in a Pollora application, `ReflectionInstanceResolver` otherwise) and `DiscoveryRegistrarInterface` adds the environment's discoveries before each run (`DiscoveryRegistrar` keeps scanning the container's `*Discovery` bindings). `Psr16DiscoverCacheDriver` caches scans in any PSR-16 cache +- Post type and taxonomy attributes say what they register: each implements `ConfiguresArguments`, a pure function that returns the registration arguments with its own contribution (`#[HasArchive('library')]` sets `has_archive`, `#[Labels]` merges into the generated labels), and the method attributes (`#[AdminCol]`, `#[RegisterMetaBoxCb]`, `#[MetaBoxCb]`, `#[MetaBoxSanitizeCb]`, `#[UpdateCountCallback]`) implement `ConfiguresArgumentsWithMethod`, the discovery handing them the method as a callback. The `Pollora\Attributes` namespace no longer reaches Laravel or a framework service, which an architecture test keeps: the first step towards `pollora/attributes`, a package to declare attributes without the framework ### Changed - `DiscoveryEngine`, `DiscoveryCacheManager`, `InstancePool` and `ReflectionCache` take their dependencies instead of reaching for `app()` and `config()`; reading `structure-discoverer.cache` moved to `LaravelDiscoveryCacheManagerFactory`. `DiscoveryRegistrar::registerFromContainer()` is now `registerInto()`. These are infrastructure classes, outside the public contracts (`Pollora\Discovery\Domain\*`), which do not change - One `DiscoveryCacheManager` is shared by the engine and the `pollora:doctor` discovery cache check, which built its own - The discovery benchmarks measure memory as PHP allocates it, from a reset peak: they read 2 MB whatever the load +- `#[WpRestRoute\Method]` no longer registers its route: `WpRestRouteRegistrar`, in the WpRest module, does, with the same arguments, permissions and request injection. Default slugs, meta keys and command names come from `Pollora\Attributes\Support\Naming`, which gives the same results as `Str::kebab()` and `Str::snake()`; `Pollora\Support\Slug` uses it ### Deprecated - `DiscoveryCacheInterface` and `LaravelDiscoveryCache`, removed in 15.0: nothing reads this cache, scans are cached by the Spatie driver given to `DiscoveryCacheManager` +- `Pollora\Attributes\PostType\PostTypeAttribute` and `Pollora\Attributes\Taxonomy\TaxonomyAttribute`, removed in 15.0: no built-in attribute extends them any more; implement `ConfiguresArguments` + +### Removed +- `handle()` on `#[Action]`, `#[Filter]`, `#[WpRestRoute]` and `#[WpRestRoute\Method]`, and `Hook` no longer implements `HandlesAttributes`: `HookDiscovery` and `WpRestDiscovery` register hooks and routes themselves. Only the bug below reached `Action::handle()`, and `WpRestRoute::handle()` wrote to a read-only wrapper + +### Fixed +- A public `#[Action]` or `#[Filter]` method in a `#[PostType]` or `#[Taxonomy]` class is registered once: the post type and taxonomy discoveries also handed it to `Action::handle()`, which registered it a second time on the discovery's configuration object, so the page firing that hook failed with `call_user_func(): Argument #1 ($callback) must be a valid callback, class Pollora\PostType\Infrastructure\Services\PostTypeConfiguration does not have a method …` ## [v13.35.6](https://github.com/Pollora/framework/compare/v13.35.5...v13.35.6) - 2026-10-12 diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index f75c76db..c13e879f 100755 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -18,24 +18,6 @@ parameters: count: 1 path: src/Asset/Infrastructure/Services/ViteManager.php - - - message: '#^Access to an undefined property Pollora\\Attributes\\Attributable\:\:\$classPermission\.$#' - identifier: property.notFound - count: 1 - path: src/Attributes/WpRestRoute/Method.php - - - - message: '#^Access to an undefined property Pollora\\Attributes\\Attributable\:\:\$namespace\.$#' - identifier: property.notFound - count: 1 - path: src/Attributes/WpRestRoute/Method.php - - - - message: '#^Access to an undefined property Pollora\\Attributes\\Attributable\:\:\$route\.$#' - identifier: property.notFound - count: 2 - path: src/Attributes/WpRestRoute/Method.php - - message: '#^Constant AUTH_COOKIE not found\.$#' identifier: constant.notFound diff --git a/src/Attributes/Ability.php b/src/Attributes/Ability.php index 5d8dcc35..1367df84 100644 --- a/src/Attributes/Ability.php +++ b/src/Attributes/Ability.php @@ -5,16 +5,13 @@ namespace Pollora\Attributes; use Attribute; -use Pollora\Abilities\Domain\Contracts\AbilityHandler; use Pollora\Abilities\Domain\Model\Behaviour; -use Pollora\Ability\Infrastructure\Services\AbilityDiscovery; /** * Attribute for declarative WordPress ability registration. * - * Place this attribute on a class implementing {@see AbilityHandler}. The class - * is discovered and registered with the WordPress Abilities API through the - * {@see AbilityDiscovery} system, and published to AI clients — an MCP server, + * Place this attribute on a class implementing `AbilityHandler` (pollora/abilities). + * The class is discovered and registered with the WordPress Abilities API, and published to AI clients — an MCP server, * the core abilities REST controllers — by whatever consumes them. * * The attribute carries what the ability *is*; the handler carries what it @@ -33,8 +30,7 @@ * The category must exist. Declare it once — in a service provider, through the * `Ability` facade — before the abilities that file under it. * - * @see AbilityHandler The interface the decorated class implements. - * @see Behaviour How the ability's effect on the site is described to clients. + * @see Behaviour How the ability's effect on the site is described to clients. */ #[Attribute(Attribute::TARGET_CLASS)] class Ability diff --git a/src/Attributes/Action.php b/src/Attributes/Action.php index fad73f62..39987ebc 100755 --- a/src/Attributes/Action.php +++ b/src/Attributes/Action.php @@ -5,12 +5,6 @@ namespace Pollora\Attributes; use Attribute; -use Pollora\Hook\Application\Services\AsyncDeclarationFailures; -use Pollora\Hook\Domain\Contract\Action as ActionService; -use Pollora\Hook\Infrastructure\Services\AsyncAttributeRegistrar; -use Psr\Log\LoggerInterface; -use ReflectionClass; -use ReflectionMethod; /** * Class Action @@ -19,62 +13,4 @@ * This class is used to define an action hook in WordPress. */ #[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] -class Action extends Hook -{ - /** - * Handle the attribute processing. - * - * @param object $serviceLocator Service locator used to resolve dependencies - * @param object $instance The instance being processed - * @param ReflectionMethod|ReflectionClass $context The reflection context - * @param object $attribute The attribute instance - */ - public function handle( - $serviceLocator, - object $instance, - ReflectionMethod|ReflectionClass $context, - object $attribute, - ): void { - // Retrieve the Action service from the locator - $actionService = $serviceLocator->get(ActionService::class); - if (! $actionService) { - return; - } - - // #[Action] targets methods only - if (! $context instanceof ReflectionMethod) { - return; - } - - // Asynchronously when the method or its class carries #[Async] - $logger = $this->optional($serviceLocator, LoggerInterface::class); - $failures = $this->optional($serviceLocator, AsyncDeclarationFailures::class); - - (new AsyncAttributeRegistrar($actionService, $logger, $failures))->register( - $attribute->hook, - $instance, - $context, - $attribute->priority, - $context->getNumberOfParameters() - ); - } - - /** - * A service the service locator may not provide: the logger, the record of ignored #[Async]. - * - * @template T of object - * - * @param class-string $class - * @return T|null - */ - private function optional(object $serviceLocator, string $class): ?object - { - try { - $service = $serviceLocator->get($class); - } catch (\Throwable) { - return null; - } - - return $service instanceof $class ? $service : null; - } -} +class Action extends Hook {} diff --git a/src/Attributes/Ajax.php b/src/Attributes/Ajax.php index 5dd17909..d4880a3f 100644 --- a/src/Attributes/Ajax.php +++ b/src/Attributes/Ajax.php @@ -6,14 +6,12 @@ use Attribute; use Pollora\Ajax\Domain\Model\AjaxAccess; -use Pollora\Ajax\Infrastructure\Services\AjaxDiscovery; /** * Attribute for declarative WordPress AJAX action registration. * * Place this attribute on a public method to register it as a WordPress AJAX - * handler. The method is automatically discovered and wired via the - * {@see AjaxDiscovery} system. + * handler. The method is discovered and wired automatically. * * By default, the action is restricted to **logged-in users** (security-by-default). * Use the `access` parameter to change targeting. diff --git a/src/Attributes/Contracts/ConfiguresArguments.php b/src/Attributes/Contracts/ConfiguresArguments.php new file mode 100644 index 00000000..139c560f --- /dev/null +++ b/src/Attributes/Contracts/ConfiguresArguments.php @@ -0,0 +1,24 @@ + $arguments The arguments gathered so far + * @return array + */ + public function configureArguments(array $arguments): array; +} diff --git a/src/Attributes/Contracts/ConfiguresArgumentsWithMethod.php b/src/Attributes/Contracts/ConfiguresArgumentsWithMethod.php new file mode 100644 index 00000000..934c04ce --- /dev/null +++ b/src/Attributes/Contracts/ConfiguresArgumentsWithMethod.php @@ -0,0 +1,24 @@ + $arguments The arguments gathered so far + * @param callable $method The decorated method, bound to an instance of its class + * @return array + */ + public function configureArguments(array $arguments, callable $method): array; +} diff --git a/src/Attributes/Filter.php b/src/Attributes/Filter.php index 2722e8b0..9f33c92f 100755 --- a/src/Attributes/Filter.php +++ b/src/Attributes/Filter.php @@ -5,9 +5,6 @@ namespace Pollora\Attributes; use Attribute; -use Pollora\Hook\Domain\Contract\Filter as FilterService; -use ReflectionClass; -use ReflectionMethod; /** * Class Filter @@ -16,33 +13,4 @@ * This class is used to define a filter hook in WordPress. */ #[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] -class Filter extends Hook -{ - /** - * Handle the attribute processing. - * - * @param object $serviceLocator Service locator used to resolve dependencies - * @param object $instance The instance being processed - * @param ReflectionMethod|ReflectionClass $context The reflection context - * @param object $attribute The attribute instance - */ - public function handle( - $serviceLocator, - object $instance, - ReflectionMethod|ReflectionClass $context, - object $attribute - ): void { - // Retrieve the Filter service from the locator - $filterService = $serviceLocator->get(FilterService::class); - if (! $filterService) { - return; - } - - $filterService->add( - $attribute->hook, - [$instance, $context->getName()], - $attribute->priority, - $context->getNumberOfParameters() - ); - } -} +class Filter extends Hook {} diff --git a/src/Attributes/Hook.php b/src/Attributes/Hook.php index 511667ec..7f550c0a 100755 --- a/src/Attributes/Hook.php +++ b/src/Attributes/Hook.php @@ -4,15 +4,13 @@ namespace Pollora\Attributes; -use Pollora\Attributes\Contracts\HandlesAttributes; - /** * Class Hook * * Abstract base class for WordPress hook attributes. * Provides common properties for hook attributes. */ -abstract class Hook implements HandlesAttributes +abstract class Hook { /** * Constructor for the Hook class. diff --git a/src/Attributes/Meta.php b/src/Attributes/Meta.php index e9262f8d..d0acaa7e 100644 --- a/src/Attributes/Meta.php +++ b/src/Attributes/Meta.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes; use Attribute; -use Illuminate\Support\Str; +use Pollora\Attributes\Support\Naming; use Pollora\Meta\Domain\Enums\Control; /** @@ -71,6 +71,6 @@ public function __construct( */ public function resolveKey(string $property): string { - return $this->key ?? Str::snake($property); + return $this->key ?? Naming::snake($property); } } diff --git a/src/Attributes/PostType.php b/src/Attributes/PostType.php index 80ba791f..94fac8ed 100644 --- a/src/Attributes/PostType.php +++ b/src/Attributes/PostType.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes; use Attribute; -use Illuminate\Support\Str; +use Pollora\Attributes\Support\Naming; /** * PostType Attribute @@ -51,6 +51,6 @@ public function __construct( */ public function resolveSlug(string $className): string { - return $this->slug ?? substr(Str::kebab(class_basename($className)), 0, 20); + return $this->slug ?? substr(Naming::kebab(Naming::classBasename($className)), 0, 20); } } diff --git a/src/Attributes/PostType/AdminCol.php b/src/Attributes/PostType/AdminCol.php index 645c882e..d5696bd3 100755 --- a/src/Attributes/PostType/AdminCol.php +++ b/src/Attributes/PostType/AdminCol.php @@ -5,10 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\Attributes\Contracts\HandlesAttributes; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; -use ReflectionClass; -use ReflectionMethod; +use Pollora\Attributes\Contracts\ConfiguresArgumentsWithMethod; /** * Attribute to define an admin column for a post type. @@ -19,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_METHOD)] -class AdminCol implements HandlesAttributes +class AdminCol implements ConfiguresArgumentsWithMethod { /** * Constructor. @@ -57,32 +54,20 @@ public function __construct( ) {} /** - * Handle the attribute processing. + * Add this column to the admin_cols argument, rendered by the decorated method. * - * @param object $container The container instance - * @param object $instance The instance being processed - * @param ReflectionMethod|ReflectionClass $context The method the attribute is applied to - * @param object $attribute The attribute instance + * @param array $arguments The registration arguments gathered so far + * @param callable $method The decorated method + * @return array */ - public function handle($container, object $instance, ReflectionMethod|ReflectionClass $context, object $attribute): void + public function configureArguments(array $arguments, callable $method): array { - if (! $instance instanceof PostTypeAttributeInterface) { - return; - } - - // Initialize admin_cols array if it doesn't exist - $adminCols = $instance->getArg('admin_cols', []); - // Build column configuration based on extended-cpts options $columnConfig = [ 'title' => $this->title, + 'function' => $method, ]; - // Add custom function callback if this is a method-level attribute - if ($context instanceof ReflectionMethod) { - $columnConfig['function'] = [new $context->class, $context->getName()]; - } - // Add optional configuration based on extended-cpts features if ($this->titleIcon !== null) { $columnConfig['title_icon'] = $this->titleIcon; @@ -133,8 +118,8 @@ public function handle($container, object $instance, ReflectionMethod|Reflection $columnConfig['sortable'] = $this->sortable; } - // Add the column to the admin_cols array - $adminCols[$this->key] = $columnConfig; - $instance->setArg('admin_cols', $adminCols); + $arguments['admin_cols'] = [...$arguments['admin_cols'] ?? [], $this->key => $columnConfig]; + + return $arguments; } } diff --git a/src/Attributes/PostType/AdminCols.php b/src/Attributes/PostType/AdminCols.php index ac92f1ba..2b43b9e2 100755 --- a/src/Attributes/PostType/AdminCols.php +++ b/src/Attributes/PostType/AdminCols.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the admin_cols parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class AdminCols extends PostTypeAttribute +class AdminCols implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the admin_cols parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('admin_cols', $this->value); + $arguments['admin_cols'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/AdminFilters.php b/src/Attributes/PostType/AdminFilters.php index 6c6de4d1..4fbf2818 100755 --- a/src/Attributes/PostType/AdminFilters.php +++ b/src/Attributes/PostType/AdminFilters.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the admin_filters parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class AdminFilters extends PostTypeAttribute +class AdminFilters implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the admin_filters parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('admin_filters', $this->value); + $arguments['admin_filters'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/Archive.php b/src/Attributes/PostType/Archive.php index d65294de..3d081ac4 100755 --- a/src/Attributes/PostType/Archive.php +++ b/src/Attributes/PostType/Archive.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the archive parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class Archive extends PostTypeAttribute +class Archive implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the archive parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('archive', $this->value); + $arguments['archive'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/BlockEditor.php b/src/Attributes/PostType/BlockEditor.php index 145fdf1b..cdfc9f2b 100755 --- a/src/Attributes/PostType/BlockEditor.php +++ b/src/Attributes/PostType/BlockEditor.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the block_editor parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class BlockEditor extends PostTypeAttribute +class BlockEditor implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the block_editor parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('show_in_rest', $this->value); + $arguments['show_in_rest'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/CanExport.php b/src/Attributes/PostType/CanExport.php index 73199a2c..7f1f5d76 100755 --- a/src/Attributes/PostType/CanExport.php +++ b/src/Attributes/PostType/CanExport.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the can_export parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class CanExport extends PostTypeAttribute +class CanExport implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the can_export parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('can_export', $this->value); + $arguments['can_export'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/Capabilities.php b/src/Attributes/PostType/Capabilities.php index 7fc3e65d..b3459422 100755 --- a/src/Attributes/PostType/Capabilities.php +++ b/src/Attributes/PostType/Capabilities.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the capabilities parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class Capabilities extends PostTypeAttribute +class Capabilities implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the capabilities parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('capabilities', $this->value); + $arguments['capabilities'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/CapabilityType.php b/src/Attributes/PostType/CapabilityType.php index 886630d6..e7196eca 100755 --- a/src/Attributes/PostType/CapabilityType.php +++ b/src/Attributes/PostType/CapabilityType.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the capability_type parameter for a post type. @@ -21,7 +21,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class CapabilityType extends PostTypeAttribute +class CapabilityType implements ConfiguresArguments { /** * Constructor. @@ -35,10 +35,13 @@ public function __construct( /** * Configure the post type with the capability_type parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('capability_type', $this->value); + $arguments['capability_type'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/Chronological.php b/src/Attributes/PostType/Chronological.php index 10065f3b..728fe084 100755 --- a/src/Attributes/PostType/Chronological.php +++ b/src/Attributes/PostType/Chronological.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the chronological parameter for a post type. @@ -17,7 +17,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class Chronological extends PostTypeAttribute +class Chronological implements ConfiguresArguments { /** * Constructor. @@ -31,14 +31,17 @@ public function __construct( /** * Configure the post type with the chronological parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { if ($this->value) { // Set default ordering to date, descending - $postType->setArg('orderby', 'date'); - $postType->setArg('order', 'DESC'); + $arguments['orderby'] = 'date'; + $arguments['order'] = 'DESC'; } + + return $arguments; } } diff --git a/src/Attributes/PostType/DashboardActivity.php b/src/Attributes/PostType/DashboardActivity.php index 97ae5c52..a808f98a 100755 --- a/src/Attributes/PostType/DashboardActivity.php +++ b/src/Attributes/PostType/DashboardActivity.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the dashboard_activity parameter for a post type. @@ -17,7 +17,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class DashboardActivity extends PostTypeAttribute +class DashboardActivity implements ConfiguresArguments { /** * Constructor. @@ -31,10 +31,13 @@ public function __construct( /** * Configure the post type with the dashboard_activity parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('dashboard_activity', $this->value); + $arguments['dashboard_activity'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/DashboardGlance.php b/src/Attributes/PostType/DashboardGlance.php index 99a0e08f..81df2cfa 100755 --- a/src/Attributes/PostType/DashboardGlance.php +++ b/src/Attributes/PostType/DashboardGlance.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the dashboard_glance parameter for a post type. @@ -15,7 +15,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class DashboardGlance extends PostTypeAttribute +class DashboardGlance implements ConfiguresArguments { /** * Constructor. @@ -29,10 +29,13 @@ public function __construct( /** * Configure the post type with the dashboard_glance parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('dashboard_glance', $this->value); + $arguments['dashboard_glance'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/DeleteWithUser.php b/src/Attributes/PostType/DeleteWithUser.php index b762aeed..2abeb23c 100755 --- a/src/Attributes/PostType/DeleteWithUser.php +++ b/src/Attributes/PostType/DeleteWithUser.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the delete_with_user parameter for a post type. @@ -17,7 +17,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class DeleteWithUser extends PostTypeAttribute +class DeleteWithUser implements ConfiguresArguments { /** * Constructor. @@ -31,10 +31,13 @@ public function __construct( /** * Configure the post type with the delete_with_user parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('delete_with_user', $this->value); + $arguments['delete_with_user'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/Description.php b/src/Attributes/PostType/Description.php index 7ba770eb..6654747f 100755 --- a/src/Attributes/PostType/Description.php +++ b/src/Attributes/PostType/Description.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the description parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class Description extends PostTypeAttribute +class Description implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the description parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('description', $this->value); + $arguments['description'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/ExcludeFromSearch.php b/src/Attributes/PostType/ExcludeFromSearch.php index 69e75e60..a6cb5d53 100755 --- a/src/Attributes/PostType/ExcludeFromSearch.php +++ b/src/Attributes/PostType/ExcludeFromSearch.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the exclude_from_search parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class ExcludeFromSearch extends PostTypeAttribute +class ExcludeFromSearch implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the exclude_from_search parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('exclude_from_search', $this->value); + $arguments['exclude_from_search'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/FeaturedImage.php b/src/Attributes/PostType/FeaturedImage.php index 8e8c8daa..d2af7006 100755 --- a/src/Attributes/PostType/FeaturedImage.php +++ b/src/Attributes/PostType/FeaturedImage.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the featured_image parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class FeaturedImage extends PostTypeAttribute +class FeaturedImage implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the featured_image parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('featured_image', $this->value); + $arguments['featured_image'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/HasArchive.php b/src/Attributes/PostType/HasArchive.php index cc2c7786..59befcda 100755 --- a/src/Attributes/PostType/HasArchive.php +++ b/src/Attributes/PostType/HasArchive.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the has_archive parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class HasArchive extends PostTypeAttribute +class HasArchive implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the has_archive parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('has_archive', $this->value); + $arguments['has_archive'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/Hierarchical.php b/src/Attributes/PostType/Hierarchical.php index f7e2610e..b4e085b8 100755 --- a/src/Attributes/PostType/Hierarchical.php +++ b/src/Attributes/PostType/Hierarchical.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the hierarchical parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class Hierarchical extends PostTypeAttribute +class Hierarchical implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the hierarchical parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('hierarchical', $this->value); + $arguments['hierarchical'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/Label.php b/src/Attributes/PostType/Label.php index 33720b71..b9bbb22a 100755 --- a/src/Attributes/PostType/Label.php +++ b/src/Attributes/PostType/Label.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the label parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class Label extends PostTypeAttribute +class Label implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the label parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('label', $this->value); + $arguments['label'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/Labels.php b/src/Attributes/PostType/Labels.php index 026fd71b..0ac367ff 100755 --- a/src/Attributes/PostType/Labels.php +++ b/src/Attributes/PostType/Labels.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set custom labels for a post type. @@ -23,7 +23,7 @@ * For runtime translations, use withArgs() in the class instead. */ #[Attribute(Attribute::TARGET_CLASS)] -class Labels extends PostTypeAttribute +class Labels implements ConfiguresArguments { /** @var array */ private readonly array $labels; @@ -92,9 +92,11 @@ public function __construct( ], fn (?string $v): bool => $v !== null); } - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $existing = $postType->getArg('labels', []); - $postType->setArg('labels', array_merge($existing, $this->labels)); + $existing = ($arguments['labels'] ?? []); + $arguments['labels'] = array_merge($existing, $this->labels); + + return $arguments; } } diff --git a/src/Attributes/PostType/MapMetaCap.php b/src/Attributes/PostType/MapMetaCap.php index 1dea4737..04b1ff39 100755 --- a/src/Attributes/PostType/MapMetaCap.php +++ b/src/Attributes/PostType/MapMetaCap.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the map_meta_cap parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class MapMetaCap extends PostTypeAttribute +class MapMetaCap implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the map_meta_cap parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('map_meta_cap', $this->value); + $arguments['map_meta_cap'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/MenuIcon.php b/src/Attributes/PostType/MenuIcon.php index 7450e6ea..f2218cdc 100755 --- a/src/Attributes/PostType/MenuIcon.php +++ b/src/Attributes/PostType/MenuIcon.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the menu_icon parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class MenuIcon extends PostTypeAttribute +class MenuIcon implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the menu_icon parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('menu_icon', $this->value); + $arguments['menu_icon'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/MenuPosition.php b/src/Attributes/PostType/MenuPosition.php index 41cb7b1c..0952d950 100755 --- a/src/Attributes/PostType/MenuPosition.php +++ b/src/Attributes/PostType/MenuPosition.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the menu_position parameter for a post type. @@ -27,7 +27,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class MenuPosition extends PostTypeAttribute +class MenuPosition implements ConfiguresArguments { /** * Constructor. @@ -41,10 +41,13 @@ public function __construct( /** * Configure the post type with the menu_position parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('menu_position', $this->value); + $arguments['menu_position'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/PostTypeAttribute.php b/src/Attributes/PostType/PostTypeAttribute.php index 0ecb963f..ef612c31 100755 --- a/src/Attributes/PostType/PostTypeAttribute.php +++ b/src/Attributes/PostType/PostTypeAttribute.php @@ -5,6 +5,7 @@ namespace Pollora\Attributes\PostType; use Pollora\Attributes\Attributable; +use Pollora\Attributes\Contracts\ConfiguresArguments; use Pollora\Attributes\Contracts\HandlesAttributes; use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; use ReflectionClass; @@ -16,7 +17,9 @@ * This attribute serves as the base for all post type configuration attributes. * It provides common functionality for handling post type registration arguments. * - * @Attribute + * @deprecated since 13.36, removed in 15.0. Implement + * {@see ConfiguresArguments}, as the + * built-in attributes now do. */ abstract class PostTypeAttribute implements HandlesAttributes { diff --git a/src/Attributes/PostType/Priority.php b/src/Attributes/PostType/Priority.php index a1176bb5..f416ca56 100755 --- a/src/Attributes/PostType/Priority.php +++ b/src/Attributes/PostType/Priority.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the priority a post type declaration. @@ -13,7 +13,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class Priority extends PostTypeAttribute +class Priority implements ConfiguresArguments { /** * Constructor. @@ -25,10 +25,13 @@ public function __construct( /** * Configure the post type priority declaration parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('priority', $this->priority); + $arguments['priority'] = $this->priority; + + return $arguments; } } diff --git a/src/Attributes/PostType/PublicPostType.php b/src/Attributes/PostType/PublicPostType.php index fdcc04a6..53b905c4 100755 --- a/src/Attributes/PostType/PublicPostType.php +++ b/src/Attributes/PostType/PublicPostType.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the public parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class PublicPostType extends PostTypeAttribute +class PublicPostType implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the public parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('public', $this->value); + $arguments['public'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/PubliclyQueryable.php b/src/Attributes/PostType/PubliclyQueryable.php index 1957ecbe..42c5b3e5 100755 --- a/src/Attributes/PostType/PubliclyQueryable.php +++ b/src/Attributes/PostType/PubliclyQueryable.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the publicly_queryable parameter for a post type. @@ -15,7 +15,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class PubliclyQueryable extends PostTypeAttribute +class PubliclyQueryable implements ConfiguresArguments { /** * Constructor. @@ -29,10 +29,13 @@ public function __construct( /** * Configure the post type with the publicly_queryable parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('publicly_queryable', $this->value); + $arguments['publicly_queryable'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/QueryVar.php b/src/Attributes/PostType/QueryVar.php index d70da40e..54091c73 100755 --- a/src/Attributes/PostType/QueryVar.php +++ b/src/Attributes/PostType/QueryVar.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the query_var parameter for a post type. @@ -18,7 +18,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class QueryVar extends PostTypeAttribute +class QueryVar implements ConfiguresArguments { /** * Constructor. @@ -32,10 +32,13 @@ public function __construct( /** * Configure the post type with the query_var parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('query_var', $this->value); + $arguments['query_var'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/QuickEdit.php b/src/Attributes/PostType/QuickEdit.php index bbc2cc47..9a7cbe77 100755 --- a/src/Attributes/PostType/QuickEdit.php +++ b/src/Attributes/PostType/QuickEdit.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the quick_edit parameter for a post type. @@ -17,7 +17,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class QuickEdit extends PostTypeAttribute +class QuickEdit implements ConfiguresArguments { /** * Constructor. @@ -31,10 +31,13 @@ public function __construct( /** * Configure the post type with the quick_edit parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('quick_edit', $this->value); + $arguments['quick_edit'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/RegisterMetaBoxCb.php b/src/Attributes/PostType/RegisterMetaBoxCb.php index 40e66c41..8daff699 100755 --- a/src/Attributes/PostType/RegisterMetaBoxCb.php +++ b/src/Attributes/PostType/RegisterMetaBoxCb.php @@ -5,10 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\Attributes\Contracts\HandlesAttributes; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; -use ReflectionClass; -use ReflectionMethod; +use Pollora\Attributes\Contracts\ConfiguresArgumentsWithMethod; /** * Attribute to define a callback function that will be called when setting up meta boxes for the edit form. @@ -21,22 +18,19 @@ * @Attribute */ #[Attribute(Attribute::TARGET_METHOD)] -class RegisterMetaBoxCb implements HandlesAttributes +class RegisterMetaBoxCb implements ConfiguresArgumentsWithMethod { /** - * Handle the attribute processing. + * Set the register_meta_box_cb argument to the decorated method. * - * @param object $instance The instance being processed - * @param ReflectionMethod|ReflectionClass $context The method the attribute is applied to - * @param object $attribute The attribute instance + * @param array $arguments The registration arguments gathered so far + * @param callable $method The decorated method + * @return array */ - public function handle($container, object $instance, ReflectionMethod|ReflectionClass $context, object $attribute): void + public function configureArguments(array $arguments, callable $method): array { - if (! $instance instanceof PostTypeAttributeInterface) { - return; - } + $arguments['register_meta_box_cb'] = $method; - // Set the register_meta_box_cb parameter to the method - $instance->setArg('register_meta_box_cb', [new $context->class, $context->getName()]); + return $arguments; } } diff --git a/src/Attributes/PostType/RestBase.php b/src/Attributes/PostType/RestBase.php index 0dff0967..8ca0e7fd 100755 --- a/src/Attributes/PostType/RestBase.php +++ b/src/Attributes/PostType/RestBase.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the rest_base parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class RestBase extends PostTypeAttribute +class RestBase implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the rest_base parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('rest_base', $this->value); + $arguments['rest_base'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/RestControllerClass.php b/src/Attributes/PostType/RestControllerClass.php index ac8ba8a6..d3d9004c 100755 --- a/src/Attributes/PostType/RestControllerClass.php +++ b/src/Attributes/PostType/RestControllerClass.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the rest_controller_class parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class RestControllerClass extends PostTypeAttribute +class RestControllerClass implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the rest_controller_class parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('rest_controller_class', $this->value); + $arguments['rest_controller_class'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/RestNamespace.php b/src/Attributes/PostType/RestNamespace.php index 77244233..1eae5ba9 100755 --- a/src/Attributes/PostType/RestNamespace.php +++ b/src/Attributes/PostType/RestNamespace.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the rest_namespace parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class RestNamespace extends PostTypeAttribute +class RestNamespace implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the rest_namespace parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('rest_namespace', $this->value); + $arguments['rest_namespace'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/Rewrite.php b/src/Attributes/PostType/Rewrite.php index 122e1c0d..1fa15cfb 100755 --- a/src/Attributes/PostType/Rewrite.php +++ b/src/Attributes/PostType/Rewrite.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the rewrite parameter for a post type. @@ -18,7 +18,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class Rewrite extends PostTypeAttribute +class Rewrite implements ConfiguresArguments { /** * Constructor. @@ -32,10 +32,13 @@ public function __construct( /** * Configure the post type with the rewrite parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('rewrite', $this->value); + $arguments['rewrite'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/ShowInAdminBar.php b/src/Attributes/PostType/ShowInAdminBar.php index 99cf0690..cdcf1aa8 100755 --- a/src/Attributes/PostType/ShowInAdminBar.php +++ b/src/Attributes/PostType/ShowInAdminBar.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the show_in_admin_bar parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class ShowInAdminBar extends PostTypeAttribute +class ShowInAdminBar implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the show_in_admin_bar parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('show_in_admin_bar', $this->value); + $arguments['show_in_admin_bar'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/ShowInFeed.php b/src/Attributes/PostType/ShowInFeed.php index b7cc0ce0..129b9f2b 100755 --- a/src/Attributes/PostType/ShowInFeed.php +++ b/src/Attributes/PostType/ShowInFeed.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the show_in_feed parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class ShowInFeed extends PostTypeAttribute +class ShowInFeed implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the show_in_feed parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('show_in_feed', $this->value); + $arguments['show_in_feed'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/ShowInMenu.php b/src/Attributes/PostType/ShowInMenu.php index fd2371fa..b9724ac2 100755 --- a/src/Attributes/PostType/ShowInMenu.php +++ b/src/Attributes/PostType/ShowInMenu.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the show_in_menu parameter for a post type. @@ -18,7 +18,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class ShowInMenu extends PostTypeAttribute +class ShowInMenu implements ConfiguresArguments { /** * Constructor. @@ -32,10 +32,13 @@ public function __construct( /** * Configure the post type with the show_in_menu parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('show_in_menu', $this->value); + $arguments['show_in_menu'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/ShowInNavMenus.php b/src/Attributes/PostType/ShowInNavMenus.php index c42b0f97..6dd21be9 100755 --- a/src/Attributes/PostType/ShowInNavMenus.php +++ b/src/Attributes/PostType/ShowInNavMenus.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the show_in_nav_menus parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class ShowInNavMenus extends PostTypeAttribute +class ShowInNavMenus implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the show_in_nav_menus parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('show_in_nav_menus', $this->value); + $arguments['show_in_nav_menus'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/ShowInRest.php b/src/Attributes/PostType/ShowInRest.php index 81527a44..f83f97f2 100755 --- a/src/Attributes/PostType/ShowInRest.php +++ b/src/Attributes/PostType/ShowInRest.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the show_in_rest parameter for a post type. @@ -17,7 +17,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class ShowInRest extends PostTypeAttribute +class ShowInRest implements ConfiguresArguments { /** * Constructor. @@ -31,10 +31,13 @@ public function __construct( /** * Configure the post type with the show_in_rest parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('show_in_rest', $this->value); + $arguments['show_in_rest'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/ShowUI.php b/src/Attributes/PostType/ShowUI.php index eda88522..74c8eb82 100755 --- a/src/Attributes/PostType/ShowUI.php +++ b/src/Attributes/PostType/ShowUI.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the show_ui parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class ShowUI extends PostTypeAttribute +class ShowUI implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the show_ui parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('show_ui', $this->value); + $arguments['show_ui'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/SiteFilters.php b/src/Attributes/PostType/SiteFilters.php index cc7ff9ab..a8b52288 100755 --- a/src/Attributes/PostType/SiteFilters.php +++ b/src/Attributes/PostType/SiteFilters.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the site_filters parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class SiteFilters extends PostTypeAttribute +class SiteFilters implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the site_filters parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('site_filters', $this->value); + $arguments['site_filters'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/SiteSortables.php b/src/Attributes/PostType/SiteSortables.php index 0462fa75..772c99c3 100755 --- a/src/Attributes/PostType/SiteSortables.php +++ b/src/Attributes/PostType/SiteSortables.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the site_sortables parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class SiteSortables extends PostTypeAttribute +class SiteSortables implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the site_sortables parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('site_sortables', $this->value); + $arguments['site_sortables'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/Supports.php b/src/Attributes/PostType/Supports.php index 5ed03d98..9fe01558 100755 --- a/src/Attributes/PostType/Supports.php +++ b/src/Attributes/PostType/Supports.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the supports parameter for a post type. @@ -15,7 +15,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class Supports extends PostTypeAttribute +class Supports implements ConfiguresArguments { /** * Constructor. @@ -29,10 +29,13 @@ public function __construct( /** * Configure the post type with the supports parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('supports', $this->features); + $arguments['supports'] = $this->features; + + return $arguments; } } diff --git a/src/Attributes/PostType/Taxonomies.php b/src/Attributes/PostType/Taxonomies.php index ec1fe97a..a4908587 100755 --- a/src/Attributes/PostType/Taxonomies.php +++ b/src/Attributes/PostType/Taxonomies.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the taxonomies parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class Taxonomies extends PostTypeAttribute +class Taxonomies implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the taxonomies parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('taxonomies', $this->value); + $arguments['taxonomies'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/Template.php b/src/Attributes/PostType/Template.php index a4582bb8..ac33e809 100755 --- a/src/Attributes/PostType/Template.php +++ b/src/Attributes/PostType/Template.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the template parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class Template extends PostTypeAttribute +class Template implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the template parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('template', $this->value); + $arguments['template'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/TemplateLock.php b/src/Attributes/PostType/TemplateLock.php index a06cb614..442afb56 100755 --- a/src/Attributes/PostType/TemplateLock.php +++ b/src/Attributes/PostType/TemplateLock.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the template_lock parameter for a post type. @@ -18,7 +18,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class TemplateLock extends PostTypeAttribute +class TemplateLock implements ConfiguresArguments { /** * Constructor. @@ -32,10 +32,13 @@ public function __construct( /** * Configure the post type with the template_lock parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('template_lock', $this->value); + $arguments['template_lock'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/PostType/TitlePlaceholder.php b/src/Attributes/PostType/TitlePlaceholder.php index 26418f29..c3ccd2b0 100755 --- a/src/Attributes/PostType/TitlePlaceholder.php +++ b/src/Attributes/PostType/TitlePlaceholder.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\PostType; use Attribute; -use Pollora\PostType\Domain\Contracts\PostTypeAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the title_placeholder parameter for a post type. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class TitlePlaceholder extends PostTypeAttribute +class TitlePlaceholder implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the post type with the title_placeholder parameter. * - * @param PostTypeAttributeInterface $postType The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(PostTypeAttributeInterface $postType): void + public function configureArguments(array $arguments): array { - $postType->setArg('title_placeholder', $this->value); + $arguments['title_placeholder'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Support/Naming.php b/src/Attributes/Support/Naming.php new file mode 100644 index 00000000..0640640e --- /dev/null +++ b/src/Attributes/Support/Naming.php @@ -0,0 +1,73 @@ +slug ?? substr(Str::kebab(class_basename($className)), 0, 20); + return $this->slug ?? substr(Naming::kebab(Naming::classBasename($className)), 0, 20); } } diff --git a/src/Attributes/Taxonomy/AllowHierarchy.php b/src/Attributes/Taxonomy/AllowHierarchy.php index d9eff1ea..ebdee900 100755 --- a/src/Attributes/Taxonomy/AllowHierarchy.php +++ b/src/Attributes/Taxonomy/AllowHierarchy.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set whether to allow hierarchy in the taxonomy's rewrite rules. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class AllowHierarchy extends TaxonomyAttribute +class AllowHierarchy implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the taxonomy with the allow_hierarchy parameter. * - * @param TaxonomyAttributeInterface $taxonomy The taxonomy to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('allow_hierarchy', $this->value); + $arguments['allow_hierarchy'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/Args.php b/src/Attributes/Taxonomy/Args.php index fc4889b2..90740947 100755 --- a/src/Attributes/Taxonomy/Args.php +++ b/src/Attributes/Taxonomy/Args.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set arguments to automatically use inside wp_get_object_terms() for this taxonomy. @@ -13,7 +13,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class Args extends TaxonomyAttribute +class Args implements ConfiguresArguments { /** * Constructor. @@ -27,10 +27,13 @@ public function __construct( /** * Configure the taxonomy with the args parameter. * - * @param TaxonomyAttributeInterface $taxonomy The taxonomy to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('args', $this->value); + $arguments['args'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/Capabilities.php b/src/Attributes/Taxonomy/Capabilities.php index 36d87d9a..f7fea2ce 100644 --- a/src/Attributes/Taxonomy/Capabilities.php +++ b/src/Attributes/Taxonomy/Capabilities.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the capabilities parameter for a taxonomy. @@ -22,7 +22,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class Capabilities extends TaxonomyAttribute +class Capabilities implements ConfiguresArguments { /** * Constructor. @@ -36,10 +36,13 @@ public function __construct( /** * Configure the taxonomy with the capabilities parameter. * - * @param TaxonomyAttributeInterface $taxonomy The taxonomy to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('capabilities', $this->value); + $arguments['capabilities'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/CheckedOntop.php b/src/Attributes/Taxonomy/CheckedOntop.php index bb8b092c..72cb1d68 100755 --- a/src/Attributes/Taxonomy/CheckedOntop.php +++ b/src/Attributes/Taxonomy/CheckedOntop.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set whether checked terms should appear on top. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class CheckedOntop extends TaxonomyAttribute +class CheckedOntop implements ConfiguresArguments { /** * Constructor. @@ -30,8 +30,10 @@ public function __construct( /** * Configure the taxonomy with the checked_ontop parameter. */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('checked_ontop', $this->value); + $arguments['checked_ontop'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/DefaultTerm.php b/src/Attributes/Taxonomy/DefaultTerm.php index a6fa0e3a..8e7d553e 100755 --- a/src/Attributes/Taxonomy/DefaultTerm.php +++ b/src/Attributes/Taxonomy/DefaultTerm.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the default term name for this taxonomy. @@ -13,7 +13,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class DefaultTerm extends TaxonomyAttribute +class DefaultTerm implements ConfiguresArguments { /** * Constructor. @@ -27,10 +27,13 @@ public function __construct( /** * Configure the taxonomy with the default_term parameter. * - * @param TaxonomyAttributeInterface $taxonomy The taxonomy to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('default_term', $this->value); + $arguments['default_term'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/Description.php b/src/Attributes/Taxonomy/Description.php index 6a9d91ca..bc613822 100755 --- a/src/Attributes/Taxonomy/Description.php +++ b/src/Attributes/Taxonomy/Description.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the description for a taxonomy. @@ -13,7 +13,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class Description extends TaxonomyAttribute +class Description implements ConfiguresArguments { /** * Constructor. @@ -27,10 +27,13 @@ public function __construct( /** * Configure the taxonomy with the description parameter. * - * @param TaxonomyAttributeInterface $taxonomy The taxonomy to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('description', $this->value); + $arguments['description'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/Exclusive.php b/src/Attributes/Taxonomy/Exclusive.php index 5a14a75e..d34334f0 100755 --- a/src/Attributes/Taxonomy/Exclusive.php +++ b/src/Attributes/Taxonomy/Exclusive.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set whether the taxonomy should be exclusive. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class Exclusive extends TaxonomyAttribute +class Exclusive implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the taxonomy with the exclusive parameter. * - * @param TaxonomyAttributeInterface $taxonomy The taxonomy to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('exclusive', $this->value); + $arguments['exclusive'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/Hierarchical.php b/src/Attributes/Taxonomy/Hierarchical.php index 96b633e4..afea0792 100755 --- a/src/Attributes/Taxonomy/Hierarchical.php +++ b/src/Attributes/Taxonomy/Hierarchical.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set whether the taxonomy is hierarchical. @@ -13,7 +13,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class Hierarchical extends TaxonomyAttribute +class Hierarchical implements ConfiguresArguments { /** * Constructor. @@ -27,10 +27,13 @@ public function __construct( /** * Configure the taxonomy with the hierarchical parameter. * - * @param TaxonomyAttributeInterface $taxonomy The taxonomy to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('hierarchical', $this->value); + $arguments['hierarchical'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/Label.php b/src/Attributes/Taxonomy/Label.php index 18d11a03..5f8ec4c3 100755 --- a/src/Attributes/Taxonomy/Label.php +++ b/src/Attributes/Taxonomy/Label.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the singular label for a taxonomy. @@ -13,7 +13,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class Label extends TaxonomyAttribute +class Label implements ConfiguresArguments { /** * Constructor. @@ -27,10 +27,13 @@ public function __construct( /** * Configure the taxonomy with the label parameter. * - * @param TaxonomyAttributeInterface $taxonomy The taxonomy to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('label', $this->value); + $arguments['label'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/Labels.php b/src/Attributes/Taxonomy/Labels.php index 0f9173dc..09efa745 100755 --- a/src/Attributes/Taxonomy/Labels.php +++ b/src/Attributes/Taxonomy/Labels.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set custom labels for a taxonomy. @@ -24,7 +24,7 @@ * evaluated at runtime (PHP attributes only accept constants). */ #[Attribute(Attribute::TARGET_CLASS)] -class Labels extends TaxonomyAttribute +class Labels implements ConfiguresArguments { /** @var array */ private readonly array $labels; @@ -79,9 +79,11 @@ public function __construct( ], fn (?string $v): bool => $v !== null); } - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $existing = $taxonomy->getArg('labels', []); - $taxonomy->setArg('labels', array_merge($existing, $this->labels)); + $existing = ($arguments['labels'] ?? []); + $arguments['labels'] = array_merge($existing, $this->labels); + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/MetaBoxCb.php b/src/Attributes/Taxonomy/MetaBoxCb.php index 5eb310bd..6fe8045c 100755 --- a/src/Attributes/Taxonomy/MetaBoxCb.php +++ b/src/Attributes/Taxonomy/MetaBoxCb.php @@ -5,10 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Attributes\Contracts\HandlesAttributes; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; -use ReflectionClass; -use ReflectionMethod; +use Pollora\Attributes\Contracts\ConfiguresArgumentsWithMethod; /** * Attribute to define a callback function for the meta box display. @@ -19,22 +16,19 @@ * @Attribute */ #[Attribute(Attribute::TARGET_METHOD)] -class MetaBoxCb implements HandlesAttributes +class MetaBoxCb implements ConfiguresArgumentsWithMethod { /** - * Handle the attribute processing. + * Set the meta_box_cb argument to the decorated method. * - * @param object $instance The instance being processed - * @param ReflectionMethod|ReflectionClass $context The method the attribute is applied to - * @param object $attribute The attribute instance + * @param array $arguments The registration arguments gathered so far + * @param callable $method The decorated method + * @return array */ - public function handle($container, object $instance, ReflectionMethod|ReflectionClass $context, object $attribute): void + public function configureArguments(array $arguments, callable $method): array { - if (! $instance instanceof TaxonomyAttributeInterface) { - return; - } + $arguments['meta_box_cb'] = $method; - // Set the meta_box_cb parameter to the method - $instance->setArg('meta_box_cb', [new $context->class, $context->getName()]); + return $arguments; } } diff --git a/src/Attributes/Taxonomy/MetaBoxSanitizeCb.php b/src/Attributes/Taxonomy/MetaBoxSanitizeCb.php index e24dcdcf..994a2fc8 100755 --- a/src/Attributes/Taxonomy/MetaBoxSanitizeCb.php +++ b/src/Attributes/Taxonomy/MetaBoxSanitizeCb.php @@ -5,10 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Attributes\Contracts\HandlesAttributes; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; -use ReflectionClass; -use ReflectionMethod; +use Pollora\Attributes\Contracts\ConfiguresArgumentsWithMethod; /** * Attribute to define a callback function for sanitizing taxonomy data saved from a meta box. @@ -19,22 +16,19 @@ * @Attribute */ #[Attribute(Attribute::TARGET_METHOD)] -class MetaBoxSanitizeCb implements HandlesAttributes +class MetaBoxSanitizeCb implements ConfiguresArgumentsWithMethod { /** - * Handle the attribute processing. + * Set the meta_box_sanitize_cb argument to the decorated method. * - * @param object $instance The instance being processed - * @param ReflectionMethod|ReflectionClass $context The method the attribute is applied to - * @param object $attribute The attribute instance + * @param array $arguments The registration arguments gathered so far + * @param callable $method The decorated method + * @return array */ - public function handle($container, object $instance, ReflectionMethod|ReflectionClass $context, object $attribute): void + public function configureArguments(array $arguments, callable $method): array { - if (! $instance instanceof TaxonomyAttributeInterface) { - return; - } + $arguments['meta_box_sanitize_cb'] = $method; - // Set the meta_box_sanitize_cb parameter to the method - $instance->setArg('meta_box_sanitize_cb', [new $context->class, $context->getName()]); + return $arguments; } } diff --git a/src/Attributes/Taxonomy/ObjectType.php b/src/Attributes/Taxonomy/ObjectType.php index c39a90e9..7c39b7f3 100755 --- a/src/Attributes/Taxonomy/ObjectType.php +++ b/src/Attributes/Taxonomy/ObjectType.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the post types this taxonomy is associated with. @@ -13,7 +13,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class ObjectType extends TaxonomyAttribute +class ObjectType implements ConfiguresArguments { /** * Constructor. @@ -27,10 +27,13 @@ public function __construct( /** * Configure the taxonomy with the object_type parameter. * - * @param TaxonomyAttributeInterface $taxonomy The taxonomy to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('object_type', $this->value); + $arguments['object_type'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/Priority.php b/src/Attributes/Taxonomy/Priority.php index fc9e3d44..7a37cbc4 100755 --- a/src/Attributes/Taxonomy/Priority.php +++ b/src/Attributes/Taxonomy/Priority.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the priority a post type declaration. @@ -13,7 +13,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class Priority extends TaxonomyAttribute +class Priority implements ConfiguresArguments { /** * Constructor. @@ -25,10 +25,13 @@ public function __construct( /** * Configure the post type priority declaration parameter. * - * @param TaxonomyAttributeInterface $taxonomy The post type to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('priority', $this->priority); + $arguments['priority'] = $this->priority; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/PublicTaxonomy.php b/src/Attributes/Taxonomy/PublicTaxonomy.php index b8205a15..29da08b8 100755 --- a/src/Attributes/Taxonomy/PublicTaxonomy.php +++ b/src/Attributes/Taxonomy/PublicTaxonomy.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the public parameter for a taxonomy. @@ -16,7 +16,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class PublicTaxonomy extends TaxonomyAttribute +class PublicTaxonomy implements ConfiguresArguments { /** * Constructor. @@ -30,10 +30,13 @@ public function __construct( /** * Configure the taxonomy with the public parameter. * - * @param TaxonomyAttributeInterface $taxonomy The taxonomy to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('public', $this->value); + $arguments['public'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/QueryVar.php b/src/Attributes/Taxonomy/QueryVar.php index 61e3a168..4fbd686f 100755 --- a/src/Attributes/Taxonomy/QueryVar.php +++ b/src/Attributes/Taxonomy/QueryVar.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the query_var key for this taxonomy. @@ -13,7 +13,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class QueryVar extends TaxonomyAttribute +class QueryVar implements ConfiguresArguments { /** * Constructor. @@ -27,10 +27,13 @@ public function __construct( /** * Configure the taxonomy with the query_var parameter. * - * @param TaxonomyAttributeInterface $taxonomy The taxonomy to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('query_var', $this->value); + $arguments['query_var'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/RestBase.php b/src/Attributes/Taxonomy/RestBase.php index 9eb7c2ab..b7548aed 100755 --- a/src/Attributes/Taxonomy/RestBase.php +++ b/src/Attributes/Taxonomy/RestBase.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the base URL segment that will be used in REST API endpoints for this taxonomy. @@ -13,7 +13,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class RestBase extends TaxonomyAttribute +class RestBase implements ConfiguresArguments { /** * Constructor. @@ -27,10 +27,13 @@ public function __construct( /** * Configure the taxonomy with the rest_base parameter. * - * @param TaxonomyAttributeInterface $taxonomy The taxonomy to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('rest_base', $this->value); + $arguments['rest_base'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/RestControllerClass.php b/src/Attributes/Taxonomy/RestControllerClass.php index e54a48c7..fcc5aaa4 100755 --- a/src/Attributes/Taxonomy/RestControllerClass.php +++ b/src/Attributes/Taxonomy/RestControllerClass.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the controller class that should be used for handling REST API requests for this taxonomy. @@ -13,7 +13,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class RestControllerClass extends TaxonomyAttribute +class RestControllerClass implements ConfiguresArguments { /** * Constructor. @@ -27,10 +27,13 @@ public function __construct( /** * Configure the taxonomy with the rest_controller_class parameter. * - * @param TaxonomyAttributeInterface $taxonomy The taxonomy to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('rest_controller_class', $this->value); + $arguments['rest_controller_class'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/RestNamespace.php b/src/Attributes/Taxonomy/RestNamespace.php index c2a28acd..9bf20d6e 100755 --- a/src/Attributes/Taxonomy/RestNamespace.php +++ b/src/Attributes/Taxonomy/RestNamespace.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the REST API namespace for the taxonomy. @@ -13,7 +13,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class RestNamespace extends TaxonomyAttribute +class RestNamespace implements ConfiguresArguments { /** * Constructor. @@ -27,10 +27,13 @@ public function __construct( /** * Configure the taxonomy with the rest_namespace parameter. * - * @param TaxonomyAttributeInterface $taxonomy The taxonomy to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('rest_namespace', $this->value); + $arguments['rest_namespace'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/Rewrite.php b/src/Attributes/Taxonomy/Rewrite.php index ebd639d1..687946d7 100755 --- a/src/Attributes/Taxonomy/Rewrite.php +++ b/src/Attributes/Taxonomy/Rewrite.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the rewrite rules for the taxonomy. @@ -13,7 +13,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class Rewrite extends TaxonomyAttribute +class Rewrite implements ConfiguresArguments { /** * Constructor. @@ -27,10 +27,13 @@ public function __construct( /** * Configure the taxonomy with the rewrite parameter. * - * @param TaxonomyAttributeInterface $taxonomy The taxonomy to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('rewrite', $this->value); + $arguments['rewrite'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/ShowAdminColumn.php b/src/Attributes/Taxonomy/ShowAdminColumn.php index eef42295..b5521ef6 100755 --- a/src/Attributes/Taxonomy/ShowAdminColumn.php +++ b/src/Attributes/Taxonomy/ShowAdminColumn.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set whether to display a column for the taxonomy on its post type listing screens. @@ -13,7 +13,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class ShowAdminColumn extends TaxonomyAttribute +class ShowAdminColumn implements ConfiguresArguments { /** * Constructor. @@ -27,10 +27,13 @@ public function __construct( /** * Configure the taxonomy with the show_admin_column parameter. * - * @param TaxonomyAttributeInterface $taxonomy The taxonomy to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('show_admin_column', $this->value); + $arguments['show_admin_column'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/ShowInMenu.php b/src/Attributes/Taxonomy/ShowInMenu.php index 249546f6..6e40e179 100755 --- a/src/Attributes/Taxonomy/ShowInMenu.php +++ b/src/Attributes/Taxonomy/ShowInMenu.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set the show_in_menu parameter for a taxonomy. @@ -18,7 +18,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class ShowInMenu extends TaxonomyAttribute +class ShowInMenu implements ConfiguresArguments { /** * Constructor. @@ -32,10 +32,13 @@ public function __construct( /** * Configure the taxonomy with the show_in_menu parameter. * - * @param TaxonomyAttributeInterface $taxonomy The taxonomy to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('show_in_menu', $this->value); + $arguments['show_in_menu'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/ShowInNavMenus.php b/src/Attributes/Taxonomy/ShowInNavMenus.php index 9bf014cc..e9a1a1ec 100755 --- a/src/Attributes/Taxonomy/ShowInNavMenus.php +++ b/src/Attributes/Taxonomy/ShowInNavMenus.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set whether this taxonomy is available for selection in navigation menus. @@ -13,7 +13,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class ShowInNavMenus extends TaxonomyAttribute +class ShowInNavMenus implements ConfiguresArguments { /** * Constructor. @@ -27,10 +27,13 @@ public function __construct( /** * Configure the taxonomy with the show_in_nav_menus parameter. * - * @param TaxonomyAttributeInterface $taxonomy The taxonomy to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('show_in_nav_menus', $this->value); + $arguments['show_in_nav_menus'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/ShowInQuickEdit.php b/src/Attributes/Taxonomy/ShowInQuickEdit.php index 8105d502..49e29153 100755 --- a/src/Attributes/Taxonomy/ShowInQuickEdit.php +++ b/src/Attributes/Taxonomy/ShowInQuickEdit.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set whether to show the taxonomy in the quick/bulk edit panel. @@ -13,7 +13,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class ShowInQuickEdit extends TaxonomyAttribute +class ShowInQuickEdit implements ConfiguresArguments { /** * Constructor. @@ -27,10 +27,13 @@ public function __construct( /** * Configure the taxonomy with the show_in_quick_edit parameter. * - * @param TaxonomyAttributeInterface $taxonomy The taxonomy to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('show_in_quick_edit', $this->value); + $arguments['show_in_quick_edit'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/ShowInRest.php b/src/Attributes/Taxonomy/ShowInRest.php index ed6fe08d..0cbaf204 100755 --- a/src/Attributes/Taxonomy/ShowInRest.php +++ b/src/Attributes/Taxonomy/ShowInRest.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set whether this taxonomy should appear in the REST API. @@ -13,7 +13,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class ShowInRest extends TaxonomyAttribute +class ShowInRest implements ConfiguresArguments { /** * Constructor. @@ -27,10 +27,13 @@ public function __construct( /** * Configure the taxonomy with the show_in_rest parameter. * - * @param TaxonomyAttributeInterface $taxonomy The taxonomy to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('show_in_rest', $this->value); + $arguments['show_in_rest'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/ShowTagcloud.php b/src/Attributes/Taxonomy/ShowTagcloud.php index 603d7d5a..c915f2a1 100755 --- a/src/Attributes/Taxonomy/ShowTagcloud.php +++ b/src/Attributes/Taxonomy/ShowTagcloud.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set whether to show the taxonomy in the tag cloud widget. @@ -13,7 +13,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class ShowTagcloud extends TaxonomyAttribute +class ShowTagcloud implements ConfiguresArguments { /** * Constructor. @@ -27,10 +27,13 @@ public function __construct( /** * Configure the taxonomy with the show_tagcloud parameter. * - * @param TaxonomyAttributeInterface $taxonomy The taxonomy to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('show_tagcloud', $this->value); + $arguments['show_tagcloud'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/ShowUI.php b/src/Attributes/Taxonomy/ShowUI.php index 6c0e346d..908e13a1 100755 --- a/src/Attributes/Taxonomy/ShowUI.php +++ b/src/Attributes/Taxonomy/ShowUI.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set whether to generate a default UI for managing this taxonomy in the admin. @@ -13,7 +13,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class ShowUI extends TaxonomyAttribute +class ShowUI implements ConfiguresArguments { /** * Constructor. @@ -27,10 +27,13 @@ public function __construct( /** * Configure the taxonomy with the show_ui parameter. * - * @param TaxonomyAttributeInterface $taxonomy The taxonomy to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('show_ui', $this->value); + $arguments['show_ui'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/Sort.php b/src/Attributes/Taxonomy/Sort.php index f9902ada..bc65b152 100755 --- a/src/Attributes/Taxonomy/Sort.php +++ b/src/Attributes/Taxonomy/Sort.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; +use Pollora\Attributes\Contracts\ConfiguresArguments; /** * Attribute to set whether terms in this taxonomy should be sorted in the order they are provided. @@ -13,7 +13,7 @@ * @Attribute */ #[Attribute(Attribute::TARGET_CLASS)] -class Sort extends TaxonomyAttribute +class Sort implements ConfiguresArguments { /** * Constructor. @@ -27,10 +27,13 @@ public function __construct( /** * Configure the taxonomy with the sort parameter. * - * @param TaxonomyAttributeInterface $taxonomy The taxonomy to configure + * @param array $arguments The registration arguments gathered so far + * @return array */ - protected function configure(TaxonomyAttributeInterface $taxonomy): void + public function configureArguments(array $arguments): array { - $taxonomy->setArg('sort', $this->value); + $arguments['sort'] = $this->value; + + return $arguments; } } diff --git a/src/Attributes/Taxonomy/TaxonomyAttribute.php b/src/Attributes/Taxonomy/TaxonomyAttribute.php index 292210a3..9ca188dc 100755 --- a/src/Attributes/Taxonomy/TaxonomyAttribute.php +++ b/src/Attributes/Taxonomy/TaxonomyAttribute.php @@ -5,6 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Pollora\Attributes\Attributable; +use Pollora\Attributes\Contracts\ConfiguresArguments; use Pollora\Attributes\Contracts\HandlesAttributes; use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; use ReflectionClass; @@ -16,7 +17,9 @@ * This attribute serves as the base for all taxonomy configuration attributes. * It provides common functionality for handling taxonomy registration arguments. * - * @Attribute + * @deprecated since 13.36, removed in 15.0. Implement + * {@see ConfiguresArguments}, as the + * built-in attributes now do. */ abstract class TaxonomyAttribute implements HandlesAttributes { diff --git a/src/Attributes/Taxonomy/UpdateCountCallback.php b/src/Attributes/Taxonomy/UpdateCountCallback.php index 218a2465..b289db34 100755 --- a/src/Attributes/Taxonomy/UpdateCountCallback.php +++ b/src/Attributes/Taxonomy/UpdateCountCallback.php @@ -5,10 +5,7 @@ namespace Pollora\Attributes\Taxonomy; use Attribute; -use Pollora\Attributes\Contracts\HandlesAttributes; -use Pollora\Taxonomy\Domain\Contracts\TaxonomyAttributeInterface; -use ReflectionClass; -use ReflectionMethod; +use Pollora\Attributes\Contracts\ConfiguresArgumentsWithMethod; /** * Attribute to define a callback function that will be called when the count is updated. @@ -19,22 +16,19 @@ * @Attribute */ #[Attribute(Attribute::TARGET_METHOD)] -class UpdateCountCallback implements HandlesAttributes +class UpdateCountCallback implements ConfiguresArgumentsWithMethod { /** - * Handle the attribute processing. + * Set the update_count_callback argument to the decorated method. * - * @param object $instance The instance being processed - * @param ReflectionMethod|ReflectionClass $context The method the attribute is applied to - * @param object $attribute The attribute instance + * @param array $arguments The registration arguments gathered so far + * @param callable $method The decorated method + * @return array */ - public function handle($container, object $instance, ReflectionMethod|ReflectionClass $context, object $attribute): void + public function configureArguments(array $arguments, callable $method): array { - if (! $instance instanceof TaxonomyAttributeInterface) { - return; - } + $arguments['update_count_callback'] = $method; - // Set the update_count_callback parameter to the method - $instance->setArg('update_count_callback', [new $context->class, $context->getName()]); + return $arguments; } } diff --git a/src/Attributes/WpCli.php b/src/Attributes/WpCli.php index 7248b637..df7ff388 100644 --- a/src/Attributes/WpCli.php +++ b/src/Attributes/WpCli.php @@ -4,7 +4,7 @@ namespace Pollora\Attributes; -use Pollora\Support\Slug; +use Pollora\Attributes\Support\Naming; /** * Attribute to mark a class as a WordPress CLI command. @@ -39,6 +39,6 @@ public function __construct( */ public function getCommandName(string $className): string { - return $this->commandName ?? Slug::fromClassName($className); + return $this->commandName ?? Naming::commandFromClassName($className); } } diff --git a/src/Attributes/WpCli/Command.php b/src/Attributes/WpCli/Command.php index f6b02382..6e485bfd 100644 --- a/src/Attributes/WpCli/Command.php +++ b/src/Attributes/WpCli/Command.php @@ -5,7 +5,7 @@ namespace Pollora\Attributes\WpCli; use Attribute; -use Pollora\Support\Slug; +use Pollora\Attributes\Support\Naming; /** * WP CLI Command Attribute for Method-Level Subcommands @@ -42,6 +42,6 @@ public function __construct( */ public function getSubcommandName(string $methodName): string { - return $this->commandName ?? Slug::fromMethodName($methodName); + return $this->commandName ?? Naming::slug($methodName); } } diff --git a/src/Attributes/WpRestRoute.php b/src/Attributes/WpRestRoute.php index d3836ceb..2f40377e 100755 --- a/src/Attributes/WpRestRoute.php +++ b/src/Attributes/WpRestRoute.php @@ -5,16 +5,13 @@ namespace Pollora\Attributes; use Attribute; -use Pollora\Attributes\Contracts\HandlesAttributes; use Pollora\Attributes\WpRestRoute\Permission; -use ReflectionClass; -use ReflectionMethod; /** * Attribute for defining WordPress REST API routes. */ #[Attribute(Attribute::TARGET_CLASS)] -class WpRestRoute implements HandlesAttributes +class WpRestRoute { /** * Constructor for WpRestRoute attribute. @@ -28,19 +25,4 @@ public function __construct( public readonly string $route, public readonly string|Permission|null $permissionCallback = null ) {} - - /** - * Handle the attribute processing. - * - * @param object $serviceLocator Service locator used to resolve dependencies - * @param object $instance The instance to which the attribute applies - * @param ReflectionClass|ReflectionMethod $context The reflection context. - * @param object $attribute The attribute instance containing provided arguments. - */ - public function handle($serviceLocator, object $instance, ReflectionClass|ReflectionMethod $context, object $attribute): void - { - $instance->namespace = $attribute->namespace; - $instance->route = $attribute->route; - $instance->classPermission = $attribute->permissionCallback; - } } diff --git a/src/Attributes/WpRestRoute/Method.php b/src/Attributes/WpRestRoute/Method.php index a43417bb..53d4a066 100755 --- a/src/Attributes/WpRestRoute/Method.php +++ b/src/Attributes/WpRestRoute/Method.php @@ -6,16 +6,9 @@ use Attribute; use InvalidArgumentException; -use Pollora\Attributes\Attributable; -use Pollora\Attributes\Contracts\HandlesAttributes; -use Pollora\Support\WpGlobals; -use ReflectionClass; -use ReflectionMethod; -use WP_Error; -use WP_REST_Request; #[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)] -class Method implements HandlesAttributes +class Method { private const array ALLOWED_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']; @@ -64,120 +57,4 @@ public function getMethods(): array { return $this->methods; } - - /** - * Handles the registration of the REST route for the method. - * - * @param object $serviceLocator Service locator used to resolve dependencies - * @param Attributable $instance The class instance - * @param ReflectionMethod|ReflectionClass $context The reflection context - * @param object $attribute The attribute instance - */ - public function handle($serviceLocator, Attributable $instance, ReflectionMethod|ReflectionClass $context, object $attribute): void - { - $methodPermission = $attribute->permissionCallback; - $permissionCallback = $methodPermission ?? $instance->classPermission; - - register_rest_route( - $instance->namespace, - $instance->route, - [ - 'methods' => $attribute->getMethods(), - 'callback' => WpGlobals::wrap( - fn (WP_REST_Request $request): mixed => $this->handleRequest($instance, $context, $request) - ), - 'args' => $this->extractArgsFromRoute($instance->route), - 'permission_callback' => $this->resolvePermissionCallback($permissionCallback), - ] - ); - } - - /** - * Executes the class method associated with the REST request. - * - * @param Attributable $instance The class instance - * @param ReflectionMethod $method The method to invoke - * @param WP_REST_Request $request The REST request instance - * @return mixed The result of the method invocation - */ - private function handleRequest(Attributable $instance, ReflectionMethod $method, WP_REST_Request $request): mixed - { - $args = []; - - foreach ($method->getParameters() as $param) { - // A WP_REST_Request type hint gets the request itself. Looking its - // name up among the route parameters found nothing — no route - // declares one called "request" — so the handler was invoked with - // null and a controller declaring the type fataled before running. - if ($this->expectsTheRequest($param)) { - $args[] = $request; - - continue; - } - - $args[] = $request->get_param($param->getName()); - } - - // Get the real instance if available, otherwise use the provided instance - $realInstance = method_exists($instance, 'getRealInstance') ? $instance->getRealInstance() : $instance; - - return $method->invoke($realInstance, ...$args); - } - - /** - * Whether a parameter asks for the request object rather than a route value. - */ - private function expectsTheRequest(\ReflectionParameter $param): bool - { - $type = $param->getType(); - - if (! $type instanceof \ReflectionNamedType || $type->isBuiltin()) { - return false; - } - - $name = $type->getName(); - - // The name comparison stands on its own: ::class resolves whether or - // not WordPress is loaded, so this holds outside a WordPress runtime. - return $name === WP_REST_Request::class - || is_a($name, WP_REST_Request::class, true); - } - - /** - * Extracts dynamic arguments from a WordPress route. - * - * @param string $route The route pattern - * @return array The extracted arguments - */ - private function extractArgsFromRoute(string $route): array - { - preg_match_all('/\(\?P<(\w+)>/', $route, $matches); - - return array_fill_keys($matches[1], [ - 'validate_callback' => fn ($param): bool => is_string($param) || is_numeric($param), - ]); - } - - /** - * Resolves and executes the permission callback. - * - * @param class-string|Permission|null $permissionCallback The permission class or instance to use - * @return callable The permission function - */ - private function resolvePermissionCallback(string|Permission|null $permissionCallback): callable - { - if ($permissionCallback === null) { - return '__return_true'; - } - - if (is_string($permissionCallback) && (! class_exists($permissionCallback) || ! is_subclass_of($permissionCallback, Permission::class))) { - return fn (): WP_Error => new WP_Error('rest_forbidden', __('Invalid permission handler.'), ['status' => 403]); - } - - return WpGlobals::wrap(function (WP_REST_Request $request) use ($permissionCallback): bool|WP_Error { - $permissionInstance = is_string($permissionCallback) ? new $permissionCallback : $permissionCallback; - - return $permissionInstance->allow($request); - }); - } } diff --git a/src/PostType/Infrastructure/Services/PostTypeConfiguration.php b/src/PostType/Infrastructure/Services/PostTypeConfiguration.php index 27ddefd8..3b7e2f15 100644 --- a/src/PostType/Infrastructure/Services/PostTypeConfiguration.php +++ b/src/PostType/Infrastructure/Services/PostTypeConfiguration.php @@ -79,6 +79,16 @@ public function getArg(string $key, mixed $default = null): mixed return $this->attributeArgs[$key] ?? $default; } + /** + * Replace every attribute argument. + * + * @param array $args The arguments to keep + */ + public function replaceArgs(array $args): void + { + $this->attributeArgs = $args; + } + /** * Merge additional arguments into the configuration. * diff --git a/src/PostType/Infrastructure/Services/PostTypeDiscovery.php b/src/PostType/Infrastructure/Services/PostTypeDiscovery.php index 0095dd91..de149d40 100644 --- a/src/PostType/Infrastructure/Services/PostTypeDiscovery.php +++ b/src/PostType/Infrastructure/Services/PostTypeDiscovery.php @@ -5,6 +5,9 @@ namespace Pollora\PostType\Infrastructure\Services; use Illuminate\Support\Str; +use Pollora\Attributes\Contracts\ConfiguresArguments; +use Pollora\Attributes\Contracts\ConfiguresArgumentsWithMethod; +use Pollora\Attributes\Contracts\HandlesAttributes; use Pollora\Attributes\PostType; use Pollora\Discovery\Domain\Contracts\ConfigurableDiscoveryInterface; use Pollora\Discovery\Domain\Contracts\DiscoveryInterface; @@ -302,8 +305,13 @@ private function processClassAttribute(ReflectionClass $reflectionClass, \Reflec { $attributeInstance = $attribute->newInstance(); - // Check if the attribute has a handle method and call it - if (method_exists($attributeInstance, 'handle')) { + if ($attributeInstance instanceof ConfiguresArguments) { + $config->replaceArgs($attributeInstance->configureArguments($config->withArgs())); + + return; + } + + if ($attributeInstance instanceof HandlesAttributes) { $attributeInstance->handle(app(), $config, $reflectionClass, $attributeInstance); } } @@ -325,8 +333,13 @@ private function processMethodAttribute( ): void { $attributeInstance = $attribute->newInstance(); - // Check if the attribute has a handle method and call it - if (method_exists($attributeInstance, 'handle')) { + if ($attributeInstance instanceof ConfiguresArgumentsWithMethod) { + $config->replaceArgs($attributeInstance->configureArguments($config->withArgs(), [new $method->class, $method->getName()])); + + return; + } + + if ($attributeInstance instanceof HandlesAttributes) { $attributeInstance->handle(app(), $config, $method, $attributeInstance); } } diff --git a/src/Support/Slug.php b/src/Support/Slug.php index 7d0ae61a..94a1c9a9 100644 --- a/src/Support/Slug.php +++ b/src/Support/Slug.php @@ -4,6 +4,8 @@ namespace Pollora\Support; +use Pollora\Attributes\Support\Naming; + /** * Utility class for slug generation. * @@ -25,15 +27,7 @@ class Slug */ public static function fromClassName(string $className): string { - // Extract the base class name (remove namespace) - $baseName = basename(str_replace('\\', '/', $className)); - - // Remove "Command" suffix if present - if (self::endsWith($baseName, 'Command')) { - $baseName = substr($baseName, 0, -7); - } - - return self::toKebabCase($baseName); + return Naming::commandFromClassName($className); } /** @@ -50,7 +44,7 @@ public static function fromClassName(string $className): string */ public static function fromMethodName(string $methodName): string { - return self::toKebabCase($methodName); + return Naming::slug($methodName); } /** @@ -61,35 +55,6 @@ public static function fromMethodName(string $methodName): string */ public static function toKebabCase(string $string): string { - // Insert a hyphen before any uppercase letter that follows a lowercase letter or number - $string = preg_replace('/([a-z0-9])([A-Z])/', '$1-$2', $string); - - // Convert to lowercase - $string = strtolower((string) $string); - - // Replace multiple consecutive hyphens with a single hyphen - $string = preg_replace('/-+/', '-', $string); - - // Trim hyphens from the beginning and end - $string = trim((string) $string, '-'); - - return $string; - } - - /** - * Check if a string ends with a specific suffix. - * - * @param string $haystack The string to check - * @param string $needle The suffix to look for - * @return bool True if the string ends with the suffix - */ - private static function endsWith(string $haystack, string $needle): bool - { - $length = strlen($needle); - if ($length === 0) { - return true; - } - - return substr($haystack, -$length) === $needle; + return Naming::slug($string); } } diff --git a/src/Taxonomy/Infrastructure/Services/TaxonomyConfiguration.php b/src/Taxonomy/Infrastructure/Services/TaxonomyConfiguration.php index c6acae9b..0a289bc8 100644 --- a/src/Taxonomy/Infrastructure/Services/TaxonomyConfiguration.php +++ b/src/Taxonomy/Infrastructure/Services/TaxonomyConfiguration.php @@ -96,6 +96,16 @@ public function getArg(string $key, mixed $default = null): mixed return $this->attributeArgs[$key] ?? $default; } + /** + * Replace every attribute argument. + * + * @param array $args The arguments to keep + */ + public function replaceArgs(array $args): void + { + $this->attributeArgs = $args; + } + /** * Merge additional arguments into the configuration. * diff --git a/src/Taxonomy/Infrastructure/Services/TaxonomyDiscovery.php b/src/Taxonomy/Infrastructure/Services/TaxonomyDiscovery.php index 2cb6ca82..c5f4880e 100644 --- a/src/Taxonomy/Infrastructure/Services/TaxonomyDiscovery.php +++ b/src/Taxonomy/Infrastructure/Services/TaxonomyDiscovery.php @@ -5,6 +5,9 @@ namespace Pollora\Taxonomy\Infrastructure\Services; use Illuminate\Support\Str; +use Pollora\Attributes\Contracts\ConfiguresArguments; +use Pollora\Attributes\Contracts\ConfiguresArgumentsWithMethod; +use Pollora\Attributes\Contracts\HandlesAttributes; use Pollora\Attributes\Taxonomy; use Pollora\Discovery\Domain\Contracts\ConfigurableDiscoveryInterface; use Pollora\Discovery\Domain\Contracts\DiscoveryInterface; @@ -307,8 +310,13 @@ private function processClassAttribute(ReflectionClass $reflectionClass, \Reflec { $attributeInstance = $attribute->newInstance(); - // Check if the attribute has a handle method and call it - if (method_exists($attributeInstance, 'handle')) { + if ($attributeInstance instanceof ConfiguresArguments) { + $config->replaceArgs($attributeInstance->configureArguments($config->withArgs())); + + return; + } + + if ($attributeInstance instanceof HandlesAttributes) { $attributeInstance->handle(app(), $config, $reflectionClass, $attributeInstance); } } @@ -330,8 +338,13 @@ private function processMethodAttribute( ): void { $attributeInstance = $attribute->newInstance(); - // Check if the attribute has a handle method and call it - if (method_exists($attributeInstance, 'handle')) { + if ($attributeInstance instanceof ConfiguresArgumentsWithMethod) { + $config->replaceArgs($attributeInstance->configureArguments($config->withArgs(), [new $method->class, $method->getName()])); + + return; + } + + if ($attributeInstance instanceof HandlesAttributes) { $attributeInstance->handle(app(), $config, $method, $attributeInstance); } } diff --git a/src/WpRest/Infrastructure/Services/WpRestDiscovery.php b/src/WpRest/Infrastructure/Services/WpRestDiscovery.php index 62e89c78..f8781370 100644 --- a/src/WpRest/Infrastructure/Services/WpRestDiscovery.php +++ b/src/WpRest/Infrastructure/Services/WpRestDiscovery.php @@ -4,7 +4,9 @@ namespace Pollora\WpRest\Infrastructure\Services; +use Pollora\Attributes\Contracts\HandlesAttributes; use Pollora\Attributes\WpRestRoute; +use Pollora\Attributes\WpRestRoute\Method; use Pollora\Discovery\Domain\Contracts\DiscoveryInterface; use Pollora\Discovery\Domain\Contracts\DiscoveryLocationInterface; use Pollora\Discovery\Domain\Contracts\ReflectionCacheInterface; @@ -33,7 +35,8 @@ final class WpRestDiscovery implements DiscoveryInterface private array $wrapperCache = []; public function __construct( - private readonly ?LoggerInterface $logger = null + private readonly ?LoggerInterface $logger = null, + private readonly WpRestRouteRegistrar $registrar = new WpRestRouteRegistrar, ) {} /** @@ -205,15 +208,19 @@ private function processMethodAttribute( try { $attributeInstance = $attribute->newInstance(); - // Check if the attribute has a handle method and call it - if (! method_exists($attributeInstance, 'handle')) { + if ($attributeInstance instanceof Method) { + add_action('rest_api_init', function () use ($attributeInstance, $attributableWrapper, $method): void { + $this->registrar->register($attributableWrapper, $method, $attributeInstance); + }); + return; } - // Let the attribute handle the registration - add_action('rest_api_init', function () use ($attributeInstance, $attributableWrapper, $method): void { - $attributeInstance->handle(app(), $attributableWrapper, $method, $attributeInstance); - }); + if ($attributeInstance instanceof HandlesAttributes) { + add_action('rest_api_init', function () use ($attributeInstance, $attributableWrapper, $method): void { + $attributeInstance->handle(app(), $attributableWrapper, $method, $attributeInstance); + }); + } } catch (\Throwable $throwable) { $className = $method->getDeclaringClass()->getName(); $this->logger?->error(sprintf('Failed to process method attribute for %s::%s: ', $className, $method->getName()).$throwable->getMessage()); diff --git a/src/WpRest/Infrastructure/Services/WpRestRouteRegistrar.php b/src/WpRest/Infrastructure/Services/WpRestRouteRegistrar.php new file mode 100644 index 00000000..ef519021 --- /dev/null +++ b/src/WpRest/Infrastructure/Services/WpRestRouteRegistrar.php @@ -0,0 +1,133 @@ +permissionCallback; + $permissionCallback = $methodPermission ?? $instance->classPermission; + + register_rest_route( + $instance->namespace, + $instance->route, + [ + 'methods' => $attribute->getMethods(), + 'callback' => WpGlobals::wrap( + fn (WP_REST_Request $request): mixed => $this->handleRequest($instance, $context, $request) + ), + 'args' => $this->extractArgsFromRoute($instance->route), + 'permission_callback' => $this->resolvePermissionCallback($permissionCallback), + ] + ); + } + + /** + * Executes the class method associated with the REST request. + * + * @param WpRestAttributableWrapper $instance The route class wrapper + * @param ReflectionMethod $method The method to invoke + * @param WP_REST_Request $request The REST request instance + * @return mixed The result of the method invocation + */ + private function handleRequest(WpRestAttributableWrapper $instance, ReflectionMethod $method, WP_REST_Request $request): mixed + { + $args = []; + + foreach ($method->getParameters() as $param) { + // A WP_REST_Request type hint gets the request itself. Looking its + // name up among the route parameters found nothing — no route + // declares one called "request" — so the handler was invoked with + // null and a controller declaring the type fataled before running. + if ($this->expectsTheRequest($param)) { + $args[] = $request; + + continue; + } + + $args[] = $request->get_param($param->getName()); + } + + return $method->invoke($instance->getRealInstance(), ...$args); + } + + /** + * Whether a parameter asks for the request object rather than a route value. + */ + private function expectsTheRequest(\ReflectionParameter $param): bool + { + $type = $param->getType(); + + if (! $type instanceof \ReflectionNamedType || $type->isBuiltin()) { + return false; + } + + $name = $type->getName(); + + // The name comparison stands on its own: ::class resolves whether or + // not WordPress is loaded, so this holds outside a WordPress runtime. + return $name === WP_REST_Request::class + || is_a($name, WP_REST_Request::class, true); + } + + /** + * Extracts dynamic arguments from a WordPress route. + * + * @param string $route The route pattern + * @return array The extracted arguments + */ + private function extractArgsFromRoute(string $route): array + { + preg_match_all('/\(\?P<(\w+)>/', $route, $matches); + + return array_fill_keys($matches[1], [ + 'validate_callback' => fn ($param): bool => is_string($param) || is_numeric($param), + ]); + } + + /** + * Resolves and executes the permission callback. + * + * @param class-string|Permission|null $permissionCallback The permission class or instance to use + * @return callable The permission function + */ + private function resolvePermissionCallback(string|Permission|null $permissionCallback): callable + { + if ($permissionCallback === null) { + return '__return_true'; + } + + if (is_string($permissionCallback) && (! class_exists($permissionCallback) || ! is_subclass_of($permissionCallback, Permission::class))) { + return fn (): WP_Error => new WP_Error('rest_forbidden', __('Invalid permission handler.'), ['status' => 403]); + } + + return WpGlobals::wrap(function (WP_REST_Request $request) use ($permissionCallback): bool|WP_Error { + $permissionInstance = is_string($permissionCallback) ? new $permissionCallback : $permissionCallback; + + return $permissionInstance->allow($request); + }); + } +} diff --git a/tests/Feature/Hook/Async/AsyncAttributeTest.php b/tests/Feature/Hook/Async/AsyncAttributeTest.php index 8506b5bb..96b402da 100644 --- a/tests/Feature/Hook/Async/AsyncAttributeTest.php +++ b/tests/Feature/Hook/Async/AsyncAttributeTest.php @@ -11,7 +11,6 @@ use Pollora\Hook\Async\Async; use Pollora\Hook\Async\AsyncPayload; use Pollora\Hook\Async\QueuedHandler; -use Pollora\Hook\Domain\Contract\Action as ActionContract; use Pollora\Hook\Infrastructure\Services\AsyncAttributeRegistrar; use Pollora\Hook\Infrastructure\Services\HookDiscovery; use Psr\Log\AbstractLogger; @@ -149,42 +148,3 @@ function registeredCallback(Action $actions, string $hook): mixed ->and($this->logger->errors)->toContain(sprintf('#[Async] on %s::orphan() is ignored: the method has no #[Action] to make asynchronous.', AsyncAttributeHooks::class)); }); }); - -describe('Action::handle()', function (): void { - it('registers through the registrar, asynchronously when #[Async] says so', function (): void { - $locator = new readonly class($this->actions, $this->logger) - { - public function __construct(private ActionContract $actions, private AbstractLogger $logger) {} - - public function get(string $id): mixed - { - return $id === ActionContract::class ? $this->actions : $this->logger; - } - }; - $method = new ReflectionMethod(AsyncAttributeHooks::class, 'notify'); - $attribute = new ActionAttribute('transition_post_status'); - - $attribute->handle($locator, new AsyncAttributeHooks, $method, $attribute); - - expect(registeredCallback($this->actions, 'transition_post_status'))->toBeInstanceOf(QueuedHandler::class); - }); - - it('works without a logger in the service locator, and ignores a class context', function (): void { - $locator = new readonly class($this->actions) - { - public function __construct(private ActionContract $actions) {} - - public function get(string $id): mixed - { - return $id === ActionContract::class ? $this->actions : throw new RuntimeException('Not bound'); - } - }; - $attribute = new ActionAttribute('save_post'); - - $attribute->handle($locator, new AsyncAttributeInvalidHooks, new ReflectionMethod(AsyncAttributeInvalidHooks::class, 'typo'), $attribute); - $attribute->handle($locator, new AsyncAttributeHooks, new ReflectionClass(AsyncAttributeHooks::class), new ActionAttribute('init')); - - expect(registeredCallback($this->actions, 'save_post'))->toBeArray() - ->and($this->actions->callbacks('init'))->toBeNull(); - }); -}); diff --git a/tests/Feature/Hook/HookDiscoveryTest.php b/tests/Feature/Hook/HookDiscoveryTest.php new file mode 100644 index 00000000..487f9a15 --- /dev/null +++ b/tests/Feature/Hook/HookDiscoveryTest.php @@ -0,0 +1,94 @@ + */ + public array $calls = []; + + #[ActionAttribute('init')] + public function boot(): void + { + $this->calls[] = 'boot'; + } + + #[ActionAttribute('save_post', priority: 99)] + #[ActionAttribute('edit_post', priority: 20)] + public function syncPost(int $postId, object $post): void + { + $this->calls[] = sprintf('syncPost(%d, %s)', $postId, $post::class); + } + + #[FilterAttribute('the_title')] + public function title(string $title): string + { + return 'modified_'.$title; + } + + #[FilterAttribute('the_content', priority: 5)] + public function content(string $content): string + { + return $content; + } + + public function notAHook(): void + { + $this->calls[] = 'notAHook'; + } +} + +beforeEach(function (): void { + $this->actions = new Action; + $this->filters = new Filter; + $discovery = new HookDiscovery($this->actions, $this->filters); + $discovery->discover(new DiscoveryLocation('', __DIR__), DiscoveredClass::fromReflection(new ReflectionClass(DiscoveredHooks::class)), new ReflectionCache); + $discovery->apply(); +}); + +/** + * The registration made on a hook: its method, priority and accepted arguments. + * + * @return array{method: string, priority: int, args: int} + */ +function hookRegistration(Action|Filter $hooks, string $hook): array +{ + $registration = $hooks->callbacks($hook)[0]; + + return ['method' => $registration['callback'][1], 'priority' => $registration['priority'], 'args' => $registration['args']]; +} + +it('registers each #[Action] with its priority, 10 by default', function (): void { + expect(hookRegistration($this->actions, 'init'))->toBe(['method' => 'boot', 'priority' => 10, 'args' => 0]) + ->and(hookRegistration($this->actions, 'save_post'))->toBe(['method' => 'syncPost', 'priority' => 99, 'args' => 2]) + ->and(hookRegistration($this->actions, 'edit_post'))->toBe(['method' => 'syncPost', 'priority' => 20, 'args' => 2]); +}); + +it('registers each #[Filter] with its priority, 10 by default', function (): void { + expect(hookRegistration($this->filters, 'the_title'))->toBe(['method' => 'title', 'priority' => 10, 'args' => 1]) + ->and(hookRegistration($this->filters, 'the_content'))->toBe(['method' => 'content', 'priority' => 5, 'args' => 1]); +}); + +it('calls the method of an instance of the declaring class', function (): void { + $callback = $this->filters->callbacks('the_title')[0]['callback']; + + expect($callback[0])->toBeInstanceOf(DiscoveredHooks::class) + ->and($callback('Hello'))->toBe('modified_Hello'); +}); + +it('registers nothing for a method without hook attribute', function (): void { + $registrations = array_merge(...array_values($this->actions->all()), ...array_values($this->filters->all())); + + expect(array_map(fn (array $registration): string => $registration['callback'][1], $registrations)) + ->toHaveCount(5) + ->not->toContain('notAHook'); +}); diff --git a/tests/Unit/Attributes/ActionTest.php b/tests/Unit/Attributes/ActionTest.php deleted file mode 100644 index 4cd33586..00000000 --- a/tests/Unit/Attributes/ActionTest.php +++ /dev/null @@ -1,187 +0,0 @@ -mockAction = Mockery::mock(ActionService::class); - - // Create a fake service locator - $this->mockServiceLocator = new class($this->mockAction) - { - public function __construct(private $actionService) {} - - public function get($serviceClass) - { - if ($serviceClass === ActionService::class) { - return $this->actionService; - } - - return null; - } - }; -}); - -class SingleActionClass -{ - #[Action('test_action', priority: 10)] - public function actionMethod($param = null): string - { - // Test method - return $param ? 'processed_'.$param : 'processed'; - } -} - -class MultipleActionClass -{ - #[Action('test_action', priority: 10)] - public function actionMethod($param = null): string - { - // Test method - return $param ? 'processed_'.$param : 'processed'; - } - - #[Action('another_action', priority: 20)] - public function anotherActionMethod(): string - { - // Another test method - return 'another_processed'; - } -} - -class DefaultPriorityActionClass -{ - #[Action('test_action')] - public function actionMethod($param = null): string - { - // Test method with default priority - return $param ? 'processed_'.$param : 'processed'; - } -} - -class CustomPriorityActionClass -{ - #[Action('test_action', priority: 42)] - public function actionMethod($param = null): string - { - // Test method with custom priority - return $param ? 'processed_'.$param : 'processed'; - } -} - -it('registers an action hook correctly', function (): void { - // Set up expectations - $this->mockAction->shouldReceive('add') - ->once() - ->withArgs(fn ($hook, $callback, $priority, $acceptedArgs): bool => $hook === 'test_action' - && is_array($callback) - && $callback[0] instanceof SingleActionClass - && $callback[1] === 'actionMethod' - && $priority === 10 - && $acceptedArgs === 1); - - // Test the action attribute directly using handle method - $testClass = new SingleActionClass; - $actionAttribute = new Action('test_action', 10); - $methodReflection = new ReflectionMethod($testClass, 'actionMethod'); - - $actionAttribute->handle($this->mockServiceLocator, $testClass, $methodReflection, $actionAttribute); -}); - -it('registers multiple action hooks with different priorities', function (): void { - $testClass = new MultipleActionClass; - - // Test first action - $this->mockAction->shouldReceive('add') - ->once() - ->withArgs(fn ($hook, $callback, $priority, $acceptedArgs): bool => $hook === 'test_action' - && is_array($callback) - && $callback[0] instanceof MultipleActionClass - && $callback[1] === 'actionMethod' - && $priority === 10); - - $actionAttribute1 = new Action('test_action', 10); - $methodReflection1 = new ReflectionMethod($testClass, 'actionMethod'); - $actionAttribute1->handle($this->mockServiceLocator, $testClass, $methodReflection1, $actionAttribute1); - - // Test second action - $this->mockAction->shouldReceive('add') - ->once() - ->withArgs(fn ($hook, $callback, $priority, $acceptedArgs): bool => $hook === 'another_action' - && is_array($callback) - && $callback[0] instanceof MultipleActionClass - && $callback[1] === 'anotherActionMethod' - && $priority === 20); - - $actionAttribute2 = new Action('another_action', 20); - $methodReflection2 = new ReflectionMethod($testClass, 'anotherActionMethod'); - $actionAttribute2->handle($this->mockServiceLocator, $testClass, $methodReflection2, $actionAttribute2); -}); - -it('registers an action hook with default priority (10)', function (): void { - // Set up expectations - $this->mockAction->shouldReceive('add') - ->once() - ->withArgs(fn ($hook, $callback, $priority, $acceptedArgs): bool => $hook === 'test_action' - && is_array($callback) - && $callback[0] instanceof DefaultPriorityActionClass - && $callback[1] === 'actionMethod' - && $priority === 10 // Default priority should be 10 - && $acceptedArgs === 1); - - // Test with default priority - $testClass = new DefaultPriorityActionClass; - $actionAttribute = new Action('test_action'); // No priority specified, should default to 10 - $methodReflection = new ReflectionMethod($testClass, 'actionMethod'); - - $actionAttribute->handle($this->mockServiceLocator, $testClass, $methodReflection, $actionAttribute); -}); - -it('registers an action hook with custom priority', function (): void { - // Set up expectations - $this->mockAction->shouldReceive('add') - ->once() - ->withArgs(fn ($hook, $callback, $priority, $acceptedArgs): bool => $hook === 'test_action' - && is_array($callback) - && $callback[0] instanceof CustomPriorityActionClass - && $callback[1] === 'actionMethod' - && $priority === 42 // Custom priority - && $acceptedArgs === 1); - - // Test with custom priority - $testClass = new CustomPriorityActionClass; - $actionAttribute = new Action('test_action', 42); - $methodReflection = new ReflectionMethod($testClass, 'actionMethod'); - - $actionAttribute->handle($this->mockServiceLocator, $testClass, $methodReflection, $actionAttribute); -}); - -it('handles null service locator resolution gracefully', function (): void { - // Create a service locator that returns null for the service - $mockServiceLocator = new class - { - public function get($serviceClass): null - { - return null; - } - }; - - $testClass = new SingleActionClass; - $actionAttribute = new Action('test_action', 10); - $methodReflection = new ReflectionMethod($testClass, 'actionMethod'); - - // This should not throw an exception - $actionAttribute->handle($mockServiceLocator, $testClass, $methodReflection, $actionAttribute); - - // No assertions needed - we're just checking that it doesn't throw - expect(true)->toBeTrue(); -}); - -afterEach(function (): void { - Mockery::close(); - Facade::clearResolvedInstances(); -}); diff --git a/tests/Unit/Attributes/ArchitectureTest.php b/tests/Unit/Attributes/ArchitectureTest.php new file mode 100644 index 00000000..2cb2cff2 --- /dev/null +++ b/tests/Unit/Attributes/ArchitectureTest.php @@ -0,0 +1,38 @@ +expect('Pollora\Attributes') + ->toOnlyUse([ + 'Pollora\Attributes', + AjaxAccess::class, + Behaviour::class, + BindingFieldType::class, + Control::class, + Access::class, + Every::class, + Interval::class, + ]) + // Permission::allow() names WordPress's request and error types in its + // signature only, so the attributes never load them. + ->ignoring([PostTypeAttribute::class, TaxonomyAttribute::class, 'WP_REST_Request', 'WP_Error']); diff --git a/tests/Unit/Attributes/FilterTest.php b/tests/Unit/Attributes/FilterTest.php deleted file mode 100644 index abf6cd87..00000000 --- a/tests/Unit/Attributes/FilterTest.php +++ /dev/null @@ -1,209 +0,0 @@ -mockFilter = Mockery::mock(FilterService::class); - - // Create a fake service locator - $this->mockServiceLocator = new class($this->mockFilter) - { - public function __construct(private $filterService) {} - - public function get($serviceClass) - { - if ($serviceClass === FilterService::class) { - return $this->filterService; - } - - return null; - } - }; -}); - -class SingleFilterClass -{ - #[Filter('test_filter', priority: 10)] - public function filterMethod(string $value): string - { - return 'modified_'.$value; - } -} - -class MultipleFilterClass -{ - #[Filter('test_filter', priority: 10)] - public function filterMethod(string $value): string - { - return 'modified_'.$value; - } - - #[Filter('another_filter', priority: 20)] - public function anotherFilterMethod(array $data): array - { - $data['modified'] = true; - - return $data; - } -} - -class DefaultPriorityFilterClass -{ - #[Filter('test_filter')] - public function filterMethod(string $value): string - { - // Test method with default priority - return 'modified_'.$value; - } -} - -class CustomPriorityFilterClass -{ - #[Filter('test_filter', priority: 99)] - public function filterMethod(string $value): string - { - // Test method with custom priority - return 'modified_'.$value; - } -} - -it('registers a filter hook correctly', function (): void { - // Set up expectations - $this->mockFilter->shouldReceive('add') - ->once() - ->withArgs(fn ($hook, $callback, $priority, $acceptedArgs): bool => $hook === 'test_filter' - && is_array($callback) - && $callback[0] instanceof SingleFilterClass - && $callback[1] === 'filterMethod' - && $priority === 10 - && $acceptedArgs === 1); - - // Test the filter attribute directly using handle method - $testClass = new SingleFilterClass; - $filterAttribute = new Filter('test_filter', 10); - $methodReflection = new ReflectionMethod($testClass, 'filterMethod'); - - $filterAttribute->handle($this->mockServiceLocator, $testClass, $methodReflection, $filterAttribute); -}); - -it('registers multiple filter hooks with different priorities', function (): void { - $testClass = new MultipleFilterClass; - - // Test first filter - $this->mockFilter->shouldReceive('add') - ->once() - ->withArgs(fn ($hook, $callback, $priority, $acceptedArgs): bool => $hook === 'test_filter' - && is_array($callback) - && $callback[0] instanceof MultipleFilterClass - && $callback[1] === 'filterMethod' - && $priority === 10); - - $filterAttribute1 = new Filter('test_filter', 10); - $methodReflection1 = new ReflectionMethod($testClass, 'filterMethod'); - $filterAttribute1->handle($this->mockServiceLocator, $testClass, $methodReflection1, $filterAttribute1); - - // Test second filter - $this->mockFilter->shouldReceive('add') - ->once() - ->withArgs(fn ($hook, $callback, $priority, $acceptedArgs): bool => $hook === 'another_filter' - && is_array($callback) - && $callback[0] instanceof MultipleFilterClass - && $callback[1] === 'anotherFilterMethod' - && $priority === 20); - - $filterAttribute2 = new Filter('another_filter', 20); - $methodReflection2 = new ReflectionMethod($testClass, 'anotherFilterMethod'); - $filterAttribute2->handle($this->mockServiceLocator, $testClass, $methodReflection2, $filterAttribute2); -}); - -it('registers a filter hook with default priority (10)', function (): void { - // Set up expectations - $this->mockFilter->shouldReceive('add') - ->once() - ->withArgs(fn ($hook, $callback, $priority, $acceptedArgs): bool => $hook === 'test_filter' - && is_array($callback) - && $callback[0] instanceof DefaultPriorityFilterClass - && $callback[1] === 'filterMethod' - && $priority === 10 // Default priority should be 10 - && $acceptedArgs === 1); - - // Test with default priority - $testClass = new DefaultPriorityFilterClass; - $filterAttribute = new Filter('test_filter'); // No priority specified, should default to 10 - $methodReflection = new ReflectionMethod($testClass, 'filterMethod'); - - $filterAttribute->handle($this->mockServiceLocator, $testClass, $methodReflection, $filterAttribute); -}); - -it('registers a filter hook with custom priority', function (): void { - // Set up expectations - $this->mockFilter->shouldReceive('add') - ->once() - ->withArgs(fn ($hook, $callback, $priority, $acceptedArgs): bool => $hook === 'test_filter' - && is_array($callback) - && $callback[0] instanceof CustomPriorityFilterClass - && $callback[1] === 'filterMethod' - && $priority === 99 // Custom priority - && $acceptedArgs === 1); - - // Test with custom priority - $testClass = new CustomPriorityFilterClass; - $filterAttribute = new Filter('test_filter', 99); - $methodReflection = new ReflectionMethod($testClass, 'filterMethod'); - - $filterAttribute->handle($this->mockServiceLocator, $testClass, $methodReflection, $filterAttribute); -}); - -it('executes filter and returns modified value', function (): void { - // Set up expectations for the add method - $this->mockFilter->shouldReceive('add') - ->once() - ->withAnyArgs(); - - // Set up expectations for the apply method - $this->mockFilter->shouldReceive('apply') - ->once() - ->with('test_filter', 'original') - ->andReturn('modified_original'); - - // Test filter attribute directly - $testClass = new SingleFilterClass; - $filterAttribute = new Filter('test_filter', 10); - $methodReflection = new ReflectionMethod($testClass, 'filterMethod'); - $filterAttribute->handle($this->mockServiceLocator, $testClass, $methodReflection, $filterAttribute); - - // Test the apply method - $result = $this->mockFilter->apply('test_filter', 'original'); - expect($result)->toBe('modified_original'); -}); - -it('handles null service locator resolution gracefully', function (): void { - // Create a service locator that returns null for the service - $mockServiceLocator = new class - { - public function get($serviceClass): null - { - return null; - } - }; - - $testClass = new SingleFilterClass; - $filterAttribute = new Filter('test_filter', 10); - $methodReflection = new ReflectionMethod($testClass, 'filterMethod'); - - // This should not throw an exception - $filterAttribute->handle($mockServiceLocator, $testClass, $methodReflection, $filterAttribute); - - // No assertions needed - we're just checking that it doesn't throw - expect(true)->toBeTrue(); -}); - -afterEach(function (): void { - Mockery::close(); - Facade::clearResolvedInstances(); -}); diff --git a/tests/Unit/Attributes/HookTest.php b/tests/Unit/Attributes/HookTest.php index 72b02c9c..c153b323 100644 --- a/tests/Unit/Attributes/HookTest.php +++ b/tests/Unit/Attributes/HookTest.php @@ -12,17 +12,7 @@ */ // Create a concrete implementation of the abstract Hook class for testing -class ConcreteHook extends Hook -{ - public function handle( - $serviceLocator, - object $instance, - ReflectionClass|ReflectionMethod $context, - object $attribute - ): void { - // Implementation for testing - } -} +class ConcreteHook extends Hook {} it('initializes with default priority', function (): void { $hook = new ConcreteHook('test_hook'); diff --git a/tests/Unit/Attributes/LabelsAttributeTest.php b/tests/Unit/Attributes/LabelsAttributeTest.php index 84bde720..2f6b4af1 100644 --- a/tests/Unit/Attributes/LabelsAttributeTest.php +++ b/tests/Unit/Attributes/LabelsAttributeTest.php @@ -56,9 +56,7 @@ class GenreWithLabels {} $reflection = new ReflectionClass(ProjectWithLabels::class); $attr = $reflection->getAttributes(PostTypeLabels::class)[0]->newInstance(); - $attr->handle(null, $config, $reflection, $attr); - - $labels = $config->attributeArgs['labels']; + $labels = $attr->configureArguments($config->attributeArgs)['labels']; // Overridden by #[Labels] expect($labels['all_items'])->toBe('All Projects'); @@ -88,10 +86,7 @@ class GenreWithLabels {} ); $config = new PostTypeConfiguration('item', 'Item', 'Items'); - $reflection = new ReflectionClass(ArticleWithoutLabels::class); - $labels->handle(null, $config, $reflection, $labels); - - $result = $config->attributeArgs['labels']; + $result = $labels->configureArguments($config->attributeArgs)['labels']; expect($result)->toBe([ 'name' => 'My Items', @@ -126,9 +121,7 @@ class GenreWithLabels {} $reflection = new ReflectionClass(GenreWithLabels::class); $attr = $reflection->getAttributes(TaxonomyLabels::class)[0]->newInstance(); - $attr->handle(null, $config, $reflection, $attr); - - $labels = $config->attributeArgs['labels']; + $labels = $attr->configureArguments($config->attributeArgs)['labels']; // Overridden expect($labels['all_items'])->toBe('All Genres'); diff --git a/tests/Unit/Attributes/NamingTest.php b/tests/Unit/Attributes/NamingTest.php new file mode 100644 index 00000000..c0d9c143 --- /dev/null +++ b/tests/Unit/Attributes/NamingTest.php @@ -0,0 +1,49 @@ + 'SyntheseDePresse', + 'single word' => 'Book', + 'already lower' => 'book', + 'camelCase property' => 'eventDate', + 'acronym' => 'HTMLParser', + 'digits' => 'Top10Books', + 'underscore' => 'event_date', + 'spaces' => 'event date time', + 'accented capital' => 'ÉvénementPublic', + 'trailing capital' => 'PostA', +]); + +it('writes snake case as Str::snake() does', function (string $name): void { + expect(Naming::snake($name))->toBe(Str::snake($name)); +})->with('names'); + +it('writes kebab case as Str::kebab() does', function (string $name): void { + expect(Naming::kebab($name))->toBe(Str::kebab($name)); +})->with('names'); + +it('strips the namespace as class_basename() does', function (): void { + expect(Naming::classBasename('App\\Cms\\PostTypes\\Book'))->toBe(class_basename('App\\Cms\\PostTypes\\Book')) + ->and(Naming::classBasename('Book'))->toBe('Book'); +}); + +it('derives command slugs, the Command suffix dropped', function (): void { + expect(Naming::commandFromClassName('App\\Cli\\UserManagementCommand'))->toBe('user-management') + ->and(Naming::commandFromClassName('Cache'))->toBe('cache') + ->and(Naming::slug('deleteById'))->toBe('delete-by-id') + ->and(Naming::slug('HTMLParser'))->toBe('htmlparser') + ->and(Slug::fromClassName('App\\Cli\\UserManagementCommand'))->toBe('user-management') + ->and(Slug::fromMethodName('listItems'))->toBe('list-items'); +}); diff --git a/tests/Unit/Attributes/TaxonomyAttributeTest.php b/tests/Unit/Attributes/TaxonomyAttributeTest.php index 66edb2fb..52a346d5 100644 --- a/tests/Unit/Attributes/TaxonomyAttributeTest.php +++ b/tests/Unit/Attributes/TaxonomyAttributeTest.php @@ -5,6 +5,7 @@ use Illuminate\Container\Container; use Illuminate\Support\Facades\Facade; use Mockery as m; +use Pollora\Attributes\Contracts\ConfiguresArguments; use Pollora\Attributes\Taxonomy; use Pollora\Attributes\Taxonomy\AllowHierarchy; use Pollora\Attributes\Taxonomy\Args; @@ -161,8 +162,8 @@ function testBooleanAttribute(string $attributeName, string $argName): void $reflectionClass = new ReflectionClass($taxonomy); foreach ($reflectionClass->getAttributes() as $attribute) { $attributeInstance = $attribute->newInstance(); - if (method_exists($attributeInstance, 'handle')) { - $attributeInstance->handle(app(), $taxonomy, $reflectionClass, $attributeInstance); + if ($attributeInstance instanceof ConfiguresArguments) { + $taxonomy->attributeArgs = $attributeInstance->configureArguments($taxonomy->attributeArgs); } } @@ -180,8 +181,8 @@ function testValueAttribute(string $attributeName, string $argName, mixed $expec $reflectionClass = new ReflectionClass($taxonomy); foreach ($reflectionClass->getAttributes() as $attribute) { $attributeInstance = $attribute->newInstance(); - if (method_exists($attributeInstance, 'handle')) { - $attributeInstance->handle(app(), $taxonomy, $reflectionClass, $attributeInstance); + if ($attributeInstance instanceof ConfiguresArguments) { + $taxonomy->attributeArgs = $attributeInstance->configureArguments($taxonomy->attributeArgs); } } @@ -206,7 +207,7 @@ function testMethodAttribute(string $attributeName, string $argName, string $met $attributes = $method->getAttributes($attributeClass); foreach ($attributes as $attribute) { $attributeInstance = $attribute->newInstance(); - $attributeInstance->handle(null, $taxonomy, $method, $attributeInstance); + $taxonomy->attributeArgs = $attributeInstance->configureArguments($taxonomy->attributeArgs, [$taxonomy, $method->getName()]); } } @@ -259,8 +260,8 @@ function testMethodAttribute(string $attributeName, string $argName, string $met $reflectionClass = new ReflectionClass($taxonomy); foreach ($reflectionClass->getAttributes() as $attribute) { $attributeInstance = $attribute->newInstance(); - if (method_exists($attributeInstance, 'handle')) { - $attributeInstance->handle(app(), $taxonomy, $reflectionClass, $attributeInstance); + if ($attributeInstance instanceof ConfiguresArguments) { + $taxonomy->attributeArgs = $attributeInstance->configureArguments($taxonomy->attributeArgs); } } diff --git a/tests/Unit/PostType/PostTypeDiscoveryTest.php b/tests/Unit/PostType/PostTypeDiscoveryTest.php new file mode 100644 index 00000000..be45aed2 --- /dev/null +++ b/tests/Unit/PostType/PostTypeDiscoveryTest.php @@ -0,0 +1,114 @@ + */ + public array $calls = []; + + #[AdminCol('isbn', 'ISBN', sortable: true)] + public function isbnColumn(): string + { + return '978'; + } + + #[RegisterMetaBoxCb] + public function metaBoxes(): void + { + $this->calls[] = 'metaBoxes'; + } + + #[Action('init')] + public function onInit(): void + { + $this->calls[] = 'onInit'; + } +} + +/** + * Run the discovery over the given classes and return what reached the post type service. + * + * @return array{slug: string, singular: string, plural: string, args: array, priority: int} + */ +function registeredPostType(string $class): array +{ + $registered = []; + $service = Mockery::mock(PostTypeServiceInterface::class); + $service->shouldReceive('register')->once()->andReturnUsing( + function (string $slug, string $singular, string $plural, array $args, int $priority) use (&$registered): object { + $registered = ['slug' => $slug, 'singular' => $singular, 'plural' => $plural, 'args' => $args, 'priority' => $priority]; + + return new stdClass; + } + ); + + $discovery = new PostTypeDiscovery($service); + $discovery->discover(new DiscoveryLocation('Tests\\', __DIR__), DiscoveredClass::fromReflection(new ReflectionClass($class)), new ReflectionCache); + $discovery->apply(); + + return $registered; +} + +it('registers the arguments its attributes declare', function (): void { + $registered = registeredPostType(DiscoveredBook::class); + + expect($registered['slug'])->toBe('fixture_book') + ->and($registered['priority'])->toBe(7) + ->and($registered['args'])->toMatchArray([ + 'has_archive' => 'library', + 'supports' => ['title', 'excerpt'], + 'orderby' => 'date', + 'order' => 'DESC', + 'text_domain' => 'pollora', + ]) + ->and($registered['args'])->not->toHaveKey('priority'); +}); + +it('merges declared labels into the generated ones', function (): void { + $labels = registeredPostType(DiscoveredBook::class)['args']['labels']; + + expect($labels['menu_name'])->toBe('Library') + ->and($labels['singular_name'])->toBe('Book'); +}); + +it('turns method attributes into callbacks on the declaring class', function (): void { + $args = registeredPostType(DiscoveredBook::class)['args']; + + expect($args['admin_cols']['isbn'])->toMatchArray(['title' => 'ISBN', 'sortable' => true]) + ->and($args['admin_cols']['isbn']['function'][0])->toBeInstanceOf(DiscoveredBook::class) + ->and($args['admin_cols']['isbn']['function'][1])->toBe('isbnColumn') + ->and($args['register_meta_box_cb'][0])->toBeInstanceOf(DiscoveredBook::class) + ->and($args['register_meta_box_cb'][1])->toBe('metaBoxes'); +}); + +it('leaves the actions of a post type class to the hook discovery', function (): void { + $actions = Mockery::mock(ActionService::class); + $actions->shouldNotReceive('add'); + + app()->instance(ActionService::class, $actions); + + registeredPostType(DiscoveredBook::class); +}); diff --git a/tests/Unit/Taxonomy/TaxonomyDiscoveryTest.php b/tests/Unit/Taxonomy/TaxonomyDiscoveryTest.php new file mode 100644 index 00000000..c025d307 --- /dev/null +++ b/tests/Unit/Taxonomy/TaxonomyDiscoveryTest.php @@ -0,0 +1,86 @@ + */ + public array $calls = []; + + #[MetaBoxCb] + public function metaBox(): void + { + $this->calls[] = 'metaBox'; + } + + #[Action('init')] + public function onInit(): void + { + $this->calls[] = 'onInit'; + } +} + +/** + * Run the discovery over the class and return the arguments it registered. + * + * @return array + */ +function registeredTaxonomyArgs(string $class): array +{ + $registered = []; + $service = Mockery::mock(TaxonomyServiceInterface::class); + $service->shouldReceive('register')->once()->andReturnUsing( + function (string $slug, string|array $objectType, string $singular, string $plural, array $args) use (&$registered): object { + $registered = $args; + + return new stdClass; + } + ); + + $discovery = new TaxonomyDiscovery($service); + $discovery->discover(new DiscoveryLocation('Tests\\', __DIR__), DiscoveredClass::fromReflection(new ReflectionClass($class)), new ReflectionCache); + $discovery->apply(); + + return $registered; +} + +it('registers the arguments its attributes declare, labels merged', function (): void { + $args = registeredTaxonomyArgs(DiscoveredGenre::class); + + expect($args)->toMatchArray(['hierarchical' => true, 'show_in_rest' => true]) + ->and($args['labels']['menu_name'])->toBe('Shelves') + ->and($args['labels']['singular_name'])->toBe('Genre'); +}); + +it('turns a method attribute into a callback on the declaring class', function (): void { + $callback = registeredTaxonomyArgs(DiscoveredGenre::class)['meta_box_cb']; + + expect($callback[0])->toBeInstanceOf(DiscoveredGenre::class) + ->and($callback[1])->toBe('metaBox'); +}); + +it('leaves the actions of a taxonomy class to the hook discovery', function (): void { + $actions = Mockery::mock(ActionService::class); + $actions->shouldNotReceive('add'); + + app()->instance(ActionService::class, $actions); + + registeredTaxonomyArgs(DiscoveredGenre::class); +}); diff --git a/tests/Unit/WpRest/CanPermissionTest.php b/tests/Unit/WpRest/CanPermissionTest.php index e2c2b1e9..d977c375 100644 --- a/tests/Unit/WpRest/CanPermissionTest.php +++ b/tests/Unit/WpRest/CanPermissionTest.php @@ -3,7 +3,7 @@ declare(strict_types=1); use Brain\Monkey\Functions; -use Pollora\Attributes\WpRestRoute\Method; +use Pollora\WpRest\Infrastructure\Services\WpRestRouteRegistrar; use Pollora\WpRest\Permissions\Can; use Tests\Unit\Role\Fixtures\EventCap; @@ -37,9 +37,9 @@ it('is accepted as an instance by #[Method] and #[WpRestRoute]', function (): void { Functions\when('current_user_can')->justReturn(true); - $resolve = new ReflectionMethod(Method::class, 'resolvePermissionCallback'); + $resolve = new ReflectionMethod(WpRestRouteRegistrar::class, 'resolvePermissionCallback'); - $callback = $resolve->invoke(new Method('GET', permissionCallback: new Can('edit_posts')), new Can('edit_posts')); + $callback = $resolve->invoke(new WpRestRouteRegistrar, new Can('edit_posts')); expect($callback(Mockery::mock(WP_REST_Request::class)))->toBeTrue(); }); diff --git a/tests/Unit/WpRest/RestRequestInjectionTest.php b/tests/Unit/WpRest/RestRequestInjectionTest.php index d69922c1..56684741 100644 --- a/tests/Unit/WpRest/RestRequestInjectionTest.php +++ b/tests/Unit/WpRest/RestRequestInjectionTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -use Pollora\Attributes\WpRestRoute\Method; +use Pollora\WpRest\Infrastructure\Services\WpRestRouteRegistrar; /** * Which arguments a REST handler is invoked with. @@ -27,12 +27,12 @@ public function withBuiltin(int $page): void {} $param = (new ReflectionMethod($probe, $signatureMethod))->getParameters()[0]; - $method = (new ReflectionClass(Method::class))->getMethod('expectsTheRequest'); + $method = (new ReflectionClass(WpRestRouteRegistrar::class))->getMethod('expectsTheRequest'); - return $method->invoke(new Method('GET'), $param); + return $method->invoke(new WpRestRouteRegistrar, $param); } -describe('Method::expectsTheRequest()', function (): void { +describe('WpRestRouteRegistrar::expectsTheRequest()', function (): void { it('gives the request to a parameter that asks for it by type', function (): void { expect(expectsRequest('withRequest'))->toBeTrue(); });