solving the namespace conflict issues between function/static method class constant/ns constant
| From: | Greg Beaver | Date: | Sat, 20 Sep 2008 03:59:46 +0000 |
| Subject: | solving the namespace conflict issues between function/static method class constant/ns constant | ||
| Groups: | php.internals | ||
| Request: | Send a blank email to internals+get-40569@lists.php.net to get a copy of this message | ||
Hi all,
There is a problem in the namespace implementation. This code demonstrates the issue:
code.inc:
<?php
namespace foo;
class test {
const my = 1;
static function bar(){}
}
namespace foo::test;
const my = 2;
function bar(){}
?>
main.php:
<?php
include 'code.inc';
foo::test::bar(); // always calls namespace function
call_user_func(array('foo::test', 'bar')); // the only way to call static method
echo foo::test::my; // always 2
$a = new foo::test;
echo $a::my; // the only way to access foo::test::my
?>
There are 5 ways to solve this:
1) document it and hope no one uses it [this is the current strategy, minus the documentation part]
2) add a fatal error on conflicting names.
http://pear.php.net/~greg/ns_method.func.conflict.patch.txt
Unfortunately, this can only work for methods/functions, as namespace constants are actually defined at run-time, and so conflicts can't be detected until then, which does not provide any benefit (you want to catch this conflict at compile-time).
3) create a disambiguation method.
http://pear.php.net/~greg/ns.func_const.patch.txt
This introduces function:: and const:: as prefixes to disambiguate.
<?php
function::foo::test::bar(); // always calls namespace function
foo::test::bar(); // always calls static method if defined, falls back to ns function if not
const::foo::test::my; // namespace constant
foo::test::my; // class constant
?>
The drawback of this approach is that "foo::test::bar" will always look for class "foo::test", which means if autoload is defined, it will be called for all non-prefixed function calls. Unfortunately, the only other keyword for disambiguating would be "class::" as in "class::foo::test::bar()" but this is excessively confusing.
4) remove functions/constants from namespaces
5) a simply syntax change to namespaces, introducing a new concept: namespace element.
A namespace element is a class, function or constant defined within a namespace declaration:
<?php
namespace foo;
class bar {} // class element bar in namespace foo
function bar(){} // function element bar in namespace foo
const bar=1; // const element bar in namespace foo
?>
This is similar to class elements:
<?php
class foo {
function bar(){} // method element bar in class foo
const bar=1; // constant element bar in class foo
public $bar=1; // variable element bar in class foo
}
?>
Currently, this code:
<?php
namespace foo::bar;
class buh{}
?>
creates a class named "foo::bar::buh", essentially joining the namespace "foo::bar" and its class element "buh" with the separator "::". This turns out to be the root of the problem with the conflicts between class elements and namespace elements. The last patch introduces a new namespace element operator to delineate the boundary between namespace name and element name. For the patch, I recycled T_OBJECT_OPERATOR (->).
current way:
<?php
foo::bar->test(); // namespace foo::bar, call to function element test()
foo->bar::test(); // namespace foo, call to static method element test() in class element bar
foo->myconst; // namespace foo constant myconst
foo::myconst; // class foo constant myconst
?>
The patch is at:
http://pear.php.net/~greg/ns.element.patch.txt
This is the most extensive change. The patch preserves :: as global element accessor (::strlen() calls strlen internal function, for instance). I'm happy to answer any other questions.
So, these are the choices. I suggest we all take a rational look at the options, and understand the consequences of each, and make the best choice possible.
Thanks,
Greg