Skip to content

Add new check for production-time PHP error reporting changes #1315

@davidperezgar

Description

@davidperezgar

Add a new check that flags plugins which modify PHP error-reporting configuration at runtime — i.e. calls and constants that turn on debug output or override the host's error settings from within the plugin code.

Background

A plugin must not change PHP error reporting in production. The host (or wp-config.php) is responsible for choosing whether errors are shown, logged or hidden. When a plugin overrides that, several things go wrong:

  • It can leak sensitive information (paths, secrets, stack traces) on production sites.
  • It breaks debugging for everyone else: another plugin or theme author trying to debug their own code on a site that uses this plugin will get a polluted output.
  • It defeats WP_DEBUG: if the plugin forces display_errors off or redefines WP_DEBUG, the site owner can no longer use the standard debugging workflow.
  • It changes runtime behaviour silently for every other plugin loaded on the site.

The recommended position has been consistent for years: a plugin should not call error_reporting(), should not ini_set('display_errors', ...), and should not redefine debug constants such as WP_DEBUG, WP_DEBUG_LOG, WP_DEBUG_DISPLAY or SCRIPT_DEBUG.

Check definition

  • Name (proposed): Php_Error_Reporting_Check
  • Category: General (or Performance if preferred — this is a "bad practice / production hygiene" check)
  • Type: Static (extends Abstract_File_Check or piggybacks on the PHPCS runner)
  • Severity: Warning (8 in the Internal Scanner's scale)
  • Scope: All PHP files inside the plugin

Detection logic

Flag any of the following patterns:

  1. Direct calls to error_reporting() with any argument.
  2. ini_set() / ini_alter() whose first argument (string literal) is one of:
    • error_reporting
    • display_errors
  3. define() / defined() overrides for the PHP debug constants:
    • WP_DEBUG
    • WP_DEBUG_LOG
    • WP_DEBUG_DISPLAY
    • SCRIPT_DEBUG
'php_error' => [
    'title'    => '# ░░ BAD PRACTICE: Changing error reporting configuration',
    'severity' => 8,
    'functions' => [
        [
            'names' => [ 'error_reporting' ],
        ],
        [
            'names'  => [ 'ini_set', 'ini_alter' ],
            'params' => [
                0 => [[
                    'type'    => 'PhpParser\Node\Scalar\String_',
                    'compare' => 'value',
                    'value'   => [ 'error_reporting', 'display_errors' ],
                ]],
            ],
        ],
        [
            'names'  => [ 'define' ],
            'params' => [
                0 => [[
                    'type'    => 'PhpParser\Node\Scalar\String_',
                    'compare' => 'value',
                    'value'   => $wp_constants_php_error, // WP_DEBUG, WP_DEBUG_LOG, SCRIPT_DEBUG, WP_DEBUG_DISPLAY
                ]],
            ],
        ],
    ],
],

Consider it an issue if

  • The plugin calls error_reporting() anywhere in shipped code.
  • The plugin calls ini_set() / ini_alter() for error_reporting or display_errors.
  • The plugin define()s any of WP_DEBUG, WP_DEBUG_LOG, WP_DEBUG_DISPLAY, SCRIPT_DEBUG.

Do NOT consider it an issue if

  • The call lives only inside the plugin's own test suite (e.g. tests, phpunit*).
  • The call is guarded explicitly behind a developer-only constant the plugin itself defines for its own dev workflow, and that block is statically obvious (best-effort heuristic — otherwise, still warn).

Existing coverage / overlap

Plugin Check already references WordPress.PHP.DevelopmentFunctions in plugin-check.ruleset.xml, but that sniff focuses on debug output functions (var_dump, print_r, error_log, …), not on changing the error-reporting configuration itself. WPCS also ships WordPress.PHP.IniSet, which catches some ini_set('display_errors', ...) patterns but does not catch:

  • Direct error_reporting() calls.
  • define() overrides of WP_DEBUG* / SCRIPT_DEBUG.

So the proposed check fills a real gap. Two valid implementation paths:

  • Option A (recommended): dedicated Php_Error_Reporting_Check extending Abstract_File_Check, using token-level (or PHPParser) detection — gives precise control and the exact message below.
  • Option B (lighter): enable WordPress.PHP.IniSet + extend WordPress.PHP.DiscouragedPHPFunctions (or a custom mini-sniff) to also flag error_reporting and the define() of debug constants. Faster to ship but harder to phrase the message clearly.

User-facing message

When the check triggers:

Do not change PHP error reporting in production code

A plugin should not modify PHP's error-reporting configuration. Calls such as error_reporting(), ini_set('display_errors', …), or redefining WP_DEBUG, WP_DEBUG_LOG, WP_DEBUG_DISPLAY or SCRIPT_DEBUG change behaviour for every other plugin and theme on the site.

This can leak sensitive information (paths, secrets, stack traces) and breaks the standard debugging workflow for site owners and other developers. The host's php.ini and the site's wp-config.php are the correct places to control this.

Please remove these calls, or move them behind a strictly developer-only flag that is never set in shipped code.

See: https://www.php.net/manual/en/function.error-reporting.php

Acceptance criteria

  • New check class detecting the three patterns above (error_reporting() call, ini_set/ini_alter of error_reporting/display_errors, define() of WP_DEBUG* / SCRIPT_DEBUG).
  • Registered in Default_Check_Repository.
  • One result per occurrence, with file path, line number and the message above.
  • Unit/integration tests covering:
    • error_reporting( 0 ); → flagged.
    • error_reporting( E_ALL ); → flagged.
    • ini_set( 'display_errors', 1 ); → flagged.
    • ini_set( 'error_reporting', E_ALL ); → flagged.
    • define( 'WP_DEBUG', true ); → flagged.
    • define( 'WP_DEBUG_LOG', true ); → flagged.
    • define( 'SCRIPT_DEBUG', true ); → flagged.
    • ini_set( 'memory_limit', '256M' );not flagged by this check (covered separately, see php_settings in the Internal Scanner).
    • Same calls inside tests → not flagged.

References


This check is inspired by the work of Fran Torres (@frantorres) on the php_error check in the Internal Scanner.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ChecksAudit/test of the particular part of the plugin[Team] PluginsIssues owned by Plugins Team
    No fields configured for Enhancement.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions