# Order of class resolution with namespaces and autoload

- Thread: https://externals.io/message/32877
- Original archive: https://news-web.php.net/php.internals/32877
- Started by Chuck Hagenbuch on 2007-10-20T02:42:02+00:00
- Messages: 28
- Page 1 of 2: messages 1 to 25 (25 per page, add `?page=N` to the URL)
- Next page: https://externals.io/message/32877.md?page=2
- 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

---

## #32877 — Chuck Hagenbuch — 2007-10-20T02:42:02+00:00

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

Hi folks-

I've hit upon something with namespaces and autoloading that I think  
is a little out of order. If you take the attached 3 files, put them  
in the same directory, and run demo.php, you will see this:

Fatal error: Uncaught exception 'Exception' in /Users/chuck/Desktop/test.php:7


If you look at the code, test.php defines a Test:: namespace with a  
Tester class that throws an exception, test_exception.php provides an  
Exception derivative (Test::Exception) for that namespace, and  
demo.php includes test.php, sets up an __autoload function to load the  
exception class, and triggers the exception throwing code.

However autoload is never called and the exception is of type  
::Exception, because the class resolution order seems to look like this:

- does the class exist (_already defined_) in the current namespace?
- does the class exist in the global (non-prefixed or "::") namespace?
- then try autoload

What I think should happen is:

- does the class exist in the current namespace?
- can the class be autoloaded with the current namespace?
- does the class exist in the global scope
- global autoload

Otherwise if you are re-defining a global class, which I imagine will  
be quite common with Exception at least, you will have to explicitly  
load your Exception classes or they'll never be used.

The other alternative would be to say that if you want to use a global  
class inside a namespace you MUST prefix it with ::. I think this is  
reasonable since if you have a namespaced file it's already not going  
to work with anything before PHP 5.3, and that would remove the two  
potential autoload checks.

Thanks,
-chuck

---

## #32878 — Gregory Beaver — 2007-10-20T02:59:38+00:00

URL: https://externals.io/message/32877#32878 · In reply to: #32877

Chuck Hagenbuch wrote:
> Hi folks-
> 
> I've hit upon something with namespaces and autoloading that I think is
> a little out of order. If you take the attached 3 files, put them in the
> same directory, and run demo.php, you will see this:
> 
> Fatal error: Uncaught exception 'Exception' in
> /Users/chuck/Desktop/test.php:7
> 
> 
> If you look at the code, test.php defines a Test:: namespace with a
> Tester class that throws an exception, test_exception.php provides an
> Exception derivative (Test::Exception) for that namespace, and demo.php
> includes test.php, sets up an __autoload function to load the exception
> class, and triggers the exception throwing code.
> 
> However autoload is never called and the exception is of type
> ::Exception, because the class resolution order seems to look like this:
> 
> - does the class exist (_already defined_) in the current namespace?
> - does the class exist in the global (non-prefixed or "::") namespace?
> - then try autoload
> 
> What I think should happen is:
> 
> - does the class exist in the current namespace?
> - can the class be autoloaded with the current namespace?
> - does the class exist in the global scope
> - global autoload

Hi Chuck,

Are you suggesting there be namespace-specific autoload?  This sounds
interesting to me, but also quite complex, not sure the benefits would
outweigh the difficulties for implementation.  For instance, PEAR2
namespace would be different from PEAR2::Someclass, and one would need
to register the same autoload handler for each possible namespace,
otherwise the class resolution code would have to grep through each
classname to find sub-namespaces, which is most likely an unacceptable
performance hog.

> Otherwise if you are re-defining a global class, which I imagine will be
> quite common with Exception at least, you will have to explicitly load
> your Exception classes or they'll never be used.

There is another alternative:

<?php
namespace Test;
// explicitly tell PHP that we want Test::Exception to be autoloaded (in
essence)
import Test::Exception;

class Tester {
    public function fail() {
        throw new Exception();
    }
}
?>

This calls autoload, as expected, and works wonderfully.  It is also
quite efficient, but at the expensive of intuitiveness.  However, it
also has the benefit of explicitly declaring external classes at the top
of the file, which is a big plus for maintainability when others are
looking at your code.

For me, the fact that this coding practice is both more efficient than
the alternative you suggest and more maintainable makes the choice quite
appealing, even if it isn't immediately intuitive.

Greg

---

## #32879 — Chuck Hagenbuch — 2007-10-20T03:26:04+00:00

URL: https://externals.io/message/32877#32879 · In reply to: #32878

Quoting Gregory Beaver <greg@chiaraquartet.net>:

