# [RFC] Strict Namespace Resolution

- Thread: https://externals.io/message/131929
- Original archive: https://news-web.php.net/php.internals/131929
- Started by Paul M. Jones on 2026-07-15T00:54:32+00:00
- Messages: 21
- RFC: Strict Namespace Resolution (Under discussion) — https://externals.io/rfc/strict-namespace.md
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #131929 — Paul M. Jones — 2026-07-15T00:54:32+00:00

URL: https://externals.io/message/131929#131929

Hi all,

Per feedback from Tim Tim Düsterhus <https://externals.io/message/131332#131899> I have extracted the `declare(strict_namespace=1)` portion of the function autoloading proposal to its own RFC.

- RFC: https://wiki.php.net/rfc/strict-namespace
- PR:  https://github.com/php/php-src/pull/22736

(This RFC is an aid to, but not strictly required for, function autoloading.)


-- pmj

---

## #131932 — Rowan Tommins [IMSoP] — 2026-07-15T09:13:41+00:00

URL: https://externals.io/message/131929#131932 · In reply to: #131929

>Hi all,
>
>Per feedback from Tim Tim Düsterhus <https://externals.io/message/131332#131899> I have extracted the `declare(strict_namespace=1)` portion of the function autoloading proposal to its own RFC.


I said this already on the other thread, but if we're going to do this, please let's come up with a better name.

"Strict" implies some extra check that namespaces are "correct" in some way, which isn't really what this is about. And without reading the manual (or RFC), I would genuinely have no idea if setting this to "1" meant "all namespaced names have to be qualified" or "all global names have to be qualified".

There's also at least two prior RFCs on this subject which we should look back at: 

- https://wiki.php.net/rfc/fallback-to-root-scope-deprecation - 2017; proposed removing the fallback completely; discussed but never voted
- https://wiki.php.net/rfc/use_global_elements - 2020; proposed a declare for global-only lookups; Declined


There are probably other relevant threads in the list archives as well.

Regards,

Rowan Tommins
[IMSoP]

---

## #131935 — Michael Morris — 2026-07-15T12:48:07+00:00

URL: https://externals.io/message/131929#131935 · In reply to: #131932

The RFC draft I put up that seems to have fell into the lost stack might be
coupled with this. If I'm going to run that command, whatever it's called,
the namespace autoload process seems the logical place to do it to insure
it is called before the namespace is invoked the first time. Otherwise
you'll have to have this declare at the top of every file in the namespace
which could be awkward.

On Wed, Jul 15, 2026 at 5:14 AM Rowan Tommins [IMSoP] <imsop.php@rwec.co.uk>
wrote:

> 
> > […]
> https://externals.io/message/131332#131899> I have extracted the
> `declare(strict_namespace=1)` portion of the function autoloading proposal
> to its own RFC.
>
>
> I said this already on the other thread, but if we're going to do this,
> please let's come up with a better name.
>
> "Strict" implies some extra check that namespaces are "correct" in some
> way, which isn't really what this is about. And without reading the manual
> (or RFC), I would genuinely have no idea if setting this to "1" meant "all
> namespaced names have to be qualified" or "all global names have to be
> qualified".
>
> There's also at least two prior RFCs on this subject which we should look
> back at:
>
> - https://wiki.php.net/rfc/fallback-to-root-scope-deprecation - 2017;
> proposed removing the fallback completely; discussed but never voted
> - https://wiki.php.net/rfc/use_global_elements - 2020; proposed a declare
> for global-only lookups; Declined
>
>
> There are probably other relevant threads in the list archives as well.
>
> Regards,
>
> Rowan Tommins
> [IMSoP]

---

## #131938 — Rowan Tommins [IMSoP] — 2026-07-15T13:46:14+00:00

URL: https://externals.io/message/131929#131938 · In reply to: #131935

>The RFC draft I put up that seems to have fell into the lost stack might be
>coupled with this. If I'm going to run that command, whatever it's called,
>the namespace autoload process seems the logical place to do it to insure
>it is called before the namespace is invoked the first time. Otherwise
>you'll have to have this declare at the top of every file in the namespace
>which could be awkward.


Setting declare directives across multiple files is something that's come up many times over the years, but it needs a lot more than autoloading. 

