# 5.3 Namespace resolution rules suggestions

- Thread: https://externals.io/message/37654
- Original archive: https://news-web.php.net/php.internals/37654
- Started by Stan Vassilev | FM on 2008-05-14T18:52:29+00:00
- Messages: 17
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #37654 — Stan Vassilev | FM — 2008-05-14T18:52:29+00:00

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

Hi,

I'm looking at the current namespace resolution rules here:
http://www.php.net/manual/en/language.namespaces.rules.php

Something that bothers me is that for the first time in PHP we introduce a
hard difference in how internal functions and user functions are
resolved/called:

::foo(); // resolves as user function in global namespace
foo(); // can resolve as function in the current namespace, else as internal
function

Whether a function/class is internal is implementation detail that I believe
shouldn't be forced in such a fashion in user code.

It makes people to check/think whether a function is internal or not,
additionally to thinking about namespace scope. It's unneded complexity. If
you check the PHP manual comments, you'll notice people often introduce PHP
userspace equivalents to various functions and extensions, there's for
example a full PHP-only implementation of curl:
http://code.blitzaffe.com/pages/phpclasses/files/libcurl_emulator_52-7

There was also a wave of drop-in replacements for PHP5 function in PHP4, or
for example json_en/decode() drop-in for PHP 5.1 users who don't have the
PECL extension.

None of those will be possible in namespace scope in 5.3, and a developer
will need to have plenty of special cases in code or special factories, if
it's not known in advance whether a class/function will be internal or not.

To further convolute things, the drop-ins will work if *not* in namespace 
context.

A second problem is one of future-proofing. Many of the procedural API-s in
PHP4 saw transition to OOP in PHP5. If namespaces have success and are
adopted widely, we may see new or existing internal functions make similar
transitions to being namespaced. This will leave "global internal" function
stick our like a sore thumb, as a solitary special case with specific
syntax, as opposed to all other cases we have.

A third problem is one of performance. Every time a developer calls an
internal function, an attempt will be made to resolve it to a user function
in the current namespace, and only then call the internal function. Typical
PHP code contains plenty of internal function calls, while I believe the
novelty of being able to transparently override intenral functions will wear
off quickly and be used quite sparingly (and quite likely be considered a
bad practice, leading to confusing code).

What I suggest:

::foo(); // to resolve a user or internal function in global namespace (in
namespace context)
foo(); // to only resolve at compile time, in the current namespace (in
namespace context)

Much simpler to understand, more future-proofed, faster (more cases can be
covered by compile-time resolution), and allows PHP drop-ins for
binary/internal functionality. 

To recap, problems with the current approach that are resolved by what I 
suggest:

1. Exposing internal/user functions in userspace code as two different 
things.
2. Drop-in replacements for internal functionality won't work in namespace 
context.
3. Problems when internal functions/classes start showing up in namespaces 
in the future.
4. Performance hit, as more things need to be resolved at runtime that it's 
needed.

Please give me your feedback and use cases.

Regards,
Stan Vassilev

---

## #37657 — Jessie Hernandez — 2008-05-15T02:27:34+00:00

URL: https://externals.io/message/37654#37657 · In reply to: #37654

Hi Stan,

I made a proposal and patch a few months ago at 
http://news.php.net/php.internals/34097, which Julian later adapted to a 
more recent version of PHP 5.3 at the time at 
http://news.php.net/php.internals/36822 (don't know if the patch still 
applies cleanly).

Unfortunately I haven't received many comments on the patch nor any 
reasons on why it shouldn't be merged. Let me know what you think of the 
resolution rules I proposed.


-- 