> Are you suggesting there be namespace-specific autoload?  This sounds
> interesting to me, but also quite complex, not sure the benefits would
> outweigh the difficulties for implementation.  For instance, PEAR2
> namespace would be different from PEAR2::Someclass, and one would need
> to register the same autoload handler for each possible namespace,
> otherwise the class resolution code would have to grep through each
> classname to find sub-namespaces, which is most likely an unacceptable
> performance hog.

Yes, I agree, that sounds awful. :) I was thinking in terms of a  
duality - say, A::B::C::Exception vs. ::Exception - but when you throw  
in the possibility that A::B::Exception exists, it sure gets ugly.

> > […]
>
> There is another alternative:
>
> <?php
> namespace Test;
> // explicitly tell PHP that we want Test::Exception to be autoloaded (in
> essence)
> import Test::Exception;
>
> class Tester {
>     public function fail() {
>         throw new Exception();
>     }
> }
> ?>
>
> This calls autoload, as expected, and works wonderfully.  It is also
> quite efficient, but at the expensive of intuitiveness.  However, it
> also has the benefit of explicitly declaring external classes at the top
> of the file, which is a big plus for maintainability when others are
> looking at your code.

Yes, this works. And I'll readily admit that I don't have a better  
idea right now. An observation, though: when combined with autoload  
this can change import from only affecting a single file to affecting  
all subsequent files.

1.php
<?

namespace Test;
import Test::Exception;

throw new Exception();

?>

2.php

<?

namespace Test;

throw new Exception();

?>


If I define a Test::Exception class and include those files in any  
order, both will create Test::Exception objects regardless of the  
import statement. However, if I set up autoload, then things become  
order-dependent:

- If I include 1.php, then 2.php, both files throw Test::Exception
- If I include 2.php, then 1.php, I get an Exception and then a  
Test::Exception


I can live with this, but I wonder if taking a bit of magic away from  
nested namespaces would be better. What's wrong with:

- If you are inside a namespace, all classnames that do not include a  
namespace separator (::) refer to classes of the current namespace.

So, if I want a "global" exception, I use ::Exception. If I say  
Exception, then I mean my namespace's Exception with no fallback. And  
if I want another namespace's Exception, I import it (import  
Other::Exception).

-chuck

---

## #32880 — Gregory Beaver — 2007-10-20T03:33:33+00:00

URL: https://externals.io/message/32877#32880 · In reply to: #32879

Chuck Hagenbuch wrote:
> So, if I want a "global" exception, I use ::Exception. If I say
> Exception, then I mean my namespace's Exception with no fallback. And
> if I want another namespace's Exception, I import it (import
> Other::Exception). 

I (and PEAR) could live with this.  I don't have a patch to back this
up, so it is sort of up to Dmitry/Stas unless someone else feels like
grokking the source and making one.

Greg

---

## #32882 — Stanislav Malyshev — 2007-10-20T20:55:14+00:00

URL: https://externals.io/message/32877#32882 · In reply to: #32877

> ::Exception, because the class resolution order seems to look like this:
> 
> - does the class exist (_already defined_) in the current namespace?
> - does the class exist in the global (non-prefixed or "::") namespace?
> - then try autoload

That's exactly how it works, with the amendment that only internal 
classes are considered on step 2.

> What I think should happen is:
> 
> - does the class exist in the current namespace?
> - can the class be autoloaded with the current namespace?
> - does the class exist in the global scope
> - global autoload

If it worked this way, you couldn't easily use global classes and old 
applications would be much harder to convert to namespaces. We think 
that using global Exception is much more frequent than defining your own 
Exception - and in the latter case you have the choice to name it 
something that is not the same as existing class, while in the latter 
case you do not. So we decided to make resolution rules so that it makes 
easier to work with the most frequent case and makes easier to work with 
existing code.

> The other alternative would be to say that if you want to use a global 
> class inside a namespace you MUST prefix it with ::. I think this is 

We consider it not the best choice. Using of global classes is 
ubiquitous in current applications, always requiring :: would make it 
very hard to convert them to namespaced code. We think it would require 
much more work than just having "require_once" in places when you 
override the internal classes.

> reasonable since if you have a namespaced file it's already not going to 
> work with anything before PHP 5.3, and that would remove the two 

The problem is not working with PHP before 5.3, the problem is using 
code that was written before 5.3. We try to make this code as easy as 
possible to upgrade, and it seems that blocking access to internal 
classes would not do us much good.
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #32883 — Stanislav Malyshev — 2007-10-20T20:57:42+00:00

URL: https://externals.io/message/32877#32883 · In reply to: #32878

