====== PHP RFC: Strict Namespace Resolution ====== * Version: 0.1 * Date: 2026-07-14 * Author: Paul M. Jones, pmjones@pmjones.io * Status: Draft * Implementation: https://github.com/php/php-src/pull/22736 * Discussion thread: https://externals.io/message/131929 * Voting thread: tbd ===== Introduction ===== Inside a namespace, PHP resolves an unqualified function or constant name in the current namespace first, then falls back to the global namespace. Unqualified class names do not fall back; they resolve in the current namespace only. For example, an unqualified ''strlen()'' in ''namespace Foo'' binds to the global ''strlen()'', not ''Foo\strlen()''. This RFC proposes ''declare(strict_namespace=1)'', a file-scoped compile-time directive that turns off the global fallback for unqualified function and constant names. When ''strict_namespace'' is enabled, unqualified names resolve to the current namespace only, just as class names already do. declare(strict_namespace=1); namespace Foo; function strlen(string $s): int { return -1; } // Foo\strlen echo strlen('bar'); // -1 (unqualified: Foo\strlen, no fallback) echo \strlen('bar'); // 3 (fully qualified: global strlen) This proposal is extracted from the Function Autoloading RFC; it serves both as an aid to that RFC and as an independent improvement. Whether or not function autoloading is adopted, this RFC helps make function and constant resolution match class resolution. ===== Proposal ===== ''declare(strict_namespace=1)'' disables the global-namespace fallback for the file that declares it. An unqualified function or constant name then resolves only within the current namespace: an unqualified ''strlen()'' in ''namespace Foo'' is ''Foo\strlen''. If ''Foo\strlen'' is not defined, the call will not fall back to the global ''strlen()''. A name still reaches the global table when asked for directly: * A fully-qualified name (''\strlen()'', ''\PHP_INT_MAX'') resolves to the global symbol with no namespace lookup. * An imported name reaches the global symbol through the import: ''use function strlen;'', ''use function str_repeat as repeat;'', and the ''use const'' equivalents. * The ''true'', ''false'', and ''null'' keywords are unaffected by the directive. The declaration follows the ''strict_types'' rules: * it must be the first statement in the file; * it cannot use block mode; * its value must be ''0'' or ''1''; ''0'' (current behavior) is the default. It resolves entirely at compile time, in the declaring file only. Callers and callees in other files are unaffected, and there is no runtime or JIT cost. ==== Functions ==== The effect on functions is as follows: declare(strict_namespace=1); namespace Foo; use function str_repeat as repeat; function strlen(string $s): int { return -1; } // Foo\strlen var_dump(strlen('hello')); // int(-1) (unqualified: Foo\strlen) var_dump(\strlen('hello')); // int(5) (fully qualified: global) var_dump(repeat('ab', 3)); // string(6) "ababab" (imported: global) ==== Constants ==== The effect on constants is the same. Under ''strict_namespace=1'' an unqualified ''GREETING'' in ''namespace Foo'' is ''Foo\GREETING'', and a built-in like ''PHP_INT_MAX'' resolves to ''Foo\PHP_INT_MAX'' unless written ''\PHP_INT_MAX'' or imported with ''use const''. Constant expressions follow suit, such as a ''const'' value, a parameter default, or an attribute argument. (The ''true''/''false''/''null'' keywords are unaffected.) declare(strict_namespace=1); namespace Foo; const PHP_INT_MAX = 42; // Foo\PHP_INT_MAX echo PHP_INT_MAX; // 42 (unqualified: Foo\PHP_INT_MAX) echo \PHP_INT_MAX; // (fully qualified: global constant) ===== Backward Incompatible Changes ===== None. The directive is opt-in and defaults to ''0'', the current behavior. Only a file that declares ''strict_namespace=1'' changes; nothing else is affected. ===== Proposed PHP Version(s) ===== PHP 8.6. ===== RFC Impact ===== ==== To the Ecosystem ==== None; code that does not opt in behaves as before. ==== To Existing Extensions ==== None. ==== To SAPIs ==== None. ===== Open Issues ===== None known. ===== Future Scope ===== * The companion Function Autoloading RFC is assisted by but does not strictly require this RFC. ===== Voting Choices ===== Primary Vote requiring a 2/3 majority to accept the RFC: * Yes * No * Abstain ===== Patches and Tests ===== Implementation: [[https://github.com/php/php-src/pull/22736|php/php-src pull request #22736]]. Tests: * ''Zend/tests/strict_namespace_constants.phpt'': the fallback is disabled for unqualified constants, while ''use const'' and fully-qualified names still reach globals; * ''Zend/tests/strict_namespace_bad_value.phpt'': the value must be ''0'' or ''1''; * ''Zend/tests/strict_namespace_block_mode.phpt'': the declaration cannot use block mode; * ''Zend/tests/strict_namespace_not_first.phpt'': the declaration must be the first statement. ===== Implementation ===== After the RFC is implemented, this section should contain: - the version(s) it was merged into - a link to the git commit(s) - a link to the PHP manual entry for the feature ===== References ===== * the companion Function Autoloading RFC; * [[https://wiki.php.net/rfc/core-autoloading|rfc/core-autoloading]] (Gina P. Banyard / Dan Ackroyd, 2023), which discussed namespaced-name resolution and the cost of probing the global table. ===== Rejected Features ===== * **Strict resolution by default.** It would break code that relies on unqualified namespaced calls reaching globals, so the directive is opt-in, defaulting to ''0''. * **''use function''/''use const'' as the only fix.** Importing each name from its own namespace works, but it is a per-name chore in every file; the directive covers the whole file at once. * **Per-''use'' or per-statement opt-in.** Rejected for the file-scoped ''declare'', which mirrors ''strict_types'' and resolves once at compile time. * **Other directive names.** ''strict_names'', ''namespace_fallback=0'', and the like were considered; ''strict_namespace'' parallels ''strict_types''. ===== Changelog ===== * 2026-07-14: Initial draft (0.1), extracted from the Function Autoloading RFC as a standalone proposal.