Jessie Hernandez
Zend Certified Engineer (http://zend.com/zce.php?c=ZEND006359&r=222727282)


Stan Vassilev | FM wrote:
> Hi,
> 
> I'm looking at the current namespace resolution rules here:
> http://www.php.net/manual/en/language.namespaces.rules.php
> 
> Something that bothers me is that for the first time in PHP we introduce a
> hard difference in how internal functions and user functions are
> resolved/called:
> 
> ::foo(); // resolves as user function in global namespace
> foo(); // can resolve as function in the current namespace, else as internal
> function
> 
> Whether a function/class is internal is implementation detail that I believe
> shouldn't be forced in such a fashion in user code.
> 
> It makes people to check/think whether a function is internal or not,
> additionally to thinking about namespace scope. It's unneded complexity. If
> you check the PHP manual comments, you'll notice people often introduce PHP
> userspace equivalents to various functions and extensions, there's for
> example a full PHP-only implementation of curl:
> http://code.blitzaffe.com/pages/phpclasses/files/libcurl_emulator_52-7
> 
> There was also a wave of drop-in replacements for PHP5 function in PHP4, or
> for example json_en/decode() drop-in for PHP 5.1 users who don't have the
> PECL extension.
> 
> None of those will be possible in namespace scope in 5.3, and a developer
> will need to have plenty of special cases in code or special factories, if
> it's not known in advance whether a class/function will be internal or not.
> 
> To further convolute things, the drop-ins will work if *not* in namespace 
> context.
> 
> A second problem is one of future-proofing. Many of the procedural API-s in
> PHP4 saw transition to OOP in PHP5. If namespaces have success and are
> adopted widely, we may see new or existing internal functions make similar
> transitions to being namespaced. This will leave "global internal" function
> stick our like a sore thumb, as a solitary special case with specific
> syntax, as opposed to all other cases we have.
> 
> A third problem is one of performance. Every time a developer calls an
> internal function, an attempt will be made to resolve it to a user function
> in the current namespace, and only then call the internal function. Typical
> PHP code contains plenty of internal function calls, while I believe the
> novelty of being able to transparently override intenral functions will wear
> off quickly and be used quite sparingly (and quite likely be considered a
> bad practice, leading to confusing code).
> 
> What I suggest:
> 
> ::foo(); // to resolve a user or internal function in global namespace (in
> namespace context)
> foo(); // to only resolve at compile time, in the current namespace (in
> namespace context)
> 
> Much simpler to understand, more future-proofed, faster (more cases can be
> covered by compile-time resolution), and allows PHP drop-ins for
> binary/internal functionality. 
> 
> To recap, problems with the current approach that are resolved by what I 
> suggest:
> 
> 1. Exposing internal/user functions in userspace code as two different 
> things.
> 2. Drop-in replacements for internal functionality won't work in namespace 
> context.
> 3. Problems when internal functions/classes start showing up in namespaces 
> in the future.
> 4. Performance hit, as more things need to be resolved at runtime that it's 
> needed.
> 
> Please give me your feedback and use cases.
> 
> Regards,
> Stan Vassilev

---

## #37671 — Ryan Panning — 2008-05-16T01:40:08+00:00

URL: https://externals.io/message/37654#37671 · In reply to: #37657

Jessie Hernandez wrote:
 > Hi Stan,
 >
 > I made a proposal and patch a few months ago...

The developers should really take a serious look at this issue or it 
will come back to haunt them later. I'm not sure why no one seems 
comment on your proposal and patch. It seemed like a well thought-out 
and good proposal to me.

I'm also wondering what happened to the namespace declaration change to 
brackets {}. I've downloaded the latest snap of 5.3 and that has not 
changed as of yet.

---

## #37674 — Nathan Rixham — 2008-05-16T10:44:43+00:00

URL: https://externals.io/message/37654#37674 · In reply to: #37671

Ryan Panning wrote:
> Jessie Hernandez wrote:
> > […]
> 
> The developers should really take a serious look at this issue or it 
> will come back to haunt them later. I'm not sure why no one seems 
> comment on your proposal and patch. It seemed like a well thought-out 
> and good proposal to me.
> 
> I'm also wondering what happened to the namespace declaration change to 
> brackets {}. I've downloaded the latest snap of 5.3 and that has not 
> changed as of yet.

is it too late to scrap all this and go with Java/AS3 style 
base.package.class please?

---

## #37677 — Gregory Beaver — 2008-05-16T14:51:18+00:00

URL: https://externals.io/message/37654#37677 · In reply to: #37674

Nathan Rixham wrote:
> Ryan Panning wrote:
> > […]
> 
> is it too late to scrap all this and go with Java/AS3 style
> base.package.class please?

yes

Greg

---

## #37680 — Pierre Joye — 2008-05-16T15:23:39+00:00

URL: https://externals.io/message/37654#37680 · In reply to: #37677

> Nathan Rixham wrote:
> > […]
>
> yes

Not saying that it is a good thing to do that but it is not too late
to change things.

Cheers,
-- 
Pierre
http://blog.thepimp.net | http://www.libgd.org

---

## #37681 — Stan Vassilev | FM — 2008-05-16T15:44:42+00:00

URL: https://externals.io/message/37654#37681 · In reply to: #37680

Hi,

I'd like to nudge the discussion back to issues with the resolution rules 
that we're discovering :)
The actual char(s) used can only be mildly annoying to some (at worst), 
compared.

Can we please agree on those (or explain why you'd not):

1) functions, constants and classes should use the same resolution rules. 
Making special cases just for some of them, or just for user or just 
internal ones will lead to confusion.
2) can someone please explain why is it useful to override internal function 
inside a namespace. Isn't it better to explicitly refer to global functions 
as global, which would allow compile-time resolution in a lot more cases 
than now.