> <?php
> namespace Test;
> // explicitly tell PHP that we want Test::Exception to be autoloaded (in
> essence)
> import Test::Exception;
> 
> class Tester {
>     public function fail() {
>         throw new Exception();
>     }
> }
> ?>

This is even better than requiring, and makes the intent very clear. I 
don't think it decreases intuitiveness, on the contrary - from the look 
of the code it is immediately clear which exception would be used.

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

---

## #32884 — Chuck Hagenbuch — 2007-10-20T21:26:58+00:00

URL: https://externals.io/message/32877#32884 · In reply to: #32882

Quoting Stanislav Malyshev <stas@zend.com>:

> If it worked this way, you couldn't easily use global classes and  
> old applications would be much harder to convert to namespaces. We  
> think that using global Exception is much more frequent than  
> defining your own Exception - and in the latter case you have the  
> choice to name it something that is not the same as existing class,  
> while in the latter case you do not.

If you look at the various PHP 5 class libraries or frameworks, pretty  
much all of them define an Exception for every package -  
Zend_Db_Exception, Zend_Http_Exception, etc.

Calling exception classes something other than Exception is kind of  
like giving up on the namespace - if I have to name my classes,  
*inside* their namespace, specially in order to avoid global class  
names, then that seems very unintuitive.

> > […]
>
> We consider it not the best choice. Using of global classes is  
> ubiquitous in current applications, always requiring :: would make  
> it very hard to convert them to namespaced code. We think it would  
> require much more work than just having "require_once" in places  
> when you override the internal classes.

I agree. I think Greg and I have a better solution: if you want to use  
a global class, import it. You can use :: if you want, but again I  
will say that we are talking about code _inside_ of a namespace. It  
would not add much conversion burden to say that if you want to use  
global classes in a file you are adding a namespace to, then just add  
imports for those classes. If PHP provided a PHP:: namespace with all  
global classes in it (as aliases), then you could just do import PHP  
and be done with it. People who wanted their namespace to be used  
instead would just not do that.

-chuck

---

## #32885 — Chuck Hagenbuch — 2007-10-20T21:28:32+00:00

URL: https://externals.io/message/32877#32885 · In reply to: #32883

Quoting Stanislav Malyshev <stas@zend.com>:

> > […]
>
> This is even better than requiring, and makes the intent very clear.  
> I don't think it decreases intuitiveness, on the contrary - from the  
> look of the code it is immediately clear which exception would be  
> used.

Except that it makes it unclear what happens in _other_ files, which  
is even worse. Once you include the file above, any other file in the  
Test:: namespace that throws an Exception will throw Test::Exception,  
not Exception, even if it doesn't import Test::Exception.

I think it makes much more sense to import classes that are _outside_  
the current namespace. Having to import pieces of your own namespace  
makes namespaces less useful and intuitive.

-chuck

---

## #32886 — Stanislav Malyshev — 2007-10-20T21:54:58+00:00

URL: https://externals.io/message/32877#32886 · In reply to: #32885

> Except that it makes it unclear what happens in _other_ files, which is 

In other files you specify what happens to other files.

> even worse. Once you include the file above, any other file in the 
> Test:: namespace that throws an Exception will throw Test::Exception, 
> not Exception, even if it doesn't import Test::Exception.

I'm afraid I don't understand. Regardless of what you include where, the 
name resolution for the file is defined by the code and imports in this 
file. Since that file does not define test::exception, it would not 
influence resolution of any file it is included in.

> I think it makes much more sense to import classes that are _outside_ 
> the current namespace. Having to import pieces of your own namespace 
> makes namespaces less useful and intuitive.

Import is just a way to write names shorter. Thus, you can employ it to 
achieve different goals.
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #32887 — Stanislav Malyshev — 2007-10-20T22:09:12+00:00

URL: https://externals.io/message/32877#32887 · In reply to: #32884

> Calling exception classes something other than Exception is kind of like 
> giving up on the namespace - if I have to name my classes, *inside* 

Not entirely correct - namespaces needed to reconcile _multiple_ 
libraries, while in case of Exception you need to stay away only from 
_one_ set of classes - namely, internal ones. While indeed even that 
could be inconvenient, the problem is that it is not possible to avoid 
ambiguity in all cases successfully, so we had to choose how we solve 
ambiguous cases. We decided to solve it in a way that gives most old 
code most chance to work, while knowing that some people would find some 
code less convenient with any code. Out solution allows to explicitly 
specify your intent in one of these ways:
1. Defining the class
2. Importing the name

> say that we are talking about code _inside_ of a namespace. It would not 
> add much conversion burden to say that if you want to use global classes 
> in a file you are adding a namespace to, then just add imports for those 
> classes. If PHP provided a PHP:: namespace with all global classes in it 