Right now, the compiler looks at one file at a time, and OpCache will reuse that compiled form whenever that same file is included.

For one file to influence how another file is compiled, you need a way to say either "this code file will *only ever* be loaded in the context of this setting file", or "the cache for this code file is *only valid* in the context of this setting file".

Either way, it's not enough to just say "declare this setting for any matching code files from now on", whether you have an autoloader callback or not.

In other words, this strays firmly into the "native package"/"module" design space.



Rowan Tommins
[IMSoP]

---

## #131940 — Ilija Tovilo — 2026-07-15T15:54:04+00:00

URL: https://externals.io/message/131929#131940 · In reply to: #131929

Hi Paul


> Per feedback from Tim Tim Düsterhus<https://externals.io/message/131332#131899> I have extracted the `declare(strict_namespace=1)` portion of the function autoloading proposal to its own RFC.
>
> - RFC:https://wiki.php.net/rfc/strict-namespace
> - PR:https://github.com/php/php-src/pull/22736
>
> (This RFC is an aid to, but not strictly required for, function autoloading.)

This flag forces the use of a \ prefix, but it's worth noting there are 
some communities (e.g. Laravel and Symfony) who have explicitly rejected 
the idea of prefixing all functions for readability reasons. We can 
force them into conforming, or forever keep the old behavior, but that's 
probably not the best approach.

If PHP were designed today, what I would personally like is:

  * The rules for classes, functions and constants are equivalent.
  * The default is short / easy to type.
  * The rules are consistent inside and outside namespaces.
  * There is no fallback.

