From 1a5cb3d7272bf9d0ff39b09951aac97a52a64672 Mon Sep 17 00:00:00 2001 From: Olivier Gorzalka Date: Mon, 26 Oct 2026 13:17:42 +0100 Subject: [PATCH] chore: adopt the Pollora harness in observe mode Profile core, observe mode: every gate runs and reports, none blocks. The WordPress PHPStan extension is included once, through the harness preset. --- .claude/settings.json | 27 +++ .github/workflows/harness.yml | 19 ++ .gitignore | 2 + .harness.json | 11 ++ AGENTS.md | 56 ++++++ api-surface.json | 325 ++++++++++++++++++++++++++++++++++ composer.json | 5 +- phpstan.neon | 2 +- rector.php | 11 ++ tests/Arch.php | 15 ++ 10 files changed, 471 insertions(+), 2 deletions(-) create mode 100644 .claude/settings.json create mode 100644 .github/workflows/harness.yml create mode 100644 .harness.json create mode 100644 AGENTS.md create mode 100644 api-surface.json create mode 100644 rector.php create mode 100644 tests/Arch.php diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..c8079f4 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,27 @@ +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Edit|Write", + "hooks": [ + { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/vendor/bin/pollora-qa hook pre-edit" } + ] + } + ], + "PostToolUse": [ + { + "matcher": "Edit|Write", + "hooks": [ + { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/vendor/bin/pollora-qa hook post-edit" } + ] + } + ], + "Stop": [ + { + "hooks": [ + { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/vendor/bin/pollora-qa hook stop" } + ] + } + ] + } +} diff --git a/.github/workflows/harness.yml b/.github/workflows/harness.yml new file mode 100644 index 0000000..784ecf5 --- /dev/null +++ b/.github/workflows/harness.yml @@ -0,0 +1,19 @@ +name: Harness + +on: + push: + branches: [main] + pull_request: + # A label (api-change, breaking) changes what the public surface gate allows. + types: [opened, synchronize, reopened, labeled, unlabeled] + schedule: + - cron: '17 3 * * *' + workflow_dispatch: + +permissions: {} + +jobs: + harness: + uses: Pollora/harness/.github/workflows/php-package.yml@v0.1.0 + permissions: + contents: read diff --git a/.gitignore b/.gitignore index d211724..47bdcc3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ vendor/* composer.lock /.phpunit.cache +# Pollora harness reports +/.harness/ diff --git a/.harness.json b/.harness.json new file mode 100644 index 0000000..9c6d333 --- /dev/null +++ b/.harness.json @@ -0,0 +1,11 @@ +{ + "profile": "core", + "mode": "observe", + "downstream": [ + "framework" + ], + "ratchet": { + "phpstan_level": 5, + "phpstan_baseline": 0 + } +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..82fd517 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,56 @@ +# Agent instructions + + +## Pollora harness + +This repository follows the Pollora harness (profile `core`, observe mode). The same gates run on your machine, in agent hooks and in CI; a pull request merges only when they pass. + +### Before you hand back + +- `composer qa:fast` runs the fast gates on what you changed (style, static analysis, architecture, dependencies). It must pass. +- `composer qa` runs every gate, as CI does. +- `vendor/bin/pollora-qa rules ` explains a rule quoted in a failure. + +### Never + +- Never edit `.harness.json`, `phpstan-baseline.neon`, `api-surface.json`: they hold the thresholds. If one is wrong, stop and explain why to the user. +- Never add `@phpstan-ignore`, a baseline entry or a `nosemgrep` comment without a written reason on the same line. +- Never weaken a test to make it pass, and never mark a test skipped to get a green run. +- Never break the public API (attributes, hooks fired, configuration keys, commands) in a minor version: deprecate first. + +### Always + +- A bug fix comes with a test that fails without the fix. +- Domain code depends on ports (interfaces), never on WordPress functions, Laravel or the Infrastructure layer. +- The common case of a public API needs no configuration; options are chained methods or named arguments. + +### Rules checked + +- **ARCH-01** Domain code imports neither Illuminate nor the Infrastructure or UI layers _(Pest arch, G1)_ +- **ARCH-02** Domain code calls no global WordPress function _(PHPStan rule pollora.noWordPressCallInDomain, G1)_ +- **ARCH-03** Application code depends on the Domain layer only _(Pest arch, G1)_ +- **ARCH-04** A core package requires neither illuminate/* nor laravel/*, unless listed in .harness.json _(pollora-qa deps, G1)_ +- **ARCH-05** The framework core references no third-party plugin (WooCommerce, ACF, Meta Box, WP Rocket) _(Semgrep, G1)_ +- **ARCH-06** Only integration packages, templates and projects depend on pollora/framework _(pollora-qa deps, G1)_ +- **ARCH-07** Every dependency used is declared, none declared is unused _(composer-dependency-analyser, G1)_ +- **ARCH-08** Attribute classes are final, readonly and declare their targets _(Pest arch, G1)_ +- **ARCH-09** declare(strict_types=1) in every file _(Pest arch + Pint, G1)_ +- **CONV-03** Laravel naming: ServiceProvider, Command, Exception, Repository, Factory suffixes _(Pest arch, G1)_ +- **CONV-04** Hooks fired by Pollora are named pollora// _(pollora-qa surface, G2)_ +- **CONV-05** Configuration lives under pollora.; env() is only called in config/ _(Pest arch, G1)_ +- **CONV-06** Commands are named pollora: or pollora:make- _(pollora-qa surface, G2)_ +- **CONV-07** Domain and Application code never calls app() or resolve(); it receives contracts _(Pest arch, G1)_ +- **CONV-10** One named exception per business case; never throw a bare \Exception _(Semgrep, G1)_ +- **CONV-11** Native types everywhere; PHPDoc only for generics, array shapes, @internal, @deprecated _(PHPStan + Pest type coverage, G1)_ +- **SEC-01** A REST route declared by attribute states its permission; a public route says so explicitly _(Semgrep, G1)_ +- **SEC-03** No $wpdb query built by interpolation without prepare() _(Semgrep, G1)_ +- **SEC-04** No unescaped Blade output {!! !!} outside a commented allowlist _(Semgrep, G1)_ +- **SEC-05** No unserialize, eval or extract _(Semgrep, G1)_ +- **SEC-08** No secret in the repository _(gitleaks, G1)_ +- **SEC-09** No dependency with a known vulnerability _(composer audit, G1)_ +- **QA-01** Ratchet: no metric gets worse (PHPStan level and baseline, coverage, mutation score) _(pollora-qa ratchet, G2)_ +- **QA-02** The harness files are protected: baseline, .harness.json and api-surface.json change only through a human _(Agent hooks + CODEOWNERS, G0)_ +- **QA-04** The public surface (attributes, hooks fired, configuration keys, commands) changes only with a label: api-change to add, breaking to remove or change _(pollora-qa surface, G2)_ +- **QA-05** No PHP signature breaks backward compatibility outside a major version _(roave/backward-compatibility-check, G2)_ +- **QA-03** A change to a core package keeps the framework green _(Downstream job, G2)_ + diff --git a/api-surface.json b/api-surface.json new file mode 100644 index 0000000..02297fd --- /dev/null +++ b/api-surface.json @@ -0,0 +1,325 @@ +{ + "version": 1, + "surface": { + "attribute:Pollora\\Attributes\\Ability": "targets class", + "attribute:Pollora\\Attributes\\Ability($behaviour)": "Pollora\\Abilities\\Domain\\Model\\Behaviour, optional = Pollora\\Abilities\\Domain\\Model\\Behaviour", + "attribute:Pollora\\Attributes\\Ability($category)": "string, required", + "attribute:Pollora\\Attributes\\Ability($description)": "string, required", + "attribute:Pollora\\Attributes\\Ability($label)": "string, optional = ''", + "attribute:Pollora\\Attributes\\Ability($name)": "string, required", + "attribute:Pollora\\Attributes\\Action": "targets method, repeatable", + "attribute:Pollora\\Attributes\\Action($hook)": "string, required", + "attribute:Pollora\\Attributes\\Action($priority)": "int, optional = 10", + "attribute:Pollora\\Attributes\\Ajax": "targets method", + "attribute:Pollora\\Attributes\\Ajax($access)": "Pollora\\Ajax\\Domain\\Model\\AjaxAccess, optional = Pollora\\Ajax\\Domain\\Model\\AjaxAccess", + "attribute:Pollora\\Attributes\\Ajax($action)": "string, required", + "attribute:Pollora\\Attributes\\Async": "targets class, method", + "attribute:Pollora\\Attributes\\Async($asUser)": "?bool, optional = NULL", + "attribute:Pollora\\Attributes\\Async($backoff)": "array|int|null, optional = NULL", + "attribute:Pollora\\Attributes\\Async($capture)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Async($delay)": "DateInterval|int|null, optional = NULL", + "attribute:Pollora\\Attributes\\Async($except)": "array|string, optional = array", + "attribute:Pollora\\Attributes\\Async($keepMissing)": "bool, optional = false", + "attribute:Pollora\\Attributes\\Async($onQueue)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Async($tries)": "?int, optional = NULL", + "attribute:Pollora\\Attributes\\Async($unique)": "int|bool, optional = false", + "attribute:Pollora\\Attributes\\Async($via)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Async($when)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\BlockBinding": "targets class", + "attribute:Pollora\\Attributes\\BlockBinding($label)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\BlockBinding($name)": "string, required", + "attribute:Pollora\\Attributes\\BlockBinding($postTypes)": "array|string, optional = array", + "attribute:Pollora\\Attributes\\BlockBinding($usesContext)": "array, optional = array", + "attribute:Pollora\\Attributes\\BlockBinding\\BindingField": "targets method", + "attribute:Pollora\\Attributes\\BlockBinding\\BindingField($label)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\BlockBinding\\BindingField($name)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\BlockBinding\\BindingField($type)": "Pollora\\BlockBinding\\Domain\\Enums\\BindingFieldType|string, optional = Pollora\\BlockBinding\\Domain\\Enums\\BindingFieldType", + "attribute:Pollora\\Attributes\\CapabilitySet": "targets class", + "attribute:Pollora\\Attributes\\CapabilitySet($label)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\CommentMeta": "targets class", + "attribute:Pollora\\Attributes\\Filter": "targets method, repeatable", + "attribute:Pollora\\Attributes\\Filter($hook)": "string, required", + "attribute:Pollora\\Attributes\\Filter($priority)": "int, optional = 10", + "attribute:Pollora\\Attributes\\Meta": "targets property", + "attribute:Pollora\\Attributes\\Meta($capability)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Meta($control)": "?Pollora\\Meta\\Domain\\Enums\\Control, optional = NULL", + "attribute:Pollora\\Attributes\\Meta($description)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Meta($group)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Meta($hints)": "array, optional = array", + "attribute:Pollora\\Attributes\\Meta($items)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Meta($key)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Meta($label)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Meta($media)": "bool, optional = false", + "attribute:Pollora\\Attributes\\Meta($public)": "bool, optional = false", + "attribute:Pollora\\Attributes\\Meta($revisions)": "bool, optional = false", + "attribute:Pollora\\Attributes\\Meta($rules)": "array, optional = array", + "attribute:Pollora\\Attributes\\Meta($sanitize)": "array|string|null, optional = NULL", + "attribute:Pollora\\Attributes\\Meta($showInRest)": "bool, optional = false", + "attribute:Pollora\\Attributes\\Meta($single)": "bool, optional = true", + "attribute:Pollora\\Attributes\\ModifyRole": "targets class", + "attribute:Pollora\\Attributes\\ModifyRole($allowSensitive)": "bool, optional = false", + "attribute:Pollora\\Attributes\\ModifyRole($slug)": "string, required", + "attribute:Pollora\\Attributes\\PostMeta": "targets class", + "attribute:Pollora\\Attributes\\PostMeta($postTypes)": "array|string, required", + "attribute:Pollora\\Attributes\\PostType": "targets class", + "attribute:Pollora\\Attributes\\PostType($plural)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType($singular)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType($slug)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType($textDomain)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\AdminCol": "targets method", + "attribute:Pollora\\Attributes\\PostType\\AdminCol($cap)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\AdminCol($dateFormat)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\AdminCol($default)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\AdminCol($featuredImage)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\AdminCol($key)": "string, required", + "attribute:Pollora\\Attributes\\PostType\\AdminCol($link)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\AdminCol($metaKey)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\AdminCol($postCap)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\AdminCol($postField)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\AdminCol($sortable)": "string|bool, optional = false", + "attribute:Pollora\\Attributes\\PostType\\AdminCol($taxonomy)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\AdminCol($title)": "string, required", + "attribute:Pollora\\Attributes\\PostType\\AdminCol($titleIcon)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\AdminCol($width)": "?int, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\AdminCols": "targets class", + "attribute:Pollora\\Attributes\\PostType\\AdminCols($value)": "array, required", + "attribute:Pollora\\Attributes\\PostType\\AdminFilters": "targets class", + "attribute:Pollora\\Attributes\\PostType\\AdminFilters($value)": "array, required", + "attribute:Pollora\\Attributes\\PostType\\Archive": "targets class", + "attribute:Pollora\\Attributes\\PostType\\Archive($value)": "array, required", + "attribute:Pollora\\Attributes\\PostType\\BlockEditor": "targets class", + "attribute:Pollora\\Attributes\\PostType\\BlockEditor($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\PostType\\CanExport": "targets class", + "attribute:Pollora\\Attributes\\PostType\\CanExport($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\PostType\\Capabilities": "targets class", + "attribute:Pollora\\Attributes\\PostType\\Capabilities($value)": "array, required", + "attribute:Pollora\\Attributes\\PostType\\CapabilityType": "targets class", + "attribute:Pollora\\Attributes\\PostType\\CapabilityType($value)": "string, required", + "attribute:Pollora\\Attributes\\PostType\\Chronological": "targets class", + "attribute:Pollora\\Attributes\\PostType\\Chronological($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\PostType\\DashboardActivity": "targets class", + "attribute:Pollora\\Attributes\\PostType\\DashboardActivity($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\PostType\\DashboardGlance": "targets class", + "attribute:Pollora\\Attributes\\PostType\\DashboardGlance($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\PostType\\DeleteWithUser": "targets class", + "attribute:Pollora\\Attributes\\PostType\\DeleteWithUser($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\PostType\\Description": "targets class", + "attribute:Pollora\\Attributes\\PostType\\Description($value)": "string, required", + "attribute:Pollora\\Attributes\\PostType\\ExcludeFromSearch": "targets class", + "attribute:Pollora\\Attributes\\PostType\\ExcludeFromSearch($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\PostType\\FeaturedImage": "targets class", + "attribute:Pollora\\Attributes\\PostType\\FeaturedImage($value)": "string, required", + "attribute:Pollora\\Attributes\\PostType\\HasArchive": "targets class", + "attribute:Pollora\\Attributes\\PostType\\HasArchive($value)": "string|bool, optional = true", + "attribute:Pollora\\Attributes\\PostType\\Hierarchical": "targets class", + "attribute:Pollora\\Attributes\\PostType\\Hierarchical($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\PostType\\Label": "targets class", + "attribute:Pollora\\Attributes\\PostType\\Label($value)": "string, required", + "attribute:Pollora\\Attributes\\PostType\\Labels": "targets class", + "attribute:Pollora\\Attributes\\PostType\\Labels($addNew)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($addNewItem)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($allItems)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($archives)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($attributes)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($editItem)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($featuredImage)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($filterItemsList)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($insertIntoItem)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($itemPublished)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($itemReverted)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($itemScheduled)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($itemUpdated)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($itemsList)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($itemsListNavigation)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($menuName)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($name)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($newItem)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($notFound)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($notFoundInTrash)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($parentItemColon)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($removeFeaturedImage)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($searchItems)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($setFeaturedImage)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($singularName)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($uploadedToThisItem)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($useFeaturedImage)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($viewItem)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\Labels($viewItems)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\PostType\\MapMetaCap": "targets class", + "attribute:Pollora\\Attributes\\PostType\\MapMetaCap($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\PostType\\MenuIcon": "targets class", + "attribute:Pollora\\Attributes\\PostType\\MenuIcon($value)": "string, required", + "attribute:Pollora\\Attributes\\PostType\\MenuPosition": "targets class", + "attribute:Pollora\\Attributes\\PostType\\MenuPosition($value)": "int, required", + "attribute:Pollora\\Attributes\\PostType\\Priority": "targets class", + "attribute:Pollora\\Attributes\\PostType\\Priority($priority)": "int, optional = 5", + "attribute:Pollora\\Attributes\\PostType\\PublicPostType": "targets class", + "attribute:Pollora\\Attributes\\PostType\\PublicPostType($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\PostType\\PubliclyQueryable": "targets class", + "attribute:Pollora\\Attributes\\PostType\\PubliclyQueryable($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\PostType\\QueryVar": "targets class", + "attribute:Pollora\\Attributes\\PostType\\QueryVar($value)": "string|bool, optional = true", + "attribute:Pollora\\Attributes\\PostType\\QuickEdit": "targets class", + "attribute:Pollora\\Attributes\\PostType\\QuickEdit($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\PostType\\RegisterMetaBoxCb": "targets method", + "attribute:Pollora\\Attributes\\PostType\\RestBase": "targets class", + "attribute:Pollora\\Attributes\\PostType\\RestBase($value)": "string, required", + "attribute:Pollora\\Attributes\\PostType\\RestControllerClass": "targets class", + "attribute:Pollora\\Attributes\\PostType\\RestControllerClass($value)": "string, required", + "attribute:Pollora\\Attributes\\PostType\\RestNamespace": "targets class", + "attribute:Pollora\\Attributes\\PostType\\RestNamespace($value)": "string, required", + "attribute:Pollora\\Attributes\\PostType\\Rewrite": "targets class", + "attribute:Pollora\\Attributes\\PostType\\Rewrite($value)": "array|bool, optional = true", + "attribute:Pollora\\Attributes\\PostType\\ShowInAdminBar": "targets class", + "attribute:Pollora\\Attributes\\PostType\\ShowInAdminBar($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\PostType\\ShowInFeed": "targets class", + "attribute:Pollora\\Attributes\\PostType\\ShowInFeed($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\PostType\\ShowInMenu": "targets class", + "attribute:Pollora\\Attributes\\PostType\\ShowInMenu($value)": "string|bool, optional = true", + "attribute:Pollora\\Attributes\\PostType\\ShowInNavMenus": "targets class", + "attribute:Pollora\\Attributes\\PostType\\ShowInNavMenus($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\PostType\\ShowInRest": "targets class", + "attribute:Pollora\\Attributes\\PostType\\ShowInRest($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\PostType\\ShowUI": "targets class", + "attribute:Pollora\\Attributes\\PostType\\ShowUI($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\PostType\\SiteFilters": "targets class", + "attribute:Pollora\\Attributes\\PostType\\SiteFilters($value)": "array, required", + "attribute:Pollora\\Attributes\\PostType\\SiteSortables": "targets class", + "attribute:Pollora\\Attributes\\PostType\\SiteSortables($value)": "array, required", + "attribute:Pollora\\Attributes\\PostType\\Supports": "targets class", + "attribute:Pollora\\Attributes\\PostType\\Supports($features)": "array, optional = array", + "attribute:Pollora\\Attributes\\PostType\\Taxonomies": "targets class", + "attribute:Pollora\\Attributes\\PostType\\Taxonomies($value)": "array, required", + "attribute:Pollora\\Attributes\\PostType\\Template": "targets class", + "attribute:Pollora\\Attributes\\PostType\\Template($value)": "array, required", + "attribute:Pollora\\Attributes\\PostType\\TemplateLock": "targets class", + "attribute:Pollora\\Attributes\\PostType\\TemplateLock($value)": "string|bool, required", + "attribute:Pollora\\Attributes\\PostType\\TitlePlaceholder": "targets class", + "attribute:Pollora\\Attributes\\PostType\\TitlePlaceholder($value)": "string, required", + "attribute:Pollora\\Attributes\\Role": "targets class", + "attribute:Pollora\\Attributes\\Role($allowSensitive)": "bool, optional = false", + "attribute:Pollora\\Attributes\\Role($inherits)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Role($label)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Role($slug)": "string, required", + "attribute:Pollora\\Attributes\\Role($textDomain)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Role\\Grants": "targets class, repeatable", + "attribute:Pollora\\Attributes\\Role\\Grants($capabilities)": "BackedEnum|string, variadic", + "attribute:Pollora\\Attributes\\Role\\GrantsPostType": "targets class, repeatable", + "attribute:Pollora\\Attributes\\Role\\GrantsPostType($access)": "Pollora\\Role\\Domain\\Enums\\Access, optional = Pollora\\Role\\Domain\\Enums\\Access", + "attribute:Pollora\\Attributes\\Role\\GrantsPostType($postType)": "string, required", + "attribute:Pollora\\Attributes\\Role\\GrantsTaxonomy": "targets class, repeatable", + "attribute:Pollora\\Attributes\\Role\\GrantsTaxonomy($taxonomy)": "string, required", + "attribute:Pollora\\Attributes\\Role\\Without": "targets class, repeatable", + "attribute:Pollora\\Attributes\\Role\\Without($capabilities)": "BackedEnum|string, variadic", + "attribute:Pollora\\Attributes\\Schedule": "targets method", + "attribute:Pollora\\Attributes\\Schedule($args)": "array, optional = array", + "attribute:Pollora\\Attributes\\Schedule($hook)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Schedule($recurrence)": "Pollora\\Schedule\\Every|Pollora\\Schedule\\Interval|array|string, required", + "attribute:Pollora\\Attributes\\Taxonomy": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy($objectType)": "array|string|null, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy($plural)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy($singular)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy($slug)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy($textDomain)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy\\AllowHierarchy": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\AllowHierarchy($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\Taxonomy\\Args": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\Args($value)": "array, required", + "attribute:Pollora\\Attributes\\Taxonomy\\Capabilities": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\Capabilities($value)": "array, required", + "attribute:Pollora\\Attributes\\Taxonomy\\CheckedOntop": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\CheckedOntop($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\Taxonomy\\DefaultTerm": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\DefaultTerm($value)": "array|string, required", + "attribute:Pollora\\Attributes\\Taxonomy\\Description": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\Description($value)": "string, required", + "attribute:Pollora\\Attributes\\Taxonomy\\Exclusive": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\Exclusive($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\Taxonomy\\Hierarchical": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\Hierarchical($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\Taxonomy\\Label": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\Label($value)": "string, required", + "attribute:Pollora\\Attributes\\Taxonomy\\Labels": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\Labels($addNewItem)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy\\Labels($addOrRemoveItems)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy\\Labels($allItems)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy\\Labels($backToItems)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy\\Labels($chooseFromMostUsed)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy\\Labels($editItem)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy\\Labels($filterByItem)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy\\Labels($itemsList)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy\\Labels($itemsListNavigation)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy\\Labels($menuName)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy\\Labels($name)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy\\Labels($newItemName)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy\\Labels($noTerms)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy\\Labels($notFound)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy\\Labels($parentItem)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy\\Labels($parentItemColon)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy\\Labels($popularItems)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy\\Labels($searchItems)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy\\Labels($separateItemsWithCommas)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy\\Labels($singularName)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy\\Labels($updateItem)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy\\Labels($viewItem)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\Taxonomy\\MetaBoxCb": "targets method", + "attribute:Pollora\\Attributes\\Taxonomy\\MetaBoxSanitizeCb": "targets method", + "attribute:Pollora\\Attributes\\Taxonomy\\ObjectType": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\ObjectType($value)": "array|string, required", + "attribute:Pollora\\Attributes\\Taxonomy\\Priority": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\Priority($priority)": "int, optional = 5", + "attribute:Pollora\\Attributes\\Taxonomy\\PublicTaxonomy": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\PublicTaxonomy($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\Taxonomy\\QueryVar": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\QueryVar($value)": "string|bool, optional = true", + "attribute:Pollora\\Attributes\\Taxonomy\\RestBase": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\RestBase($value)": "string, required", + "attribute:Pollora\\Attributes\\Taxonomy\\RestControllerClass": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\RestControllerClass($value)": "string, required", + "attribute:Pollora\\Attributes\\Taxonomy\\RestNamespace": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\RestNamespace($value)": "string, required", + "attribute:Pollora\\Attributes\\Taxonomy\\Rewrite": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\Rewrite($value)": "array|bool, optional = true", + "attribute:Pollora\\Attributes\\Taxonomy\\ShowAdminColumn": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\ShowAdminColumn($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\Taxonomy\\ShowInMenu": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\ShowInMenu($value)": "string|bool, optional = true", + "attribute:Pollora\\Attributes\\Taxonomy\\ShowInNavMenus": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\ShowInNavMenus($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\Taxonomy\\ShowInQuickEdit": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\ShowInQuickEdit($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\Taxonomy\\ShowInRest": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\ShowInRest($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\Taxonomy\\ShowTagcloud": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\ShowTagcloud($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\Taxonomy\\ShowUI": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\ShowUI($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\Taxonomy\\Sort": "targets class", + "attribute:Pollora\\Attributes\\Taxonomy\\Sort($value)": "bool, optional = true", + "attribute:Pollora\\Attributes\\Taxonomy\\UpdateCountCallback": "targets method", + "attribute:Pollora\\Attributes\\TermMeta": "targets class", + "attribute:Pollora\\Attributes\\TermMeta($taxonomies)": "array|string, required", + "attribute:Pollora\\Attributes\\UserMeta": "targets class", + "attribute:Pollora\\Attributes\\WpCli": "targets class", + "attribute:Pollora\\Attributes\\WpCli($commandName)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\WpCli\\AfterInvoke": "targets class, method", + "attribute:Pollora\\Attributes\\WpCli\\AfterInvoke($callback)": "array|string, required", + "attribute:Pollora\\Attributes\\WpCli\\BeforeInvoke": "targets class, method", + "attribute:Pollora\\Attributes\\WpCli\\BeforeInvoke($callback)": "array|string, required", + "attribute:Pollora\\Attributes\\WpCli\\Command": "targets method", + "attribute:Pollora\\Attributes\\WpCli\\Command($commandName)": "?string, optional = NULL", + "attribute:Pollora\\Attributes\\WpCli\\IsDeferred": "targets class, method", + "attribute:Pollora\\Attributes\\WpCli\\IsDeferred($deferred)": "bool, optional = true", + "attribute:Pollora\\Attributes\\WpCli\\Synopsis": "targets class, method", + "attribute:Pollora\\Attributes\\WpCli\\Synopsis($synopsis)": "array|string, required", + "attribute:Pollora\\Attributes\\WpCli\\When": "targets class, method", + "attribute:Pollora\\Attributes\\WpCli\\When($hook)": "string, required", + "attribute:Pollora\\Attributes\\WpRestRoute": "targets class", + "attribute:Pollora\\Attributes\\WpRestRoute($namespace)": "string, required", + "attribute:Pollora\\Attributes\\WpRestRoute($permissionCallback)": "Pollora\\Attributes\\WpRestRoute\\Permission|string|null, optional = NULL", + "attribute:Pollora\\Attributes\\WpRestRoute($route)": "string, required", + "attribute:Pollora\\Attributes\\WpRestRoute\\Method": "targets method, repeatable", + "attribute:Pollora\\Attributes\\WpRestRoute\\Method($methods)": "array|string, required", + "attribute:Pollora\\Attributes\\WpRestRoute\\Method($permissionCallback)": "Pollora\\Attributes\\WpRestRoute\\Permission|string|null, optional = NULL" + } +} diff --git a/composer.json b/composer.json index b51c11e..5a786c4 100644 --- a/composer.json +++ b/composer.json @@ -33,6 +33,7 @@ "laravel/pint": "^1.22.1", "pestphp/pest": "^3.8.2", "phpstan/phpstan": "^2.1.16", + "pollora/harness": "^0.1", "szepeviktor/phpstan-wordpress": "^2.0" }, "autoload": { @@ -60,7 +61,9 @@ "phpstan": "phpstan analyse", "phpstan:ci": "phpstan analyse --no-progress", "lint": "pint", - "lint:check": "pint --test" + "lint:check": "pint --test", + "qa": "@php vendor/bin/pollora-qa check", + "qa:fast": "@php vendor/bin/pollora-qa check --fast" }, "config": { "sort-packages": true, diff --git a/phpstan.neon b/phpstan.neon index 80f6750..50804e6 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,5 @@ includes: - - vendor/szepeviktor/phpstan-wordpress/extension.neon + - vendor/pollora/harness/presets/phpstan/base.neon parameters: level: 5 diff --git a/rector.php b/rector.php new file mode 100644 index 0000000..46e828a --- /dev/null +++ b/rector.php @@ -0,0 +1,11 @@ +withSkip([ + // Each skip says why, like the framework's rector.php. + ]); diff --git a/tests/Arch.php b/tests/Arch.php new file mode 100644 index 0000000..3aa969e --- /dev/null +++ b/tests/Arch.php @@ -0,0 +1,15 @@ +register();