I think it would. It is much easier to keep track on your own classes 
than on system classes create by somebody else. And with exception of 
Exception (no pun intended :) I think there would be not many classes 
that would have names coinciding with internal class names.

> (as aliases), then you could just do import PHP and be done with it. 
> People who wanted their namespace to be used instead would just not do 
> that.

If you are OK with writing imports, then it shouldn't matter too much 
which imports you write. Right now you need to write your imports - or 
explicitly specify the class, or load the class. I would like to find 
better solution, but completely masking system classes with overloads 
and allowing only :: to work doesn't seem the best way to me currently. 
Also it would cause autoload call on each use of system classes even if 
you never had overridden them at all, which can have significant impact 
on the performance - exhaustive autoload search can be very expensive.
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #32888 — Chuck Hagenbuch — 2007-10-20T22:24:34+00:00

URL: https://externals.io/message/32877#32888 · In reply to: #32886

Quoting Stanislav Malyshev <stas@zend.com>:

> > […]
>
> In other files you specify what happens to other files.
>
> > […]
>
> I'm afraid I don't understand. Regardless of what you include where,  
> the name resolution for the file is defined by the code and imports  
> in this file. Since that file does not define test::exception, it  
> would not influence resolution of any file it is included in.

This isn't true with the current implementation. Consider these four files:

test_exception.php:
-------------------
<?php

namespace Test;

class Exception extends ::Exception {
}


1.php:
------
<?php

namespace Test;
import Test::Exception;

throw new Exception();


2.php:
------
<?php

namespace Test;

throw new Exception();


And finally the main file, say parent.php:
------------------------------------------

<?php

function __autoload($class) {
     include './test_exception.php';
}

try {
     include '2.php';
} catch (Exception $e) {
     echo get_class($e) . "\n";
}

try {
     include '1.php';
} catch (Exception $e) {
     echo get_class($e) . "\n";
}



If I run parent.php as written above, I see this:
Exception
Test::Exception

... which is correct as far as it goes, there are no side effects.  
However, if I flip the include order, including 1.php before 2.php,  
then I get this:

Test::Exception
Test::Exception

This is the side effect I'm talking about. Because 1.php causes  
test_exception.php to be loaded, defining Test::Exception, it doesn't  
matter that 2.php doesn't import Test::Exception - I'm in the Test::  
namespace, and Test::Exception exists, so it's used.

> > […]
>
> Import is just a way to write names shorter. Thus, you can employ it  
> to achieve different goals.

This is true. The goal I am looking at is making sure that when I use  
a namespace I have full control over the things inside my namespace.  
The way things work right now I either need to list every single class  
in my namespace in every file of that namespace - which is going to be  
a bit burdensome, and seems like a discouragement from using  
namespaces - or I am at the mercy of PHP adding a new class that might  
suddenly take the place of one of my namespace classes. Or presumably  
someone who used my code could include a different librry that  
suddenly overrode one of my classes with a global class.

I understand the argument that namespaces should require minimal  
change to other code, but I think that PHP should encourage people to  
think a little bit when putting "namespace Foo" at the top of a file.  
That should really mean, in my opinion, that everything in that file  
is relative to that namespace by default.

It's not hard to then import the global bit you want.

Think about how variable scope works in PHP. The way I am proposing  
namespaces to work, they would match scope. You would have:

$GLOBALS => ::   - if you want to reference something from global  
scope without aliasing it, you can reference it explicitly

global $variable => this is like import, but import lets you alias the  
name as well, for using things from global scope but making them look  
like they are local

Everything else is in the local scope.

I don't have a grand theory of why variable scope and class scope  
should match, but it seems reasonable.

The main thing is that I think that the sort of people who are going  
to want to use namespaces are going to want namespaces to be as  
self-contained as possible, and that means explicitly importing  
external bits, not internal bits.

Sincerely,
-chuck

---

## #32889 — Chuck Hagenbuch — 2007-10-20T22:45:54+00:00

URL: https://externals.io/message/32877#32889 · In reply to: #32887

Quoting Stanislav Malyshev <stas@zend.com>:

> Not entirely correct - namespaces needed to reconcile _multiple_  
> libraries, while in case of Exception you need to stay away only  
> from _one_ set of classes - namely, internal ones. While indeed even  
> that could be inconvenient, the problem is that it is not possible  
> to avoid ambiguity in all cases successfully, so we had to choose  
> how we solve ambiguous cases. We decided to solve it in a way that  
> gives most old code most chance to work, while knowing that some  
> people would find some code less convenient with any code. Out  
> solution allows to explicitly specify your intent in one of these  
> ways:
> 1. Defining the class
> 2. Importing the name

