diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml
index c6317a2..8485ff7 100644
--- a/.github/workflows/qa.yml
+++ b/.github/workflows/qa.yml
@@ -9,6 +9,14 @@ on:
- '**phpunit.xml.dist'
- '**psalm.xml'
- '**composer.json'
+ pull_request:
+ paths:
+ - '**workflows/qa.yml'
+ - '**.php'
+ - '**phpcs.xml.dist'
+ - '**phpunit.xml.dist'
+ - '**psalm.xml'
+ - '**composer.json'
workflow_dispatch:
inputs:
jobs:
@@ -20,7 +28,8 @@ on:
- 'Run all'
- 'Run PHPCS only'
- 'Run Psalm only'
- - 'Run static analysis (PHPCS + Psalm)'
+ - 'Run lint only'
+ - 'Run static analysis (PHPCS + Psalm + lint)'
- 'Run unit tests only'
concurrency:
@@ -28,19 +37,37 @@ concurrency:
cancel-in-progress: true
jobs:
+ lint-php:
+ uses: inpsyde/reusable-workflows/.github/workflows/lint-php.yml@main
+ if: ${{ (github.event_name != 'workflow_dispatch') || ((github.event.inputs.jobs != 'Run PHPCS only') && (github.event.inputs.jobs != 'Run Psalm only') && (github.event.inputs.jobs != 'Run unit tests only')) }}
+ strategy:
+ matrix:
+ php: [ '7.4', '8.0', '8.1', '8.2', '8.3' ]
+ with:
+ PHP_VERSION: ${{ matrix.php }}
+
coding-standards-analysis-php:
- if: ${{ (github.event_name != 'workflow_dispatch') || ((github.event.inputs.jobs != 'Run unit tests only') && (github.event.inputs.jobs != 'Run Psalm only')) }}
+ if: ${{ (github.event_name != 'workflow_dispatch') || ((github.event.inputs.jobs != 'Run lint only') && (github.event.inputs.jobs != 'Run Psalm only') && (github.event.inputs.jobs != 'Run unit tests only')) }}
uses: inpsyde/reusable-workflows/.github/workflows/coding-standards-php.yml@main
+ with:
+ PHP_VERSION: '8.0'
static-code-analysis-php:
- if: ${{ (github.event_name != 'workflow_dispatch') || ((github.event.inputs.jobs != 'Run unit tests only') && (github.event.inputs.jobs != 'Run PHPCS only')) }}
+ if: ${{ (github.event_name != 'workflow_dispatch') || ((github.event.inputs.jobs != 'Run PHPCS only') && (github.event.inputs.jobs != 'Run lint only') && (github.event.inputs.jobs != 'Run unit tests only')) }}
uses: inpsyde/reusable-workflows/.github/workflows/static-analysis-php.yml@main
+ strategy:
+ matrix:
+ php: [ '7.4', '8.0', '8.1', '8.2', '8.3' ]
+ with:
+ PHP_VERSION: ${{ matrix.php }}
+ PSALM_ARGS: '--no-suggestions --report-show-info=false --find-unused-psalm-suppress --no-diff --no-cache --no-file-cache --long-progress'
tests-unit-php:
if: ${{ (github.event_name != 'workflow_dispatch') || ((github.event.inputs.jobs == 'Run all') || (github.event.inputs.jobs == 'Run unit tests only')) }}
uses: inpsyde/reusable-workflows/.github/workflows/tests-unit-php.yml@main
strategy:
matrix:
- php: ["7.1", "7.2", "7.3", "7.4", "8.0", "8.1"]
+ php: [ '7.4', '8.0', '8.1', '8.2', '8.3' ]
with:
PHP_VERSION: ${{ matrix.php }}
+ PHPUNIT_ARGS: '--no-coverage'
diff --git a/.psalm/autoloader.php b/.psalm/autoloader.php
deleted file mode 100644
index 3592d19..0000000
--- a/.psalm/autoloader.php
+++ /dev/null
@@ -1,17 +0,0 @@
-is(WpContext::INSTALLING)` / `->isInstalling()`
- `->is(WpContext::WP_ACTIVATE)` / `->isWpActivate()`
+
+
### About "core" and "installing" contexts
`WpContext::isCore()` checks for the constants `ABSPATH` being defined, which means that it will normally be true when all the check for other contexts is also true, but `WpContext::isInstalling()` is an exception to that (more on this below).
@@ -73,6 +77,8 @@ if (Inpsyde\WpContext::determine()->isCore()) {
which might look very fine, could break if `WP_INSTALLING` is true, considering in that case the options table might not be there at all. Thanks to the fact that `WpContext::isCore()` returns false when `WP_INSTALLING` is true the `get_option` call above is not executed during installation (when
it is not safe to call).
+
+
### About "installing" and "activate" contexts
The previous section states:
@@ -140,6 +146,13 @@ Note that `$context->force(WpContext::CLI)` can still be used to "simulate" requ
+## Requirements
+
+- WordPress 6.1+
+- PHP 7.4+
+
+
+
## Crafted by Inpsyde
The team at [Inpsyde](https://inpsyde.com) is engineering the Web since 2006.
@@ -148,7 +161,7 @@ The team at [Inpsyde](https://inpsyde.com) is engineering the Web since 2006.
## License
-Copyright (c) 2020 Inpsyde GmbH
+Copyright (c) 2024 Inpsyde GmbH
This library is released under ["GPL 2.0 or later" License](LICENSE).
diff --git a/composer.json b/composer.json
index e96ed0d..e7617c9 100644
--- a/composer.json
+++ b/composer.json
@@ -16,16 +16,26 @@
"role": "Developer"
}
],
+ "repositories": [
+ {
+ "type": "composer",
+ "url": "https://raw.githubusercontent.com/inpsyde/wp-stubs/main",
+ "only": [
+ "inpsyde/wp-stubs-versions"
+ ]
+ }
+ ],
"require": {
- "php": ">=7.1"
+ "php": ">=7.4 < 8.4",
+ "ext-json": "*"
},
"require-dev": {
- "phpunit/phpunit": "~7.5.20 || ^8",
- "inpsyde/php-coding-standards": "^1",
- "vimeo/psalm": "^4.27.0",
- "mockery/mockery": "~1.3.6",
+ "phpunit/phpunit": "^9.6.17",
+ "inpsyde/php-coding-standards": "^2@dev",
+ "vimeo/psalm": "^5.22.2",
"brain/monkey": "^2.6.1",
- "inpsyde/wp-stubs": "dev-main"
+ "roots/wordpress-no-content": ">=6.1",
+ "inpsyde/wp-stubs-versions": "dev-latest"
},
"autoload": {
"psr-4": {
@@ -44,10 +54,8 @@
"psalm": "@php ./vendor/vimeo/psalm/psalm --no-cache --output-format=compact",
"tests": "@php ./vendor/phpunit/phpunit/phpunit",
"tests:no-cov": "@php ./vendor/phpunit/phpunit/phpunit --no-coverage",
- "phpcompat": "@php ./vendor/squizlabs/php_codesniffer/bin/phpcs -p . --standard=PHPCompatibility --ignore=*/vendor/* --extensions=php --basepath=./ --runtime-set testVersion 7.1-",
"qa": [
"@cs",
- "@phpcompat",
"@psalm",
"@tests:no-cov"
]
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
index 6708883..8599be2 100644
--- a/phpcs.xml.dist
+++ b/phpcs.xml.dist
@@ -6,7 +6,7 @@
-
+
diff --git a/psalm.xml b/psalm.xml
index 607e584..80dcd76 100644
--- a/psalm.xml
+++ b/psalm.xml
@@ -4,16 +4,15 @@
useDocblockPropertyTypes="true"
usePhpDocMethodsWithoutMagicCall="true"
strictBinaryOperands="true"
- ignoreInternalFunctionFalseReturn="false"
- ignoreInternalFunctionNullReturn="false"
+ findUnusedBaselineEntry="true"
+ findUnusedCode="false"
hideExternalErrors="true"
- allowNamedArgumentCalls="false"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
-
+
diff --git a/src/WpContext.php b/src/WpContext.php
index 0962c16..9bc3e51 100644
--- a/src/WpContext.php
+++ b/src/WpContext.php
@@ -18,6 +18,7 @@ class WpContext implements \JsonSerializable
public const XML_RPC = 'xml-rpc';
public const WP_ACTIVATE = 'wp-activate';
+ private const ACTIONS_PRIORITY = -300982;
private const ALL = [
self::AJAX,
self::BACKOFFICE,
@@ -32,15 +33,11 @@ class WpContext implements \JsonSerializable
self::WP_ACTIVATE,
];
- /**
- * @var array
- */
- private $data;
+ /** @var array, bool> */
+ private array $data;
- /**
- * @var array
- */
- private $actionCallbacks = [];
+ /** @var array */
+ private array $actionCallbacks = [];
/**
* @return WpContext
@@ -107,9 +104,10 @@ final public static function determine(): WpContext
*/
private static function isRestRequest(): bool
{
+ /** @psalm-suppress RedundantCondition */
if (
- (defined('REST_REQUEST') && REST_REQUEST)
- || !empty($_GET['rest_route']) // phpcs:ignore
+ (defined('REST_REQUEST') && \REST_REQUEST)
+ || (isset($_GET['rest_route']) && (bool) $_GET['rest_route']) // phpcs:ignore
) {
return true;
}
@@ -127,8 +125,11 @@ private static function isRestRequest(): bool
$GLOBALS['wp_rewrite'] = new \WP_Rewrite();
}
- $currentPath = trim((string)parse_url((string)add_query_arg([]), PHP_URL_PATH), '/') . '/';
- $restPath = trim((string)parse_url((string)get_rest_url(), PHP_URL_PATH), '/') . '/';
+ $currentUrl = (string) parse_url((string) add_query_arg([]), PHP_URL_PATH);
+ $currentPath = trim($currentUrl, '/') . '/';
+
+ $restUrlPath = (string) parse_url((string) get_rest_url(), PHP_URL_PATH);
+ $restPath = trim($restUrlPath, '/') . '/';
return strpos($currentPath, $restPath) === 0;
}
@@ -138,28 +139,7 @@ private static function isRestRequest(): bool
*/
private static function isLoginRequest(): bool
{
- /**
- * New core function with WordPress 6.1
- * @link https://make.wordpress.org/core/2022/09/11/new-is_login-function-for-determining-if-a-page-is-the-login-screen/
- */
- if (function_exists('is_login')) {
- return is_login() !== false;
- }
-
- if (!empty($_REQUEST['interim-login'])) { // phpcs:ignore
- return true;
- }
-
- /**
- * Fallback and 1:1 copy from is_login() in case, the function is
- * not available for WP < 6.1.
- * phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotValidated
- * phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash
- * phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
- */
- $scriptName = $_SERVER['SCRIPT_NAME'] ?? '';
-
- return false !== stripos(wp_login_url(), $scriptName);
+ return is_login() !== false;
}
/**
@@ -177,19 +157,19 @@ private static function isWpActivateRequest(): bool
*/
private static function isPageNow(string $page, string $url): bool
{
- $pageNow = (string)($GLOBALS['pagenow'] ?? '');
+ $pageNow = (string) ($GLOBALS['pagenow'] ?? '');
if ($pageNow && (basename($pageNow) === $page)) {
return true;
}
- $currentPath = (string)parse_url(add_query_arg([]), PHP_URL_PATH);
- $targetPath = (string)parse_url($url, PHP_URL_PATH);
+ $currentPath = (string) parse_url(add_query_arg([]), PHP_URL_PATH);
+ $targetPath = (string) parse_url($url, PHP_URL_PATH);
return trim($currentPath, '/') === trim($targetPath, '/');
}
/**
- * @param array $data
+ * @param array, bool> $data
*/
private function __construct(array $data)
{
@@ -203,7 +183,7 @@ private function __construct(array $data)
final public function force(string $context): WpContext
{
if (!in_array($context, self::ALL, true)) {
- throw new \LogicException("'{$context}' is not a valid context.");
+ throw new \LogicException(esc_html("'{$context}' is not a valid context."));
}
$this->removeActionHooks();
@@ -336,7 +316,7 @@ public function isWpActivate(): bool
}
/**
- * @return array
+ * @return array, bool>
*/
public function jsonSerialize(): array
{
@@ -372,7 +352,7 @@ private function addActionHooks(): void
];
foreach ($this->actionCallbacks as $action => $callback) {
- add_action($action, $callback, PHP_INT_MIN);
+ add_action($action, $callback, self::ACTIONS_PRIORITY);
}
}
@@ -385,7 +365,7 @@ private function addActionHooks(): void
private function removeActionHooks(): void
{
foreach ($this->actionCallbacks as $action => $callback) {
- remove_action($action, $callback, PHP_INT_MIN);
+ remove_action($action, $callback, self::ACTIONS_PRIORITY);
}
$this->actionCallbacks = [];
}
diff --git a/tests/WpContextTest.php b/tests/WpContextTest.php
index b03a650..cc03745 100644
--- a/tests/WpContextTest.php
+++ b/tests/WpContextTest.php
@@ -16,10 +16,7 @@ class WpContextTest extends TestCase
{
use MockeryPHPUnitIntegration;
- /**
- * @var string
- */
- private $currentPath = '/';
+ private string $currentPath = '/';
/**
* @return void
@@ -107,9 +104,11 @@ public function testIsLoginLate(): void
$onLoginInit = null;
Monkey\Actions\expectAdded('login_init')
- ->whenHappen(static function (callable $callback) use (&$onLoginInit) {
- $onLoginInit = $callback;
- });
+ ->whenHappen(
+ static function (callable $callback) use (&$onLoginInit): void {
+ $onLoginInit = $callback;
+ }
+ );
$context = WpContext::determine();
@@ -163,9 +162,11 @@ public function testIsRestLate(): void
$onRestInit = null;
Monkey\Actions\expectAdded('rest_api_init')
- ->whenHappen(static function (callable $callback) use (&$onRestInit) {
- $onRestInit = $callback;
- });
+ ->whenHappen(
+ static function (callable $callback) use (&$onRestInit): void {
+ $onRestInit = $callback;
+ }
+ );
$context = WpContext::determine();
@@ -374,9 +375,11 @@ public function testIsWpActivateLate(): void
$onActivateHeader = null;
Monkey\Actions\expectAdded('activate_header')
- ->whenHappen(static function (callable $callback) use (&$onActivateHeader) {
- $onActivateHeader = $callback;
- });
+ ->whenHappen(
+ static function (callable $callback) use (&$onActivateHeader): void {
+ $onActivateHeader = $callback;
+ }
+ );
$context = WpContext::determine();
@@ -431,7 +434,7 @@ public function testJsonSerialize(): void
$this->mockIsLoginRequest(true);
$context = WpContext::determine();
- $decoded = (array)json_decode((string)json_encode($context), true);
+ $decoded = (array) json_decode((string) json_encode($context), true);
static::assertTrue($decoded[WpContext::CORE]);
static::assertTrue($decoded[WpContext::LOGIN]);
@@ -445,6 +448,7 @@ public function testJsonSerialize(): void
/**
* @param bool $is
+ * @return void
*/
private function mockIsRestRequest(bool $is): void
{
@@ -456,6 +460,7 @@ private function mockIsRestRequest(bool $is): void
/**
* @param bool $is
+ * @return void
*/
private function mockIsLoginRequest(bool $is): void
{
@@ -465,6 +470,7 @@ private function mockIsLoginRequest(bool $is): void
/**
* @param bool $is
+ * @return void
*/
private function mockIsActivateRequest(bool $is): void
{