I said before, it looks like the current situation where you can define 
globally a PHP-only clone of a missing binary/internal features is a far 
more predictable behaviour, and people use it a *lot* right now. Do we 
really want to break this in 5.3?

Regards,
Stan Vassilev


> > […]
>
> Not saying that it is a good thing to do that but it is not too late
> to change things.
>
> Cheers,
> -- 
> Pierre

---

## #37682 — Gregory Beaver — 2008-05-16T19:01:34+00:00

URL: https://externals.io/message/37654#37682 · In reply to: #37681

Stan Vassilev | FM wrote:
> Hi,
> 
> I'd like to nudge the discussion back to issues with the resolution
> rules that we're discovering :)
> The actual char(s) used can only be mildly annoying to some (at worst),
> compared.
> 
> Can we please agree on those (or explain why you'd not):
> 
> 1) functions, constants and classes should use the same resolution
> rules. Making special cases just for some of them, or just for user or
> just internal ones will lead to confusion.

I have to agree.  In fact, there is a simple and elegant solution to the
problem that involves augmenting Jessie's patch to include a more
helpful error message on class/function not found, and a simple 51-line
PHP script for converting non-namespaced to namespaced code.  With these
two things, I think we could safely change the name resolution in PHP.

> 2) can someone please explain why is it useful to override internal
> function inside a namespace. Isn't it better to explicitly refer to
> global functions as global, which would allow compile-time resolution in
> a lot more cases than now.

The reasoning behind this decision that I was given by Dmitry and
Stanislav was 2-fold.

1) Currently, the lookup order is optimized for internal
functions/classes [more detail below]
2) migrating existing code to namespaces will require much more work if
all internal functions/classes must be prefixed with :: or "used" to be
avialable

Detail:

1) Currently the lookup order is optimized for internal functions/classes.

At compile-time, all internal classes/functions exist.  However, it is
possible that not all userspace functions/classes exist yet.  As such,
if a `T_STRING` is encountered, here is how the resolution works currently
in the worst case (roughly, in pseudo-code):

=> is it an internal function/class? if so, use it
=> does it exist in the current namespace? if so use it
=> otherwise, mark it for runtime resolution
=> run code
=> when we reach this opcode, resolve the function

In the best case:

=> is it an internal function/class? use it

However, changing this to allow resolving always to the current
namespace would change this to:

worst case:
=> does it exist in the current namespace? if so use it
=> otherwise, mark it for runtime resolution
=> run code
=> when we reach this opcode, resolve the function

best case:
=> does it exist in the current namespace? if so use it

This results in a fatal error for all internal functions/classes.  The
error message for a non-existing class/function should at least hint
that a use statement is needed, an element missing from Jessie's patch.

In any case, #1 proves to be false, as adding a "use" clause for each
internal function/class is no performance hit at all.

2) migrating existing code to namespaces will require more work if all
internal functions/classes must be prefixed with :: or "used"

As a test, I tried adding a namespace declaration to the top of one of
PEAR's larger file, and then ran it with PHP 5.3 and the patch.  To my
(initial) surprise, it ran without error.  On further investigation, I
realized why this is so - none of the internal functions/classes were
actually resolved, all of them were delayed until runtime, and none were
actually executed.

What this means is that by adding a namespace declaration, it is quite
possible that your code will execute happily until it suddenly
encounters a function/class you forgot to "use" or prefix with ::.

Fortunately, I just wrote a simple namespace conversion script that
solves this problem decisively.  The eventual script should be checked
over for missing userspace class/functions, as only internal
class/functions will be "use"d.  Here it is:

<?php
namespace NSParser;
class Parser
{
    protected $tokens;
    protected $i = -1;
    protected $classes = array();
    protected $functions = array();
    protected $use = array();
    protected $ret;

    function __construct($path, $namespace)
    {
        $classes = ::get_declared_classes();
        $classes = ::array_merge($classes, ::get_declared_interfaces());
        $this->classes = ::array_flip($classes);
        unset($this->classes['NSParser::Parser']);
        $functions = ::get_defined_functions();
        $this->functions = ::array_flip($functions['internal']);
        if (@::is_file($path)) {
            $path = ::file_get_contents($path);
        }
        $this->tokens = ::token_get_all($path);
        foreach ($this->tokens as &$token) {
            if (!::is_array($token)) {
                $token = array(::ord($token), $token);
            }
        }
        $ret = "<?php\nnamespace " . $namespace . ";\n";
        do {
            if ($this->tokens[$this->i][0] == T_STRING) {
                if (isset($this->classes[$this->tokens[$this->i][1]])) {
                    $this->use[$this->tokens[$this->i][1]] = 1;
                }
                if (isset($this->functions[$this->tokens[$this->i][1]])) {
                    $this->use[$this->tokens[$this->i][1]] = 1;
                }
            }
        } while (++$this->i < ::count($this->tokens));
        foreach ($this->use as $name => $unused) {
            $ret .= "use ::$name;\n";
        }
        $ret .= "?>" . $path;
        $this->ret = $ret;
    }

