Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
9 changes: 9 additions & 0 deletions src/WordPress/Bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
174 changes: 174 additions & 0 deletions tests/Unit/WordPress/BootstrapEnvironmentTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
<?php

declare(strict_types=1);

namespace Tests\Unit\WordPress;

use Illuminate\Config\Repository;
use Illuminate\Foundation\Application;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Facade;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\PreserveGlobalState;
use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
use Pollora\Application\Application\Services\ConsoleDetectionService;
use Pollora\Application\Infrastructure\Services\LaravelConsoleDetector;
use Pollora\Application\Infrastructure\Services\LaravelDebugDetector;
use Pollora\Hook\Domain\Contract\Action;
use Pollora\WordPress\Bootstrap;
use Pollora\WordPress\Config\ConstantManager;
use Tests\TestCase;

// Pest does not set the Composer path used by PHPUnit's isolated runner.
if (! defined('PHPUNIT_COMPOSER_INSTALL')) {
define('PHPUNIT_COMPOSER_INSTALL', dirname(__DIR__, 3).'/vendor/autoload.php');
}

#[RunTestsInSeparateProcesses]
#[PreserveGlobalState(false)]
final class BootstrapEnvironmentTest extends TestCase
{
public function test_local_environment_is_registered_before_word_press_loads(): void
{
putenv('WP_ENVIRONMENT_TYPE');
$this->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<string, array{string|null, string}>
*/
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<string, array{bool}>
*/
public static function debugValues(): array
{
return ['enabled' => [true], 'disabled' => [false]];
}

/**
* @return array<string, array{string}>
*/
public static function nativeEnvironments(): array
{
return [
'local' => ['local'],
'development' => ['development'],
'staging' => ['staging'],
'production' => ['production'],
'empty' => [''],
'invalid' => ['preview'],
];
}

/**
* @return array<string, array{mixed}>
*/
public static function explicitValues(): array
{
return [
'valid' => ['staging'],
'empty' => [''],
'invalid' => ['preview'],
'false' => [false],
];
}

/**
* @param array<string, mixed> $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();
}
}
3 changes: 3 additions & 0 deletions tests/e2e/fixtures/plugins/e2e-features/e2e-features.php
Original file line number Diff line number Diff line change
Expand Up @@ -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__);
Loading