I agree with this approach, I just disagree about which set of names  
we should require people to import.

> > […]
>
> I think it would. It is much easier to keep track on your own  
> classes than on system classes create by somebody else.

I agree and I think this is an excellent reason that external classes  
should be imported - that way they are easier to keep track of.

Also, below you say:

> If you are OK with writing imports, then it shouldn't matter too  
> much which imports you write.

I think that goes both ways. I am okay with importing classes outside  
of my namespace. It doesn't make sense to me to have to import names  
inside my own namespace. And the fact that changes to what classes PHP  
provides can change the order of class resolution seems like  
unnecessary fragility to me.

I really think people should think about the way their code is  
organized when they put it in a namespace. And I really don't think  
it's a bad thing if you need to list your external dependencies using  
import.

> And with exception of Exception (no pun intended :) I think there  
> would be not many classes that would have names coinciding with  
> internal class names.

Remember Date? (now DateTime)? This is mostly true right now because  
there was no concept of namespacing and only very well organized class  
libraries prefixed their classes. When people start moving their code  
into namespaces and then taking advantage of that to remove the  
prefixing, I think this will be more common.

> Also it would cause autoload call on each use of system classes even  
> if you never had overridden them at all, which can have significant  
> impact on the performance - exhaustive autoload search can be very  
> expensive.

I am not understanding why the extra autoload for system classes is  
necessary (I do understand why my initial re-ordering of include rules  
would cause two autoload calls and I agree that that is an awful idea  
now).

- inside a namespace (Foo::), I try to create a new Exception
- if no Foo::Exception class exists, we try to autoload  
"Foo::Exception". There is no need to autoload just "Exception".

If what I meant was to create an ::Exception, then I just do import  
Exception; inside my namespace and the class exists - no autoload is  
necessary.

Am I missing something?

Sincerely,
-chuck

---

## #32890 — Stanislav Malyshev — 2007-10-20T23:03:02+00:00

URL: https://externals.io/message/32877#32890 · In reply to: #32888

> ... which is correct as far as it goes, there are no side effects. 
> However, if I flip the include order, including 1.php before 2.php, then 
> I get this:
> 
> Test::Exception
> Test::Exception

Of course, since once Test::Exception is defined, Exception in namespace 
Test refers to it. It does not influence imports though.

> namespace I have full control over the things inside my namespace. The 
> way things work right now I either need to list every single class in my 
> namespace in every file of that namespace - which is going to be a bit 

Of course not - the problem even exists only for those classes that 
override internal class names, and only in the context where the 
overriding class is not defined but you still want to use it. If you 
either explicitly load it or specify full name - it would be OK. And so 
far we have one such name - Exception, so telling "every single class" 
is a gross overstatement unless your package does nothing but throwing 
exceptions :)

> The main thing is that I think that the sort of people who are going to 
> want to use namespaces are going to want namespaces to be as 
> self-contained as possible, and that means explicitly importing external 
> bits, not internal bits.

You can not be entirely self-contained as you are going to rely heavily 
on internal classes and functions anyway.
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #32891 — Stanislav Malyshev — 2007-10-20T23:06:58+00:00

URL: https://externals.io/message/32877#32891 · In reply to: #32889

> my own namespace. And the fact that changes to what classes PHP provides 
> can change the order of class resolution seems like unnecessary 
> fragility to me.

I agree that it is not ideal way for all setups, however the alternative 
is worse - it leads to more problems.

> I am not understanding why the extra autoload for system classes is 
> necessary (I do understand why my initial re-ordering of include rules 
> would cause two autoload calls and I agree that that is an awful idea now).

Because if you say "Exception" inside any namespace, we should check if 
Foo::Exception not exists anywhere, which means full search of all 
autoloading possibilities. Only after we ensured that no such class 
exists anywhere, we might use Exception. Since using system classes is 
much more frequent than overloading them, performance would greatly 
suffer each time you use system class name inside namespace.

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

---

## #32892 — Chuck Hagenbuch — 2007-10-20T23:19:48+00:00

URL: https://externals.io/message/32877#32892 · In reply to: #32891

Quoting Stanislav Malyshev <stas@zend.com>:

> Because if you say "Exception" inside any namespace, we should check  
> if Foo::Exception not exists anywhere, which means full search of  
> all autoloading possibilities. Only after we ensured that no such  
> class exists anywhere, we might use Exception. Since using system  
> classes is much more frequent than overloading them, performance  
> would greatly suffer each time you use system class name inside  
> namespace.

