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

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to