In practice, this would mean pretty much the opposite of this proposal:

  * `new Foo`/foo()/FOO always refers to the global symbol. If you want
    the local symbol, add a local use like for everything else.
  * The only conceivable exception could be a local
    class/function/constant declaration within the same file, which
    could conceptually add a use for you.
  * Global function calls (which is the vast majority of all function
    calls) can remain unprefixed, and references to global classes no
    longer need a prefix.
  * Class names behave equivalently inside and outside namespaces.
  * The lack of a fallback solves ambiguity issue that prevents many
    optimizations (https://externals.io/message/124718).

I'm not sure if there's a world where we can still move into that 
direction, e.g. via a declare as a migration path.

Ilija

---

## #131941 — Rowan Tommins [IMSoP] — 2026-07-15T17:18:28+00:00

URL: https://externals.io/message/131929#131941 · In reply to: #131940

>This flag forces the use of a \ prefix, but it's worth noting there are some communities (e.g. Laravel and Symfony) who have explicitly rejected the idea of prefixing all functions for readability reasons.

I know it won't change anyone's mind, but for the record, I've always found this baffling. Adding a single punctuation mark next to function calls seems far more readable to me than having a huge block of "use function" lines at the top of every file.

The often-suggested "scalar methods" would add more punctuation than moving to "no global fallback" - "\strlen($foo)" vs "$foo->length()".


Rowan Tommins
[IMSoP]

---

## #131942 — Paul M. Jones — 2026-07-15T17:48:40+00:00

URL: https://externals.io/message/131929#131942 · In reply to: #131940

Hi Ilia,

> 
> 
> Hi Paul
> 
> > […]

> This flag forces the use of a \ prefix, but it's worth noting there are some communities (e.g. Laravel and Symfony) who have explicitly rejected the idea of prefixing all functions for readability reasons. We can force them into conforming, or forever keep the old behavior, but that's probably not the best approach.

I sympathize with that rejection -- I don't especially like prefixing everything with \ myself. Even having to import every global function explicitly, as required under some coding stndards rulesets, feels noisy to me at times.

But to be clear, `declare(strict_namespace=1)` is an opt-in behavior.  If you want the global fallback behavior, no need to declare the directive.  Further, I would not advise "forcing" anyone to use it who doesn't want it.

Have I maybe missed what you were getting at?


-- pmj

---

## #131943 — Ilija Tovilo — 2026-07-15T17:57:23+00:00

URL: https://externals.io/message/131929#131943 · In reply to: #131942

Hi Paul


> Hi Ilia,
I normally don't correct people on my name, but just to avoid confusion: 
We now have myself and Ilia Alshanetsky being active again. Just want to 
point out that we're two different people.
> > […]
> I sympathize with that rejection -- I don't especially like prefixing everything with \ myself. Even having to import every global function explicitly, as required under some coding stndards rulesets, feels noisy to me at times.
>
> But to be clear, `declare(strict_namespace=1)` is an opt-in behavior.  If you want the global fallback behavior, no need to declare the directive.  Further, I would not advise "forcing" anyone to use it who doesn't want it.
>
> Have I maybe missed what you were getting at?

I understand, but the value-add of this declaration is very small if the 
plan isn't ever to deprecate/remove the old behavior.

  * People who opt-in don't gain anything new. They were already relying
    on the exact same behavior, with the fallback never triggering.
  * It might help with conformance, but not beyond what a linter could
    already do automatically.
  * The people who don't opt-in won't overcome the optimization limitations.
  * The ecosystem remains fragmented.

---

## #131944 — Paul M. Jones — 2026-07-15T18:17:43+00:00

URL: https://externals.io/message/131929#131944 · In reply to: #131943

Hi Ilija,

> 
> 
> Hi Paul
> 
> > […]
> I normally don't correct people on my name, but just to avoid confusion: We now have myself and Ilia Alshanetsky being active again. Just want to point out that we're two different people.

/me grimaces

You are right to correct me. It was a typo on my part; I apologize for failing to catch it before sending.


> the value-add of this declaration is very small if the plan isn't ever to deprecate/remove the old behavior.

I can see why some might think that; even so, it is still a valuable addition (however small) even if the old behavior is never removed, in the same way that strict_types is valuable even though the old behavior has not been removed.

Further, note that this is an extraction from the function-autoloading RFC; its origin there was due to the shadow-global problem which (thankfully) turns out to be relatively rare. In those rare cases, this directive becomes an option to mitigate the situation.


-- pmj

---

## #131949 — Rowan Tommins [IMSoP] — 2026-07-15T22:23:59+00:00

URL: https://externals.io/message/131929#131949 · In reply to: #131944

>I can see why some might think that; even so, it is still a valuable addition (however small) even if the old behavior is never removed, in the same way that strict_types is valuable even though the old behavior has not been removed.


I'm going to bang this drum again: neither mode of strict_types is older than the other. Both were designed at the same time, when scalar type declarations were added to the language, and the directive was to let users choose between two competing designs.

In hindsight, both the name of the directive, and the details of "mode 0", really needed more polish. Unfortunately, by then everyone was exhausted by several thousand emails arguing about the topic, and relieved that any sort of consensus had been reached.

That's why I'm so keen *not* to base the name and options of this feature (or any other) on the strict_types precedent.

If this is truly about offering choice, name it clearly as such, and offer all three modes (global-only, current-ns-only, fallback).

If the purpose is to somehow move towards removing the fallback completely, then we should explicitly work towards that goal, e.g.: 

- name the directive to imply future vs past, e.g. legacy_name_fallback
- in 8.next, emit a deprecation in certain situations if neither legacy_name_fallback=1 or legacy_name_fallback=0 is set
- in 9.0, that could become an error, but both modes still available
- in 10.0, legacy_name_fallback=1 would become an error, and legacy_name_fallback=0 would be a no-op as it would be the default behaviour

Or, as proposed several years ago, just deprecate the behaviour directly and pick a new behaviour for all code as of PHP 9.0. People will complain about finding their backslash keys, but they'll get used to it.

Regards,

Rowan Tommins
[IMSoP]

---

## #131954 — Paul M. Jones — 2026-07-16T02:45:56+00:00

URL: https://externals.io/message/131929#131954 · In reply to: #131949

Hi Rowan,

> 
> 
> 
> 
> > […]
> 
> I'm going to bang this drum again: neither mode of strict_types is older than the other. Both were designed at the same time, when scalar type declarations were added to the language, and the directive was to let users choose between two competing designs.
> 
> In hindsight, both the name of the directive, and the details of "mode 0", really needed more polish. Unfortunately, by then everyone was exhausted by several thousand emails arguing about the topic, and relieved that any sort of consensus had been reached.
> 
> That's why I'm so keen *not* to base the name and options of this feature (or any other) on the strict_types precedent.

A reasonable stand, and thanks for the historical context.


> If this is truly about offering choice, name it clearly as such, and offer all three modes (global-only, current-ns-only, fallback).
> 
> If the purpose is to somehow move towards removing the fallback completely, then we should explicitly work towards that goal

To be clear, it is driven neither by "offering choice" nor by "removing the fallback entirely". It is driven by offering a way to mitigate the shadow-global case in the Function Autoloading RFC. As it turns out, that case is relatively rare, but Tim opined this feature should be separate from Function Autoloading, so here we are.

* * *

Having said all of that, I am good with *some* form of a directive that helps with the rare but not non-existent shadow-global case.

As this RFC stands now, is the directive name alone the main blocker for you, or is it also the values?

Thank you for your patient attention.


-- pmj

---

## #131960 — Alex Rock — 2026-07-16T08:59:36+00:00

URL: https://externals.io/message/131929#131960 · In reply to: #131949

Le 16/07/2026 à 00:23, Rowan Tommins [IMSoP] a écrit :
> 
>
> > […]
>
> I'm going to bang this drum again: neither mode of strict_types is older than the other. Both were designed at the same time, when scalar type declarations were added to the language, and the directive was to let users choose between two competing designs.
>
> In hindsight, both the name of the directive, and the details of "mode 0", really needed more polish. Unfortunately, by then everyone was exhausted by several thousand emails arguing about the topic, and relieved that any sort of consensus had been reached.
>
> That's why I'm so keen *not* to base the name and options of this feature (or any other) on the strict_types precedent.
>
> If this is truly about offering choice, name it clearly as such, and offer all three modes (global-only, current-ns-only, fallback).
>
> If the purpose is to somehow move towards removing the fallback completely, then we should explicitly work towards that goal, e.g.:
>
> - name the directive to imply future vs past, e.g. legacy_name_fallback
> - in 8.next, emit a deprecation in certain situations if neither legacy_name_fallback=1 or legacy_name_fallback=0 is set
> - in 9.0, that could become an error, but both modes still available
> - in 10.0, legacy_name_fallback=1 would become an error, and legacy_name_fallback=0 would be a no-op as it would be the default behaviour
>
> Or, as proposed several years ago, just deprecate the behaviour directly and pick a new behaviour for all code as of PHP 9.0. People will complain about finding their backslash keys, but they'll get used to it.
>
> Regards,
>
> Rowan Tommins
> [IMSoP]


I agree with the fact that "strict_namespaces" is confusing: we're not 
talking about a namespace being strict, but about a function name 
reference to fall back to global scope if called in a namespace context. 
Naming it "legacy" implies something temporary, which I both like and 
dislike (because of the old adage "there's nothing more permanent than a 
temporary solution"), which can also be confusing in some eyes.

The whole concept (aka "not falling back to global scope") is something 
I would like to see too, because I often configure my CS fixers to 
either prepend the "\" for all function/const calls, or add "use const" 
and "use function" statements on top of the file. CS fixers could detect 
this quite easily in an AST: either the function is fully-qualified 
(then the fixer does nothing), and if it's not, rely on either "add root 
ns" or "add use stmt on top of the file" to fix it. It's already the 
case today, and in the future, it could include a third option "add 
declare stmt" to ensure no fallback is ever used anyway.

I would be picky on the naming, and maybe suggest something like 
"disable_root_ns_fallback=0|1"  ?

---

## #131966 — tim@bastelstu.be — 2026-07-16T12:53:11+00:00

URL: https://externals.io/message/131929#131966 · In reply to: #131929

Hi


> Per feedback from Tim Tim Düsterhus 
> <https://externals.io/message/131332#131899> I have extracted the 
> `declare(strict_namespace=1)` portion of the function autoloading 
> proposal to its own RFC.
> 
> - RFC: https://wiki.php.net/rfc/strict-namespace
> - PR:  https://github.com/php/php-src/pull/22736

Thank you for the RFC. I'm, probably unsurprisingly, in favor.

However I agree with Rowan that the naming of the directive could be 
made clearer. I could imagine `declare(global_fallback=0)` (with 1 being 
the default) being a reasonable option between clarity and brevity.

With regard to the “RFC Impact” section, the “None” for the ecosystem is 
not quite accurate: Static analyzers and IDEs will want to learn about 
the directive to

1. Emit diagnostics when a function or constant is not explicitly 
imported and doesn't exist in the namespace.
2. Emit diagnostics when the directive is not used despite being 
configured as “desirable” to make sure it is consistently applied within 
an entire project.

This kind of “work being imposed because otherwise the tools would be 
worse than they could be” on downstream projects is the main goal of the 
“Ecosystem Impact” section, obvious compatibility breaks are already 
listed in the “Backward Incompatible Changes” section :-)

Best regards
Tim Düsterhus

---

## #131968 — tim@bastelstu.be — 2026-07-16T13:08:34+00:00

URL: https://externals.io/message/131929#131968 · In reply to: #131940

Hi


>  * The rules for classes, functions and constants are equivalent.

Agreed.

>  * The default is short / easy to type.

Agreed, but I'm probably drawing a different conclusion than you, see 
below.

>  * The rules are consistent inside and outside namespaces.

Agreed.

>  * There is no fallback.

Agreed.

> In practice, this would mean pretty much the opposite of this proposal:
> 
>  * `new Foo`/foo()/FOO always refers to the global symbol. If you want
>    the local symbol, add a local use like for everything else.

I believe that “unqualified names are relative” is both an obvious, and 
a pragmatic choice. Given that a majority of class-based code is 
namespaced, global classes are rarely used, whereas it's often the case 
that related classes from the same namespace are required. An obvious 
example would be exception classes, which are colocated within the same 
namespaces under the Throwable policy of PHP.

>  * Global function calls (which is the vast majority of all function
>    calls) can remain unprefixed, and references to global classes no
>    longer need a prefix.

While this is true today for large parts of PHP’s standard library, our 
policy strongly recommends the use of namespaces for “new components” 
and this includes functions and constants. The new DOM API in PHP 8.4 
aliased many of the global constants into the `Dom\*` namespace, and 
there's also `Dom\import_simplexml()`. Similarly, I'm expecting that the 
new date and time API will include some functions in addition to the 
OO-API and these would also be put into the `Time\*` namespace. One 
example I can think of would be a `Time\now(): Instant` function for 
easy retrieval of an instant from the system clock. Ideas for a new 
iterable-API also suggested the use of a `iterable\*` namespace, e.g. 
`iterable\map()` (for `array_map()`) or `iterable\filter()` (for 
`array_filter()`).

Thus PHP developers already need to be prepared to explicitly import or 
fully-qualify function calls and any change we're making should be 
reasonably “forward looking” with regard to how the standard library is 
going to be modernized.

Best regards
Tim Düsterhus

---

## #131969 — tim@bastelstu.be — 2026-07-16T13:23:34+00:00

URL: https://externals.io/message/131929#131969 · In reply to: #131941

Hi


> On 15 July 2026 16:54:04 BST, Ilija Tovilo <tovilo.ilija@gmail.com> 
> wrote:
> > […]
> 
> I know it won't change anyone's mind, but for the record, I've always 
> found this baffling. Adding a single punctuation mark next to function 
> calls seems far more readable to me than having a huge block of "use 
> function" lines at the top of every file.

FWIW, I agree. In Visual Studio Code it's also possible to adjust the 
styling of namespaces specifically, which would allow tuning down the 
backslash:

As an example, this config renders namespaces in black with 50% opacity:

     "editor.tokenColorCustomizations": {
         "textMateRules": [
             {
                 "scope": "support.other.namespace.php",
                 "settings": {
                     "foreground": "#0000007F"
                 }
             }
         ]
     }

Using `punctuation.separator.inheritance.php` as the scope will just 
affect the backslash specifically.

Best regards
Tim Düsterhus

---

## #132001 — Paul M. Jones — 2026-07-17T18:39:00+00:00

URL: https://externals.io/message/131929#132001 · In reply to: #131929

Hi all,

Primary pushback thus far has not been on the functionality per se but on the directive name itself. The currently-suggested equivalent alternatives are:


```
strict_namespace=1            (RFC as-is)
legacy_name_fallback=0        (R. Tommins)
global_fallback=0             (T. Düsterhus)
disable_root_ns_fallback=1    (A. Rock)
```

Of those, I'd be OK with `global_fallback=0` (though I do still prefer `strict_namespace=1`).

Are there any further suggestions or objections on this point?


-- pmj

---

## #132004 — Theodore Brown — 2026-07-17T21:41:40+00:00

URL: https://externals.io/message/131929#132004 · In reply to: #132001

> Primary pushback thus far has not been on the functionality per se but on the directive name itself. The currently-suggested equivalent alternatives are:
> 
>     strict_namespace=1            (RFC as-is)
>     legacy_name_fallback=0        (R. Tommins)
>     global_fallback=0             (T. Düsterhus)
>     disable_root_ns_fallback=1    (A. Rock)
> 
> Of those, I'd be OK with `global_fallback=0` (though I do still prefer `strict_namespace=1`).
>
> Are there any further suggestions or objections on this point?

Hi Paul,

Thank you for working on this RFC! It's essentially the same as my `declare(root_fallback=0)` idea from 2019. [1]

I think the `global_fallback=0` alternative from Tim indicates the resulting behavior most clearly, but I don't have a strong preference between this and `strict_namespace=1`.

-Theodore

[1]: https://externals.io/message/105757

---

## #132012 — Paul M. Jones — 2026-07-18T18:08:31+00:00

URL: https://externals.io/message/131929#132012 · In reply to: #132004

Hi Theodore,

> 
> 
> 
> 
> > […]
> 
> Hi Paul,
> 
> Thank you for working on this RFC! It's essentially the same as my `declare(root_fallback=0)` idea from 2019. [1]

Ah so -- my bad for not including it in the prior art on this RFC.


> I think the `global_fallback=0` alternative from Tim indicates the resulting behavior most clearly, but I don't have a strong preference between this and `strict_namespace=1`.

Very good!


-- pmj

---

## #132013 — kontakt@beberlei.de — 2026-07-18T18:20:26+00:00

URL: https://externals.io/message/131929#132013 · In reply to: #132001

Am 17.07.2026, 20:39:00 schrieb Paul M. Jones <pmjones@pmjones.io>:

> Hi all,
>
> Primary pushback thus far has not been on the functionality per se but on
> the directive name itself. The currently-suggested equivalent alternatives
> are:
>




>
> ```
> strict_namespace=1            (RFC as-is)
> legacy_name_fallback=0        (R. Tommins)
> global_fallback=0             (T. Düsterhus)
> disable_root_ns_fallback=1    (A. Rock)
> ```
>
> Of those, I'd be OK with `global_fallback=0` (though I do still prefer
> `strict_namespace=1`).
>