But you shouldn't even look for Exception then. I'm saying that if you  
want the global Exception class you should import it.

-chuck

---

## #32893 — Chuck Hagenbuch — 2007-10-20T23:25:25+00:00

URL: https://externals.io/message/32877#32893 · In reply to: #32890

Quoting Stanislav Malyshev <stas@zend.com>:

> > […]
>
> Of course, since once Test::Exception is defined, Exception in  
> namespace Test refers to it. It does not influence imports though.
>
> > […]
>
> Of course not - the problem even exists only for those classes that  
> override internal class names, and only in the context where the  
> overriding class is not defined but you still want to use it. If you  
> either explicitly load it or specify full name - it would be OK. And  
> so far we have one such name - Exception, so telling "every single  
> class" is a gross overstatement unless your package does nothing but  
> throwing exceptions :)

Right, it's not going to affect most classes most of the time, but my  
point is that you can't predict which classes will be affected.  
Exception is just the most obvious example. Looking at the built-in  
class list, Directory and DateTime jump out as ones that will probably  
come up a lot.

Also looking at the global classes list, it's not that big. I really  
don't think importing your external dependencies would be a hardship.

I know Stas gets a lot of weight here because he can write the code  
for it. I'd like to know if any other developers have strong feelings  
either way, because I can write tests or even (scary as it might be  
for others) look at the engine code to change this behavior if that's  
the only difference maker.

-chuck

---

## #32894 — Stanislav Malyshev — 2007-10-20T23:32:13+00:00

URL: https://externals.io/message/32877#32894 · In reply to: #32893

> Right, it's not going to affect most classes most of the time, but my 
> point is that you can't predict which classes will be affected. 

You can - set of internal classes is very well known, unless you are 
using some exotic extensions.

In any case, I am not 100% happy with this solution, but the only 
alternative proposed so far (and we did consider it before deciding) is 
worse both in terms of easiness to use for the code and performance. If 
you can think of any way to solve this which not includes calling 
autoload on each instance of internal class used in namespace and 
requiring to always use :: to refer to internal classes - I'd be happy 
to hear it.
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #32895 — Stanislav Malyshev — 2007-10-20T23:33:29+00:00

URL: https://externals.io/message/32877#32895 · In reply to: #32892

> But you shouldn't even look for Exception then. I'm saying that if you 
> want the global Exception class you should import it.

That would be double bad - that means you have to go over all your code 
and add :: to all instances of global classes, even though you never 
ever intended to override any of them. I don't think it's a good idea.
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #32897 — Chuck Hagenbuch — 2007-10-20T23:42:17+00:00

URL: https://externals.io/message/32877#32897 · In reply to: #32895

Quoting Stanislav Malyshev <stas@zend.com>:

> > […]
>
> That would be double bad - that means you have to go over all your  
> code and add :: to all instances of global classes, even though you  
> never ever intended to override any of them. I don't think it's a  
> good idea.

No, you just have to import them into your namespace, right next to  
the namespace declaration. Which solves both problems - ease of using  
global classes and the extra autoload.

Honestly I don't know if having a PHP:: namespace with an alias to the  
builtin classes is feasible, but if it is, then the simple, "put  
global classes into my namespace" ("register_globals" ;) option is:

namespace Test;
import PHP;

-chuck

---

## #32899 — Stanislav Malyshev — 2007-10-21T06:18:14+00:00

URL: https://externals.io/message/32877#32899 · In reply to: #32897

> builtin classes is feasible, but if it is, then the simple, "put global 
> classes into my namespace" ("register_globals" ;) option is:
> 
> namespace Test;
> import PHP;

"import PHP" is a no-op now. However, I have another idea about it 
(besides the fix for the import thing that indeed looks like a bug to 
me) - what if we add some syntax to say "use this namespace" which would 
use autoload, so you'd say:

namespace Test;
throw new this::Exception();

and it would mean Test::Exception (including autoloading). Without 
concentrating on 'this' name (we may have another, better name for that) 
would it make things better?
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #32903 — Chuck Hagenbuch — 2007-10-21T14:51:24+00:00

URL: https://externals.io/message/32877#32903 · In reply to: #32899

Quoting Stanislav Malyshev <stas@zend.com>:

> "import PHP" is a no-op now. However, I have another idea about it  
> (besides the fix for the import thing that indeed looks like a bug  
> to me) - what if we add some syntax to say "use this namespace"  
> which would use autoload, so you'd say:
>
> namespace Test;
> throw new this::Exception();
>
> and it would mean Test::Exception (including autoloading). Without  
> concentrating on 'this' name (we may have another, better name for  
> that) would it make things better?

