Hi,
Please look into patch that implements __autoload() support for namespaces.
The patch touches not only ZE but also SPL.
In the following code we cannot exactly know if "Exception" class is from
current namespace or it is an internal PHP class.
<?php
namespace Foo;
throw new Exception;
In this case PHP first looks for Foo::Exception and only then for internal
Exception, but the first lookup may call __autoload (or corresponding SPL
functions) and it can emit error or throw exception.
The patch provides an additional boolean argument to __autoload() that say
if class is really required. In case if it false, user code shouldn't emit
errors or throw exceptions.
I am going to commit the patch on Wednesday.
It is the last semantic patch for namespaces we have.
After its commit we are going to look into syntax problems (namespace or
package, brackets, import or using, ...).
Thanks. Dmitry.
<?php
namespace Foo;
throw new Exception;In this case PHP first looks for Foo::Exception and only then for internal
Exception, but the first lookup may call __autoload (or corresponding SPL
functions) and it can emit error or throw exception.The patch provides an additional boolean argument to __autoload() that say
if class is really required. In case if it false, user code shouldn't emit
errors or throw exceptions.
There's two problems here:
- On each access to internal class, like Exception, SPL classes,
DateTime, reflection classes, etc. - we'd have call to autoload and
subsequent disk access, maybe more than one if we have include path.
Performance of it would be awful. - All libraries having autoloaders would have to rewrite them to
support the new mode.
I would propose different solution. When we have unresolved unqualified
name, we do the following:
- Check if we already know such class in namespace at compile-time. If
so, it's resolved. - If not, will be resolved at run-time.
- At run-time, check if we know such class in namespace now. If yes,
it's resolved. - If not, check if we know internal class with such name. If yes, it's
resolved to internal class. - If not, try to autoload this class. If autoloading fails, it's the
undefined class error.
This rule is a bit more complex, but allows to resolve common cases
without extra filesystem accesses and allows to keep autoloader without
modification.
Comments?
Stanislav Malyshev, Zend Software Architect
stas@zend.com http://www.zend.com/
(408)253-8829 MSN: stas@zend.com
Stanislav Malyshev wrote:
<?php
namespace Foo;
throw new Exception;In this case PHP first looks for Foo::Exception and only then for
internal
Exception, but the first lookup may call __autoload (or corresponding SPL
functions) and it can emit error or throw exception.The patch provides an additional boolean argument to __autoload() that
say
if class is really required. In case if it false, user code shouldn't
emit
errors or throw exceptions.There's two problems here:
- On each access to internal class, like Exception, SPL classes,
DateTime, reflection classes, etc. - we'd have call to autoload and
subsequent disk access, maybe more than one if we have include path.
Performance of it would be awful.
This is a problem, agreed.
- All libraries having autoloaders would have to rewrite them to
support the new mode.
This is not an issue - won't they have to support
Class::Names::Like::This anyways? Backwards compatibility has already
been broken.
I would propose different solution. When we have unresolved unqualified
name, we do the following:
- Check if we already know such class in namespace at compile-time. If
so, it's resolved.- If not, will be resolved at run-time.
- At run-time, check if we know such class in namespace now. If yes,
it's resolved.- If not, check if we know internal class with such name. If yes, it's
resolved to internal class.- If not, try to autoload this class. If autoloading fails, it's the
undefined class error.
The problem is that with this autoload technique this code now throws an
Exception rather than a Foo::Exception:
Foo/Exception.php:
<?php
namespace Foo;
class Exception extends ::Exception {}
?>
Foo/Something.php:
<?php
namespace Foo;
function __autoload($class)
{
include str_replace('::', '/', $class) . '.php';
}
class Something
{
function __construct($param)
{
if ($param == 3) {
throw new Exception('oops');
}
}
}
$a = new Something(3);
?>
This would mean that all naming conflicts with internal classes must
have a different import or be fully qualified.
However, I wonder if using an import would be a clever way to get around
the problem?
Foo/Something.php:
<?php
namespace Foo;
import Foo::Exception;
function __autoload($class)
{
include str_replace('::', '/', $class) . '.php';
}
class Something
{
function __construct($param)
{
if ($param == 3) {
throw new Exception('oops');
}
}
}
$a = new Something(3);
?>
As I understand it, this would make the file act as if we had written it
like:
Foo/Something.php:
<?php
function __autoload($class)
{
include str_replace('::', '/', $class) . '.php';
}
class Foo::Something
{
function __construct($param)
{
if ($param == 3) {
throw new Foo::Exception('oops');
}
}
}
$a = new Foo::Something(3);
?>
If this is indeed the case, then it would satisfy my concerns with the
patch and would simply be up to the documentation team to document this
gotcha.
Greg
The problem is that with this autoload technique this code now throws an
Exception rather than a Foo::Exception:
Yep, unless you do require 'Foo/Exception.php'. Yes, I know it's not as
nice as it might be. But the alternative is either use :: always or say
goodbye to performance.
However, I wonder if using an import would be a clever way to get around
the problem?
It would solve the issue too, of course.
Stanislav Malyshev, Zend Software Architect
stas@zend.com http://www.zend.com/
(408)253-8829 MSN: stas@zend.com
Why don't we add an optional second argument to __autoload() that
receives the fully qualified namespace name of the class that should
be autoloaded? That doesn't break BC and it prevents conflicts.
David
Am 27.08.2007 um 19:49 schrieb Stanislav Malyshev:
<?php
namespace Foo;
throw new Exception;
In this case PHP first looks for Foo::Exception and only then for
internal
Exception, but the first lookup may call __autoload (or
corresponding SPL
functions) and it can emit error or throw exception.
The patch provides an additional boolean argument to __autoload()
that say
if class is really required. In case if it false, user code
shouldn't emit
errors or throw exceptions.There's two problems here:
- On each access to internal class, like Exception, SPL classes,
DateTime, reflection classes, etc. - we'd have call to autoload and
subsequent disk access, maybe more than one if we have include
path. Performance of it would be awful.- All libraries having autoloaders would have to rewrite them to
support the new mode.I would propose different solution. When we have unresolved
unqualified name, we do the following:
- Check if we already know such class in namespace at compile-
time. If so, it's resolved.- If not, will be resolved at run-time.
- At run-time, check if we know such class in namespace now. If
yes, it's resolved.- If not, check if we know internal class with such name. If yes,
it's resolved to internal class.- If not, try to autoload this class. If autoloading fails, it's
the undefined class error.This rule is a bit more complex, but allows to resolve common cases
without extra filesystem accesses and allows to keep autoloader
without modification.Comments?
Stanislav Malyshev, Zend Software Architect
stas@zend.com http://www.zend.com/
(408)253-8829 MSN: stas@zend.com
Why don't we add an optional second argument to __autoload() that
receives the fully qualified namespace name of the class that should be
autoloaded? That doesn't break BC and it prevents conflicts.
The first argument already receives that, why would you need the second one?
Stanislav Malyshev, Zend Software Architect
stas@zend.com http://www.zend.com/
(408)253-8829 MSN: stas@zend.com
In this case PHP first looks for Foo::Exception and only then for internal
Exception, but the first lookup may call __autoload (or corresponding SPL
functions) and it can emit error or throw exception.The patch provides an additional boolean argument to __autoload() that say
if class is really required. In case if it false, user code shouldn't emit
errors or throw exceptions.
It looks like this is going to break BC. Are current autoload functions
guaranteed to work with the new implementation?
Derick
--
Derick Rethans
http://derickrethans.nl | http://ez.no | http://xdebug.org
Hi,
From: Dmitry Stogov [mailto:dmitry@zend.com]
The patch provides an additional boolean argument to
__autoload() that say if class is really required. In case if
it false, user code shouldn't emit errors or throw exceptions.
Actually, an autoload handler should never emit errors or throw exceptions. With handlers registered through SPL, it is already the case. When using an __autoload() functions, raising an error when a symbol is not found is useless because we know that the PHP interpreter will do it. So, I propose to have PHP ignore any error or exception raised from autoload handlers. This would make the additional argument useless.
Actually, I also wanted to add a second argument to autoload handlers. It would give the type of the symbol we are looking for. Today, we are unable to know if we are searching for a class or an interface. So, an autoload handler like PHK's has to try both. It is not very important because most existing autoload handlers are so primitive (generally filename-based) that they treat both the same way. But, in the future, if we extend autoloading to functions and constants, we will need this argument. I cannot work on that now but, as you are considering modifying the autoload handler arguments, I thought you could be interested.
Regards
Francois