    function __toString()
    {
        return $this->ret;
    }
}
?>

Greg

---

## #37683 — Stanislav Malyshev — 2008-05-16T19:06:58+00:00

URL: https://externals.io/message/37654#37683 · In reply to: #37674

> is it too late to scrap all this and go with Java/AS3 style 
> base.package.class please?

Is it too late to switch to Java/AS3? ;)
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #37684 — Stanislav Malyshev — 2008-05-16T19:25:03+00:00

URL: https://externals.io/message/37654#37684 · In reply to: #37681

Hi!

> 1) functions, constants and classes should use the same resolution 
> rules. Making special cases just for some of them, or just for user or 
> just internal ones will lead to confusion.

No, not necessarily. Functions, constants and classes have different 
traditional usage patterns in PHP, and as such may have resolution rules 
that follow these patterns. I don't say we should look for making them 
different, but if necessary, we should use rules that benefit the most 
frequent use cases,  even if it upsets some purists that like everything 
being lined up and painted the same color.

> 2) can someone please explain why is it useful to override internal 
> function inside a namespace. Isn't it better to explicitly refer to 
> global functions as global, which would allow compile-time resolution in 
> a lot more cases than now.

It is not better, since it means that if you have non-namespace code 
library, you would have to edit a lot of code to convert it to namespace 
- you would have to edit every function call. Aaside form looking 
awkward, it is a lot of work. Of course, you can still do it, but you 
shouldn't have to.
Since OO libraries rarely use (read: should never use, unless they have 
very super-duper good reason to, and even then probably not) global 
non-internal functions, usage pattern for internal and non-internal 
functions differ.

I know this does not account for the use case of having some function 
both internal and implemented in PHP. This is not a frequent use case, 
but it happens. I see it much less frequent use case than converting 
non-namespaced code to namespaces. In this case, you will have to do 
some additional work - you'd have to prepend :: to any global function 
you do such tricks on. It's not worse than prepending :: to *ALL* 
functions as you seem to suggest.

> globally a PHP-only clone of a missing binary/internal features is a far 
> more predictable behaviour, and people use it a *lot* right now. Do we 

What do you mean by "predictable"? Current behavior is 100% predictable 
and described in the manual.

> really want to break this in 5.3?

We don't, and we won't. You'd just need to alter your code slightly to 
achieve same behavior. As I understood, the proposal is to force *ALL* 
functions to work this way - how it's better?
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #37685 — Stanislav Malyshev — 2008-05-16T19:29:15+00:00

URL: https://externals.io/message/37654#37685 · In reply to: #37682

Hi!

>         $classes = ::get_declared_classes();
>         $classes = ::array_merge($classes, ::get_declared_interfaces());
>         $this->classes = ::array_flip($classes);
>         unset($this->classes['NSParser::Parser']);
>         $functions = ::get_defined_functions();
>         $this->functions = ::array_flip($functions['internal']);
>         if (@::is_file($path)) {
>             $path = ::file_get_contents($path);

Do you really think that's how PHP code should look like - constant 
obsessive ::-ing? For my taste, it looks very bad.
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #37686 — Gregory Beaver — 2008-05-16T19:59:28+00:00

URL: https://externals.io/message/37654#37686 · In reply to: #37685

Stanislav Malyshev wrote:
> Hi!
>
> > […]
>
> Do you really think that's how PHP code should look like - constant
> obsessive ::-ing? For my taste, it looks very bad.
Actually, no.  The irony of your message is that the code you quote is
used to generate use statements so ::this ::is ::unnecessary.

Greg

---

## #37687 — Nathan Rixham — 2008-05-16T20:11:19+00:00

URL: https://externals.io/message/37654#37687 · In reply to: #37683

Stanislav Malyshev wrote:
> > […]
> 
> Is it too late to switch to Java/AS3? ;)

Already have switched front end design to flex3/as3 which is why I'm 
asking :o)

Nath

---

## #37688 — Stan Vassilev | FM — 2008-05-16T23:30:54+00:00

URL: https://externals.io/message/37654#37688 · In reply to: #37684

Hi,