In my opinion it makes sense to vote on the concept and then have a
secondary vote on the name.

 I‘d argue everyone agrees we need this but with only one option it might
fail.


> Are there any further suggestions or objections on this point?
>
>
> -- pmj

---

## #132015 — Rowan Tommins [IMSoP] — 2026-07-18T19:19:08+00:00

URL: https://externals.io/message/131929#132015 · In reply to: #132012

> > […]
> Ah so -- my bad for not including it in the prior art on this RFC.


I pointed out a couple of other prior discussions earlier: 
https://externals.io/message/131929#131932

Most notably, one of those is an RFC which went to vote and was 
almost-unanimously declined, so it would be really good to add something 
to the RFC comparing your proposal with Tyson's.


-- 
Rowan Tommins
[IMSoP]

---

## #132016 — Rowan Tommins [IMSoP] — 2026-07-18T19:28:45+00:00

URL: https://externals.io/message/131929#132016 · In reply to: #132013

> In my opinion it makes sense to vote on the concept and then have a 
> secondary vote on the name.
>
>  I‘d argue everyone agrees we need this but with only one option it 
> might fail.


Firstly, I'm not sure it's true that "everyone agrees we need this". I'm 
not totally convinced that a per-file declare() is the right solution to 
this problem; and there's clear disagreement about whether "always 
global" or "always current NS" makes the most sense.

Secondly, a split vote leaves voters who actively dislike a particular 
name with an awkward choice: vote Yes, and risk the "bad" name being 
chosen; or vote No, even though you would support the feature under a 
different name.

I think the name here is more than just a detail, it really changes the 
perception of the whole feature.


-- 
Rowan Tommins
[IMSoP]
