Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
a4ebb58
refactor: let discovery run without Laravel's application
ogorzalka Oct 12, 2026
7618574
refactor: require pollora/discovery instead of shipping it
ogorzalka Oct 12, 2026
20dbd72
Merge pull request #460 from Pollora/feat/discovery-decoupling
ogorzalka Oct 12, 2026
6f47ded
Merge pull request #461 from Pollora/feat/require-discovery-package
ogorzalka Oct 12, 2026
492718e
refactor: let attributes declare what they register
ogorzalka Oct 12, 2026
d857a9b
style: import the enumerations the architecture test allows
ogorzalka Oct 12, 2026
d4e7a85
test: use the parameters of the hook fixture so Rector keeps them
ogorzalka Oct 12, 2026
ecc3a12
Merge pull request #462 from Pollora/refactor/attributes-declarations
ogorzalka Oct 12, 2026
155fc33
refactor: require pollora/attributes instead of shipping the attributes
ogorzalka Oct 12, 2026
631525f
Merge pull request #463 from Pollora/feat/require-attributes-package
ogorzalka Oct 12, 2026
bc3d39e
refactor: let the WpRest module run without Laravel's application
ogorzalka Oct 12, 2026
1343d8e
Merge pull request #464 from Pollora/refactor/wp-rest-decoupling
ogorzalka Oct 12, 2026
dfa210d
refactor: let the WpCli module run without Laravel's application
ogorzalka Oct 12, 2026
ee1b195
Merge pull request #465 from Pollora/refactor/wp-cli-decoupling
ogorzalka Oct 13, 2026
19bf600
refactor: let #[Schedule] run without Laravel
ogorzalka Oct 12, 2026
c445de3
Merge pull request #466 from Pollora/refactor/schedule-decoupling
ogorzalka Oct 13, 2026
26cad69
refactor: require pollora/wp-rest instead of shipping the WpRest module
ogorzalka Oct 13, 2026
c64a6e7
Merge pull request #467 from Pollora/feat/require-wp-rest-package
ogorzalka Oct 13, 2026
5e7c7b8
refactor: require pollora/wp-cli instead of shipping the WpCli module
ogorzalka Oct 13, 2026
71457a7
Merge pull request #468 from Pollora/feat/require-wp-cli-package
ogorzalka Oct 13, 2026
3251825
refactor: require pollora/schedule instead of shipping ScheduleDiscovery
ogorzalka Oct 13, 2026
1931e8e
feat: choose the scheduling driver, list and migrate scheduled events
ogorzalka Oct 13, 2026
2610509
feat: tell the administrator which scheduled events wait for a migration
ogorzalka Oct 13, 2026
321d9b2
fix: require the schedule release that has the index, and judge a dri…
ogorzalka Oct 13, 2026
46fec63
Merge pull request #469 from Pollora/feat/require-schedule-package
ogorzalka Oct 13, 2026
7408de0
chore: rerun CI against the released pollora/schedule
ogorzalka Oct 13, 2026
ef45852
style: apply Rector to the schedule tests
ogorzalka Oct 13, 2026
9a856ff
Merge pull request #470 from Pollora/feat/schedule-index-commands
ogorzalka Oct 13, 2026
1cf53a3
feat: require pollora/hook 2.0, queue asynchronous actions through po…
ogorzalka Oct 13, 2026
3bf2796
chore(deps-dev): update larastan/larastan requirement
dependabot[bot] Oct 13, 2026
d711439
chore(deps): update laravel/prompts requirement from ^0.3.24 to ^0.3.25
dependabot[bot] Oct 13, 2026
e7d5c88
style: apply Rector to the inspector test
ogorzalka Oct 13, 2026
2d5f13e
Merge pull request #474 from Pollora/feat/require-hook-2
ogorzalka Oct 13, 2026
c823272
feat: laravel schedule driver, and remove the Laravel scheduler bridge
ogorzalka Oct 13, 2026
65df8ce
fix: build the laravel driver before the bus exists, extend before th…
ogorzalka Oct 13, 2026
b3287f6
Merge pull request #475 from Pollora/feat/schedule-laravel-driver
ogorzalka Oct 13, 2026
6e15e16
Merge pull request #471 from Pollora/dependabot/composer/develop/lara…
ogorzalka Oct 13, 2026
475e718
Merge pull request #472 from Pollora/dependabot/composer/develop/lara…
ogorzalka Oct 13, 2026
830eeb6
fix(wordpress): map app.env to WP_ENVIRONMENT_TYPE
Boulea7 Oct 13, 2026
7d46f38
test: keep pings on in the E2E fixture, WordPress 7.1 disables them o…
ogorzalka Oct 13, 2026
6415fdd
Merge pull request #444 from Boulea7/hotfix/443-wordpress-environment
ogorzalka Oct 14, 2026
2234eb7
chore(deps-dev): update orchestra/testbench requirement (#473)
dependabot[bot] Oct 14, 2026
142af2e
chore(deps): require Laravel 13.36
ogorzalka Oct 14, 2026
3c72e48
Merge pull request #476 from Pollora/chore/laravel-13.36
ogorzalka Oct 14, 2026
c5890c2
fix(schedule): give a recurring event of the laravel driver its interval
ogorzalka Oct 14, 2026
b628820
Merge pull request #477 from Pollora/fix/laravel-driver-recurring-int…
ogorzalka Oct 14, 2026
45592d8
fix: listen to queued asynchronous actions at boot
ogorzalka Oct 14, 2026
8ca4bdf
Merge pull request #478 from Pollora/fix/async-listen-at-boot
ogorzalka Oct 14, 2026
9da479d
chore: release v13.36.0
ogorzalka Oct 14, 2026
68309d0
Merge pull request #479 from Pollora/chore/release-v13.36.0
ogorzalka Oct 14, 2026
1003a61
Merge develop into release/v13.36.0
ogorzalka Oct 14, 2026
2d11577
Merge pull request #482 from Pollora/merge/develop-into-release
ogorzalka Oct 14, 2026
a711f91
Merge pull request #480 from Pollora/release/v13.36.0
ogorzalka Oct 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 44 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,50 @@ All notable changes to the Pollora framework will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased](https://github.com/Pollora/framework/compare/v13.35.6...develop)
## [Unreleased](https://github.com/Pollora/framework/compare/v13.36.0...develop)

## [v13.36.0](https://github.com/Pollora/framework/compare/v13.35.6...v13.36.0) - 2026-10-14

### Added
- Discovery lives in its own package, [`pollora/discovery`](https://github.com/Pollora/discovery), which the framework requires: the engine, its contracts, `DiscoveryManager`, the cache and the `#[SkipDiscovery]` attribute, with their tests and benchmarks. Class names do not change. The framework keeps what needs Laravel: the service provider, `discovery:run` / `discovery:clear`, `DiscoveryRegistrar`, `ServiceProviderDiscovery`, the Laravel adapters, and `HasConfiguringSupport`, which builds post types and taxonomies
- Discovery can run without Laravel's application, the first step towards `pollora/discovery`: `new DiscoveryEngine()` works with no argument. Two contracts let an environment plug in — `InstanceResolverInterface` builds the classes discovery needs (Laravel's container in a Pollora application, `ReflectionInstanceResolver` otherwise) and `DiscoveryRegistrarInterface` adds the environment's discoveries before each run (`DiscoveryRegistrar` keeps scanning the container's `*Discovery` bindings). `Psr16DiscoverCacheDriver` caches scans in any PSR-16 cache
- Post type and taxonomy attributes say what they register: each implements `ConfiguresArguments`, a pure function that returns the registration arguments with its own contribution (`#[HasArchive('library')]` sets `has_archive`, `#[Labels]` merges into the generated labels), and the method attributes (`#[AdminCol]`, `#[RegisterMetaBoxCb]`, `#[MetaBoxCb]`, `#[MetaBoxSanitizeCb]`, `#[UpdateCountCallback]`) implement `ConfiguresArgumentsWithMethod`, the discovery handing them the method as a callback. The `Pollora\Attributes` namespace no longer reaches Laravel or a framework service, which an architecture test keeps: the first step towards `pollora/attributes`, a package to declare attributes without the framework
- 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
- REST routes live in their own package, [`pollora/wp-rest`](https://github.com/Pollora/wp-rest), which the framework requires: `WpRestDiscovery`, the route registrar, the permissions (`IsLoggedIn`, `IsAdmin`, `Can`) and `Pollora\Support\WpGlobals`. Class names do not change. The framework keeps `WpRestAttributeServiceProvider`
- WP-CLI commands live in their own package, [`pollora/wp-cli`](https://github.com/Pollora/wp-cli), which the framework requires: `WpCliDiscovery`, `WpCliService`, the WP-CLI adapters and `WordpressCommand`. Class names do not change. The framework keeps `WpCliServiceProvider` and `pollora:make:wp-cli`
- Scheduling lives in its own package, [`pollora/schedule`](https://github.com/Pollora/schedule), which the framework requires: one engine behind `#[Schedule]` and a fluent API (`Schedule::event()`, `Schedule::once()`, `cancel()`, `next()`, `events()`), WP-Cron as the default driver, and `Schedule::fake()` for tests. `ScheduleDiscovery` keeps its class name and moves there, with the same hook names and schedule keys. The framework gives the engine its container, so the classes passed to `call()` get their dependencies, and keeps the bridge to Laravel's scheduler (`use_laravel_scheduler`)
- `config/schedule.php` (publishable with the `pollora-schedule` tag) chooses the scheduling driver, `wp-cron` (the default) or `action-scheduler`, by `POLLORA_SCHEDULE_DRIVER`, and lists the hooks that stay on WP-Cron whatever the driver. The `cron` option now indexes every driver's events, so `wp_next_scheduled()`, Site Health and WP Crontrol see the ones Action Scheduler keeps, and WP-Cron never runs them. Changing the driver moves nothing by itself
- `pollora:schedule:list` shows the scheduled events with their type, frequency and driver (`--type`, `--driver`, `--site`, `--network`, `--json`), read from the `cron` option with no query to a driver; an event on WP-Cron on purpose reads `(protected)` or `(fallback)`, one a former driver keeps reads `(to migrate)`. `pollora:schedule:migrate` moves the pending events from one driver to another (`--from`, `--to`, `--dry-run`), in both directions and safe to run again
- `pollora:doctor`, Site Health and an administration notice report a driver that does not exist or is not available (events stay on WP-Cron), and the events a former driver still keeps, with the command that moves them
- The `laravel` scheduling driver (`POLLORA_SCHEDULE_DRIVER=laravel`): every event is a job delayed until it is due, in the application's queue (`database`, Redis or Horizon, SQS), and a worker runs it. There is no table of Pollora's own and no `schedule:run`: the `cron` option is the record of what is scheduled, and cancelling an event takes its entry out of it. A job runs only if the entry still exists with its identifier and time, so a cancelled, moved or duplicated job stops; a recurring event queues its next occurrence once it ran or failed for good, never in the past. `config/schedule.php` gains `laravel.connection`, `queue`, `max_delay` (a job due later than the connection allows, 900 seconds on SQS, wakes and waits again) and `late_after`. On a multisite network the queue is shared, with one worker, and each job runs in the site that scheduled it
- Lost jobs (a flushed Redis, a purged queue) are found again: at most every five minutes, as a request ends and under a lock, and with `php artisan pollora:schedule:recover` (`--network`, `--dry-run`), an event more than `late_after` seconds (600) past its time is queued again with the same identifier, so it never runs twice. `pollora:doctor` and Site Health report late events (no worker), a queue connection that is `sync` or `null`, and a scheduled event that failed for good in the last week

### Changed
- Requires Laravel 13.36: `illuminate/*` `^13.35` → `^13.36`. The framework version tracks Laravel's, so this release is v13.36.0
- `DiscoveryEngine`, `DiscoveryCacheManager`, `InstancePool` and `ReflectionCache` take their dependencies instead of reaching for `app()` and `config()`; reading `structure-discoverer.cache` moved to `LaravelDiscoveryCacheManagerFactory`. `DiscoveryRegistrar::registerFromContainer()` is now `registerInto()`. These are infrastructure classes, outside the public contracts (`Pollora\Discovery\Domain\*`), which do not change
- One `DiscoveryCacheManager` is shared by the engine and the `pollora:doctor` discovery cache check, which built its own
- The discovery benchmarks measure memory as PHP allocates it, from a reset peak: they read 2 MB whatever the load
- `#[WpRestRoute\Method]` no longer registers its route: `WpRestRouteRegistrar`, in the WpRest module, does, with the same arguments, permissions and request injection. Default slugs, meta keys and command names come from `Pollora\Attributes\Support\Naming`, which gives the same results as `Str::kebab()` and `Str::snake()`; `Pollora\Support\Slug` uses it
- The WpRest module no longer reaches for Laravel's application, the first step towards `pollora/wp-rest`: custom `HandlesAttributes` attributes receive the PSR-11 container given to `WpRestDiscovery`, and `WpRestAttributableWrapper` takes the controller instance instead of building it. An architecture test keeps the module to the attributes, discovery, PSR contracts and WordPress
- The WpCli module no longer reaches for Laravel's application, the first step towards `pollora/wp-cli`: `WpCliDiscovery` builds commands through the discovery's instance pool instead of `resolve()`. Its service provider and `pollora:make:wp-cli` stay in the framework; an architecture test keeps the rest to the attributes, discovery, PSR logging and WP-CLI
- `#[Schedule]`, which runs on WP-Cron alone, no longer reaches for Laravel, the first step towards `pollora/schedule`: `ScheduleDiscovery` derives default hook names itself, with the same results as `Str::snake()` (a parity test keeps them), so hooks already scheduled keep their name. The bridge to Laravel's scheduler (`use_laravel_scheduler`) stays in the framework

- Asynchronous actions queue through the WP-Cron and Action Scheduler drivers of `pollora/schedule`, with `pollora/hook` 2.0: `->async()`, `#[Async]`, `Async::fake()`, `via()` and their options do not change, and work already queued runs once the upgrade is in. `Async\Drivers\WpCronDriver` and `ActionSchedulerDriver` are gone with no alias; the framework never used them, and the `queue` driver is unchanged

### Deprecated
- `DiscoveryCacheInterface` and `LaravelDiscoveryCache`, removed in 15.0: nothing reads this cache, scans are cached by the Spatie driver given to `DiscoveryCacheManager`
- `Pollora\Attributes\PostType\PostTypeAttribute` and `Pollora\Attributes\Taxonomy\TaxonomyAttribute`, removed in 15.0: no built-in attribute extends them any more; implement `ConfiguresArguments`

### Removed
- **The Laravel scheduling bridge, with no replacement under the same name**: `wordpress.use_laravel_scheduler`, `Pollora\Schedule\Scheduler`, `SchedulerInterface`, `Events\*` (`AbstractEvent`, `SingleEvent`, `RecurringEvent`), `Jobs\JobDispatcher`, `RegisterSchedulerFiltersUseCase`, the `pre_schedule_event`, `pre_option_cron` and related filters it installed, and the `wp_events` table. Setting the option no longer does anything. To keep WP-Cron events on Laravel's queue, set `POLLORA_SCHEDULE_DRIVER=laravel` and run a worker (`php artisan queue:work`) in place of `schedule:run`; events that sat in `wp_events` are not moved (let `#[Schedule]` and your own code schedule them again, then drop the table: `DROP TABLE <prefix>wp_events`, with the prefix of your database connection). `pollora:doctor` no longer reads the option
- `handle()` on `#[Action]`, `#[Filter]`, `#[WpRestRoute]` and `#[WpRestRoute\Method]`, and `Hook` no longer implements `HandlesAttributes`: `HookDiscovery` and `WpRestDiscovery` register hooks and routes themselves. Only the bug below reached `Action::handle()`, and `WpRestRoute::handle()` wrote to a read-only wrapper

### Fixed
- A queued asynchronous action runs even when the request that runs it registers no hook: the framework listens to `pollora/async/run` at boot instead of waiting for an `Action` to be built. Before, a handler registered only in the request that queued it was never run, and Action Scheduler marked it failed ("no callbacks are registered")
- `wp_get_environment_type()` follows `APP_ENV` instead of reading `production` on every site (#443): `local` gives `local`, `dev`, `development` and `testing` give `development`, `staging` gives `staging`, anything else `production`. A `WP_ENVIRONMENT_TYPE` environment variable, a `wordpress.constants.WP_ENVIRONMENT_TYPE` value or a constant defined earlier still wins. WordPress 7.1 turns pings (incoming and outgoing pingbacks and trackbacks) off outside `production`, so they are off on a site with `APP_ENV=local` or `staging`: `add_filter('wp_should_disable_pings_for_environment', '__return_false')` brings them back
- `pollora:doctor` no longer counts as overdue WP-Cron events the ones Action Scheduler keeps: the `cron` option indexes them too, with a `driver` key
- A public `#[Action]` or `#[Filter]` method in a `#[PostType]` or `#[Taxonomy]` class is registered once: the post type and taxonomy discoveries also handed it to `Action::handle()`, which registered it a second time on the discovery's configuration object, so the page firing that hook failed with `call_user_func(): Argument #1 ($callback) must be a valid callback, class Pollora\PostType\Infrastructure\Services\PostTypeConfiguration does not have a method …`
- A REST controller declared with `#[WpRestRoute]` gets its constructor dependencies: it is resolved through the discovery's instance pool (Laravel's container in a Pollora application) instead of `new` with no argument, so a controller with a required dependency answered every request with `Trying to invoke non static method … without an object`

## [v13.35.6](https://github.com/Pollora/framework/compare/v13.35.5...v13.35.6) - 2026-10-12

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ composer create-project pollora/pollora my-project
Requirements:

- PHP 8.4+ for a new project (the skeleton's lock file ships Symfony 8; this package alone accepts PHP 8.3)
- Laravel 13.35 (Pollora's version numbers follow the Laravel release it is built on)
- Laravel 13.36 (Pollora's version numbers follow the Laravel release it is built on)
- WordPress 7.1+

## Quick start
Expand Down
25 changes: 15 additions & 10 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,21 @@
"require": {
"php": "^8.3",
"pollora/helper-overrider": "^1.2",
"illuminate/auth": "^13.35",
"illuminate/support": "^13.35",
"illuminate/database": "^13.35",
"illuminate/routing": "^13.35",
"illuminate/console": "^13.35",
"illuminate/auth": "^13.36",
"illuminate/support": "^13.36",
"illuminate/database": "^13.36",
"illuminate/routing": "^13.36",
"illuminate/console": "^13.36",
"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",
"pollora/hook": "^2.0.1",
"pollora/option": "^1.1",
"pollora/schedule": "^1.0",
"pollora/wp-cli": "^0.1 || ^1.0",
"pollora/wp-rest": "^0.1 || ^1.0",
"watson/rememberable": "^7.1",
"cweagans/composer-patches": "^2.0",
"pollora/colt": "^10.0.1",
Expand All @@ -44,8 +49,8 @@
"coduo/php-humanizer": "5.0.3",
"nwidart/laravel-modules": "^13.0",
"symfony/process": "^7.4 || ^8.0",
"illuminate/config": "^13.35",
"laravel/prompts": "^0.3.24"
"illuminate/config": "^13.36",
"laravel/prompts": "^0.3.25"
},
"autoload": {
"psr-4": {
Expand Down Expand Up @@ -108,8 +113,8 @@
"brain/monkey": "^2.7",
"mockery/mockery": "^1.6.15",
"wp-cli/wp-cli": "^2.12",
"larastan/larastan": "^3.12",
"orchestra/testbench": "^11.1.0"
"larastan/larastan": "^3.13",
"orchestra/testbench": "^11.3.0"
},
"autoload-dev": {
"psr-4": {
Expand Down
76 changes: 76 additions & 0 deletions config/schedule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

declare(strict_types=1);

return [

/*
|--------------------------------------------------------------------------
| Scheduling driver
|--------------------------------------------------------------------------
|
| Where scheduled events are kept and who runs them: #[Schedule],
| Schedule::event(), and every wp_schedule_event() call of WordPress, a
| plugin or a theme.
|
| "wp-cron" is WordPress's own and needs nothing. "action-scheduler" stores
| events in Action Scheduler (a plugin that bundles it, such as WooCommerce,
| must be active); without it, events stay on WP-Cron.
|
| Changing the driver moves nothing: events already scheduled stay where they
| are until php artisan pollora:schedule:migrate moves them, and
| pollora:doctor reminds you.
|
| "laravel" makes each event a job delayed until it is due, in the
| application's queue: it needs a connection that accepts delays (not
| "sync" or "null") and a worker, php artisan queue:work. Nothing else
| runs the events, so without a worker none of them fires.
|
| Supported: "wp-cron", "action-scheduler", "laravel"
|
*/

'driver' => env('POLLORA_SCHEDULE_DRIVER', 'wp-cron'),

/*
|--------------------------------------------------------------------------
| The "laravel" driver
|--------------------------------------------------------------------------
|
| connection: the queue connection of the jobs; null is the application's
| default. On a multisite network every site uses the same one, with a
| single worker.
|
| queue: the queue they wait in; null is the connection's default.
|
| max_delay: the longest delay, in seconds, the connection accepts. A job
| due later waits again when it wakes. null: 900 for SQS, none otherwise.
|
| late_after: an event this many seconds past its time is queued again
| (same identifier, so it never runs twice) when its job was lost.
| Checked every five minutes, or with php artisan pollora:schedule:recover.
|
*/

'laravel' => [
'connection' => env('POLLORA_SCHEDULE_QUEUE_CONNECTION'),
'queue' => env('POLLORA_SCHEDULE_QUEUE'),
'max_delay' => env('POLLORA_SCHEDULE_MAX_DELAY'),
'late_after' => (int) env('POLLORA_SCHEDULE_LATE_AFTER', 600),
],

/*
|--------------------------------------------------------------------------
| Hooks that stay on WP-Cron
|--------------------------------------------------------------------------
|
| Action Scheduler's own queue runner always does: handed to Action
| Scheduler, it would never run. Add hooks that must run by WP-Cron
| whatever the driver. The pollora/schedule/protected_hooks filter extends
| the list too.
|
*/

'protected_hooks' => [],

];
23 changes: 0 additions & 23 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,6 @@ parameters:
count: 1
path: src/Asset/Infrastructure/Services/ViteManager.php

-
message: '#^Access to an undefined property Pollora\\Attributes\\Attributable\:\:\$classPermission\.$#'
identifier: property.notFound
count: 1
path: src/Attributes/WpRestRoute/Method.php

-
message: '#^Access to an undefined property Pollora\\Attributes\\Attributable\:\:\$namespace\.$#'
identifier: property.notFound
count: 1
path: src/Attributes/WpRestRoute/Method.php

-
message: '#^Access to an undefined property Pollora\\Attributes\\Attributable\:\:\$route\.$#'
identifier: property.notFound
count: 2
path: src/Attributes/WpRestRoute/Method.php

-
message: '#^Constant AUTH_COOKIE not found\.$#'
identifier: constant.notFound
Expand Down Expand Up @@ -228,8 +210,3 @@ parameters:
count: 1
path: src/View/Application/UseCases/MakeTemplateIncludableUseCase.php

-
message: '#^Constant WP_CLI_VERSION not found\.$#'
identifier: constant.notFound
count: 1
path: src/WpCli/Infrastructure/Adapters/WpCliAdapter.php
Loading
Loading