Table of Contents

PHP RFC: Strict Namespace Resolution

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:

The declaration follows the strict_types rules:

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

Voting Choices

Primary Vote requiring a 2/3 majority to accept the RFC:

Implement declare(strict_namespace=1) as described in this RFC?
Real name Yes No Abstain
Final result: 0 0 0
This poll has been closed.

Patches and Tests

Implementation: php/php-src pull request #22736.

Tests:

Implementation

After the RFC is implemented, this section should contain:

  1. the version(s) it was merged into
  2. a link to the git commit(s)
  3. a link to the PHP manual entry for the feature

References

Rejected Features

Changelog