From 830eeb614b733df05679fc9eb936f827c9015793 Mon Sep 17 00:00:00 2001 From: boulea7 Date: Tue, 13 Oct 2026 19:28:51 +0200 Subject: [PATCH 1/2] fix(wordpress): map app.env to WP_ENVIRONMENT_TYPE Map Laravel's app.env to a default WP_ENVIRONMENT_TYPE before WordPress loads, so app.env=local produces local. dev, development, testing and staging are mapped too; an explicit process environment value, a configured constant or a constant already defined still wins. Fixes #443. --- src/WordPress/Bootstrap.php | 9 + .../WordPress/BootstrapEnvironmentTest.php | 174 ++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 tests/Unit/WordPress/BootstrapEnvironmentTest.php diff --git a/src/WordPress/Bootstrap.php b/src/WordPress/Bootstrap.php index 80f908e2..f3c4554b 100644 --- a/src/WordPress/Bootstrap.php +++ b/src/WordPress/Bootstrap.php @@ -396,6 +396,15 @@ private function defineWordPressConstants(): void Constant::queue('JETPACK_DEV_DEBUG', $debugMode); + if (! function_exists('getenv') || getenv('WP_ENVIRONMENT_TYPE') === false) { + Constant::queue('WP_ENVIRONMENT_TYPE', match (config('app.env')) { + 'local' => 'local', + 'dev', 'development', 'testing' => 'development', + 'staging' => 'staging', + default => 'production', + }); + } + foreach ((array) config('wordpress.constants', []) as $key => $value) { $key = strtoupper((string) $key); Constant::queue($key, $value); diff --git a/tests/Unit/WordPress/BootstrapEnvironmentTest.php b/tests/Unit/WordPress/BootstrapEnvironmentTest.php new file mode 100644 index 00000000..599e810a --- /dev/null +++ b/tests/Unit/WordPress/BootstrapEnvironmentTest.php @@ -0,0 +1,174 @@ +registerWordPressConstants(); + + self::assertTrue(WP_DEBUG); + self::assertSame('local', constant('WP_ENVIRONMENT_TYPE')); + } + + #[DataProvider('environmentMappings')] + public function test_application_environment_is_mapped(?string $environment, string $expected): void + { + putenv('WP_ENVIRONMENT_TYPE'); + $this->registerWordPressConstants($environment); + + self::assertSame($expected, constant('WP_ENVIRONMENT_TYPE')); + } + + #[DataProvider('debugValues')] + public function test_environment_does_not_depend_on_debug_mode(bool $debug): void + { + putenv('WP_ENVIRONMENT_TYPE'); + $this->registerWordPressConstants(debug: $debug); + + self::assertSame($debug, WP_DEBUG); + self::assertSame('local', constant('WP_ENVIRONMENT_TYPE')); + } + + #[DataProvider('nativeEnvironments')] + public function test_native_environment_prevents_fallback_constant(string $environment): void + { + putenv('WP_ENVIRONMENT_TYPE='.$environment); + $this->registerWordPressConstants(); + + self::assertFalse(defined('WP_ENVIRONMENT_TYPE')); + self::assertSame($environment, getenv('WP_ENVIRONMENT_TYPE')); + } + + #[DataProvider('explicitValues')] + public function test_configured_environment_is_not_normalized(mixed $value): void + { + putenv('WP_ENVIRONMENT_TYPE'); + $this->registerWordPressConstants(constants: ['wp_environment_type' => $value]); + + self::assertSame($value, constant('WP_ENVIRONMENT_TYPE')); + } + + public function test_configured_constant_still_applies_with_native_environment(): void + { + putenv('WP_ENVIRONMENT_TYPE=production'); + $this->registerWordPressConstants(constants: ['WP_ENVIRONMENT_TYPE' => 'staging']); + + self::assertSame('staging', constant('WP_ENVIRONMENT_TYPE')); + self::assertSame('production', getenv('WP_ENVIRONMENT_TYPE')); + } + + #[DataProvider('explicitValues')] + public function test_predefined_constant_is_not_overwritten(mixed $value): void + { + putenv('WP_ENVIRONMENT_TYPE=production'); + define('WP_ENVIRONMENT_TYPE', $value); + $this->registerWordPressConstants(constants: ['WP_ENVIRONMENT_TYPE' => 'development']); + + self::assertSame($value, constant('WP_ENVIRONMENT_TYPE')); + } + + /** + * @return array + */ + public static function environmentMappings(): array + { + return [ + 'local' => ['local', 'local'], + 'dev' => ['dev', 'development'], + 'development' => ['development', 'development'], + 'testing' => ['testing', 'development'], + 'staging' => ['staging', 'staging'], + 'production' => ['production', 'production'], + 'prod' => ['prod', 'production'], + 'unknown' => ['preview', 'production'], + 'empty' => ['', 'production'], + 'missing' => [null, 'production'], + ]; + } + + /** + * @return array + */ + public static function debugValues(): array + { + return ['enabled' => [true], 'disabled' => [false]]; + } + + /** + * @return array + */ + public static function nativeEnvironments(): array + { + return [ + 'local' => ['local'], + 'development' => ['development'], + 'staging' => ['staging'], + 'production' => ['production'], + 'empty' => [''], + 'invalid' => ['preview'], + ]; + } + + /** + * @return array + */ + public static function explicitValues(): array + { + return [ + 'valid' => ['staging'], + 'empty' => [''], + 'invalid' => ['preview'], + 'false' => [false], + ]; + } + + /** + * @param array $constants + */ + private function registerWordPressConstants(?string $environment = 'local', array $constants = [], bool $debug = true): void + { + $app = new Application(dirname(__DIR__, 3)); + $app->instance('config', new Repository([ + 'app' => ['env' => $environment, 'debug' => $debug, 'url' => 'http://environment.test'], + 'wordpress' => ['constants' => $constants], + ])); + $app->instance('request', Request::create('/')); + $app->instance('constant.manager', new ConstantManager); + Facade::setFacadeApplication($app); + + $bootstrap = new Bootstrap( + new ConsoleDetectionService(new LaravelConsoleDetector($app)), + new LaravelDebugDetector($app), + \Mockery::mock(Action::class), + ); + + $bootstrap->register(); + } +} From 7d46f388ed52bef1939cba39c8f231d2bdc4acd3 Mon Sep 17 00:00:00 2001 From: Olivier Gorzalka Date: Tue, 13 Oct 2026 19:42:35 +0200 Subject: [PATCH 2/2] test: keep pings on in the E2E fixture, WordPress 7.1 disables them outside production The site now reports its environment as local, so the trackback test needs the wp_should_disable_pings_for_environment filter. The changelog says what the new environment changes for pings. --- CHANGELOG.md | 1 + tests/e2e/fixtures/plugins/e2e-features/e2e-features.php | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3aff6c7..206fc948 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `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 +- `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` diff --git a/tests/e2e/fixtures/plugins/e2e-features/e2e-features.php b/tests/e2e/fixtures/plugins/e2e-features/e2e-features.php index abcee5d3..73bd5e59 100644 --- a/tests/e2e/fixtures/plugins/e2e-features/e2e-features.php +++ b/tests/e2e/fixtures/plugins/e2e-features/e2e-features.php @@ -15,4 +15,7 @@ exit; } +// The site follows APP_ENV (local), and WordPress 7.1 refuses pings outside production: the trackback test needs them. +add_filter('wp_should_disable_pings_for_environment', '__return_false'); + pollora_register(ModuleType::Plugin, 'e2e-features', __DIR__);