I don't think this helps; it means that you need to use the local  
syntax every time you use a class, instead of importing it once, and  
it doesn't resolve the fact that if you use a non-fully-qualified  
classname (like Directory, for a different example), you need to say  
::Directory or else if a previous file in your namespace has defined a  
Directory class, the class you end up using changes.

This unpredictability is I think the worst part of the current implementation.

-chuck

---

## #32904 — Stanislav Malyshev — 2007-10-21T19:23:56+00:00

URL: https://externals.io/message/32877#32904 · In reply to: #32903

> I don't think this helps; it means that you need to use the local syntax 
> every time you use a class, instead of importing it once, and it doesn't 
> resolve the fact that if you use a non-fully-qualified classname (like 
> Directory, for a different example), you need to say ::Directory or else 
> if a previous file in your namespace has defined a Directory class, the 
> class you end up using changes.

I'm not sure I follow you. If you intend to always use internal one, why 
you define the local one? If you sometimes intend to use one and 
sometimes another - I propose you a way to ensure the right one is 
always used, this without using potentially long full name. I must be 
missing something here - can you specify what exactly you want it to be? 
You do understand that if you insist on always using unqualified name, 
it must work some consistent way, and this way can not satisfy all 
possible scenarios because they are contradictory?

> This unpredictability is I think the worst part of the current 
> implementation.

There's no "unpredictability" - the behavior is entirely predictable, 
you just don't like it :) That's like saying that function call is 
unpredictable, because if you don't define the function it produces an 
error.
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #32906 — Chuck Hagenbuch — 2007-10-22T00:37:55+00:00

URL: https://externals.io/message/32877#32906 · In reply to: #32904

Quoting Stanislav Malyshev <stas@zend.com>:

> I'm not sure I follow you. If you intend to always use internal one,  
> why you define the local one? If you sometimes intend to use one and  
> sometimes another - I propose you a way to ensure the right one is  
> always used, this without using potentially long full name. I must  
> be missing something here - can you specify what exactly you want it  
> to be? You do understand that if you insist on always using  
> unqualified name, it must work some consistent way, and this way can  
> not satisfy all possible scenarios because they are contradictory?

Yes. I think that if you use an unqualified name it should always be  
relative to the namespace (and importing internal classes into your  
namespace lets you use short names for them, avoiding ::Exception).

> > […]
>
> There's no "unpredictability" - the behavior is entirely  
> predictable, you just don't like it :) That's like saying that  
> function call is unpredictable, because if you don't define the  
> function it produces an error.

Here is what I mean:

1.php
-----

<?php

namespace Test;
import Test::Exception;

throw new Exception();


2.php
-----

<?php

namespace Test;

throw new Exception();


parent.php
----------

<?php

function __autoload($class) {
     include './test_exception.php';
}

try {
     include '2.php';
} catch (Exception $e) {
     echo get_class($e) . "\n";
}

try {
     include '1.php';
} catch (Exception $e) {
     echo get_class($e) . "\n";
}



test_exception.php defines Test::Exception.

If you run parent.php as written above, the first exception will be of  
type Exception and the 2nd will be of type Test::Exception. However if  
you edit parent.php so that 1.php is included before 2.php, then both  
exceptions will be of type Test::Exception.

To me, this is unpredictable because include order changes how  
otherwise identical code behaves.

I can solve this in my case by always writing import Test::Exception;.  
However, if I really want to use the builtin Exception class, it seems  
like I _must_ type it as ::Exception every time - which is something  
you wanted to avoid as well as I.

Other options I tried:

import Exception;
-> name conflict, which seems correct