> No, not necessarily. Functions, constants and classes have different 
> traditional usage patterns in PHP, and as such may have resolution rules 
> that follow these patterns. I don't say we should look for making them 
> different, but if necessary, we should use rules that benefit the most 
> frequent use cases,  even if it upsets some purists that like everything 
> being lined up and painted the same color.

I find that line of thinking unfortunate. I'm not a purist, if I was, my 
list of issues with namespaces would be far longer than it actually is.

Trying to fudge the behavior of PHP to fit some kind of presumed usage 
patterns is what got us features like magic quotes and register globals. 
Namespaces were introduced to solve the problems of large scale projects, 
and I think anyone on this list can agree that on a large scale project, 
small hacks like these are a hindrance and not an aid.

> > […]
>
> What do you mean by "predictable"? Current behavior is 100% predictable 
> and described in the manual.
>
> > […]
>
> We don't, and we won't. You'd just need to alter your code slightly to 
> achieve same behavior. As I understood, the proposal is to force *ALL* 
> functions to work this way - how it's better?

Please can you support this with an example? Imagine we introduced 
namespaces as they are in 5.0. I want to use json_encode but I have PHP 5.1 
and don't have the PECL extension.

What I can do currently is simply drop one of the many PHP-only 
json_encode/json_decode replacements and code away without being bothered 
whether in my specific environment json_encode/decode is internal or user. 
But in namespace context I'll have to do something along the lines of:

if (function_exists('json_encode')) {
    $encoded = json_encode($raw);
} else {
    $encoded = ::json_encode($raw);
}

I hope this helps to understand the problem. Please correct this code 
slightly, as you suggest, to fix this problem.

Regards,
Stan Vassilev

---

## #37689 — Stanislav Malyshev — 2008-05-16T23:45:58+00:00

URL: https://externals.io/message/37654#37689 · In reply to: #37688

Hi!

> Trying to fudge the behavior of PHP to fit some kind of presumed usage 
> patterns is what got us features like magic quotes and register globals. 

You sound like these usage patterns are figment of my imagination. They 
are not. As for magic quotes and register globals, I understand that's 
very convenient argument to say "your ideas is exactly what lead to 
these", only this has nothing to do with current case. Yes, in past in 
PHP were some decisions that proved to be wrong later. But bringing them 
up in each discussion regardless of its relevance is not going to 
convince anybody.

> Namespaces were introduced to solve the problems of large scale 
> projects, and I think anyone on this list can agree that on a large 
> scale project, small hacks like these are a hindrance and not an aid.

It's not "small hacks". It's supporting the way PHP is used in 100% of 
existing code.

> internal or user. But in namespace context I'll have to do something 
> along the lines of:
> 
> if (function_exists('json_encode')) {
>    $encoded = json_encode($raw);
> } else {
>    $encoded = ::json_encode($raw);
> }

What this code is supposed to do? If it's supposed to call namespace's 
json_encode in case global one is not defined, I see nothing wrong with 
it. If it is intended to call globally defined json_encode, then you 
just write ::json_encode, no ifs. The thing is you'd need to do it 
*ONLY* for json_encode, and only because you want to do something 
special with it. The principle is - common case is free, special cases 
are available, but for a "fee" - additional code.

-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #37690 — Stan Vassilev | FM — 2008-05-17T00:10:00+00:00

URL: https://externals.io/message/37654#37690 · In reply to: #37689

> > […]
>
> What this code is supposed to do? If it's supposed to call namespace's 
> json_encode in case global one is not defined, I see nothing wrong with 
> it. If it is intended to call globally defined json_encode, then you just 
> write ::json_encode, no ifs. The thing is you'd need to do it *ONLY* for 
> json_encode, and only because you want to do something special with it. 
> The principle is - common case is free, special cases are available, but 
> for a "fee" - additional code.

The code is supposed to call the global internal `json_encode()` if it exists, 
and if not, call global user `json_encode()`.

Without namespaces this is achieved in the following way:

$encoded = json_encode($raw); // global user or global internal, doesn't 
matter, it's transparent.

But inside a namespace, you'll need to do the above checks since internals 
and user functions don't resolve under the same rules.

---

## #37691 — Stanislav Malyshev — 2008-05-17T00:49:00+00:00

URL: https://externals.io/message/37654#37691 · In reply to: #37690

Hi!

> But inside a namespace, you'll need to do the above checks since 
> internals and user functions don't resolve under the same rules.

No, not really - just use:
$encoded = ::json_encode($raw);

It might be even possible to allow something like:
use ::json_encode;

to allow you to write just `json_encode()` instead, but this needs to be 
checked. ::json_encode just works.
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com
