jonbaldie / explicitness-checker
A tool to analyze explicitness of PHP code (implicit inputs/outputs).
Package info
github.com/jonbaldie/explicitness-checker
Type:phpstan-extension
pkg:composer/jonbaldie/explicitness-checker
Requires
- php: >=8.0
- nikic/php-parser: ^5.1
- phpstan/phpstan: ^2.1
Requires (Dev)
- phpmd/phpmd: ^2.15
- phpunit/phpunit: ^9.6
Suggests
None
Provides
None
Conflicts
None
Replaces
None
- dev-main
- v1.4.0
- v1.3.2
- v1.3.1
- v1.3.0
- v1.2.3
- v1.2.2
- v1.2.1
- v1.2.0
- v1.1.2
- v1.1.1
- v1.1.0
- v1.0.0
- dev-ci/pr-diff-mutations
- dev-release/1.4.0
- dev-feat/157-default-property-writes
- dev-feat/158-transitive-analysis
- dev-fleet/worktree-explicitness-checker-162
- dev-refactor/133-run-aggregation
- dev-feat/156-facade-calls
- dev-feat/161-clock-reads
- dev-fix/162-random-engines
- dev-fix/163-process-state
- dev-fix/help-options-153
- dev-docs/exploratory-2026-10-12
- dev-fleet/worktree-explicitness-checker-160
- dev-cursor/fresh-shallow-rebind-a662
- dev-cursor/local-argument-origins-5a29
- dev-cursor/function-call-findings-4221
- dev-cursor/static-call-both-directions-2dcb
- dev-release/1.3.2
- dev-release/1.3.1
- dev-release/1.3.0
- dev-fleet/ci-e8d3ca4545b74cb482488390a83409df-1-0ea2b4c0d8e1
- dev-fleet/ci-3f35d7ca434f4bffb746973edd88bcde-1-37f89e822856
- dev-fleet/ci-8f789847814544c5937dc755ea09cced-1-7749f4b88b3b
- dev-enhancement/80-cached-by-reference-parameters
- dev-fleet/ci-ffc9564a09584aca92067fdfab956a12-1-f3929606a9dd
- dev-release/1.2.3
- dev-fleet/ci-e325aee9e3074494bc61dd307db7e7d9-1-21432f65631a
- dev-release/1.2.2
- dev-fleet/ci-91e6dfdb28a541d0bd38ce66e201ca7c-1-43a13a696134
- dev-fleet/ci-afbef56fbe5b4d1c909692480f83c805-1-efb10c72cf09
- dev-release/1.2.1
- dev-fleet/ci-c0306379e3064ed5a48504393b67b311-1-9a3e7cb9c268
- dev-fleet/ci-dedb718deadc4b3e8e1f274460bddaaf-1-c942b0cc4383
- dev-docs/fleet-event-maintenance
- dev-fleet/worktree-explicitness-checker-95
- dev-fix/94-dynamic-this-property
- dev-fix/88-name-bindings
- dev-fleet/ci-0834d56eb67b402cbbe5c7fcc2f54f59-1-46f302acd28e
- dev-fix/82-property-chain-base-read
- dev-fleet/worktree-explicitness-checker-42
- dev-fleet/worktree-explicitness-checker-27
- dev-fleet/ci-b9f9f6d98d7c41fea026cdf8ac57468a-1-a3de9a87d70d
This package is auto-updated.
Last update: 2026-10-12 19:22:57 UTC
README
This is an explicitness parsing tool that you can run on your PHP projects to analyze the explicitness of your code.
What is explicitness?
Simply put, explicitness refers to the clarity of data flow through your functions. Ideally all of your inputs should be provided through function arguments, and all of your outputs should be returned through function return values.
function add($a, $b) { return $a + $b; // explicit input and output: all values are provided through function arguments and returned through function return values }
If your function accesses global variables not provided through its arguments, that is an implicit input. If your function changes global state without returning a value, that is an implicit output.
$some_global_number = 10; function add($a, $b) { global $some_global_number; echo "Calling add on $a and $b...\n"; // implicit output: printing to stdout --$some_global_number; // implicit output: decrementing global variable $some_global_number return $a + $b + $some_global_number; // implicit input: reading from global variable $some_global_number }
How does the tool work?
The tool uses nikic's PHP Parser library to parse PHP code and analyze the explicitness of your functions and class methods.
Which functions are checked, and how they're named
Every function-like with a body is checked on its own, wherever it's declared, and whether or not the file declares a namespace:
| Declared as | Reported as |
|---|---|
Function (including inside if blocks or other functions) |
Fully qualified name, e.g. App\Sub\send_mail |
| Method of a named class, trait or enum | Fully qualified class, e.g. App\Sub\Mailer::send |
Method of an anonymous class (new class { ... }) |
class@anonymous::send, or App\Service::class@anonymous::send when nested in a named class |
Property hook (PHP 8.4 get/set), including on a promoted constructor parameter |
Fully qualified class, property and hook, e.g. App\Sub\Temperature::$celsius::get, or class@anonymous::$celsius::get for an anonymous class; App\Service::class@anonymous::$celsius::get when nested in a named class |
| Closure or arrow function | {closure} |
Abstract and interface methods, and hooks declared without a body, aren't checked. Hooks are reported even when the tool itself runs on an older PHP version. Rows are listed in source order.
A function's analysis stops at any closure, arrow function, nested function or class declared inside it: code in there, including global declarations, belongs to that inner function-like, which gets its own row. So a closure that declares global $x doesn't make the enclosing function's local $x look like a global.
Behaviour change: earlier versions skipped closures, arrow functions, anonymous-class methods and conditionally declared functions in files without a namespace, dropped the namespace from method names, and let a closure's global declarations leak into the enclosing function. Upgrading can therefore add rows, remove false global findings from enclosing functions, rename methods, and change the exit code.
Why should I care about explicitness?
Implicit inputs and outputs fundamentally limit the modularity and reusability of your code.
Functions with implicit dependencies are like electronic components that are hardwired to other components - they can't be easily detached and used elsewhere. When a function reads from global variables (implicit inputs) or writes to global state or performs side effects like DOM manipulation (implicit outputs), it becomes tightly coupled to its environment. This means you can only use that function in very specific contexts where those global dependencies are available and properly configured. In contrast, functions with explicit inputs (arguments) and outputs (return values) are like modular connectors that can be plugged into any compatible system.
The practical consequences of implicit inputs and outputs make your code significantly harder to test, debug, and reason about.
Implicit inputs limit when you can call a function because you must ensure all the global state is properly set up beforehand, and you have to worry about other code potentially interfering with those shared variables. Implicit outputs similarly constrain when you can call a function - you can only call it when you actually want those side effects to occur. This makes testing particularly challenging because you must set up all the implicit inputs, run the function, and then verify all the implicit outputs, which becomes exponentially more complex as the number of implicit dependencies grows. Functions with only explicit inputs and outputs are much easier to test because you simply pass in arguments and check the return value, with no external setup or cleanup required.
Installation
Install via Composer:
composer require jonbaldie/explicitness-checker --dev
Laravel
Run the checker on app:
./vendor/bin/explicitness-checker app
-
Facade calls. Static invocations resolved under
Illuminate\Support\Facades\or rootFacades\report a possible read and write in default mode, with or without arguments. Imports, aliased imports and fully qualified names agree. This is a namespace-based heuristic; no Laravel bootstrap or runtime inheritance check is performed. First-class callable creation remains clean. -
Blind spot. Helpers with arguments (
env('APP_URL'),config('app.name'), ...) and arbitrary static calls with arguments remain outside detection. Global facade aliases such as\Cache::get($key)need an import to establish facade identity; the checker does not catalogue global aliases. Helpers and static calls without arguments retain their possible read/write findings. A clean result covers the selected direct detectors, rather than proving whole-program purity. -
--propsalso reports constructor-injected services ($this->orders), so expect it to flag most controllers and services. -
PHPStan with Larastan. Add this package's config next to the Larastan include from Larastan's docs:
includes: - vendor/larastan/larastan/extension.neon - vendor/jonbaldie/explicitness-checker/extension.neon
With
phpstan/extension-installer, delete both lines instead. The installer loads both extensions, and a file included twice makes PHPStan abort with "This file is included multiple times".
How do I run the tool?
./vendor/bin/explicitness-checker [-h|--help] [-v|--verbose] [--strict] [--props] [--transitive] [--exclude=dir] [--include-pattern=pattern] [--exclude-pattern=pattern] [--min-explicitness=percent] ./path/to/your/project
Default property-write detection reuses assignment, compound update, increment/decrement, unset, reference-binding and internal by-reference-call semantics. The constructor exception covers only that method's own body, including mixed-case names. Nested closures, arrows, classes and property hooks are analysed independently. Clone-and-write withers remain outside this rule. This is syntactic initialization handling, not escape or lifetime analysis.
Transitive analysis follows named function calls (including imports and namespaced/global fallback), and named $this, self, static and parent method calls in named classes. Method lookup uses the caller's declaring class and the nearest known parent; parent starts at the parent. This is a lexical approximation: inherited methods keep their declaring context, and runtime virtual/late-static dispatch is not expanded.
Only actual invocations create edges. Nested closures, arrows, functions and classes have their own analysis; constructing or passing them does not execute their bodies. Callable parameters and first-class callable creation create no edges. Unresolved, ambiguous, excluded, unreadable and unparseable declarations add no targets. Traits/adaptations, magic dispatch, arbitrary object calls, other-class static calls, constructors, literal-callable wrappers and callback dataflow remain outside this graph.
Propagation consumes the direct findings of the active strict/props modes, including conservative helper heuristics. Zero-argument user helpers may already be direct actions, even if their bodies return constants. This is a bounded selected-file analysis, not a whole-program purity proof. PHPStan behavior and parameters are unchanged; there is no active explicitness.callsAction PHPStan diagnostic.
Flags
Clock inference uses successful parsing metadata, not sampled current times. Invalid literals produce no proven clock read. For a literal format with non-literal input, a fixed UTC sample exercises its directives; formats that cannot parse that sample remain outside this inference. This is a bounded syntactic check, not a whole-language purity proof.
Randomness findings identify construction sites. A caller-provided Randomizer engine remains explicit; the checker does not prove that passed engines are seeded or deterministic. Inline secure and unseeded engine constructions are reported separately.
--helpor-h: Show this help message and exit--verboseor-v: Enable verbose output showing detailed analysis progress--strict: Enable strict mode which detects additional implicit I/O patterns:- Standard output (
echo,print,printf,ob_*, etc.).print_randvar_exportwithreturn: truearen't reported - File I/O (
file_get_contents,fwrite,fgets, etc.) and file system checks and writes (file_exists,unlink,mkdir, etc.) - Environment variables, HTTP headers, sessions and error logging (
getenv,header,session_start,error_log,syslog, etc.) - System time (
time,hrtime,new DateTime(), etc.). Literal date strings are checked through PHP's parsed calendar fields: relative or partial dates read the clock; complete fixed dates and fixed-anchor arithmetic do not.strtotimealso reports non-literal dates unless a non-null base timestamp is supplied. Format parsing reports fields left to now, respects reset directives and escaping, and leaves non-literal formats outside its inference - Randomness (
rand,shuffle,uniqid,new Random\Randomizer()with an omitted/null engine,new Random\Engine\Secure(), and seedable Random engines with an omitted/null seed, etc.) - Direct process-setting accessors (
libxml_use_internal_errors,mb_internal_encoding,umask) read when their setting argument is omitted or literalnull, and write when another value is supplied, includingfalseor zero. - Network (
curl_exec,fsockopen,socket_*,ftp_*, etc.), databases (mysqli_*,pg_*,odbc_*,sqlsrv_*,oci_*), external processes (exec, backticks, etc.), email (mail),include/require, and runtime configuration (ini_set,set_error_handler,define,set_time_limit,json_last_error,chdir,libxml_use_internal_errors, etc.) - Superglobals read through
filter_input,filter_input_arrayandgetopt
- Standard output (
--props: Add instance property reads and constructor initialization writes. Writes through$this->propertyoutside an actual__constructmethod already report in default mode. Static properties are always reported.--transitive: CLI-only, opt-in propagation through declarations in exactly the selected files. Calls to analysed actions add SeriouscallsActionfindings for each reachable input/output direction, naming the immediate callee and its declaration line. Direct findings stay intact. Checked counts stay fixed; implicit callers lower explicitness percentages once each. Pure argument-bearing recursion stays clean, and action cycles terminate with finite descriptions.
Any other argument starting with - is an unknown option. It stops the run before anything is analysed: Unknown option: <argument> and the usage line go to stderr and the exit code is 2, so a mistyped flag such as --stict fails the build instead of quietly turning a check off.
Directory and File Filtering
--exclude=directoryor--exclude directory: Exclude specific directories from analysis- Can be used multiple times to exclude multiple directories
- By default,
vendor/is excluded - Example:
--exclude=tests --exclude=cache
--include-pattern=patternor--include-pattern pattern: Only analyze files matching the regex pattern- Can be used multiple times; a file is analyzed if it matches any of the include patterns
- Example:
--include-pattern="src/.*\.php$"to only analyze PHP files in src/
--exclude-pattern=patternor--exclude-pattern pattern: Exclude files matching the regex pattern- Can be used multiple times; a file is excluded if it matches any of the exclude patterns
- Example:
--exclude-pattern="test.*\.php$"to exclude test files
- Every pattern given is checked before anything is analysed. A pattern that isn't a valid regular expression stops the run: the reason and the usage line go to stderr and the exit code is 2, so a mistyped filter fails the build instead of quietly analysing the wrong files.
- Example:
--include-pattern="src/("printsInvalid --include-pattern: Compilation failed: missing closing parenthesis at offset 6
- Example:
Filtering Examples
# Exclude vendor and tests directories ./vendor/bin/explicitness-checker --exclude=vendor --exclude=tests ./project # Only analyze files in src/ directory ./vendor/bin/explicitness-checker --include-pattern="src/" ./project # Exclude all test files ./vendor/bin/explicitness-checker --exclude-pattern="test.*\.php$" ./project # Combine multiple filters ./vendor/bin/explicitness-checker --exclude=vendor --exclude-pattern=".*Test\.php$" ./project # Repeat a pattern flag to exclude several things at once ./vendor/bin/explicitness-checker --exclude-pattern=".*Test\.php$" --exclude-pattern="/generated/" ./project
Functional core, imperative shell. To exempt console commands, controllers or other shells while still checking the core logic they call, in either the CLI or PHPStan, see docs/functional-core-imperative-shell.md.
Severity Levels and Exit Codes
The tool categorizes violations into three severity levels:
Items marked strict are reported only with --strict (the PHPStan strict parameter); the rest are always on.
- Minor (Exit code 1): Simple output operations
echo,print,var_dump,print_r(strict)
- Serious (Exit code 2): Shared state access
- Global variables (
global,$GLOBALS) - Superglobals (
$_GET,$_POST,$_SESSION, etc.) - Static method calls with no arguments (
ClassName::method()), as a possible read and a possible write - Function calls with no arguments (
auth(),now(),request()), as a possible read and a possible write. A call the strict catalogue already recognises, includingtime(),date(),session_id(), and themysqli_/ob_families, stays out of this category even when that extension is not loaded. Other loaded internals, such asstrlen(), stay out too.collect()is still reported. - Static properties (
ClassName::$property) - Writes through arguments (
$cart[] = $itemorsort($cart)witharray &$cart,$product->price = 1, orforeach ($carts as $cart) { $cart->items = []; }) staticvariables and by-reference closure captures (use (&$x))- Writes through
$this->propertyoutside constructor bodies in default mode;--propsalso adds reads and constructor writes
- Global variables (
- Critical (Exit code 3): System-level implicit I/O
- Environment access:
$_ENV, andgetenv(strict) - File operations (
file_get_contents,fwrite,unlink, etc.) (strict) - Time functions (
time,date,microtime,new DateTime()) (strict) - Random functions (
rand,random_int,shuffle) (strict) - HTTP headers (
header,setcookie) (strict) - Session functions (
session_start,session_id) (strict) - Error logging (
error_log,trigger_error,syslog) (strict) - Network, databases, external processes, email,
include/requireand runtime configuration (strict)
- Environment access:
Object handles through locals. Assignment, foreach values, destructuring and alias chains retain the root argument: $first = $carts[0]; $first->items = []; reports wrote to argument $carts. A straight-line fresh or unrelated local rebinding, or unset($first), clears that local's origin. Conditional rebinding conservatively retains possible roots. Arrays still copy by value: $copy = $carts; $copy[] = 1; is clean, but $copy[0]->items = []; writes a shared object. A shallow clone's top-level writes are clean ($copy = clone $cart; $copy->items = [];), while nested object writes ($copy->parent->items = []; or $copy->items[0]->x = 1;) are reported. This is bounded syntactic provenance for ordinary objects and arrays, not full heap analysis: return-value aliases, magic getters, custom clone behavior and reference-containing structures are not inferred. By-value closure and arrow captures do not inherit these local origins.
By-reference built-ins. A built-in that takes an argument by reference, such as sort, array_push or preg_match's $matches, writes to it. Passing it a by-reference parameter, a global, a superglobal, a static variable or a by-reference capture is reported as a write. Iterating by reference (foreach ($cart as &$line)) and binding a reference ($r = &$cart, [&$first] = $cart) count the same way. The checker asks the PHP that runs it which parameters are by reference, so the result depends on the extensions loaded there: a call to a function from an extension that isn't loaded isn't reported, and two machines with different extensions can report different results for the same code. User-defined functions, and calls that unpack their arguments (sort(...$lists)), are never treated as by-reference.
Catalogue names and prefixes are excluded on every machine. Any other built-in is excluded only when the PHP that runs the checker can reflect it. A zero-argument call to an internal from an extension that is not loaded, and that is not in the catalogue, is reported as functionCall. mb_internal_encoding() is the example when mbstring is absent. There is no separate list of built-ins per PHP version.
Behaviour change: $_ENV access used to be Serious, like the other superglobals. It is now Critical, the same as getenv, so a run whose worst finding is $_ENV access now exits with 3 instead of 2.
Exit Codes for CI Integration
- 0: No violations found
- 1: Only minor violations found
- 2: Serious violations found (may include minor)
- 3: Critical violations found (may include serious and minor)
The tool exits with the highest severity level found, making it easy to integrate into CI pipelines with appropriate failure thresholds.
If a PHP file cannot be parsed, the tool reports the parse error, continues checking the other files, and exits with at least code 2. A critical violation in another file still raises the exit code to 3.
Explicitness Percentage Gate
--min-explicitness=percent or --min-explicitness percent lets a codebase pass while it still has some implicit function-likes, as long as enough of them are explicit. It's meant for adopting the tool gradually: set the minimum to where the codebase is today and raise it over time.
- A function-like is explicit when it has no findings. The percentage is explicit function-likes ÷ checked function-likes × 100, in the current mode, so
--strictand--propscan lower it. - When the percentage is at or above the minimum, violations no longer set the exit code, so the run exits 0. A file that fails to parse still makes it exit at least 2: the gate waives violations, not files it couldn't check. Below the minimum, the run exits with the usual severity code. The
Exit code:line in the summary shows the code the run actually exits with. - The comparison is exact: 7 of 8 is 87.5%, which meets
87.5but not87.51. The displayed percentage is rounded down to one decimal place, so 2 of 3 shows as 66.6% and still meets66.66. - A run that checks no function-likes is 100% explicit, so it meets any minimum. Files that fail to read or parse count towards neither total.
- The minimum must be a plain decimal number from 0 to 100, such as
80or87.5. Anything else, including-5,101,1e2,.5and an empty value, stops the run:Invalid --min-explicitness: <value>and the usage line go to stderr and the exit code is 2. - If the flag is repeated, the last value wins. Given without a value, it's ignored.
With a minimum set, the summary ends with one more line:
Exit code: 0
Explicit function-likes: 2 of 3 (66.6%, minimum 50%)
On a clean run it follows No implicit inputs or outputs found., without the indent.
Example Output
$ ./vendor/bin/explicitness-checker ./path/to/your/project
Analyzing...
Results:
+---------------------------------------+------+----------+-----------------------------------------------+----------------------------------------------+----------+
| File | Line | Function | Implicit Inputs | Implicit Outputs | Severity |
+---------------------------------------+------+----------+-----------------------------------------------+----------------------------------------------+----------+
| ./path/to/your/project/Calculator.php | 4 | add | read from global variable $some_global_number | wrote to global variable $some_global_number | Serious |
+---------------------------------------+------+----------+-----------------------------------------------+----------------------------------------------+----------+
Summary:
Critical violations: 0 (exit code 3)
Serious violations: 1 (exit code 2)
Minor violations: 0 (exit code 1)
Exit code: 2
PHPStan extension
This package also ships a PHPStan rule that reports the same implicit inputs and outputs as the CLI, as PHPStan errors. It finds and names function-likes the same way the CLI does (see "Which functions are checked, and how they're named" above): a function-like the CLI reports is reported by the rule under the same name, wherever it's declared. The one thing the extension doesn't inherit from the CLI is file selection: the CLI walks the path you give it, filtered by --exclude/--include-pattern/--exclude-pattern; the rule analyses whatever paths (and excludePaths) your own PHPStan configuration already includes. See #12 for the history here.
Installation
With phpstan/extension-installer, the extension is picked up automatically once both packages are required — no further configuration needed:
composer require --dev phpstan/extension-installer jonbaldie/explicitness-checker
Without it, require the package and include its config yourself:
composer require --dev jonbaldie/explicitness-checker
# phpstan.neon includes: - vendor/jonbaldie/explicitness-checker/extension.neon
Either way, run vendor/bin/phpstan analyse as usual; violations appear alongside your other PHPStan errors.
strict and props parameters
explicitness.strict and explicitness.props mirror the CLI's --strict and --props flags and both default to false:
parameters: explicitness: strict: true props: true
Categories and identifiers
Every error the rule reports carries one of these explicitness.<category> identifiers. Categories marked strict or props only appear once the matching parameter above is true; the rest are always on.
| Category | Identifier | Enabled by |
|---|---|---|
Global variable (global $x) |
explicitness.globalVariable |
default |
Superglobal ($_GET, $_ENV, etc.; filter_input with strict) |
explicitness.superglobal |
default |
$GLOBALS array |
explicitness.globalsArray |
default |
Static method call with no arguments (ClassName::method(), not self::, parent:: or static::), as a possible read and a possible write |
explicitness.staticCall |
default |
Function call with no arguments (auth(), now()), as a possible read and a possible write |
explicitness.functionCall |
default |
Static property (ClassName::$property) |
explicitness.staticProperty |
default |
| Write through a by-reference parameter or an object argument's property | explicitness.argumentMutation |
default |
static variable (static $x) |
explicitness.staticVariable |
default |
By-reference closure capture (use (&$x)) |
explicitness.capturedReference |
default |
Standard output (echo, print, var_dump, ob_start, ...) |
explicitness.standardOutput |
strict |
File I/O (file_get_contents, fwrite, fgets, ...) |
explicitness.file |
strict |
File system checks and writes (file_exists, is_dir, unlink, mkdir, ...) |
explicitness.fileSystem |
strict |
Environment variables (getenv, putenv) |
explicitness.environment |
strict |
System time (time, date, microtime, new DateTime(), ...) |
explicitness.time |
strict |
Random number generator (rand, random_int, shuffle, uniqid, ...) |
explicitness.random |
strict |
HTTP headers (header, setcookie, getallheaders, ...) |
explicitness.httpHeaders |
strict |
Error log (error_log, trigger_error, syslog, ...) |
explicitness.errorLog |
strict |
Session (session_start, session_id, ...) |
explicitness.session |
strict |
Network (curl_exec, fsockopen, socket_*, ftp_*, gethostbyname, ...) |
explicitness.network |
strict |
Database (mysqli_*, pg_*, odbc_*, sqlsrv_*, oci_*) |
explicitness.database |
strict |
External process (exec, shell_exec, backticks, proc_open, ...) |
explicitness.process |
strict |
Email (mail, mb_send_mail) |
explicitness.mail |
strict |
Included file (include, require, include_once, require_once) |
explicitness.include |
strict |
Runtime configuration (ini_set, ini_get, set_error_handler, define, ...) |
explicitness.runtimeConfig |
strict |
Object property ($this->property) |
explicitness.objectProperty |
default writes outside constructors; props adds reads and constructor writes |
Ignoring or baselining a category
To ignore a category everywhere, add it to ignoreErrors by identifier:
parameters: ignoreErrors: - identifier: explicitness.standardOutput
To grandfather in existing violations instead, run vendor/bin/phpstan analyse --generate-baseline. Each generated baseline entry keeps its identifier, so you can hand-edit phpstan-baseline.neon afterwards to drop the entries for a category you'd rather start enforcing straight away.
Overlap with jonbaldie/phpstan-extension-accessing-globals
jonbaldie/phpstan-extension-accessing-globals also reports on global and superglobal access, under its own identifiers. If you install both, the same line can be reported twice:
explicitness.globalVariableandexplicitness.globalsArrayoverlap with that extension's defaultaccess.globalandmodify.globalidentifiers (global $xand$GLOBALS[...]access and modification).explicitness.superglobaloverlaps with its defaultaccess.superglobal.nestedandmodify.superglobal.nestedidentifiers, since this extension only ever checks function bodies, which that extension always treats as a nested scope.explicitness.staticPropertyoverlaps with its opinionatedproperty.staticidentifier, if you've enabled that extension's opinionated rule set.- With
explicitness.strict: true,explicitness.time,explicitness.random,explicitness.environment,explicitness.file,explicitness.fileSystem,explicitness.httpHeaders,explicitness.errorLog,explicitness.session,explicitness.network,explicitness.process,explicitness.mailandexplicitness.runtimeConfigoverlap with its opinionatedfunction.impureidentifier, which reports calls to a fixed list of impure built-in functions. The lists only partly match:function.impurealso covers functions this extension doesn't (strtotime,getcwd,realpath,php_uname, ...) and reportsdateandmktimeeven when they're given a timestamp, and it doesn't coversrand,setrawcookie,trigger_error,curl_exec,popen,defineorinclude, among others.explicitness.standardOutput,explicitness.databaseandexplicitness.includedon't overlap.
Pick one extension as the source of truth for each pair and ignore the other's identifier for it, e.g. to keep jonbaldie/phpstan-extension-accessing-globals as the source of truth for global and superglobal access:
parameters: ignoreErrors: - identifier: explicitness.globalVariable - identifier: explicitness.globalsArray - identifier: explicitness.superglobal
For function.impure, neither side is a superset of the other, so ignoring either one loses some coverage. If you run both extensions strictly, one option is to keep this extension's per-category identifiers and ignore function.impure:
parameters: ignoreErrors: - identifier: function.impure