import Exception as Error;
-> Fatal error: Import name 'Error' conflicts with defined class in  
/Users/chuck/Desktop/php namespaces/2.php on line 4
(this I don't understand)

import ::Exception as Error;
-> parse error (can't import a top-level class name that way)


Therefore, in my mind, in order to write durable code that will not  
break no matter what other classes other developers define or import,  
I should always prefix builtin classes with ::.

Given this, the only difference between how I think things should work  
is that I think that, when I have declared a namespace, I should be  
able to rely on any short name referring to that namespace or one of  
my explicit imports. The key being that _I_ declared that namespace  
and the imports and they will affect only that file, so I am  
completely in control of what name means what.

The way things currently are, using a shortname means look inside the  
namespace, UNLESS there's a builtin class with the same short name and  
the namespaced version hasn't been autoloaded yet, or there's an  
explicit import...

I really think it's unintuitive that when you declare a namespace  
names don't necessarily refer to that namespace. It doesn't match  
other languages, either - you always need to import classes/names if  
you want to use short names for functionality outside your  
namespace/package/etc.

Sincerely,
-chuck

---

## #32921 — Stanislav Malyshev — 2007-10-23T00:53:55+00:00

URL: https://externals.io/message/32877#32921 · In reply to: #32906

> Yes. I think that if you use an unqualified name it should always be 
> relative to the namespace (and importing internal classes into your 
> namespace lets you use short names for them, avoiding ::Exception).

Unfortunately, there are problems with this solution, since it
makes common case harder to implement - internal classes are more 
frequently used than overridden. It makes harder to convert library code 
which does not use tricks like overriding system classes - which is on 
my experience most of the code - you have now to go to almost every file 
and find all the system classes you have used there and put imports for 
them.

> To me, this is unpredictable because include order changes how otherwise 
> identical code behaves.

It is entirely predictable, and you know the rules that would allow you 
to predict it.

> I can solve this in my case by always writing import Test::Exception;. 
> However, if I really want to use the builtin Exception class, it seems 
> like I _must_ type it as ::Exception every time - which is something you 
> wanted to avoid as well as I.

If you really want to use internal class, why you define and include 
non-internal class with the same name? Moreover, your own proposal above 
says that's what you have to do - write ::Exception or import. So either 
you want unqualified internal names to work or you don't?

> Other options I tried:
> 
> import Exception;
> -> name conflict, which seems correct

import Exception is a no-op, so I don't understand how you could have 
got name conflict. Do you mean "import Test::Exception"? That should 
work, if it didn't it's a bug.

> import Exception as Error;
> -> Fatal error: Import name 'Error' conflicts with defined class in 
> /Users/chuck/Desktop/php namespaces/2.php on line 4
> (this I don't understand)

I'm afraid I am missing something since in line 4 of 2.php there's no 
definition of any class or import. Can you give me full examples of 
non-working parts? It might be there's some bug in there.

> import ::Exception as Error;
> -> parse error (can't import a top-level class name that way)

Well, we might allow importing global classes, if it's needed.

> Therefore, in my mind, in order to write durable code that will not 
> break no matter what other classes other developers define or import, I 
> should always prefix builtin classes with ::.

Other developers shouldn't define or import classes into your namespace...

> Given this, the only difference between how I think things should work 
> is that I think that, when I have declared a namespace, I should be able 
> to rely on any short name referring to that namespace or one of my 
> explicit imports. The key being that _I_ declared that namespace and the 

Well, I think restricting namespaced code's access to internal classes 
is not a good idea, since internal classes are rather frequently used.

> imports and they will affect only that file, so I am completely in 
> control of what name means what.

You can control it anyway, you just have to be a bit more explicit now.
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #32925 — Chuck Hagenbuch — 2007-10-23T03:55:30+00:00

URL: https://externals.io/message/32877#32925 · In reply to: #32921

Quoting Stanislav Malyshev <stas@zend.com>:

> > […]
>
> import Exception is a no-op, so I don't understand how you could  
> have got name conflict. Do you mean "import Test::Exception"? That  
> should work, if it didn't it's a bug.

It turns out that this does not happen with CVS PHP 5.3. With Greg's  
first patch for fixing multiple uses of import Test::Exception,  
however, this is the reproduce script and error:

<?php

namespace Foo;
import Exception;

results in:

Maya:/tmp chuck$ php import_exception.php

Fatal error: Import name 'Exception' conflicts with defined class in  
/private/tmp/import_exception.php on line 4


Also, with unpatched 5_3 CVS, I get this:

Warning: The import statement with non-compound name 'Exception' has  
no effect in /tmp/import_exception.php on line 4

I think that either import ::Exception needs to work, or import  
Exception shouldn't issue a warning.

> > […]
>
> I'm afraid I am missing something since in line 4 of 2.php there's  
> no definition of any class or import. Can you give me full examples  
> of non-working parts? It might be there's some bug in there.

This is another one that only happens with Greg's initial patch (I  
haven't tried the next one yet Greg, sorry). For Greg's benefit here's  
the reproduce script:

<?php

namespace Foo;
import Exception as Error;


And result:

Maya:/tmp chuck$ php import_exception_alias.php

Fatal error: Import name 'Error' conflicts with defined class in  
/private/tmp/import_exception_alias.php on line 4


> > […]
>
> Well, we might allow importing global classes, if it's needed.

I think this would be a good thing.

> > […]
>
> Other developers shouldn't define or import classes into your namespace...

I think you need to envision working in a large team on a large  
project here, but you may simply not see the need.

-chuck

---

Next page: https://externals.io/message/32877.md?page=2
