Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: szepeviktor/phpstan-wordpress
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v2.0.0-rc.3
Choose a base ref
...
head repository: szepeviktor/phpstan-wordpress
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 2.x
Choose a head ref
  • 5 commits
  • 14 files changed
  • 1 contributor

Commits on Dec 1, 2024

  1. Copy the full SHA
    f7beb13 View commit details

Commits on Dec 2, 2024

  1. Copy the full SHA
    6686dc2 View commit details

Commits on Dec 17, 2024

  1. Add rule to suggest replacing WP constants with function calls (#276)

    IanDelMar authored Dec 17, 2024
    Copy the full SHA
    22aaab0 View commit details

Commits on Feb 12, 2025

  1. Fix typo (#278)

    IanDelMar authored Feb 12, 2025
    Copy the full SHA
    91d6888 View commit details
  2. Update examples (#279)

    IanDelMar authored Feb 12, 2025
    Copy the full SHA
    963887b View commit details
4 changes: 2 additions & 2 deletions examples/composer.json
Original file line number Diff line number Diff line change
@@ -2,10 +2,10 @@
"name": "company/wordpress-project",
"description": "WordPress project.",
"require": {
"php": "^7.1"
"php": "^7.4 || ^8.0"
},
"require-dev": {
"szepeviktor/phpstan-wordpress": "^0.6.0"
"szepeviktor/phpstan-wordpress": "^2.0"
},
"config": {
"optimize-autoloader": true
9 changes: 0 additions & 9 deletions examples/fix-callable.php

This file was deleted.

7 changes: 0 additions & 7 deletions examples/fix-parse_url.php

This file was deleted.

7 changes: 0 additions & 7 deletions examples/fix-wpdb.php

This file was deleted.

23 changes: 0 additions & 23 deletions examples/impure-have-functions.stub
Original file line number Diff line number Diff line change
@@ -1,28 +1,5 @@
<?php

/**
* Whether current WordPress query has results to loop over.
*
* @return bool
* @phpstan-impure
*/
function have_posts()
{
}

class WP_Query
{
/**
* Determines whether there are more posts available in the loop.
*
* @return bool True if posts are available, false if end of loop.
* @phpstan-impure
*/
public function have_posts()
{
}
}

/**
* ACF have_rows()
*
9 changes: 0 additions & 9 deletions examples/phpstan-full.neon
Original file line number Diff line number Diff line change
@@ -26,15 +26,6 @@ parameters:
ignoreErrors:
# Uses func_get_args()
- '#^Function apply_filters(_ref_array)? invoked with [34567] parameters, 2 required\.$#'
# Fixed in WordPress 5.3
#- '#^Function do_action(_ref_array)? invoked with [3456] parameters, 1-2 required\.$#'
#- '#^Function current_user_can invoked with 2 parameters, 1 required\.$#'
#- '#^Function add_query_arg invoked with [123] parameters?, 0 required\.$#'
#- '#^Function wp_sprintf invoked with [23456] parameters, 1 required\.$#'
#- '#^Function add_post_type_support invoked with [345] parameters, 2 required\.$#'
#- '#^Function ((get|add)_theme_support|current_theme_supports) invoked with [2345] parameters, 1 required\.$#'
# Fixed in WordPress 5.2 - https://core.trac.wordpress.org/ticket/43304
#- '/^Parameter #2 \$deprecated of function load_plugin_textdomain expects string, false given\.$/'
# WP-CLI accepts a class name as callable
- '/^Parameter #2 \$callable of static method WP_CLI::add_command\(\) expects callable\(\): mixed, \S+ given\.$/'
# Please consider commenting ignores: issue URL or reason for ignoring
1 change: 1 addition & 0 deletions extension.neon
Original file line number Diff line number Diff line change
@@ -32,6 +32,7 @@ services:
rules:
- SzepeViktor\PHPStan\WordPress\HookCallbackRule
- SzepeViktor\PHPStan\WordPress\HookDocsRule
- SzepeViktor\PHPStan\WordPress\WpConstantFetchRule
parameters:
bootstrapFiles:
- ../../php-stubs/wordpress-stubs/wordpress-stubs.php
4 changes: 4 additions & 0 deletions src/HookCallbackRule.php
Original file line number Diff line number Diff line change
@@ -66,6 +66,10 @@ public function processNode(Node $node, Scope $scope): array
return [];
}

if (! $this->reflectionProvider->hasFunction($node->name, $scope)) {
return [];
}

$functionReflection = $this->reflectionProvider->getFunction($node->name, $scope);

if (! in_array($functionReflection->getName(), self::SUPPORTED_FUNCTIONS, true)) {
91 changes: 91 additions & 0 deletions src/WpConstantFetchRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php

/**
* Custom rule to discourage using WordPress constants fetchable via functions.
*/

declare(strict_types=1);

namespace SzepeViktor\PHPStan\WordPress;

use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\RuleErrorBuilder;

use function array_key_exists;
use function sprintf;

/**
* @implements \PHPStan\Rules\Rule<\PhpParser\Node\Expr\ConstFetch>
*/
final class WpConstantFetchRule implements \PHPStan\Rules\Rule
{
protected const REPLACEMENTS = [
'BACKGROUND_COLOR' => "get_theme_support('custom-background')",
'BACKGROUND_IMAGE' => "get_theme_support('custom-background')",
'DISALLOW_FILE_MODS' => 'wp_is_file_mod_allowed()',
'DOING_AJAX' => 'wp_doing_ajax()',
'DOING_CRON' => 'wp_doing_cron()',
'FORCE_SSL_ADMIN' => 'force_ssl_admin()',
'FORCE_SSL_LOGIN' => 'force_ssl_admin()',
'FS_METHOD' => 'get_filesystem_method()',
'HEADER_IMAGE' => "get_theme_support('custom-header')",
'HEADER_IMAGE_WIDTH' => "get_theme_support('custom-header')",
'HEADER_IMAGE_HEIGHT' => "get_theme_support('custom-header')",
'HEADER_TEXTCOLOR' => "get_theme_support('custom-header')",
'MULTISITE' => 'is_multisite()',
'NO_HEADER_TEXT' => "get_theme_support('custom-header')",
'STYLESHEETPATH' => 'get_stylesheet_directory()',
'SUBDOMAIN_INSTALL' => 'is_subdomain_install()',
'TEMPLATEPATH' => 'get_template_directory()',
'UPLOADS' => 'wp_get_upload_dir() or wp_upload_dir(null, false)',
'VHOST' => 'is_subdomain_install()',
'WP_ADMIN' => 'is_admin()',
'WP_BLOG_ADMIN' => 'is_blog_admin()',
'WP_CONTENT_URL' => 'content_url()',
'WP_DEVELOPMENT_MODE' => 'wp_get_development_mode()',
'WP_HOME' => 'home_url() or get_home_url()',
'WP_INSTALLING' => 'wp_installing()',
'WP_NETWORK_ADMIN' => 'is_network_admin()',
'WP_PLUGIN_URL' => "plugins_url() or plugin_dir_url('')",
'WP_POST_REVISIONS' => 'wp_revisions_to_keep()',
'WP_SITEURL' => 'site_url() or get_site_url()',
'WP_USER_ADMIN' => 'is_user_admin()',
'WP_USE_THEMES' => 'wp_using_themes()',
'WPMU_PLUGIN_URL' => 'plugins_url()',
];

public function getNodeType(): string
{
return Node\Expr\ConstFetch::class;
}

public function processNode(Node $node, Scope $scope): array
{
if (! $this->isDiscouragedConstant($node->name)) {
return [];
}

return [
RuleErrorBuilder::message(
sprintf(
'Found usage of constant %s. Use %s instead.',
(string)$node->name,
$this->getReplacement($node->name)
)
)
->identifier('phpstanWP.wpConstant.fetch')
->build(),
];
}

private function isDiscouragedConstant(Node\Name $constantName): bool
{
return array_key_exists((string)$constantName, self::REPLACEMENTS);
}

private function getReplacement(Node\Name $constantName): string
{
return self::REPLACEMENTS[(string)$constantName];
}
}
1 change: 1 addition & 0 deletions tests/HookCallbackRuleTest.php
Original file line number Diff line number Diff line change
@@ -54,6 +54,7 @@ public function testRule(): void
$this->analyse(
[
__DIR__ . '/data/hook-callback.php',
__DIR__ . '/data/internal-error.php',
],
self::EXPECTED_ERRORS
);
1 change: 1 addition & 0 deletions tests/HookDocsRuleTest.php
Original file line number Diff line number Diff line change
@@ -31,6 +31,7 @@ public function testRule(): void
$this->analyse(
[
__DIR__ . '/data/hook-docs.php',
__DIR__ . '/data/internal-error.php',
],
[
[
63 changes: 63 additions & 0 deletions tests/WpConstantFetchRuleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

declare(strict_types=1);

namespace SzepeViktor\PHPStan\WordPress\Tests;

use PHPStan\Rules\Rule;
use SzepeViktor\PHPStan\WordPress\WpConstantFetchRule;

/**
* @extends \PHPStan\Testing\RuleTestCase<\SzepeViktor\PHPStan\WordPress\WpConstantFetchRule>
*/
class WpConstantFetchRuleTest extends \PHPStan\Testing\RuleTestCase
{
protected function getRule(): Rule
{
return new WpConstantFetchRule();
}

public function testRule(): void
{
$this->analyse(
[
__DIR__ . '/data/wp-constants.php',
__DIR__ . '/data/internal-error.php',
],
[
["Found usage of constant BACKGROUND_COLOR. Use get_theme_support('custom-background') instead.", 9],
["Found usage of constant BACKGROUND_IMAGE. Use get_theme_support('custom-background') instead.", 10],
['Found usage of constant DISALLOW_FILE_MODS. Use wp_is_file_mod_allowed() instead.', 11],
['Found usage of constant DOING_AJAX. Use wp_doing_ajax() instead.', 12],
['Found usage of constant DOING_CRON. Use wp_doing_cron() instead.', 13],
['Found usage of constant FORCE_SSL_ADMIN. Use force_ssl_admin() instead.', 14],
['Found usage of constant FORCE_SSL_LOGIN. Use force_ssl_admin() instead.', 15],
['Found usage of constant FS_METHOD. Use get_filesystem_method() instead.', 16],
['Found usage of constant MULTISITE. Use is_multisite() instead.', 17],
['Found usage of constant STYLESHEETPATH. Use get_stylesheet_directory() instead.', 18],
['Found usage of constant SUBDOMAIN_INSTALL. Use is_subdomain_install() instead.', 19],
['Found usage of constant TEMPLATEPATH. Use get_template_directory() instead.', 20],
['Found usage of constant UPLOADS. Use wp_get_upload_dir() or wp_upload_dir(null, false) instead.', 21],
['Found usage of constant VHOST. Use is_subdomain_install() instead.', 22],
['Found usage of constant WP_ADMIN. Use is_admin() instead.', 23],
['Found usage of constant WP_BLOG_ADMIN. Use is_blog_admin() instead.', 24],
['Found usage of constant WP_CONTENT_URL. Use content_url() instead.', 25],
['Found usage of constant WP_DEVELOPMENT_MODE. Use wp_get_development_mode() instead.', 26],
['Found usage of constant WP_HOME. Use home_url() or get_home_url() instead.', 27],
['Found usage of constant WP_INSTALLING. Use wp_installing() instead.', 28],
['Found usage of constant WP_NETWORK_ADMIN. Use is_network_admin() instead.', 29],
["Found usage of constant WP_PLUGIN_URL. Use plugins_url() or plugin_dir_url('') instead.", 30],
['Found usage of constant WP_POST_REVISIONS. Use wp_revisions_to_keep() instead.', 31],
['Found usage of constant WP_SITEURL. Use site_url() or get_site_url() instead.', 32],
['Found usage of constant WP_USER_ADMIN. Use is_user_admin() instead.', 33],
['Found usage of constant WP_USE_THEMES. Use wp_using_themes() instead.', 34],
['Found usage of constant WPMU_PLUGIN_URL. Use plugins_url() instead.', 35],
]
);
}

public static function getAdditionalConfigFiles(): array
{
return [dirname(__DIR__) . '/vendor/szepeviktor/phpstan-wordpress/extension.neon'];
}
}
7 changes: 7 additions & 0 deletions tests/data/internal-error.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

/*
* Intentionally call an undefined function to ensure it does not trigger an internal error.
* Related issue: https://github.com/szepeviktor/phpstan-wordpress/issues/271
*/
callingUndefinedFunction();
35 changes: 35 additions & 0 deletions tests/data/wp-constants.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace SzepeViktor\PHPStan\WordPress\Tests;

// WpConstantFetchRule
$ruleDoesNotApply = MB_IN_BYTES;
$ruleApplies = BACKGROUND_COLOR;
$ruleApplies = BACKGROUND_IMAGE;
$ruleApplies = DISALLOW_FILE_MODS;
$ruleApplies = DOING_AJAX;
$ruleApplies = DOING_CRON;
$ruleApplies = FORCE_SSL_ADMIN;
$ruleApplies = FORCE_SSL_LOGIN;
$ruleApplies = FS_METHOD;
$ruleApplies = MULTISITE;
$ruleApplies = STYLESHEETPATH;
$ruleApplies = SUBDOMAIN_INSTALL;
$ruleApplies = TEMPLATEPATH;
$ruleApplies = UPLOADS;
$ruleApplies = VHOST;
$ruleApplies = WP_ADMIN;
$ruleApplies = WP_BLOG_ADMIN;
$ruleApplies = WP_CONTENT_URL;
$ruleApplies = WP_DEVELOPMENT_MODE;
$ruleApplies = WP_HOME;
$ruleApplies = WP_INSTALLING;
$ruleApplies = WP_NETWORK_ADMIN;
$ruleApplies = WP_PLUGIN_URL;
$ruleApplies = WP_POST_REVISIONS;
$ruleApplies = WP_SITEURL;
$ruleApplies = WP_USER_ADMIN;
$ruleApplies = WP_USE_THEMES;
$ruleApplies = WPMU_PLUGIN_URL;