Hello All,

There is no way around it given the feedback I am seeing about namespace usage in the wild, to warm up this old post again. This post and the additional commentary, especially about performance by Stas can be found in the archives at:
http://marc.info/?l=php-internals&m=121527668606247

I think given the feedback from users, we really need to actually benchmark the performance slow down that Greg's proposed changes will bring. I also guess there are two types of users out there: 1) those that tend to always spice up their API's by wrapping pretty much all internal functionality (be if that the internal functionality has a procedural or an OO API). These are also the people that will have the more complex OO structures (deep inheritance). They also seem like the most heavy users of namespaces 2) those that keep their structures flat, use native API's directly and live in a mixed world of OO and procedural code

So while we are assessing the impact its important to keep in mind which user type is going to be affected in what way. Furthermore I hope that some of the people that have build up a code base that uses namespaces will do this benchmarking, because otherwise we will hardly get any useful data. Even then of course the benchmarks must be taken with a grain of salt (as always).

I am including the email from Greg at the bottom here, along with a reply from Stas (again read the archive to get the full story). Maybe if we think hard about this one (now that we know that its an important real world need), we can discover a solution that we did not see at first.

On 05.07.2008, at 18:48, Greg Beaver wrote:

1) namespaces
Here we need to make sure that the current state is now in a coherent state. I think Derick still has some issues [3] with the recent change by Greg [4], but even his criticism did not sound all to loud. So I think we are in a good state here?

My recent changes aside, there are still some fubar issues with name resolution, and there has been some offlist discussion of ways to fix this that have not led to concrete onlist proposals yet.

For those who don't know what I'm talking about, the question is how to resolve an unqualified classname inside a namespace. i.e. this code:

a.php:
<?php
namespace Foo;
throw new Exception('is this Foo::Exception or ::Exception?');
?>

Currently, the above code would throw ::Exception, because if a namespaced class does not exist, internal classes are checked before autoloading for performance reasons. However, if we execute this code instead:

b.php:
<?php
namespace Foo;
class Exception {}
include 'a.php';
?>

then a.php would throw Foo::Exception. In other words, depending on file inclusion order, classnames can change with current PHP accepted practices. The solution to this is to use each namespaced class explicitly:

a2.php:
<?php
namespace Foo;
use Foo::Exception;
throw new Exception('this is Foo::Exception always');
?>

However, all it takes is one forgotten "use" statement, and a very subtle bug could be introduced later.

Taking a step back, let's examine why autoload is not called before checking internal classes - it is for performance reasons. However, this performance enhancement causes unpredictable behavior *by default* without explicit action.

There is a simple solution, however. If one changes the name resolution to this pseudo-code:

1) check Foo::Exception for existence
2) if not found, try to autoload Foo::Exception
3) if not found, check for ::Exception existence, userspace *or* internal (addition of userspace check also fixes other problems raised on-list)
4) if not found, fail.

This fixes the logic problem, and re-introduces the performance slowdown for internal classes. FORTUNATELY there is a simple solution to this, which is to "use" all global classes:

<?php
namespace Foo;
use ::ArrayIterator;
$a = new ArrayIterator(array(1,2,3));
?>

The above code works exactly the same as:

<?php
namespace Foo;
$a = new ArrayIterator(array(1,2,3));
?>

and short-circuits the name resolution to:

1) check ::ArrayIterator for existence
2) if not found, fail.

To summarize, we should scrap the "performance enhancement" that introduces the potential for subtle and dangerous logic errors in name resolution because it is still possible to achieve that performance enhancement through one-time "use" of internal classnames.

With these changes, namespace classname resolution will be 100% deterministic regardless of file load order, it will still be possible to override internal classnames with userspace classes, and for the ultra-performance hungry, it is still easy to achieve faster classname resolution for internal classes. Win-win.


On 17.07.2008, at 19:57, Stanislav Malyshev wrote:

In other words, with the current implementation, you can just do "use Foo::Exception;" and it works, but if you *forget* to do the use statement, you can get unpredictable behavior with no warning until your

You get previctable behavior in all cases, there's no randomness in the engine.

to "use ::Exception", then your code is simply slightly slower (1 extra call to autoload if it exists), but still works properly. It also tilts

It's not slightly slower, it's *A LOT* slower - exhaustive search of whole include path for *EACH* call mentioning internal class. That means you have to declare *EACH* internal class - even if no clashes ever existed - for *EACH* use. Basically, it's the same as always using ::Class only implemented in a way that its disastrous effects are hidden until you check performance of your code. Then you have to go back and add use statement for each internal class in each of your files. Lot's of fun that would be.


regards,
Lukas Kahwe Smith
[EMAIL PROTECTED]




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

Reply via email to