From 155fc336f1d80824f9ea2cc692b075265c9e887c Mon Sep 17 00:00:00 2001 From: Olivier Gorzalka Date: Mon, 12 Oct 2026 16:58:11 +0200 Subject: [PATCH] refactor: require pollora/attributes instead of shipping the attributes --- CHANGELOG.md | 1 + composer.json | 1 + src/Attributes/Ability.php | 56 --- src/Attributes/Action.php | 16 - src/Attributes/Ajax.php | 38 -- src/Attributes/Async.php | 53 --- src/Attributes/Attributable.php | 19 - src/Attributes/BlockBinding.php | 50 --- src/Attributes/BlockBinding/BindingField.php | 33 -- src/Attributes/CapabilitySet.php | 28 -- src/Attributes/CommentMeta.php | 25 -- .../Contracts/ConfiguresArguments.php | 24 -- .../ConfiguresArgumentsWithMethod.php | 24 -- .../Contracts/HandlesAttributes.php | 14 - src/Attributes/Filter.php | 16 - src/Attributes/Hook.php | 25 -- src/Attributes/Meta.php | 76 ---- src/Attributes/ModifyRole.php | 34 -- src/Attributes/PostMeta.php | 37 -- src/Attributes/PostType.php | 56 --- src/Attributes/PostType/AdminCol.php | 125 ------- src/Attributes/PostType/AdminCols.php | 42 --- src/Attributes/PostType/AdminFilters.php | 42 --- src/Attributes/PostType/Archive.php | 42 --- src/Attributes/PostType/BlockEditor.php | 42 --- src/Attributes/PostType/CanExport.php | 42 --- src/Attributes/PostType/Capabilities.php | 42 --- src/Attributes/PostType/CapabilityType.php | 47 --- src/Attributes/PostType/Chronological.php | 47 --- src/Attributes/PostType/DashboardActivity.php | 43 --- src/Attributes/PostType/DashboardGlance.php | 41 --- src/Attributes/PostType/DeleteWithUser.php | 43 --- src/Attributes/PostType/Description.php | 42 --- src/Attributes/PostType/ExcludeFromSearch.php | 42 --- src/Attributes/PostType/FeaturedImage.php | 42 --- src/Attributes/PostType/HasArchive.php | 42 --- src/Attributes/PostType/Hierarchical.php | 42 --- src/Attributes/PostType/Label.php | 42 --- src/Attributes/PostType/Labels.php | 102 ------ src/Attributes/PostType/MapMetaCap.php | 42 --- src/Attributes/PostType/MenuIcon.php | 42 --- src/Attributes/PostType/MenuPosition.php | 53 --- src/Attributes/PostType/Priority.php | 37 -- src/Attributes/PostType/PublicPostType.php | 42 --- src/Attributes/PostType/PubliclyQueryable.php | 41 --- src/Attributes/PostType/QueryVar.php | 44 --- src/Attributes/PostType/QuickEdit.php | 43 --- src/Attributes/PostType/RegisterMetaBoxCb.php | 36 -- src/Attributes/PostType/RestBase.php | 42 --- .../PostType/RestControllerClass.php | 42 --- src/Attributes/PostType/RestNamespace.php | 42 --- src/Attributes/PostType/Rewrite.php | 44 --- src/Attributes/PostType/ShowInAdminBar.php | 42 --- src/Attributes/PostType/ShowInFeed.php | 42 --- src/Attributes/PostType/ShowInMenu.php | 44 --- src/Attributes/PostType/ShowInNavMenus.php | 42 --- src/Attributes/PostType/ShowInRest.php | 43 --- src/Attributes/PostType/ShowUI.php | 42 --- src/Attributes/PostType/SiteFilters.php | 42 --- src/Attributes/PostType/SiteSortables.php | 42 --- src/Attributes/PostType/Supports.php | 41 --- src/Attributes/PostType/Taxonomies.php | 42 --- src/Attributes/PostType/Template.php | 42 --- src/Attributes/PostType/TemplateLock.php | 44 --- src/Attributes/PostType/TitlePlaceholder.php | 42 --- src/Attributes/Role.php | 42 --- src/Attributes/Role/Grants.php | 26 -- src/Attributes/Role/GrantsPostType.php | 27 -- src/Attributes/Role/GrantsTaxonomy.php | 24 -- src/Attributes/Role/Without.php | 28 -- src/Attributes/Schedule.php | 46 --- src/Attributes/Support/Naming.php | 73 ---- src/Attributes/Taxonomy.php | 66 ---- src/Attributes/Taxonomy/AllowHierarchy.php | 42 --- src/Attributes/Taxonomy/Args.php | 39 -- src/Attributes/Taxonomy/Capabilities.php | 48 --- src/Attributes/Taxonomy/CheckedOntop.php | 39 -- src/Attributes/Taxonomy/DefaultTerm.php | 39 -- src/Attributes/Taxonomy/Description.php | 39 -- src/Attributes/Taxonomy/Exclusive.php | 42 --- src/Attributes/Taxonomy/Hierarchical.php | 39 -- src/Attributes/Taxonomy/Label.php | 39 -- src/Attributes/Taxonomy/Labels.php | 89 ----- src/Attributes/Taxonomy/MetaBoxCb.php | 34 -- src/Attributes/Taxonomy/MetaBoxSanitizeCb.php | 34 -- src/Attributes/Taxonomy/ObjectType.php | 39 -- src/Attributes/Taxonomy/Priority.php | 37 -- src/Attributes/Taxonomy/PublicTaxonomy.php | 42 --- src/Attributes/Taxonomy/QueryVar.php | 39 -- src/Attributes/Taxonomy/RestBase.php | 39 -- .../Taxonomy/RestControllerClass.php | 39 -- src/Attributes/Taxonomy/RestNamespace.php | 39 -- src/Attributes/Taxonomy/Rewrite.php | 39 -- src/Attributes/Taxonomy/ShowAdminColumn.php | 39 -- src/Attributes/Taxonomy/ShowInMenu.php | 44 --- src/Attributes/Taxonomy/ShowInNavMenus.php | 39 -- src/Attributes/Taxonomy/ShowInQuickEdit.php | 39 -- src/Attributes/Taxonomy/ShowInRest.php | 39 -- src/Attributes/Taxonomy/ShowTagcloud.php | 39 -- src/Attributes/Taxonomy/ShowUI.php | 39 -- src/Attributes/Taxonomy/Sort.php | 39 -- .../Taxonomy/UpdateCountCallback.php | 34 -- src/Attributes/TermMeta.php | 37 -- src/Attributes/UserMeta.php | 25 -- src/Attributes/WpCli.php | 44 --- src/Attributes/WpCli/AfterInvoke.php | 24 -- src/Attributes/WpCli/BeforeInvoke.php | 24 -- src/Attributes/WpCli/Command.php | 47 --- src/Attributes/WpCli/IsDeferred.php | 25 -- src/Attributes/WpCli/Synopsis.php | 26 -- src/Attributes/WpCli/When.php | 26 -- src/Attributes/WpRestRoute.php | 28 -- src/Attributes/WpRestRoute/Method.php | 60 --- src/Attributes/WpRestRoute/Permission.php | 20 - .../Domain/Enums/BindingFieldType.php | 24 -- src/Meta/Domain/Enums/Control.php | 25 -- src/Role/Domain/Enums/Access.php | 40 -- src/Schedule/Every.php | 44 --- src/Schedule/Interval.php | 34 -- tests/Unit/Attributes/ArchitectureTest.php | 38 -- tests/Unit/Attributes/HookTest.php | 56 --- tests/Unit/Attributes/NamingTest.php | 49 --- .../Unit/Attributes/PostTypeAttributeTest.php | 344 ------------------ .../Attributes/PostTypeSubAttributesTest.php | 271 -------------- tests/Unit/Attributes/ScheduleTest.php | 158 -------- .../Attributes/TaxonomySubAttributesTest.php | 313 ---------------- tests/Unit/Schedule/EveryTest.php | 64 ---- tests/Unit/Schedule/IntervalTest.php | 67 ---- tests/Unit/Support/SlugTest.php | 20 + 129 files changed, 22 insertions(+), 6108 deletions(-) delete mode 100644 src/Attributes/Ability.php delete mode 100755 src/Attributes/Action.php delete mode 100644 src/Attributes/Ajax.php delete mode 100644 src/Attributes/Async.php delete mode 100755 src/Attributes/Attributable.php delete mode 100644 src/Attributes/BlockBinding.php delete mode 100644 src/Attributes/BlockBinding/BindingField.php delete mode 100644 src/Attributes/CapabilitySet.php delete mode 100644 src/Attributes/CommentMeta.php delete mode 100644 src/Attributes/Contracts/ConfiguresArguments.php delete mode 100644 src/Attributes/Contracts/ConfiguresArgumentsWithMethod.php delete mode 100755 src/Attributes/Contracts/HandlesAttributes.php delete mode 100755 src/Attributes/Filter.php delete mode 100755 src/Attributes/Hook.php delete mode 100644 src/Attributes/Meta.php delete mode 100644 src/Attributes/ModifyRole.php delete mode 100644 src/Attributes/PostMeta.php delete mode 100644 src/Attributes/PostType.php delete mode 100755 src/Attributes/PostType/AdminCol.php delete mode 100755 src/Attributes/PostType/AdminCols.php delete mode 100755 src/Attributes/PostType/AdminFilters.php delete mode 100755 src/Attributes/PostType/Archive.php delete mode 100755 src/Attributes/PostType/BlockEditor.php delete mode 100755 src/Attributes/PostType/CanExport.php delete mode 100755 src/Attributes/PostType/Capabilities.php delete mode 100755 src/Attributes/PostType/CapabilityType.php delete mode 100755 src/Attributes/PostType/Chronological.php delete mode 100755 src/Attributes/PostType/DashboardActivity.php delete mode 100755 src/Attributes/PostType/DashboardGlance.php delete mode 100755 src/Attributes/PostType/DeleteWithUser.php delete mode 100755 src/Attributes/PostType/Description.php delete mode 100755 src/Attributes/PostType/ExcludeFromSearch.php delete mode 100755 src/Attributes/PostType/FeaturedImage.php delete mode 100755 src/Attributes/PostType/HasArchive.php delete mode 100755 src/Attributes/PostType/Hierarchical.php delete mode 100755 src/Attributes/PostType/Label.php delete mode 100755 src/Attributes/PostType/Labels.php delete mode 100755 src/Attributes/PostType/MapMetaCap.php delete mode 100755 src/Attributes/PostType/MenuIcon.php delete mode 100755 src/Attributes/PostType/MenuPosition.php delete mode 100755 src/Attributes/PostType/Priority.php delete mode 100755 src/Attributes/PostType/PublicPostType.php delete mode 100755 src/Attributes/PostType/PubliclyQueryable.php delete mode 100755 src/Attributes/PostType/QueryVar.php delete mode 100755 src/Attributes/PostType/QuickEdit.php delete mode 100755 src/Attributes/PostType/RegisterMetaBoxCb.php delete mode 100755 src/Attributes/PostType/RestBase.php delete mode 100755 src/Attributes/PostType/RestControllerClass.php delete mode 100755 src/Attributes/PostType/RestNamespace.php delete mode 100755 src/Attributes/PostType/Rewrite.php delete mode 100755 src/Attributes/PostType/ShowInAdminBar.php delete mode 100755 src/Attributes/PostType/ShowInFeed.php delete mode 100755 src/Attributes/PostType/ShowInMenu.php delete mode 100755 src/Attributes/PostType/ShowInNavMenus.php delete mode 100755 src/Attributes/PostType/ShowInRest.php delete mode 100755 src/Attributes/PostType/ShowUI.php delete mode 100755 src/Attributes/PostType/SiteFilters.php delete mode 100755 src/Attributes/PostType/SiteSortables.php delete mode 100755 src/Attributes/PostType/Supports.php delete mode 100755 src/Attributes/PostType/Taxonomies.php delete mode 100755 src/Attributes/PostType/Template.php delete mode 100755 src/Attributes/PostType/TemplateLock.php delete mode 100755 src/Attributes/PostType/TitlePlaceholder.php delete mode 100644 src/Attributes/Role.php delete mode 100644 src/Attributes/Role/Grants.php delete mode 100644 src/Attributes/Role/GrantsPostType.php delete mode 100644 src/Attributes/Role/GrantsTaxonomy.php delete mode 100644 src/Attributes/Role/Without.php delete mode 100755 src/Attributes/Schedule.php delete mode 100644 src/Attributes/Support/Naming.php delete mode 100644 src/Attributes/Taxonomy.php delete mode 100755 src/Attributes/Taxonomy/AllowHierarchy.php delete mode 100755 src/Attributes/Taxonomy/Args.php delete mode 100644 src/Attributes/Taxonomy/Capabilities.php delete mode 100755 src/Attributes/Taxonomy/CheckedOntop.php delete mode 100755 src/Attributes/Taxonomy/DefaultTerm.php delete mode 100755 src/Attributes/Taxonomy/Description.php delete mode 100755 src/Attributes/Taxonomy/Exclusive.php delete mode 100755 src/Attributes/Taxonomy/Hierarchical.php delete mode 100755 src/Attributes/Taxonomy/Label.php delete mode 100755 src/Attributes/Taxonomy/Labels.php delete mode 100755 src/Attributes/Taxonomy/MetaBoxCb.php delete mode 100755 src/Attributes/Taxonomy/MetaBoxSanitizeCb.php delete mode 100755 src/Attributes/Taxonomy/ObjectType.php delete mode 100755 src/Attributes/Taxonomy/Priority.php delete mode 100755 src/Attributes/Taxonomy/PublicTaxonomy.php delete mode 100755 src/Attributes/Taxonomy/QueryVar.php delete mode 100755 src/Attributes/Taxonomy/RestBase.php delete mode 100755 src/Attributes/Taxonomy/RestControllerClass.php delete mode 100755 src/Attributes/Taxonomy/RestNamespace.php delete mode 100755 src/Attributes/Taxonomy/Rewrite.php delete mode 100755 src/Attributes/Taxonomy/ShowAdminColumn.php delete mode 100755 src/Attributes/Taxonomy/ShowInMenu.php delete mode 100755 src/Attributes/Taxonomy/ShowInNavMenus.php delete mode 100755 src/Attributes/Taxonomy/ShowInQuickEdit.php delete mode 100755 src/Attributes/Taxonomy/ShowInRest.php delete mode 100755 src/Attributes/Taxonomy/ShowTagcloud.php delete mode 100755 src/Attributes/Taxonomy/ShowUI.php delete mode 100755 src/Attributes/Taxonomy/Sort.php delete mode 100755 src/Attributes/Taxonomy/UpdateCountCallback.php delete mode 100644 src/Attributes/TermMeta.php delete mode 100644 src/Attributes/UserMeta.php delete mode 100644 src/Attributes/WpCli.php delete mode 100644 src/Attributes/WpCli/AfterInvoke.php delete mode 100644 src/Attributes/WpCli/BeforeInvoke.php delete mode 100644 src/Attributes/WpCli/Command.php delete mode 100644 src/Attributes/WpCli/IsDeferred.php delete mode 100644 src/Attributes/WpCli/Synopsis.php delete mode 100644 src/Attributes/WpCli/When.php delete mode 100755 src/Attributes/WpRestRoute.php delete mode 100755 src/Attributes/WpRestRoute/Method.php delete mode 100755 src/Attributes/WpRestRoute/Permission.php delete mode 100644 src/BlockBinding/Domain/Enums/BindingFieldType.php delete mode 100644 src/Meta/Domain/Enums/Control.php delete mode 100644 src/Role/Domain/Enums/Access.php delete mode 100644 src/Schedule/Every.php delete mode 100644 src/Schedule/Interval.php delete mode 100644 tests/Unit/Attributes/ArchitectureTest.php delete mode 100644 tests/Unit/Attributes/HookTest.php delete mode 100644 tests/Unit/Attributes/NamingTest.php delete mode 100644 tests/Unit/Attributes/PostTypeAttributeTest.php delete mode 100644 tests/Unit/Attributes/PostTypeSubAttributesTest.php delete mode 100644 tests/Unit/Attributes/ScheduleTest.php delete mode 100644 tests/Unit/Attributes/TaxonomySubAttributesTest.php delete mode 100644 tests/Unit/Schedule/EveryTest.php delete mode 100644 tests/Unit/Schedule/IntervalTest.php create mode 100644 tests/Unit/Support/SlugTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 92321442..93b92baf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 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 +- The attributes live in their own package, [`pollora/attributes`](https://github.com/Pollora/attributes), which the framework requires: every attribute and its contracts, with the value types they take (`Every`, `Interval`, `Access`, `Control`, `BindingFieldType`). Class names do not change. The framework keeps the two deprecated bases, `PostTypeAttribute` and `TaxonomyAttribute`, and the code that reads the attributes ### 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 diff --git a/composer.json b/composer.json index a5f9f69b..af63004e 100644 --- a/composer.json +++ b/composer.json @@ -31,6 +31,7 @@ "illuminate/console": "^13.35", "pollora/abilities": "^1.0", "pollora/ajax": "^1.1", + "pollora/attributes": "^0.1 || ^1.0", "pollora/discovery": "^0.1 || ^1.0", "pollora/entity": "^1.2", "pollora/hook": "^1.5", diff --git a/src/Attributes/Ability.php b/src/Attributes/Ability.php deleted file mode 100644 index 1367df84..00000000 --- a/src/Attributes/Ability.php +++ /dev/null @@ -1,56 +0,0 @@ -async(): #[Action] keeps the hook and - * the priority, #[Async] carries the deferred-execution options. - * - * #[Action('save_post_event')] - * #[Async(delay: 60, unique: true, tries: 3)] - * public function syncToCrm(int $postId): void {} - * - * On a class, it applies to every #[Action] method; an #[Async] on a method - * replaces the class options for that method. With several #[Action] on a - * method, it applies to all of them, except the hooks listed in $except. - */ -#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_CLASS)] -final readonly class Async -{ - /** - * @param int|\DateInterval|null $delay Minimum delay before execution, in seconds - * @param string|null $via Driver: queue, action-scheduler, wp-cron, sync - * @param string|null $onQueue Queue name, the group with Action Scheduler - * @param bool|int $unique Merge identical triggers until the first one runs; an integer sets how long the lock lasts, in seconds - * @param int|null $tries Attempts when the handler throws - * @param int|list|null $backoff Seconds before each retry - * @param bool|null $asUser Run as the user who fired the hook - * @param string|null $capture Public method of the class recording values at trigger time - * @param string|null $when Public method of the class deciding, at trigger time, whether to queue - * @param bool $keepMissing Run with null in place of a referenced object deleted in the meantime - * @param string|list $except Hooks of the method that stay synchronous - */ - public function __construct( - public int|\DateInterval|null $delay = null, - public ?string $via = null, - public ?string $onQueue = null, - public bool|int $unique = false, - public ?int $tries = null, - public int|array|null $backoff = null, - public ?bool $asUser = null, - public ?string $capture = null, - public ?string $when = null, - public bool $keepMissing = false, - public string|array $except = [], - ) {} -} diff --git a/src/Attributes/Attributable.php b/src/Attributes/Attributable.php deleted file mode 100755 index 942df62d..00000000 --- a/src/Attributes/Attributable.php +++ /dev/null @@ -1,19 +0,0 @@ -|Permission|null $classPermission - * @property string $namespace - * @property string $route - */ -interface Attributable {} diff --git a/src/Attributes/BlockBinding.php b/src/Attributes/BlockBinding.php deleted file mode 100644 index e9da9805..00000000 --- a/src/Attributes/BlockBinding.php +++ /dev/null @@ -1,50 +0,0 @@ - */ - public array $usesContext; - - /** @var list */ - public array $postTypes; - - /** - * @param string $name Source name, `namespace/name` in lowercase, as WordPress requires - * @param string|null $label Label shown in the editor. Defaults to the class name, headlined - * @param list $usesContext Block context the source reads - * @param string|list $postTypes Post types the source answers for; empty for all - */ - public function __construct( - public string $name, - public ?string $label = null, - array $usesContext = ['postId', 'postType'], - string|array $postTypes = [], - ) { - $this->usesContext = array_values($usesContext); - $this->postTypes = array_values((array) $postTypes); - } -} diff --git a/src/Attributes/BlockBinding/BindingField.php b/src/Attributes/BlockBinding/BindingField.php deleted file mode 100644 index 4d8f436f..00000000 --- a/src/Attributes/BlockBinding/BindingField.php +++ /dev/null @@ -1,33 +0,0 @@ - $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 deleted file mode 100644 index 934c04ce..00000000 --- a/src/Attributes/Contracts/ConfiguresArgumentsWithMethod.php +++ /dev/null @@ -1,24 +0,0 @@ - $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/Contracts/HandlesAttributes.php b/src/Attributes/Contracts/HandlesAttributes.php deleted file mode 100755 index b334807a..00000000 --- a/src/Attributes/Contracts/HandlesAttributes.php +++ /dev/null @@ -1,14 +0,0 @@ - $rules Laravel validation rules, checked on writes from PHP and REST - * @param bool $single On an `array` property, false stores one row per item instead of one serialized array - * @param string|null $items On an `array` property, the item type: `'string'`, `'int'`, `'float'`, `'bool'` or a class. Defaults to the `@var list<…>` docblock - * @param Control|null $control The input a UI driver should build. Defaults to one derived from the type - * @param string|null $group The group of fields a UI driver puts the meta in - * @param array $hints Options passed as they are to UI drivers, by driver: `['acf' => ['wrapper' => ['width' => 50]]]` - * @param bool $media On an `int` property, the value is an attachment ID: a block binding gives its URL, alt text or caption - * @param bool $public The value may be shown to anyone: required for a user meta to be read by the `pollora/author-meta` block binding - */ - public function __construct( - public ?string $key = null, - public bool $showInRest = false, - public ?string $label = null, - public ?string $description = null, - public string|array|null $sanitize = null, - public ?string $capability = null, - public bool $revisions = false, - public array $rules = [], - public bool $single = true, - public ?string $items = null, - public ?Control $control = null, - public ?string $group = null, - public array $hints = [], - public bool $media = false, - public bool $public = false, - ) {} - - /** - * The key the meta is stored under for the given property. - */ - public function resolveKey(string $property): string - { - return $this->key ?? Naming::snake($property); - } -} diff --git a/src/Attributes/ModifyRole.php b/src/Attributes/ModifyRole.php deleted file mode 100644 index accf608b..00000000 --- a/src/Attributes/ModifyRole.php +++ /dev/null @@ -1,34 +0,0 @@ - */ - public array $postTypes; - - /** - * @param string|list $postTypes The post type slugs the meta belong to - */ - public function __construct(string|array $postTypes) - { - $this->postTypes = array_values((array) $postTypes); - } -} diff --git a/src/Attributes/PostType.php b/src/Attributes/PostType.php deleted file mode 100644 index 94fac8ed..00000000 --- a/src/Attributes/PostType.php +++ /dev/null @@ -1,56 +0,0 @@ -slug ?? substr(Naming::kebab(Naming::classBasename($className)), 0, 20); - } -} diff --git a/src/Attributes/PostType/AdminCol.php b/src/Attributes/PostType/AdminCol.php deleted file mode 100755 index d5696bd3..00000000 --- a/src/Attributes/PostType/AdminCol.php +++ /dev/null @@ -1,125 +0,0 @@ - $arguments The registration arguments gathered so far - * @param callable $method The decorated method - * @return array - */ - public function configureArguments(array $arguments, callable $method): array - { - // Build column configuration based on extended-cpts options - $columnConfig = [ - 'title' => $this->title, - 'function' => $method, - ]; - - // Add optional configuration based on extended-cpts features - if ($this->titleIcon !== null) { - $columnConfig['title_icon'] = $this->titleIcon; - } - - if ($this->dateFormat !== null) { - $columnConfig['date_format'] = $this->dateFormat; - } - - if ($this->link !== null) { - $columnConfig['link'] = $this->link; - } - - if ($this->cap !== null) { - $columnConfig['cap'] = $this->cap; - } - - if ($this->postCap !== null) { - $columnConfig['post_cap'] = $this->postCap; - } - - if ($this->default !== null) { - $columnConfig['default'] = $this->default; - } - - if ($this->metaKey !== null) { - $columnConfig['meta_key'] = $this->metaKey; - } - - if ($this->taxonomy !== null) { - $columnConfig['taxonomy'] = $this->taxonomy; - } - - if ($this->featuredImage !== null) { - $columnConfig['featured_image'] = $this->featuredImage; - } - - if ($this->postField !== null) { - $columnConfig['post_field'] = $this->postField; - } - - if ($this->width !== null) { - $columnConfig['width'] = $this->width; - } - - // Handle sortable configuration - if ($this->sortable !== false) { - $columnConfig['sortable'] = $this->sortable; - } - - $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 deleted file mode 100755 index 2b43b9e2..00000000 --- a/src/Attributes/PostType/AdminCols.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['admin_cols'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/AdminFilters.php b/src/Attributes/PostType/AdminFilters.php deleted file mode 100755 index 4fbf2818..00000000 --- a/src/Attributes/PostType/AdminFilters.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['admin_filters'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/Archive.php b/src/Attributes/PostType/Archive.php deleted file mode 100755 index 3d081ac4..00000000 --- a/src/Attributes/PostType/Archive.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['archive'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/BlockEditor.php b/src/Attributes/PostType/BlockEditor.php deleted file mode 100755 index cdfc9f2b..00000000 --- a/src/Attributes/PostType/BlockEditor.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['show_in_rest'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/CanExport.php b/src/Attributes/PostType/CanExport.php deleted file mode 100755 index 7f1f5d76..00000000 --- a/src/Attributes/PostType/CanExport.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['can_export'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/Capabilities.php b/src/Attributes/PostType/Capabilities.php deleted file mode 100755 index b3459422..00000000 --- a/src/Attributes/PostType/Capabilities.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['capabilities'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/CapabilityType.php b/src/Attributes/PostType/CapabilityType.php deleted file mode 100755 index e7196eca..00000000 --- a/src/Attributes/PostType/CapabilityType.php +++ /dev/null @@ -1,47 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['capability_type'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/Chronological.php b/src/Attributes/PostType/Chronological.php deleted file mode 100755 index 728fe084..00000000 --- a/src/Attributes/PostType/Chronological.php +++ /dev/null @@ -1,47 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - if ($this->value) { - // Set default ordering to date, descending - $arguments['orderby'] = 'date'; - $arguments['order'] = 'DESC'; - } - - return $arguments; - } -} diff --git a/src/Attributes/PostType/DashboardActivity.php b/src/Attributes/PostType/DashboardActivity.php deleted file mode 100755 index a808f98a..00000000 --- a/src/Attributes/PostType/DashboardActivity.php +++ /dev/null @@ -1,43 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['dashboard_activity'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/DashboardGlance.php b/src/Attributes/PostType/DashboardGlance.php deleted file mode 100755 index 81df2cfa..00000000 --- a/src/Attributes/PostType/DashboardGlance.php +++ /dev/null @@ -1,41 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['dashboard_glance'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/DeleteWithUser.php b/src/Attributes/PostType/DeleteWithUser.php deleted file mode 100755 index 2abeb23c..00000000 --- a/src/Attributes/PostType/DeleteWithUser.php +++ /dev/null @@ -1,43 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['delete_with_user'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/Description.php b/src/Attributes/PostType/Description.php deleted file mode 100755 index 6654747f..00000000 --- a/src/Attributes/PostType/Description.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['description'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/ExcludeFromSearch.php b/src/Attributes/PostType/ExcludeFromSearch.php deleted file mode 100755 index a6cb5d53..00000000 --- a/src/Attributes/PostType/ExcludeFromSearch.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['exclude_from_search'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/FeaturedImage.php b/src/Attributes/PostType/FeaturedImage.php deleted file mode 100755 index d2af7006..00000000 --- a/src/Attributes/PostType/FeaturedImage.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['featured_image'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/HasArchive.php b/src/Attributes/PostType/HasArchive.php deleted file mode 100755 index 59befcda..00000000 --- a/src/Attributes/PostType/HasArchive.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['has_archive'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/Hierarchical.php b/src/Attributes/PostType/Hierarchical.php deleted file mode 100755 index b4e085b8..00000000 --- a/src/Attributes/PostType/Hierarchical.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['hierarchical'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/Label.php b/src/Attributes/PostType/Label.php deleted file mode 100755 index b9bbb22a..00000000 --- a/src/Attributes/PostType/Label.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['label'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/Labels.php b/src/Attributes/PostType/Labels.php deleted file mode 100755 index 0ac367ff..00000000 --- a/src/Attributes/PostType/Labels.php +++ /dev/null @@ -1,102 +0,0 @@ - */ - private readonly array $labels; - - public function __construct( - ?string $name = null, - ?string $singularName = null, - ?string $menuName = null, - ?string $allItems = null, - ?string $addNew = null, - ?string $addNewItem = null, - ?string $editItem = null, - ?string $newItem = null, - ?string $viewItem = null, - ?string $viewItems = null, - ?string $searchItems = null, - ?string $notFound = null, - ?string $notFoundInTrash = null, - ?string $parentItemColon = null, - ?string $archives = null, - ?string $attributes = null, - ?string $insertIntoItem = null, - ?string $uploadedToThisItem = null, - ?string $featuredImage = null, - ?string $setFeaturedImage = null, - ?string $removeFeaturedImage = null, - ?string $useFeaturedImage = null, - ?string $filterItemsList = null, - ?string $itemsListNavigation = null, - ?string $itemsList = null, - ?string $itemPublished = null, - ?string $itemUpdated = null, - ?string $itemScheduled = null, - ?string $itemReverted = null, - ) { - $this->labels = array_filter([ - 'name' => $name, - 'singular_name' => $singularName, - 'menu_name' => $menuName, - 'all_items' => $allItems, - 'add_new' => $addNew, - 'add_new_item' => $addNewItem, - 'edit_item' => $editItem, - 'new_item' => $newItem, - 'view_item' => $viewItem, - 'view_items' => $viewItems, - 'search_items' => $searchItems, - 'not_found' => $notFound, - 'not_found_in_trash' => $notFoundInTrash, - 'parent_item_colon' => $parentItemColon, - 'archives' => $archives, - 'attributes' => $attributes, - 'insert_into_item' => $insertIntoItem, - 'uploaded_to_this_item' => $uploadedToThisItem, - 'featured_image' => $featuredImage, - 'set_featured_image' => $setFeaturedImage, - 'remove_featured_image' => $removeFeaturedImage, - 'use_featured_image' => $useFeaturedImage, - 'filter_items_list' => $filterItemsList, - 'items_list_navigation' => $itemsListNavigation, - 'items_list' => $itemsList, - 'item_published' => $itemPublished, - 'item_updated' => $itemUpdated, - 'item_scheduled' => $itemScheduled, - 'item_reverted_to_draft' => $itemReverted, - ], fn (?string $v): bool => $v !== null); - } - - public function configureArguments(array $arguments): array - { - $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 deleted file mode 100755 index 04b1ff39..00000000 --- a/src/Attributes/PostType/MapMetaCap.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['map_meta_cap'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/MenuIcon.php b/src/Attributes/PostType/MenuIcon.php deleted file mode 100755 index f2218cdc..00000000 --- a/src/Attributes/PostType/MenuIcon.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['menu_icon'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/MenuPosition.php b/src/Attributes/PostType/MenuPosition.php deleted file mode 100755 index 0952d950..00000000 --- a/src/Attributes/PostType/MenuPosition.php +++ /dev/null @@ -1,53 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['menu_position'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/Priority.php b/src/Attributes/PostType/Priority.php deleted file mode 100755 index f416ca56..00000000 --- a/src/Attributes/PostType/Priority.php +++ /dev/null @@ -1,37 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['priority'] = $this->priority; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/PublicPostType.php b/src/Attributes/PostType/PublicPostType.php deleted file mode 100755 index 53b905c4..00000000 --- a/src/Attributes/PostType/PublicPostType.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['public'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/PubliclyQueryable.php b/src/Attributes/PostType/PubliclyQueryable.php deleted file mode 100755 index 42c5b3e5..00000000 --- a/src/Attributes/PostType/PubliclyQueryable.php +++ /dev/null @@ -1,41 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['publicly_queryable'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/QueryVar.php b/src/Attributes/PostType/QueryVar.php deleted file mode 100755 index 54091c73..00000000 --- a/src/Attributes/PostType/QueryVar.php +++ /dev/null @@ -1,44 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['query_var'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/QuickEdit.php b/src/Attributes/PostType/QuickEdit.php deleted file mode 100755 index 9a7cbe77..00000000 --- a/src/Attributes/PostType/QuickEdit.php +++ /dev/null @@ -1,43 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['quick_edit'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/RegisterMetaBoxCb.php b/src/Attributes/PostType/RegisterMetaBoxCb.php deleted file mode 100755 index 8daff699..00000000 --- a/src/Attributes/PostType/RegisterMetaBoxCb.php +++ /dev/null @@ -1,36 +0,0 @@ - $arguments The registration arguments gathered so far - * @param callable $method The decorated method - * @return array - */ - public function configureArguments(array $arguments, callable $method): array - { - $arguments['register_meta_box_cb'] = $method; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/RestBase.php b/src/Attributes/PostType/RestBase.php deleted file mode 100755 index 8ca0e7fd..00000000 --- a/src/Attributes/PostType/RestBase.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['rest_base'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/RestControllerClass.php b/src/Attributes/PostType/RestControllerClass.php deleted file mode 100755 index d3d9004c..00000000 --- a/src/Attributes/PostType/RestControllerClass.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['rest_controller_class'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/RestNamespace.php b/src/Attributes/PostType/RestNamespace.php deleted file mode 100755 index 1eae5ba9..00000000 --- a/src/Attributes/PostType/RestNamespace.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['rest_namespace'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/Rewrite.php b/src/Attributes/PostType/Rewrite.php deleted file mode 100755 index 1fa15cfb..00000000 --- a/src/Attributes/PostType/Rewrite.php +++ /dev/null @@ -1,44 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['rewrite'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/ShowInAdminBar.php b/src/Attributes/PostType/ShowInAdminBar.php deleted file mode 100755 index cdcf1aa8..00000000 --- a/src/Attributes/PostType/ShowInAdminBar.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['show_in_admin_bar'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/ShowInFeed.php b/src/Attributes/PostType/ShowInFeed.php deleted file mode 100755 index 129b9f2b..00000000 --- a/src/Attributes/PostType/ShowInFeed.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['show_in_feed'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/ShowInMenu.php b/src/Attributes/PostType/ShowInMenu.php deleted file mode 100755 index b9724ac2..00000000 --- a/src/Attributes/PostType/ShowInMenu.php +++ /dev/null @@ -1,44 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['show_in_menu'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/ShowInNavMenus.php b/src/Attributes/PostType/ShowInNavMenus.php deleted file mode 100755 index 6dd21be9..00000000 --- a/src/Attributes/PostType/ShowInNavMenus.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['show_in_nav_menus'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/ShowInRest.php b/src/Attributes/PostType/ShowInRest.php deleted file mode 100755 index f83f97f2..00000000 --- a/src/Attributes/PostType/ShowInRest.php +++ /dev/null @@ -1,43 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['show_in_rest'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/ShowUI.php b/src/Attributes/PostType/ShowUI.php deleted file mode 100755 index 74c8eb82..00000000 --- a/src/Attributes/PostType/ShowUI.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['show_ui'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/SiteFilters.php b/src/Attributes/PostType/SiteFilters.php deleted file mode 100755 index a8b52288..00000000 --- a/src/Attributes/PostType/SiteFilters.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['site_filters'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/SiteSortables.php b/src/Attributes/PostType/SiteSortables.php deleted file mode 100755 index 772c99c3..00000000 --- a/src/Attributes/PostType/SiteSortables.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['site_sortables'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/Supports.php b/src/Attributes/PostType/Supports.php deleted file mode 100755 index 9fe01558..00000000 --- a/src/Attributes/PostType/Supports.php +++ /dev/null @@ -1,41 +0,0 @@ - $features Array of features the post type supports - */ - public function __construct( - public readonly array $features = ['title', 'editor'] - ) {} - - /** - * Configure the post type with the supports parameter. - * - * @param array $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['supports'] = $this->features; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/Taxonomies.php b/src/Attributes/PostType/Taxonomies.php deleted file mode 100755 index a4908587..00000000 --- a/src/Attributes/PostType/Taxonomies.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['taxonomies'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/Template.php b/src/Attributes/PostType/Template.php deleted file mode 100755 index ac33e809..00000000 --- a/src/Attributes/PostType/Template.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['template'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/TemplateLock.php b/src/Attributes/PostType/TemplateLock.php deleted file mode 100755 index 442afb56..00000000 --- a/src/Attributes/PostType/TemplateLock.php +++ /dev/null @@ -1,44 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['template_lock'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/PostType/TitlePlaceholder.php b/src/Attributes/PostType/TitlePlaceholder.php deleted file mode 100755 index c3ccd2b0..00000000 --- a/src/Attributes/PostType/TitlePlaceholder.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['title_placeholder'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Role.php b/src/Attributes/Role.php deleted file mode 100644 index f4909c2e..00000000 --- a/src/Attributes/Role.php +++ /dev/null @@ -1,42 +0,0 @@ - - */ - public array $capabilities; - - public function __construct(string|BackedEnum ...$capabilities) - { - $this->capabilities = array_values($capabilities); - } -} diff --git a/src/Attributes/Role/GrantsPostType.php b/src/Attributes/Role/GrantsPostType.php deleted file mode 100644 index b16c96da..00000000 --- a/src/Attributes/Role/GrantsPostType.php +++ /dev/null @@ -1,27 +0,0 @@ - - */ - public array $capabilities; - - public function __construct(string|BackedEnum ...$capabilities) - { - $this->capabilities = array_values($capabilities); - } -} diff --git a/src/Attributes/Schedule.php b/src/Attributes/Schedule.php deleted file mode 100755 index fc72a85d..00000000 --- a/src/Attributes/Schedule.php +++ /dev/null @@ -1,46 +0,0 @@ -|Every|Interval $recurrence The schedule recurrence definition - * - string: WordPress built-in schedule ('hourly', 'daily', 'weekly', 'twicedaily') - * - array: Custom schedule with 'interval' (seconds) and 'display' (name) keys - * - Every: Enum for predefined intervals (Every::HOUR, Every::DAY, etc.) - * - Interval: Custom interval instance with precise timing control - * @param string|null $hook Optional custom hook name. If null, generated from class and method names - * @param array $args Optional arguments to pass to the scheduled method - */ - public function __construct( - public readonly string|array|Every|Interval $recurrence, - public readonly ?string $hook = null, - public readonly array $args = [] - ) { - // No validation here - will be handled by ScheduleDiscovery - } -} diff --git a/src/Attributes/Support/Naming.php b/src/Attributes/Support/Naming.php deleted file mode 100644 index 0640640e..00000000 --- a/src/Attributes/Support/Naming.php +++ /dev/null @@ -1,73 +0,0 @@ -|string|null $objectType The post types this taxonomy applies to. If null, defaults to ['post']. - */ - public function __construct( - public ?string $slug = null, - public ?string $singular = null, - public ?string $plural = null, - public array|string|null $objectType = null, - public ?string $textDomain = null - ) {} - - /** - * The slug WordPress registers the taxonomy under. - * - * The explicit slug when the attribute has one; otherwise the class name in - * kebab-case, cut to 20 characters. Discovery registers with it and - * `pollora:status` reports it, so the two cannot disagree. - * - * @param string $className The class carrying the attribute - */ - public function resolveSlug(string $className): string - { - 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 deleted file mode 100755 index ebdee900..00000000 --- a/src/Attributes/Taxonomy/AllowHierarchy.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['allow_hierarchy'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/Args.php b/src/Attributes/Taxonomy/Args.php deleted file mode 100755 index 90740947..00000000 --- a/src/Attributes/Taxonomy/Args.php +++ /dev/null @@ -1,39 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['args'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/Capabilities.php b/src/Attributes/Taxonomy/Capabilities.php deleted file mode 100644 index f7fea2ce..00000000 --- a/src/Attributes/Taxonomy/Capabilities.php +++ /dev/null @@ -1,48 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['capabilities'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/CheckedOntop.php b/src/Attributes/Taxonomy/CheckedOntop.php deleted file mode 100755 index 72cb1d68..00000000 --- a/src/Attributes/Taxonomy/CheckedOntop.php +++ /dev/null @@ -1,39 +0,0 @@ -value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/DefaultTerm.php b/src/Attributes/Taxonomy/DefaultTerm.php deleted file mode 100755 index 8e7d553e..00000000 --- a/src/Attributes/Taxonomy/DefaultTerm.php +++ /dev/null @@ -1,39 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['default_term'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/Description.php b/src/Attributes/Taxonomy/Description.php deleted file mode 100755 index bc613822..00000000 --- a/src/Attributes/Taxonomy/Description.php +++ /dev/null @@ -1,39 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['description'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/Exclusive.php b/src/Attributes/Taxonomy/Exclusive.php deleted file mode 100755 index d34334f0..00000000 --- a/src/Attributes/Taxonomy/Exclusive.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['exclusive'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/Hierarchical.php b/src/Attributes/Taxonomy/Hierarchical.php deleted file mode 100755 index afea0792..00000000 --- a/src/Attributes/Taxonomy/Hierarchical.php +++ /dev/null @@ -1,39 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['hierarchical'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/Label.php b/src/Attributes/Taxonomy/Label.php deleted file mode 100755 index 5f8ec4c3..00000000 --- a/src/Attributes/Taxonomy/Label.php +++ /dev/null @@ -1,39 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['label'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/Labels.php b/src/Attributes/Taxonomy/Labels.php deleted file mode 100755 index 09efa745..00000000 --- a/src/Attributes/Taxonomy/Labels.php +++ /dev/null @@ -1,89 +0,0 @@ - */ - private readonly array $labels; - - public function __construct( - ?string $name = null, - ?string $singularName = null, - ?string $menuName = null, - ?string $allItems = null, - ?string $editItem = null, - ?string $viewItem = null, - ?string $updateItem = null, - ?string $addNewItem = null, - ?string $newItemName = null, - ?string $searchItems = null, - ?string $popularItems = null, - ?string $separateItemsWithCommas = null, - ?string $addOrRemoveItems = null, - ?string $chooseFromMostUsed = null, - ?string $notFound = null, - ?string $parentItem = null, - ?string $parentItemColon = null, - ?string $noTerms = null, - ?string $filterByItem = null, - ?string $itemsListNavigation = null, - ?string $itemsList = null, - ?string $backToItems = null, - ) { - $this->labels = array_filter([ - 'name' => $name, - 'singular_name' => $singularName, - 'menu_name' => $menuName, - 'all_items' => $allItems, - 'edit_item' => $editItem, - 'view_item' => $viewItem, - 'update_item' => $updateItem, - 'add_new_item' => $addNewItem, - 'new_item_name' => $newItemName, - 'search_items' => $searchItems, - 'popular_items' => $popularItems, - 'separate_items_with_commas' => $separateItemsWithCommas, - 'add_or_remove_items' => $addOrRemoveItems, - 'choose_from_most_used' => $chooseFromMostUsed, - 'not_found' => $notFound, - 'parent_item' => $parentItem, - 'parent_item_colon' => $parentItemColon, - 'no_terms' => $noTerms, - 'filter_by_item' => $filterByItem, - 'items_list_navigation' => $itemsListNavigation, - 'items_list' => $itemsList, - 'back_to_items' => $backToItems, - ], fn (?string $v): bool => $v !== null); - } - - public function configureArguments(array $arguments): array - { - $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 deleted file mode 100755 index 6fe8045c..00000000 --- a/src/Attributes/Taxonomy/MetaBoxCb.php +++ /dev/null @@ -1,34 +0,0 @@ - $arguments The registration arguments gathered so far - * @param callable $method The decorated method - * @return array - */ - public function configureArguments(array $arguments, callable $method): array - { - $arguments['meta_box_cb'] = $method; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/MetaBoxSanitizeCb.php b/src/Attributes/Taxonomy/MetaBoxSanitizeCb.php deleted file mode 100755 index 994a2fc8..00000000 --- a/src/Attributes/Taxonomy/MetaBoxSanitizeCb.php +++ /dev/null @@ -1,34 +0,0 @@ - $arguments The registration arguments gathered so far - * @param callable $method The decorated method - * @return array - */ - public function configureArguments(array $arguments, callable $method): array - { - $arguments['meta_box_sanitize_cb'] = $method; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/ObjectType.php b/src/Attributes/Taxonomy/ObjectType.php deleted file mode 100755 index 7c39b7f3..00000000 --- a/src/Attributes/Taxonomy/ObjectType.php +++ /dev/null @@ -1,39 +0,0 @@ - $value The post type(s) this taxonomy is associated with - */ - public function __construct( - public readonly string|array $value - ) {} - - /** - * Configure the taxonomy with the object_type parameter. - * - * @param array $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['object_type'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/Priority.php b/src/Attributes/Taxonomy/Priority.php deleted file mode 100755 index 7a37cbc4..00000000 --- a/src/Attributes/Taxonomy/Priority.php +++ /dev/null @@ -1,37 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['priority'] = $this->priority; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/PublicTaxonomy.php b/src/Attributes/Taxonomy/PublicTaxonomy.php deleted file mode 100755 index 29da08b8..00000000 --- a/src/Attributes/Taxonomy/PublicTaxonomy.php +++ /dev/null @@ -1,42 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['public'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/QueryVar.php b/src/Attributes/Taxonomy/QueryVar.php deleted file mode 100755 index 4fbd686f..00000000 --- a/src/Attributes/Taxonomy/QueryVar.php +++ /dev/null @@ -1,39 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['query_var'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/RestBase.php b/src/Attributes/Taxonomy/RestBase.php deleted file mode 100755 index b7548aed..00000000 --- a/src/Attributes/Taxonomy/RestBase.php +++ /dev/null @@ -1,39 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['rest_base'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/RestControllerClass.php b/src/Attributes/Taxonomy/RestControllerClass.php deleted file mode 100755 index fcc5aaa4..00000000 --- a/src/Attributes/Taxonomy/RestControllerClass.php +++ /dev/null @@ -1,39 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['rest_controller_class'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/RestNamespace.php b/src/Attributes/Taxonomy/RestNamespace.php deleted file mode 100755 index 9bf20d6e..00000000 --- a/src/Attributes/Taxonomy/RestNamespace.php +++ /dev/null @@ -1,39 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['rest_namespace'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/Rewrite.php b/src/Attributes/Taxonomy/Rewrite.php deleted file mode 100755 index 687946d7..00000000 --- a/src/Attributes/Taxonomy/Rewrite.php +++ /dev/null @@ -1,39 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['rewrite'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/ShowAdminColumn.php b/src/Attributes/Taxonomy/ShowAdminColumn.php deleted file mode 100755 index b5521ef6..00000000 --- a/src/Attributes/Taxonomy/ShowAdminColumn.php +++ /dev/null @@ -1,39 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['show_admin_column'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/ShowInMenu.php b/src/Attributes/Taxonomy/ShowInMenu.php deleted file mode 100755 index 6e40e179..00000000 --- a/src/Attributes/Taxonomy/ShowInMenu.php +++ /dev/null @@ -1,44 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['show_in_menu'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/ShowInNavMenus.php b/src/Attributes/Taxonomy/ShowInNavMenus.php deleted file mode 100755 index e9a1a1ec..00000000 --- a/src/Attributes/Taxonomy/ShowInNavMenus.php +++ /dev/null @@ -1,39 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['show_in_nav_menus'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/ShowInQuickEdit.php b/src/Attributes/Taxonomy/ShowInQuickEdit.php deleted file mode 100755 index 49e29153..00000000 --- a/src/Attributes/Taxonomy/ShowInQuickEdit.php +++ /dev/null @@ -1,39 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['show_in_quick_edit'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/ShowInRest.php b/src/Attributes/Taxonomy/ShowInRest.php deleted file mode 100755 index 0cbaf204..00000000 --- a/src/Attributes/Taxonomy/ShowInRest.php +++ /dev/null @@ -1,39 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['show_in_rest'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/ShowTagcloud.php b/src/Attributes/Taxonomy/ShowTagcloud.php deleted file mode 100755 index c915f2a1..00000000 --- a/src/Attributes/Taxonomy/ShowTagcloud.php +++ /dev/null @@ -1,39 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['show_tagcloud'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/ShowUI.php b/src/Attributes/Taxonomy/ShowUI.php deleted file mode 100755 index 908e13a1..00000000 --- a/src/Attributes/Taxonomy/ShowUI.php +++ /dev/null @@ -1,39 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['show_ui'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/Sort.php b/src/Attributes/Taxonomy/Sort.php deleted file mode 100755 index bc65b152..00000000 --- a/src/Attributes/Taxonomy/Sort.php +++ /dev/null @@ -1,39 +0,0 @@ - $arguments The registration arguments gathered so far - * @return array - */ - public function configureArguments(array $arguments): array - { - $arguments['sort'] = $this->value; - - return $arguments; - } -} diff --git a/src/Attributes/Taxonomy/UpdateCountCallback.php b/src/Attributes/Taxonomy/UpdateCountCallback.php deleted file mode 100755 index b289db34..00000000 --- a/src/Attributes/Taxonomy/UpdateCountCallback.php +++ /dev/null @@ -1,34 +0,0 @@ - $arguments The registration arguments gathered so far - * @param callable $method The decorated method - * @return array - */ - public function configureArguments(array $arguments, callable $method): array - { - $arguments['update_count_callback'] = $method; - - return $arguments; - } -} diff --git a/src/Attributes/TermMeta.php b/src/Attributes/TermMeta.php deleted file mode 100644 index 8bdc3d09..00000000 --- a/src/Attributes/TermMeta.php +++ /dev/null @@ -1,37 +0,0 @@ - */ - public array $taxonomies; - - /** - * @param string|list $taxonomies The taxonomy slugs the meta belong to - */ - public function __construct(string|array $taxonomies) - { - $this->taxonomies = array_values((array) $taxonomies); - } -} diff --git a/src/Attributes/UserMeta.php b/src/Attributes/UserMeta.php deleted file mode 100644 index 25a70858..00000000 --- a/src/Attributes/UserMeta.php +++ /dev/null @@ -1,25 +0,0 @@ -newsletterOptIn; - * - * @experimental The API may still change before it is declared stable. - */ -#[Attribute(Attribute::TARGET_CLASS)] -final readonly class UserMeta {} diff --git a/src/Attributes/WpCli.php b/src/Attributes/WpCli.php deleted file mode 100644 index df7ff388..00000000 --- a/src/Attributes/WpCli.php +++ /dev/null @@ -1,44 +0,0 @@ -commandName ?? Naming::commandFromClassName($className); - } -} diff --git a/src/Attributes/WpCli/AfterInvoke.php b/src/Attributes/WpCli/AfterInvoke.php deleted file mode 100644 index aaa226fa..00000000 --- a/src/Attributes/WpCli/AfterInvoke.php +++ /dev/null @@ -1,24 +0,0 @@ -commandName ?? Naming::slug($methodName); - } -} diff --git a/src/Attributes/WpCli/IsDeferred.php b/src/Attributes/WpCli/IsDeferred.php deleted file mode 100644 index bcaeaea6..00000000 --- a/src/Attributes/WpCli/IsDeferred.php +++ /dev/null @@ -1,25 +0,0 @@ -|Permission|null $permissionCallback The permission for the route: a Permission class, or an instance such as `new Can('edit_posts')`. - */ - public function __construct( - public readonly string $namespace, - public readonly string $route, - public readonly string|Permission|null $permissionCallback = null - ) {} -} diff --git a/src/Attributes/WpRestRoute/Method.php b/src/Attributes/WpRestRoute/Method.php deleted file mode 100755 index 53d4a066..00000000 --- a/src/Attributes/WpRestRoute/Method.php +++ /dev/null @@ -1,60 +0,0 @@ -|Permission|null $permissionCallback The permission for this method, replacing the route's: a Permission class, or an instance such as `new Can('edit_posts')`. - * - * @throws InvalidArgumentException If an invalid HTTP method is provided. - */ - public function __construct( - public array|string $methods, - public string|Permission|null $permissionCallback = null - ) { - $this->methods = is_array($methods) ? $methods : [$methods]; - $this->validateMethods(); - } - - /** - * Validates the provided HTTP methods. - * - * @throws InvalidArgumentException If an invalid HTTP method is found. - */ - private function validateMethods(): void - { - foreach ($this->methods as $method) { - if (! in_array(strtoupper((string) $method), self::ALLOWED_METHODS, true)) { - throw new InvalidArgumentException( - sprintf( - 'Invalid HTTP method "%s". Allowed methods are: %s', - $method, - implode(', ', self::ALLOWED_METHODS) - ) - ); - } - } - } - - /** - * Retrieves the list of HTTP methods. - * - * @return array The list of allowed HTTP methods. - */ - public function getMethods(): array - { - return $this->methods; - } -} diff --git a/src/Attributes/WpRestRoute/Permission.php b/src/Attributes/WpRestRoute/Permission.php deleted file mode 100755 index f535a20a..00000000 --- a/src/Attributes/WpRestRoute/Permission.php +++ /dev/null @@ -1,20 +0,0 @@ - - */ - public function genericCapabilities(): array - { - $contributor = ['edit_posts', 'delete_posts']; - $author = ['publish_posts', 'edit_published_posts', 'delete_published_posts']; - $editor = ['edit_others_posts', 'delete_others_posts', 'read_private_posts', 'edit_private_posts', 'delete_private_posts']; - - return match ($this) { - self::Contributor => $contributor, - self::Author => [...$contributor, ...$author], - self::Editor => [...$contributor, ...$author, ...$editor], - }; - } -} diff --git a/src/Schedule/Every.php b/src/Schedule/Every.php deleted file mode 100644 index 73f89cef..00000000 --- a/src/Schedule/Every.php +++ /dev/null @@ -1,44 +0,0 @@ - 'hourly', - self::TWICE_DAILY => 'twicedaily', - self::DAY => 'daily', - self::WEEK => 'weekly', - self::MONTH => 'monthly', - self::YEAR => 'yearly', - }; - } - - public function isCustom(): bool - { - return in_array($this, [self::MONTH, self::YEAR], true); - } - - public function toInterval(): Interval - { - return match ($this) { - self::HOUR => new Interval(hours: 1, display: 'Once Hourly'), - self::TWICE_DAILY => new Interval(hours: 12, display: 'Twice Daily'), - self::DAY => new Interval(days: 1, display: 'Once Daily'), - self::WEEK => new Interval(weeks: 1, display: 'Once Weekly'), - self::MONTH => new Interval(days: 30, display: 'Once Monthly'), - self::YEAR => new Interval(days: 365, display: 'Once Yearly'), - }; - } -} diff --git a/src/Schedule/Interval.php b/src/Schedule/Interval.php deleted file mode 100644 index 448c2c91..00000000 --- a/src/Schedule/Interval.php +++ /dev/null @@ -1,34 +0,0 @@ - $this->totalSeconds(), - 'display' => $this->display, - ]; - } - - public function totalSeconds(): int - { - return $this->seconds - + $this->minutes * 60 - + $this->hours * 3600 - + $this->days * 86400 - + $this->weeks * 604800; - } -} diff --git a/tests/Unit/Attributes/ArchitectureTest.php b/tests/Unit/Attributes/ArchitectureTest.php deleted file mode 100644 index 2cb2cff2..00000000 --- a/tests/Unit/Attributes/ArchitectureTest.php +++ /dev/null @@ -1,38 +0,0 @@ -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/HookTest.php b/tests/Unit/Attributes/HookTest.php deleted file mode 100644 index c153b323..00000000 --- a/tests/Unit/Attributes/HookTest.php +++ /dev/null @@ -1,56 +0,0 @@ -hook)->toBe('test_hook'); - expect($hook->priority)->toBe(10); -}); - -it('initializes with custom priority', function (): void { - $hook = new ConcreteHook('test_hook', 20); - expect($hook->hook)->toBe('test_hook'); - expect($hook->priority)->toBe(20); -}); - -it('stores hook name correctly', function (): void { - $hookName = 'custom_hook_name'; - $hook = new ConcreteHook($hookName); - expect($hook->hook)->toBe($hookName); -}); - -it('allows priority to be a negative number', function (): void { - $priority = -1; - $hook = new ConcreteHook('test_hook', $priority); - expect($hook->priority)->toBe($priority); -}); - -it('allows priority to be zero', function (): void { - $priority = 0; - $hook = new ConcreteHook('test_hook', $priority); - expect($hook->priority)->toBe($priority); -}); - -it('allows priority to be a large number', function (): void { - $priority = 9999; - $hook = new ConcreteHook('test_hook', $priority); - expect($hook->priority)->toBe($priority); -}); - -afterEach(function (): void { - Mockery::close(); - Facade::clearResolvedInstances(); -}); diff --git a/tests/Unit/Attributes/NamingTest.php b/tests/Unit/Attributes/NamingTest.php deleted file mode 100644 index c0d9c143..00000000 --- a/tests/Unit/Attributes/NamingTest.php +++ /dev/null @@ -1,49 +0,0 @@ - '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/PostTypeAttributeTest.php b/tests/Unit/Attributes/PostTypeAttributeTest.php deleted file mode 100644 index 2c0cd6b9..00000000 --- a/tests/Unit/Attributes/PostTypeAttributeTest.php +++ /dev/null @@ -1,344 +0,0 @@ - 'Test Product']; - } -} - -it('creates PostType attribute with all parameters', function (): void { - $postType = new PostType('custom-slug', 'Custom Type', 'Custom Types'); - - expect($postType->slug)->toBe('custom-slug'); - expect($postType->singular)->toBe('Custom Type'); - expect($postType->plural)->toBe('Custom Types'); -}); - -it('creates PostType attribute with only slug', function (): void { - $postType = new PostType('events'); - - expect($postType->slug)->toBe('events'); - expect($postType->singular)->toBeNull(); - expect($postType->plural)->toBeNull(); -}); - -it('creates PostType attribute with no parameters', function (): void { - $postType = new PostType; - - expect($postType->slug)->toBeNull(); - expect($postType->singular)->toBeNull(); - expect($postType->plural)->toBeNull(); -}); - -it('stores PostType properties as readonly', function (): void { - $postType = new PostType('test'); - - expect($postType->slug)->toBe('test'); - - // Verify properties are readonly - $reflection = new ReflectionClass($postType); - $slugProperty = $reflection->getProperty('slug'); - $singularProperty = $reflection->getProperty('singular'); - $pluralProperty = $reflection->getProperty('plural'); - - expect($slugProperty->isReadOnly())->toBeTrue(); - expect($singularProperty->isReadOnly())->toBeTrue(); - expect($pluralProperty->isReadOnly())->toBeTrue(); -}); - -it('creates HasArchive attribute with default value', function (): void { - $hasArchive = new HasArchive; - - expect($hasArchive->value)->toBeTrue(); -}); - -it('creates HasArchive attribute with custom slug', function (): void { - $hasArchive = new HasArchive('custom-archive'); - - expect($hasArchive->value)->toBe('custom-archive'); -}); - -it('creates HasArchive attribute disabled', function (): void { - $hasArchive = new HasArchive(false); - - expect($hasArchive->value)->toBeFalse(); -}); - -it('creates Supports attribute with default features', function (): void { - $supports = new Supports; - - expect($supports->features)->toBe(['title', 'editor']); -}); - -it('creates Supports attribute with custom features', function (): void { - $features = ['title', 'editor', 'thumbnail', 'excerpt']; - $supports = new Supports($features); - - expect($supports->features)->toBe($features); -}); - -it('creates MenuIcon attribute with dashicon', function (): void { - $menuIcon = new MenuIcon('dashicons-admin-post'); - - expect($menuIcon->value)->toBe('dashicons-admin-post'); -}); - -it('creates MenuIcon attribute with custom URL', function (): void { - $menuIcon = new MenuIcon('https://example.com/icon.png'); - - expect($menuIcon->value)->toBe('https://example.com/icon.png'); -}); - -it('creates PubliclyQueryable attribute with default value', function (): void { - $publiclyQueryable = new PubliclyQueryable; - - expect($publiclyQueryable->value)->toBeTrue(); -}); - -it('creates PubliclyQueryable attribute disabled', function (): void { - $publiclyQueryable = new PubliclyQueryable(false); - - expect($publiclyQueryable->value)->toBeFalse(); -}); - -it('creates AdminCol attribute with all parameters', function (): void { - $adminCol = new AdminCol( - 'price', - 'Product Price', - sortable: true, - titleIcon: 'dashicons-money', - dateFormat: 'd/m/Y', - link: 'edit', - cap: 'edit_posts', - default: 'ASC', - width: 120 - ); - - expect($adminCol->key)->toBe('price'); - expect($adminCol->title)->toBe('Product Price'); - expect($adminCol->width)->toBe(120); - expect($adminCol->sortable)->toBeTrue(); - expect($adminCol->titleIcon)->toBe('dashicons-money'); - expect($adminCol->dateFormat)->toBe('d/m/Y'); - expect($adminCol->link)->toBe('edit'); - expect($adminCol->cap)->toBe('edit_posts'); - expect($adminCol->default)->toBe('ASC'); -}); - -it('creates AdminCol attribute with minimal parameters', function (): void { - $adminCol = new AdminCol('title', 'Title'); - - expect($adminCol->key)->toBe('title'); - expect($adminCol->title)->toBe('Title'); - expect($adminCol->width)->toBeNull(); - expect($adminCol->sortable)->toBeFalse(); - expect($adminCol->titleIcon)->toBeNull(); - expect($adminCol->dateFormat)->toBeNull(); - expect($adminCol->link)->toBeNull(); - expect($adminCol->cap)->toBeNull(); - expect($adminCol->default)->toBeNull(); -}); - -it('creates AdminCol attribute for meta fields', function (): void { - $adminCol = new AdminCol( - 'custom_price', - 'Price', - sortable: 'meta_value_num', - metaKey: 'product_price' - ); - - expect($adminCol->key)->toBe('custom_price'); - expect($adminCol->title)->toBe('Price'); - expect($adminCol->sortable)->toBe('meta_value_num'); - expect($adminCol->metaKey)->toBe('product_price'); -}); - -it('creates AdminCol attribute for taxonomy fields', function (): void { - $adminCol = new AdminCol( - 'categories', - 'Categories', - taxonomy: 'product_category' - ); - - expect($adminCol->key)->toBe('categories'); - expect($adminCol->title)->toBe('Categories'); - expect($adminCol->taxonomy)->toBe('product_category'); -}); - -it('creates AdminCol attribute for featured image', function (): void { - $adminCol = new AdminCol( - 'image', - 'Featured Image', - featuredImage: 'thumbnail', - width: 80 - ); - - expect($adminCol->key)->toBe('image'); - expect($adminCol->title)->toBe('Featured Image'); - expect($adminCol->featuredImage)->toBe('thumbnail'); - expect($adminCol->width)->toBe(80); -}); - -it('creates RegisterMetaBoxCb attribute', function (): void { - $registerMetaBoxCb = new RegisterMetaBoxCb; - - expect($registerMetaBoxCb)->toBeInstanceOf(RegisterMetaBoxCb::class); -}); - -it('has correct PHP attribute configurations', function (): void { - // Test PostType attribute configuration - $postTypeReflection = new ReflectionClass(PostType::class); - $postTypeAttributes = $postTypeReflection->getAttributes(Attribute::class); - - expect($postTypeAttributes)->toHaveCount(1); - $postTypeAttribute = $postTypeAttributes[0]->newInstance(); - expect($postTypeAttribute->flags)->toBe(Attribute::TARGET_CLASS); - - // Test AdminCol attribute configuration - $adminColReflection = new ReflectionClass(AdminCol::class); - $adminColAttributes = $adminColReflection->getAttributes(Attribute::class); - - expect($adminColAttributes)->toHaveCount(1); - $adminColAttribute = $adminColAttributes[0]->newInstance(); - expect($adminColAttribute->flags)->toBe(Attribute::TARGET_METHOD); - - // Test RegisterMetaBoxCb attribute configuration - $registerMetaBoxCbReflection = new ReflectionClass(RegisterMetaBoxCb::class); - $registerMetaBoxCbAttributes = $registerMetaBoxCbReflection->getAttributes(Attribute::class); - - expect($registerMetaBoxCbAttributes)->toHaveCount(1); - $registerMetaBoxCbAttribute = $registerMetaBoxCbAttributes[0]->newInstance(); - expect($registerMetaBoxCbAttribute->flags)->toBe(Attribute::TARGET_METHOD); -}); - -it('test class has correct attributes applied', function (): void { - $reflection = new ReflectionClass(TestProduct::class); - - // Check class-level attributes - $postTypeAttrs = $reflection->getAttributes(PostType::class); - expect($postTypeAttrs)->toHaveCount(1); - - $hasArchiveAttrs = $reflection->getAttributes(HasArchive::class); - expect($hasArchiveAttrs)->toHaveCount(1); - - $supportsAttrs = $reflection->getAttributes(Supports::class); - expect($supportsAttrs)->toHaveCount(1); - - // Check method-level attributes - $priceMethod = $reflection->getMethod('getPriceColumn'); - $priceAdminColAttrs = $priceMethod->getAttributes(AdminCol::class); - expect($priceAdminColAttrs)->toHaveCount(1); - - $stockMethod = $reflection->getMethod('getStockColumn'); - $stockAdminColAttrs = $stockMethod->getAttributes(AdminCol::class); - expect($stockAdminColAttrs)->toHaveCount(1); - - $metaBoxMethod = $reflection->getMethod('registerProductMetaBoxes'); - $metaBoxAttrs = $metaBoxMethod->getAttributes(RegisterMetaBoxCb::class); - expect($metaBoxAttrs)->toHaveCount(1); -}); - -it('attribute values can be extracted correctly', function (): void { - $reflection = new ReflectionClass(TestProduct::class); - - // Extract PostType attribute values - $postTypeAttr = $reflection->getAttributes(PostType::class)[0]; - $postType = $postTypeAttr->newInstance(); - expect($postType->slug)->toBe('test-product'); - expect($postType->singular)->toBe('Product'); - expect($postType->plural)->toBe('Products'); - - // Extract HasArchive attribute value - $hasArchiveAttr = $reflection->getAttributes(HasArchive::class)[0]; - $hasArchive = $hasArchiveAttr->newInstance(); - expect($hasArchive->value)->toBeTrue(); - - // Extract Supports attribute values - $supportsAttr = $reflection->getAttributes(Supports::class)[0]; - $supports = $supportsAttr->newInstance(); - expect($supports->features)->toBe(['title', 'editor', 'thumbnail']); - - // Extract AdminCol attribute values - $priceMethod = $reflection->getMethod('getPriceColumn'); - $adminColAttr = $priceMethod->getAttributes(AdminCol::class)[0]; - $adminCol = $adminColAttr->newInstance(); - expect($adminCol->key)->toBe('price'); - expect($adminCol->title)->toBe('Price'); -}); - -it('accepts all types without validation', function (): void { - // No validation should happen in attribute constructors - // All validation is now handled by PostTypeDiscovery - - // PostType with any values - expect(fn (): PostType => new PostType('', '', ''))->not->toThrow(Exception::class); - expect(fn (): PostType => new PostType('invalid slug', 'invalid name', 'invalid plural'))->not->toThrow(Exception::class); - - // HasArchive with any values - expect(fn (): HasArchive => new HasArchive(''))->not->toThrow(Exception::class); - expect(fn (): HasArchive => new HasArchive('invalid-archive'))->not->toThrow(Exception::class); - - // Supports with any array - expect(fn (): Supports => new Supports([]))->not->toThrow(Exception::class); - expect(fn (): Supports => new Supports(['invalid-feature']))->not->toThrow(Exception::class); - - // MenuIcon with any string - expect(fn (): MenuIcon => new MenuIcon(''))->not->toThrow(Exception::class); - expect(fn (): MenuIcon => new MenuIcon('invalid-icon'))->not->toThrow(Exception::class); - - // AdminCol with any values - expect(fn (): AdminCol => new AdminCol('', ''))->not->toThrow(Exception::class); -}); - -test('resolveSlug returns the explicit slug', function (): void { - expect((new PostType('synthese-presse'))->resolveSlug('App\\Cms\\PostTypes\\SyntheseDePresse')) - ->toBe('synthese-presse'); -}); - -test('resolveSlug derives the slug from the class name, cut to 20 characters', function (): void { - expect((new PostType)->resolveSlug('App\\Cms\\PostTypes\\SyntheseDePresse'))->toBe('synthese-de-presse') - ->and((new PostType)->resolveSlug('App\\VeryLongPostTypeClassName'))->toBe('very-long-post-type-'); -}); diff --git a/tests/Unit/Attributes/PostTypeSubAttributesTest.php b/tests/Unit/Attributes/PostTypeSubAttributesTest.php deleted file mode 100644 index ae2e589c..00000000 --- a/tests/Unit/Attributes/PostTypeSubAttributesTest.php +++ /dev/null @@ -1,271 +0,0 @@ -getAttributes(PostType::class); - expect($postTypeAttrs)->toHaveCount(1); - - // Should detect HasArchive sub-attribute - $hasArchiveAttrs = $reflection->getAttributes(HasArchive::class); - expect($hasArchiveAttrs)->toHaveCount(1); - - // Should detect Supports sub-attribute - $supportsAttrs = $reflection->getAttributes(Supports::class); - expect($supportsAttrs)->toHaveCount(1); - - // Should detect MenuIcon sub-attribute - $menuIconAttrs = $reflection->getAttributes(MenuIcon::class); - expect($menuIconAttrs)->toHaveCount(1); -}); - -it('detects all method-level AdminCol attributes', function (): void { - $reflection = new ReflectionClass(ProductWithSubAttributes::class); - - // Check price method - $priceMethod = $reflection->getMethod('getPriceColumn'); - $priceAttrs = $priceMethod->getAttributes(AdminCol::class); - expect($priceAttrs)->toHaveCount(1); - - // Check stock method - $stockMethod = $reflection->getMethod('getStockColumn'); - $stockAttrs = $stockMethod->getAttributes(AdminCol::class); - expect($stockAttrs)->toHaveCount(1); - - // Check category method - $categoryMethod = $reflection->getMethod('getCategoryColumn'); - $categoryAttrs = $categoryMethod->getAttributes(AdminCol::class); - expect($categoryAttrs)->toHaveCount(1); - - // Check that method without attribute has no AdminCol attributes - $nameMethod = $reflection->getMethod('getProductName'); - $nameAttrs = $nameMethod->getAttributes(AdminCol::class); - expect($nameAttrs)->toHaveCount(0); -}); - -it('extracts correct values from class-level attributes', function (): void { - $reflection = new ReflectionClass(ProductWithSubAttributes::class); - - // Extract PostType attribute - $postTypeAttr = $reflection->getAttributes(PostType::class)[0]; - $postType = $postTypeAttr->newInstance(); - expect($postType->slug)->toBe('product'); - expect($postType->singular)->toBeNull(); - expect($postType->plural)->toBeNull(); - - // Extract HasArchive attribute - $hasArchiveAttr = $reflection->getAttributes(HasArchive::class)[0]; - $hasArchive = $hasArchiveAttr->newInstance(); - expect($hasArchive->value)->toBe('products-archive'); - - // Extract Supports attribute - $supportsAttr = $reflection->getAttributes(Supports::class)[0]; - $supports = $supportsAttr->newInstance(); - expect($supports->features)->toBe(['title', 'editor', 'thumbnail', 'excerpt']); - - // Extract MenuIcon attribute - $menuIconAttr = $reflection->getAttributes(MenuIcon::class)[0]; - $menuIcon = $menuIconAttr->newInstance(); - expect($menuIcon->value)->toBe('dashicons-cart'); -}); - -it('extracts correct values from method-level AdminCol attributes', function (): void { - $reflection = new ReflectionClass(ProductWithSubAttributes::class); - - // Extract price AdminCol - $priceMethod = $reflection->getMethod('getPriceColumn'); - $priceAttr = $priceMethod->getAttributes(AdminCol::class)[0]; - $priceAdminCol = $priceAttr->newInstance(); - expect($priceAdminCol->key)->toBe('price'); - expect($priceAdminCol->title)->toBe('Price'); - expect($priceAdminCol->width)->toBeNull(); // Default value - expect($priceAdminCol->sortable)->toBeFalse(); // Default value - - // Extract stock AdminCol - $stockMethod = $reflection->getMethod('getStockColumn'); - $stockAttr = $stockMethod->getAttributes(AdminCol::class)[0]; - $stockAdminCol = $stockAttr->newInstance(); - expect($stockAdminCol->key)->toBe('stock'); - expect($stockAdminCol->title)->toBe('Stock Status'); - expect($stockAdminCol->width)->toBeNull(); // Default value - expect($stockAdminCol->sortable)->toBeFalse(); // Default value - - // Extract category AdminCol - $categoryMethod = $reflection->getMethod('getCategoryColumn'); - $categoryAttr = $categoryMethod->getAttributes(AdminCol::class)[0]; - $categoryAdminCol = $categoryAttr->newInstance(); - expect($categoryAdminCol->key)->toBe('category'); - expect($categoryAdminCol->title)->toBe('Category'); - expect($categoryAdminCol->width)->toBeNull(); // Default value - expect($categoryAdminCol->sortable)->toBeFalse(); // Default value -}); - -it('supports multiple AdminCol attributes on different methods', function (): void { - $reflection = new ReflectionClass(ProductWithSubAttributes::class); - $adminColMethods = []; - - foreach ($reflection->getMethods() as $method) { - $adminColAttrs = $method->getAttributes(AdminCol::class); - if (! empty($adminColAttrs)) { - $adminColMethods[$method->getName()] = $adminColAttrs[0]->newInstance(); - } - } - - expect($adminColMethods)->toHaveCount(3); - expect($adminColMethods)->toHaveKeys(['getPriceColumn', 'getStockColumn', 'getCategoryColumn']); - - // Verify each column has unique key - $keys = array_map(fn (AdminCol $adminCol): string => $adminCol->key, $adminColMethods); - expect($keys)->toBe(['getPriceColumn' => 'price', 'getStockColumn' => 'stock', 'getCategoryColumn' => 'category']); - expect(array_unique($keys))->toHaveCount(3); // All keys are unique -}); - -it('demonstrates attribute composition pattern', function (): void { - // This test demonstrates how the new system works: - // 1. PostType attribute defines the main post type - // 2. Sub-attributes (HasArchive, Supports, MenuIcon) configure post type settings - // 3. Method-level attributes (AdminCol) define callbacks and additional functionality - - $reflection = new ReflectionClass(ProductWithSubAttributes::class); - - // Main post type definition - $postTypeAttr = $reflection->getAttributes(PostType::class)[0]->newInstance(); - expect($postTypeAttr->slug)->toBe('product'); - - // Archive configuration - $hasArchiveAttr = $reflection->getAttributes(HasArchive::class)[0]->newInstance(); - expect($hasArchiveAttr->value)->toBe('products-archive'); - - // Features configuration - $supportsAttr = $reflection->getAttributes(Supports::class)[0]->newInstance(); - expect($supportsAttr->features)->toContain('title', 'editor', 'thumbnail', 'excerpt'); - - // Admin UI configuration - $menuIconAttr = $reflection->getAttributes(MenuIcon::class)[0]->newInstance(); - expect($menuIconAttr->value)->toBe('dashicons-cart'); - - // Admin columns configuration (method-level) - $columnMethods = []; - foreach ($reflection->getMethods() as $method) { - $adminColAttrs = $method->getAttributes(AdminCol::class); - if (! empty($adminColAttrs)) { - $columnMethods[] = [ - 'method' => $method->getName(), - 'column' => $adminColAttrs[0]->newInstance(), - ]; - } - } - - expect($columnMethods)->toHaveCount(3); - - // Verify the composition creates a complete post type configuration - $allClassAttributes = $reflection->getAttributes(); - $allMethodAttributes = []; - foreach ($reflection->getMethods() as $method) { - $allMethodAttributes = array_merge($allMethodAttributes, $method->getAttributes()); - } - - // Should have class-level attributes for configuration - expect($allClassAttributes)->toHaveCount(4); // PostType + 3 sub-attributes - - // Should have method-level attributes for callbacks - expect($allMethodAttributes)->toHaveCount(3); // 3 AdminCol attributes -}); - -it('attributes have correct target configurations', function (): void { - // Verify class-level attributes target classes - $classLevelAttributes = [ - PostType::class, - HasArchive::class, - Supports::class, - MenuIcon::class, - ]; - - foreach ($classLevelAttributes as $attributeClass) { - $reflection = new ReflectionClass($attributeClass); - $attributes = $reflection->getAttributes(Attribute::class); - expect($attributes)->toHaveCount(1); - - $attribute = $attributes[0]->newInstance(); - expect($attribute->flags)->toBe(Attribute::TARGET_CLASS); - } - - // Verify method-level attributes target methods - $methodLevelAttributes = [ - AdminCol::class, - ]; - - foreach ($methodLevelAttributes as $attributeClass) { - $reflection = new ReflectionClass($attributeClass); - $attributes = $reflection->getAttributes(Attribute::class); - expect($attributes)->toHaveCount(1); - - $attribute = $attributes[0]->newInstance(); - expect($attribute->flags)->toBe(Attribute::TARGET_METHOD); - } -}); - -it('demonstrates no validation in attributes', function (): void { - // All attributes should accept any values without validation - // Validation will be handled by PostTypeDiscovery - - expect(fn (): PostType => new PostType('', '', ''))->not->toThrow(Exception::class); - expect(fn (): HasArchive => new HasArchive(''))->not->toThrow(Exception::class); - expect(fn (): Supports => new Supports([]))->not->toThrow(Exception::class); - expect(fn (): MenuIcon => new MenuIcon(''))->not->toThrow(Exception::class); - expect(fn (): AdminCol => new AdminCol('', ''))->not->toThrow(Exception::class); - - // Even completely invalid values should not throw - expect(fn (): PostType => new PostType('invalid slug with spaces', 'inv@lid', 'pl{ur}al'))->not->toThrow(Exception::class); - expect(fn (): HasArchive => new HasArchive(123))->not->toThrow(Exception::class); // Wrong type - expect(fn (): Supports => new Supports(['invalid-feature', '', null]))->not->toThrow(Exception::class); - expect(fn (): MenuIcon => new MenuIcon(null))->not->toThrow(Exception::class); // Wrong type -}); diff --git a/tests/Unit/Attributes/ScheduleTest.php b/tests/Unit/Attributes/ScheduleTest.php deleted file mode 100644 index 97843335..00000000 --- a/tests/Unit/Attributes/ScheduleTest.php +++ /dev/null @@ -1,158 +0,0 @@ -toBeInstanceOf(Schedule::class); - expect($schedule->recurrence)->toBe('daily'); - expect($schedule->hook)->toBeNull(); - expect($schedule->args)->toBe([]); -}); - -it('creates Schedule attribute with custom hook name', function (): void { - $schedule = new Schedule('hourly', 'custom_hook_name'); - - expect($schedule->recurrence)->toBe('hourly'); - expect($schedule->hook)->toBe('custom_hook_name'); - expect($schedule->args)->toBe([]); -}); - -it('creates Schedule attribute with arguments', function (): void { - $args = ['type' => 'full', 'force' => true]; - $schedule = new Schedule('daily', null, $args); - - expect($schedule->recurrence)->toBe('daily'); - expect($schedule->hook)->toBeNull(); - expect($schedule->args)->toBe($args); -}); - -it('creates Schedule attribute with all parameters', function (): void { - $args = ['batch_size' => 100]; - $schedule = new Schedule('weekly', 'weekly_cleanup', $args); - - expect($schedule->recurrence)->toBe('weekly'); - expect($schedule->hook)->toBe('weekly_cleanup'); - expect($schedule->args)->toBe($args); -}); - -it('creates Schedule attribute with array recurrence', function (): void { - $recurrence = ['interval' => 3600, 'display' => 'Every Hour']; - $schedule = new Schedule($recurrence); - - expect($schedule->recurrence)->toBe($recurrence); - expect($schedule->hook)->toBeNull(); - expect($schedule->args)->toBe([]); -}); - -it('creates Schedule attribute with Every enum', function (): void { - $schedule = new Schedule(Every::DAY); - - expect($schedule->recurrence)->toBe(Every::DAY); - expect($schedule->hook)->toBeNull(); - expect($schedule->args)->toBe([]); -}); - -it('creates Schedule attribute with Interval instance', function (): void { - $interval = new Interval(minutes: 30, hours: 2); - $schedule = new Schedule($interval); - - expect($schedule->recurrence)->toBe($interval); - expect($schedule->hook)->toBeNull(); - expect($schedule->args)->toBe([]); -}); - -it('creates Schedule attribute with complex Every enum and parameters', function (): void { - $args = ['source' => 'api', 'limit' => 50]; - $schedule = new Schedule(Every::MONTH, 'monthly_sync', $args); - - expect($schedule->recurrence)->toBe(Every::MONTH); - expect($schedule->hook)->toBe('monthly_sync'); - expect($schedule->args)->toBe($args); -}); - -it('creates Schedule attribute with complex Interval and parameters', function (): void { - $interval = new Interval(minutes: 30, hours: 12, days: 1); - $args = ['cleanup_type' => 'deep']; - $schedule = new Schedule($interval, 'complex_cleanup', $args); - - expect($schedule->recurrence)->toBe($interval); - expect($schedule->hook)->toBe('complex_cleanup'); - expect($schedule->args)->toBe($args); -}); - -it('stores recurrence as readonly property', function (): void { - $schedule = new Schedule('daily'); - - expect($schedule->recurrence)->toBe('daily'); - - // Verify property is readonly by checking it cannot be modified - $reflection = new ReflectionClass($schedule); - $property = $reflection->getProperty('recurrence'); - expect($property->isReadOnly())->toBeTrue(); -}); - -it('stores hook as readonly property', function (): void { - $schedule = new Schedule('daily', 'test_hook'); - - expect($schedule->hook)->toBe('test_hook'); - - // Verify property is readonly - $reflection = new ReflectionClass($schedule); - $property = $reflection->getProperty('hook'); - expect($property->isReadOnly())->toBeTrue(); -}); - -it('stores args as readonly property', function (): void { - $args = ['key' => 'value']; - $schedule = new Schedule('daily', null, $args); - - expect($schedule->args)->toBe($args); - - // Verify property is readonly - $reflection = new ReflectionClass($schedule); - $property = $reflection->getProperty('args'); - expect($property->isReadOnly())->toBeTrue(); -}); - -it('has correct PHP attribute configuration', function (): void { - $reflection = new ReflectionClass(Schedule::class); - $attributes = $reflection->getAttributes(Attribute::class); - - expect($attributes)->toHaveCount(1); - - $attribute = $attributes[0]->newInstance(); - expect($attribute->flags)->toBe(Attribute::TARGET_METHOD); -}); - -it('accepts all supported recurrence types without validation', function (): void { - // No validation should happen in the attribute constructor - // All validation is now handled by ScheduleDiscovery - - // String recurrence - expect(fn (): Schedule => new Schedule('daily'))->not->toThrow(Exception::class); - expect(fn (): Schedule => new Schedule('invalid_schedule'))->not->toThrow(Exception::class); // No validation - - // Array recurrence - expect(fn (): Schedule => new Schedule(['interval' => 3600, 'display' => 'Valid']))->not->toThrow(Exception::class); - expect(fn (): Schedule => new Schedule(['invalid' => 'array']))->not->toThrow(Exception::class); // No validation - - // Every enum - expect(fn (): Schedule => new Schedule(Every::HOUR))->not->toThrow(Exception::class); - expect(fn (): Schedule => new Schedule(Every::MONTH))->not->toThrow(Exception::class); - - // Interval instance - $interval = new Interval(minutes: 30); - expect(fn (): Schedule => new Schedule($interval))->not->toThrow(Exception::class); -}); diff --git a/tests/Unit/Attributes/TaxonomySubAttributesTest.php b/tests/Unit/Attributes/TaxonomySubAttributesTest.php deleted file mode 100644 index 3838bb40..00000000 --- a/tests/Unit/Attributes/TaxonomySubAttributesTest.php +++ /dev/null @@ -1,313 +0,0 @@ -Custom taxonomy meta box

'; - } - - #[UpdateCountCallback] - public function updateTermCount($terms, $taxonomy): void - { - // Custom count update logic - foreach ($terms as $term) { - // Update count logic here - } - } - - #[MetaBoxSanitizeCb] - public function sanitizeTerms($terms): array - { - // Sanitize the terms - return array_map(sanitize_text_field(...), (array) $terms); - } - - public function getTaxonomyName(): string - { - return 'Product Category'; - } -} - -it('detects all class-level Taxonomy attributes', function (): void { - $reflection = new ReflectionClass(ProductCategoryWithSubAttributes::class); - - // Should detect Taxonomy main attribute - $taxonomyAttrs = $reflection->getAttributes(Taxonomy::class); - expect($taxonomyAttrs)->toHaveCount(1); - - // Should detect Hierarchical sub-attribute - $hierarchicalAttrs = $reflection->getAttributes(Hierarchical::class); - expect($hierarchicalAttrs)->toHaveCount(1); - - // Should detect PublicTaxonomy sub-attribute - $publicAttrs = $reflection->getAttributes(PublicTaxonomy::class); - expect($publicAttrs)->toHaveCount(1); - - // Should detect ShowUI sub-attribute - $showUIAttrs = $reflection->getAttributes(ShowUI::class); - expect($showUIAttrs)->toHaveCount(1); - - // Should detect ShowInRest sub-attribute - $showInRestAttrs = $reflection->getAttributes(ShowInRest::class); - expect($showInRestAttrs)->toHaveCount(1); - - // Should detect ObjectType sub-attribute - $objectTypeAttrs = $reflection->getAttributes(ObjectType::class); - expect($objectTypeAttrs)->toHaveCount(1); -}); - -it('detects all method-level callback attributes', function (): void { - $reflection = new ReflectionClass(ProductCategoryWithSubAttributes::class); - - // Check custom meta box method - $metaBoxMethod = $reflection->getMethod('customMetaBox'); - $metaBoxAttrs = $metaBoxMethod->getAttributes(MetaBoxCb::class); - expect($metaBoxAttrs)->toHaveCount(1); - - // Check update count method - $updateCountMethod = $reflection->getMethod('updateTermCount'); - $updateCountAttrs = $updateCountMethod->getAttributes(UpdateCountCallback::class); - expect($updateCountAttrs)->toHaveCount(1); - - // Check sanitize method - $sanitizeMethod = $reflection->getMethod('sanitizeTerms'); - $sanitizeAttrs = $sanitizeMethod->getAttributes(MetaBoxSanitizeCb::class); - expect($sanitizeAttrs)->toHaveCount(1); - - // Check that method without attribute has no callback attributes - $nameMethod = $reflection->getMethod('getTaxonomyName'); - $nameCallbackAttrs = array_merge( - $nameMethod->getAttributes(MetaBoxCb::class), - $nameMethod->getAttributes(UpdateCountCallback::class), - $nameMethod->getAttributes(MetaBoxSanitizeCb::class) - ); - expect($nameCallbackAttrs)->toHaveCount(0); -}); - -it('extracts correct values from class-level attributes', function (): void { - $reflection = new ReflectionClass(ProductCategoryWithSubAttributes::class); - - // Extract Taxonomy attribute - $taxonomyAttr = $reflection->getAttributes(Taxonomy::class)[0]; - $taxonomy = $taxonomyAttr->newInstance(); - expect($taxonomy->slug)->toBe('product-category'); - expect($taxonomy->singular)->toBeNull(); - expect($taxonomy->plural)->toBeNull(); - expect($taxonomy->objectType)->toBeNull(); - - // Test that attributes can be instantiated successfully - $hierarchicalAttr = $reflection->getAttributes(Hierarchical::class)[0]; - $hierarchical = $hierarchicalAttr->newInstance(); - expect($hierarchical)->toBeInstanceOf(Hierarchical::class); - - $publicAttr = $reflection->getAttributes(PublicTaxonomy::class)[0]; - $public = $publicAttr->newInstance(); - expect($public)->toBeInstanceOf(PublicTaxonomy::class); - - $showUIAttr = $reflection->getAttributes(ShowUI::class)[0]; - $showUI = $showUIAttr->newInstance(); - expect($showUI)->toBeInstanceOf(ShowUI::class); - - $showInRestAttr = $reflection->getAttributes(ShowInRest::class)[0]; - $showInRest = $showInRestAttr->newInstance(); - expect($showInRest)->toBeInstanceOf(ShowInRest::class); - - $objectTypeAttr = $reflection->getAttributes(ObjectType::class)[0]; - $objectType = $objectTypeAttr->newInstance(); - expect($objectType)->toBeInstanceOf(ObjectType::class); -}); - -it('extracts correct values from method-level callback attributes', function (): void { - $reflection = new ReflectionClass(ProductCategoryWithSubAttributes::class); - - // Extract MetaBoxCb - $metaBoxMethod = $reflection->getMethod('customMetaBox'); - $metaBoxAttr = $metaBoxMethod->getAttributes(MetaBoxCb::class)[0]; - $metaBoxCb = $metaBoxAttr->newInstance(); - expect($metaBoxCb)->toBeInstanceOf(MetaBoxCb::class); - - // Extract UpdateCountCallback - $updateCountMethod = $reflection->getMethod('updateTermCount'); - $updateCountAttr = $updateCountMethod->getAttributes(UpdateCountCallback::class)[0]; - $updateCountCb = $updateCountAttr->newInstance(); - expect($updateCountCb)->toBeInstanceOf(UpdateCountCallback::class); - - // Extract MetaBoxSanitizeCb - $sanitizeMethod = $reflection->getMethod('sanitizeTerms'); - $sanitizeAttr = $sanitizeMethod->getAttributes(MetaBoxSanitizeCb::class)[0]; - $sanitizeCb = $sanitizeAttr->newInstance(); - expect($sanitizeCb)->toBeInstanceOf(MetaBoxSanitizeCb::class); -}); - -it('supports multiple callback attributes on different methods', function (): void { - $reflection = new ReflectionClass(ProductCategoryWithSubAttributes::class); - $callbackMethods = []; - - foreach ($reflection->getMethods() as $method) { - $callbackAttrs = array_merge( - $method->getAttributes(MetaBoxCb::class), - $method->getAttributes(UpdateCountCallback::class), - $method->getAttributes(MetaBoxSanitizeCb::class) - ); - - if ($callbackAttrs !== []) { - $callbackMethods[$method->getName()] = $callbackAttrs[0]->newInstance(); - } - } - - expect($callbackMethods)->toHaveCount(3); - expect($callbackMethods)->toHaveKeys(['customMetaBox', 'updateTermCount', 'sanitizeTerms']); - - // Verify each callback has correct type - expect($callbackMethods['customMetaBox'])->toBeInstanceOf(MetaBoxCb::class); - expect($callbackMethods['updateTermCount'])->toBeInstanceOf(UpdateCountCallback::class); - expect($callbackMethods['sanitizeTerms'])->toBeInstanceOf(MetaBoxSanitizeCb::class); -}); - -it('demonstrates attribute composition pattern for taxonomies', function (): void { - // This test demonstrates how the taxonomy attribute system works: - // 1. Taxonomy attribute defines the main taxonomy - // 2. Sub-attributes (Hierarchical, PublicTaxonomy, ShowUI, etc.) configure taxonomy settings - // 3. Method-level attributes (MetaBoxCb, UpdateCountCallback, etc.) define callbacks - - $reflection = new ReflectionClass(ProductCategoryWithSubAttributes::class); - - // Main taxonomy definition - $taxonomyAttr = $reflection->getAttributes(Taxonomy::class)[0]->newInstance(); - expect($taxonomyAttr->slug)->toBe('product-category'); - - // Test attribute instantiation - $hierarchicalAttr = $reflection->getAttributes(Hierarchical::class)[0]->newInstance(); - expect($hierarchicalAttr)->toBeInstanceOf(Hierarchical::class); - - $publicAttr = $reflection->getAttributes(PublicTaxonomy::class)[0]->newInstance(); - expect($publicAttr)->toBeInstanceOf(PublicTaxonomy::class); - - $showUIAttr = $reflection->getAttributes(ShowUI::class)[0]->newInstance(); - expect($showUIAttr)->toBeInstanceOf(ShowUI::class); - - $showInRestAttr = $reflection->getAttributes(ShowInRest::class)[0]->newInstance(); - expect($showInRestAttr)->toBeInstanceOf(ShowInRest::class); - - $objectTypeAttr = $reflection->getAttributes(ObjectType::class)[0]->newInstance(); - expect($objectTypeAttr)->toBeInstanceOf(ObjectType::class); - - // Callback methods configuration (method-level) - $callbackMethods = []; - foreach ($reflection->getMethods() as $method) { - $callbackAttrs = array_merge( - $method->getAttributes(MetaBoxCb::class), - $method->getAttributes(UpdateCountCallback::class), - $method->getAttributes(MetaBoxSanitizeCb::class) - ); - - if ($callbackAttrs !== []) { - $callbackMethods[] = [ - 'method' => $method->getName(), - 'attribute' => $callbackAttrs[0]->newInstance(), - ]; - } - } - - expect($callbackMethods)->toHaveCount(3); - - // Verify the composition creates a complete taxonomy configuration - $allClassAttributes = $reflection->getAttributes(); - $allMethodAttributes = []; - foreach ($reflection->getMethods() as $method) { - $allMethodAttributes = array_merge($allMethodAttributes, $method->getAttributes()); - } - - // Should have class-level attributes for configuration - expect($allClassAttributes)->toHaveCount(6); // Taxonomy + 5 sub-attributes - - // Should have method-level attributes for callbacks - expect($allMethodAttributes)->toHaveCount(3); // 3 callback attributes -}); - -it('attributes have correct target configurations', function (): void { - // Verify class-level attributes target classes - $classLevelAttributes = [ - Taxonomy::class, - Hierarchical::class, - PublicTaxonomy::class, - ShowUI::class, - ShowInRest::class, - ObjectType::class, - ]; - - foreach ($classLevelAttributes as $attributeClass) { - $reflection = new ReflectionClass($attributeClass); - $attributes = $reflection->getAttributes(Attribute::class); - expect($attributes)->toHaveCount(1); - - $attribute = $attributes[0]->newInstance(); - expect($attribute->flags)->toBe(Attribute::TARGET_CLASS); - } - - // Verify method-level attributes target methods - $methodLevelAttributes = [ - MetaBoxCb::class, - UpdateCountCallback::class, - MetaBoxSanitizeCb::class, - ]; - - foreach ($methodLevelAttributes as $attributeClass) { - $reflection = new ReflectionClass($attributeClass); - $attributes = $reflection->getAttributes(Attribute::class); - expect($attributes)->toHaveCount(1); - - $attribute = $attributes[0]->newInstance(); - expect($attribute->flags)->toBe(Attribute::TARGET_METHOD); - } -}); - -it('demonstrates no validation in attributes', function (): void { - // All attributes should accept any values without validation - // Validation will be handled by TaxonomyDiscovery - - expect(fn (): Taxonomy => new Taxonomy('', '', '', []))->not->toThrow(Throwable::class); - expect(fn (): Hierarchical => new Hierarchical(false))->not->toThrow(Throwable::class); - expect(fn (): PublicTaxonomy => new PublicTaxonomy(false))->not->toThrow(Throwable::class); - expect(fn (): ShowUI => new ShowUI(false))->not->toThrow(Throwable::class); - expect(fn (): ShowInRest => new ShowInRest(false))->not->toThrow(Throwable::class); - expect(fn (): ObjectType => new ObjectType([]))->not->toThrow(Throwable::class); - expect(fn (): MetaBoxCb => new MetaBoxCb)->not->toThrow(Throwable::class); - expect(fn (): UpdateCountCallback => new UpdateCountCallback)->not->toThrow(Throwable::class); - expect(fn (): MetaBoxSanitizeCb => new MetaBoxSanitizeCb)->not->toThrow(Throwable::class); - - // Even completely invalid values should not throw - expect(fn (): Taxonomy => new Taxonomy('invalid slug with spaces', 'inv@lid', 'pl{ur}al', 'invalid'))->not->toThrow(Throwable::class); - expect(fn (): Hierarchical => new Hierarchical('not-boolean'))->not->toThrow(Throwable::class); // Wrong type - expect(fn (): ObjectType => new ObjectType('not-array'))->not->toThrow(Throwable::class); // Wrong type -}); diff --git a/tests/Unit/Schedule/EveryTest.php b/tests/Unit/Schedule/EveryTest.php deleted file mode 100644 index 68386522..00000000 --- a/tests/Unit/Schedule/EveryTest.php +++ /dev/null @@ -1,64 +0,0 @@ -toScheduleKey())->toBe('hourly'); - expect(Every::TWICE_DAILY->toScheduleKey())->toBe('twicedaily'); - expect(Every::DAY->toScheduleKey())->toBe('daily'); - expect(Every::WEEK->toScheduleKey())->toBe('weekly'); - expect(Every::MONTH->toScheduleKey())->toBe('monthly'); - expect(Every::YEAR->toScheduleKey())->toBe('yearly'); - }); - - it('identifies custom schedules correctly', function (): void { - expect(Every::HOUR->isCustom())->toBeFalse(); - expect(Every::TWICE_DAILY->isCustom())->toBeFalse(); - expect(Every::DAY->isCustom())->toBeFalse(); - expect(Every::WEEK->isCustom())->toBeFalse(); - expect(Every::MONTH->isCustom())->toBeTrue(); - expect(Every::YEAR->isCustom())->toBeTrue(); - }); - - it('converts to correct Interval instances', function (): void { - $hourInterval = Every::HOUR->toInterval(); - expect($hourInterval)->toBeInstanceOf(Interval::class); - expect($hourInterval->totalSeconds())->toBe(3600); - - $twiceDailyInterval = Every::TWICE_DAILY->toInterval(); - expect($twiceDailyInterval->totalSeconds())->toBe(43200); - - $dayInterval = Every::DAY->toInterval(); - expect($dayInterval->totalSeconds())->toBe(86400); - - $weekInterval = Every::WEEK->toInterval(); - expect($weekInterval->totalSeconds())->toBe(604800); - - $monthInterval = Every::MONTH->toInterval(); - expect($monthInterval->totalSeconds())->toBe(2592000); - - $yearInterval = Every::YEAR->toInterval(); - expect($yearInterval->totalSeconds())->toBe(31536000); - }); - - it('provides meaningful display text in intervals', function (): void { - expect(Every::HOUR->toInterval()->display)->toBe('Once Hourly'); - expect(Every::DAY->toInterval()->display)->toBe('Once Daily'); - expect(Every::WEEK->toInterval()->display)->toBe('Once Weekly'); - expect(Every::MONTH->toInterval()->display)->toBe('Once Monthly'); - expect(Every::YEAR->toInterval()->display)->toBe('Once Yearly'); - }); - - it('generates valid schedule arrays for custom intervals', function (): void { - $monthArray = Every::MONTH->toInterval()->toScheduleArray(); - - expect($monthArray)->toHaveKeys(['interval', 'display']); - expect($monthArray['interval'])->toBeInt(); - expect($monthArray['interval'])->toBeGreaterThan(0); - expect($monthArray['display'])->toBeString(); - }); -}); diff --git a/tests/Unit/Schedule/IntervalTest.php b/tests/Unit/Schedule/IntervalTest.php deleted file mode 100644 index 5630b804..00000000 --- a/tests/Unit/Schedule/IntervalTest.php +++ /dev/null @@ -1,67 +0,0 @@ -totalSeconds())->toBe(30 + 300 + 7200 + 86400 + 604800); - }); - - it('returns zero for empty interval', function (): void { - $interval = new Interval; - - expect($interval->totalSeconds())->toBe(0); - }); - - it('calculates seconds from hours only', function (): void { - $interval = new Interval(hours: 1); - - expect($interval->totalSeconds())->toBe(3600); - }); - - it('calculates seconds from days only', function (): void { - $interval = new Interval(days: 30); - - expect($interval->totalSeconds())->toBe(2592000); - }); - - it('calculates seconds from weeks only', function (): void { - $interval = new Interval(weeks: 1); - - expect($interval->totalSeconds())->toBe(604800); - }); - - it('returns schedule array with interval and display', function (): void { - $interval = new Interval(hours: 12, display: 'Twice Daily'); - - $array = $interval->toScheduleArray(); - - expect($array)->toBe([ - 'interval' => 43200, - 'display' => 'Twice Daily', - ]); - }); - - it('uses default display text', function (): void { - $interval = new Interval(minutes: 30); - - expect($interval->display)->toBe('Custom schedule'); - }); - - it('is readonly', function (): void { - $reflection = new ReflectionClass(Interval::class); - - expect($reflection->isReadOnly())->toBeTrue(); - }); -}); diff --git a/tests/Unit/Support/SlugTest.php b/tests/Unit/Support/SlugTest.php new file mode 100644 index 00000000..3b662ca5 --- /dev/null +++ b/tests/Unit/Support/SlugTest.php @@ -0,0 +1,20 @@ +toBe('user-management') + ->and(Slug::fromClassName('Cache'))->toBe('cache'); +}); + +it('derives a subcommand from a method name', function (): void { + expect(Slug::fromMethodName('deleteById'))->toBe('delete-by-id') + ->and(Slug::toKebabCase('listItems'))->toBe('list-items'); +});