Hi Derick, I've been thinking a *lot* about your provocative email in the past couple of days, and have come to a different conclusion from my original reply (which, as a reminder stated I saw no problem with removing namespaces as long as we kept the import facility to alias classes via use). After a few hours of thinking practically about the implementation, I realized that this would be impossible for the same reasons as your first argument. So, I set out to find a solution that keeps namespaces and solves the problems.
I have found the solutions to every problem I could find, and to my surprise, even after crafting a new patch to solve some of them, have found a way to solve all of the problems within the existing implementation (with a few tweaks, but nothing major changed) or by specifying new coding conventions. Summary: 1) recommend all global non-namespaced code that wishes to import namespaced code via "use Namespace::Classname" add a "namespace __php__;" at the top of the file, and that the __php__ namespace be reserved for use by end-user applications. 2) use Name::*; is technically impossible without some kind of autoloading mechanism, but can be reasonably approximated by importing a long namespace name as a shorter one. "use PEAR2::Really::Long::Name as a;" then allows referring to PEAR2::Really::Long::Name::Classname as a::Classname, and PEAR2::Really::Long::Subnamespace::Classname as a::Subnamespace::Classname. 3) multiple namespaces per file should be allowed, but strongly discouraged as a coding practice of last resort for most users. 4) brackets for namespaces is most necessary for hierarchical namespace implementations. This is not such an implementation, and as such brackets do not add clarity but do slow down the implementation. 5) namespaces provide these benefits that are not available in PHP: a) short, unqualified descriptive class names can be used without fear of conflicting with an internal class b) a clear, uniform coding convention on how to avoid conflicts between your code and others that will help those who have to maintain the code of others. c) a way to alias longer names to increase readability and maintainability of code. 6) There are some dangers when using namespaces with autoload, but simple ways of thwarting those dangers. Also dangerous is trusting external code that is in the same namespace, as functions can be redefined. Detailed answers: Derick Rethans wrote: > 1. As it is impossible to do "use XXX as NativeClass" we get to the <snip> > extension (DateTime, DateTimeZone). However introducing the new class > DateTimeSpan might break people's code that do things like: > > <?php > use myNamespace::DateTimeZone as DateTimeZone; > ?> This is indeed the biggest problem. However, it only exists in the global "namespace" (non-namespaced code). An example script using the non-existing hypothetical PEAR2::DateTime class: <?php include 'PEAR2/Autoload.php'; use PEAR2::DateTime; // fatal error - use name conflicts with internal class $a = new DateTime; ?> However, the answer is simple and elegant. PHP applications that take advantage of namespaces should use a namespace *in the application global code* that is reserved for application code, like __php__. <?php namespace __php__; include 'PEAR2/Autoload.php'; use PEAR2::DateTime; $a = new DateTime; // $a is an object of class PEAR2::DateTime after autoloading, or if a previously included file has declared class DateTime in namespace PEAR ?> Note that the only difference here is the addition of 1 line of code at the top of the file. On the same token, this code: <?php namespace __php__; $a = new DateTime; // $a is an object of class ::DateTime ?> works as expected, accessing the internal DateTime class directly. In other words, 1 line of code is needed to take advantage of namespace's full protection and ability to import conflicting class names into the "global" (in this case unqualified, not containing :: in the name) scope, while at the same time preserving BC with existing code (no modification needed beyond addition of "namespace __php__;"). I recommend that the manual specify this convention, and will happily take on the documentation of it. > 2. You have to import every class yourself. You can currently not do: > > use myNamespace::* as *; // or similar syntax Actually, ::* would only be deterministic if use performed some kind of autoloading, it's not a question of runtime versus compile-time - it's simply not possible to determine which class is intended if more than one ::* is used unless the answer is determined by code loading, this is why Java/Python import actually loads code as well as aliasing identifiers. There are ways of simulating "use myNamespace::* as *;" that are almost as complete and are actually better for long-term maintenance. For instance, let's say you want to use several classes from PEAR2::Ultra::Long::Package::Name. You don't need to use each class individually, you can import the entire namespace: <?php namespace __php__; use PEAR2::Ultra::Long::Package::Name; $a = new Name::ExampleClass; $b = new Name::SecondThing; $c = new Name::ThirdThing; ?> or the ultra-brief method, requiring 3 extra characters over an unqualified name: <?php namespace __php__; use PEAR2::Ultra::Long::Package::Name as a; $a = new a::ExampleClass; $b = new a::SecondThing; $c = new a::ThirdThing; ?> Even more significant, you can use sub-namespaces with this system, each namespace does not need to be imported separately: <?php namespace __php__; use PEAR2::Ultra::Long::Package::Name as a; $a = new a::ExampleClass; $b = new a::SecondThing; $c = new a::ThirdThing; $d = new a::subnamespace::MyClass; // $d is an object of class PEAR2::Ultra::Long::Package::Name::subnamespace::MyClass ?> This is dramatically easier to debug than the equivalent import of ::* (with an added namespace for dramatic effect): <?php namespace __php__; use PEAR2::Ultra::Long::Package::Name::*; use Symfony::Something::*; $a = new ExampleClass; $b = new SecondThing; $c = new ThirdThing; $d = new subnamespace::MyClass; $e = new Controller; // which package is this class from? ?> Looking at the code, it is impossible to determine which package the classes come from. Here is the same example with current syntax: <?php namespace __php__; use PEAR2::Ultra::Long::Package::Name as a; use Symfony::Something as s; $a = new a::ExampleClass; $b = new a::SecondThing; $c = new a::ThirdThing; $d = new a::subnamespace::MyClass; $e = new s::Controller; // now we know for certain this is from Symfony ?> > 3. We keep bickering over using { } or not, multiple namespaces in a > file or not... etc. I understand that people want more flexibility, > but also we need a *simple* to explain implementation. With the > current implementation I see the following problems here: > > - You can't stick multiple namespaces in one file > - Unlike other constructs in PHP that mark executable blocks, > namespaces don't use { }. > - The "namespace" keyword at the start of a file is somewhat magic, > because it can only appear as first element in your script. I have wrestled with this many times, and the best conclusion I can reach is that (1) we need the flexibility of multiple namespaces per file, but that (2) we need to *strongly* discourage it for the reason that it is going to make debugging harder. There are two valid use cases for multiple namespaces per file that I have encountered: 1) performance. PHP is slightly slower when executing stuff in multiple files 2) GTK2. It's always nice to be able to put a GTK2 app in a single file and this is an alternative to using a phar archive. There are two philosophies behind namespaces out there, hierarchical and non-hierarchical. In hierarchical languages like C++ and C#, nested namespaces can be defined, and sub-namespaces can be defined using nested code blocks. Other implementations simply don't do this. The best description I've seen is from a site on Perl namespaces: "Isaac Newton is not related to Olivia Newton-John, and Newton::Isaac is not related to Newton::John::Olivia." Perl, incidentally, uses the package keyword as a combination of declaration and alias, in that there is no equivalent to PHP's use. PHP's implementation is a mixture of these approaches. Because it is closer to Perl's implementation of package, but contains the import statement from compiled languages like C++, we have to forge a new path. Having namespace as the start of the file is good because it enforces a declaration "take note: this file uses namespaces" that might otherwise be lost. I have no strong opinion on {}, but I do see a compelling argument in favor of not using {} for performance reasons if you're only going to use 1 namespace per file except in extraordinary circumstances. > 4. What is wrong with simple prefixes in the first place? Both PEAR_*, > Zend_*, ezc*, and ezp* are perfectly acceptable markers for different > 'namespaces'. We could optionally create a registry on php.net for > this to avoid conflicts. The only naming conflicts we've had have been with internal classes thus far, but there is no coding convention in place. Namespaces would provide a technical solution to a political problem, something that carries with it intrinsic risks. Fortunately, other languages have taken this risk before PHP and not had major problems, so we have a reasonable expectation of success. The implementations of namespacing vary so widely, it's kind of impossible to generalize on how they do it, but difference between prefixing and namespaces can be exemplified by one construct: use. <?php // assume we've loaded code for brevity $a = Zend_Long_Class_Name::singleton(Zend_Long_Class_Name::CONST); $b = new PEAR2_Long_Class_Name(); Zend_Long_Class_Name::$var = 2; ?> <?php namespace __php__; use Zend::Long::Class as a; use PEAR2::Long::Class as b; $a = a::Name::singleton(a::Name::CONST); $b = new b::Name(); a::Name::$var = 2; ?> Also possible with namespaces is switching out an implementation with a similar API without search/replace (assuming no dynamic class references are used) <?php // before namespace __php__; use Zend::Thing::Implementation; $a = new Implementation; ?> <?php // after namespace __php__; use PEAR2::Does::Same::Thing as Implementation; $a = new Implementation; ?> > With all the above considerations, especially my first point, I still have not > heard any good reason why namespaces in the current implementation are > actually > useful - or what particular case they solve... so I am wondering, are they > really useful? I come now to the conclusion that they are not, and for myself > (and most likely my work projects) I would have to decide not to go with > namespaces, but instead stick with the 3 letter prefixing. Something that I > have totally no problem with, as it is nice and easy. 1) The ability to guarantee short names can be used without danger of conflicting with a global name. This is possible inside any namespace declaration, but not in global scope. 2) readability, and therefore maintainability increases as redundant parts of identifiers are removed by use aliasing. This line: <?php return new PEAR2_Pyrus_PackageFile_v2Iterator_File( new PEAR2_Pyrus_PackageFile_v2Iterator_FileAttribsFilter( new PEAR2_Pyrus_PackageFile_v2Iterator_FileContents( $this->packageInfo['contents'], 'contents', $this)), RecursiveIteratorIterator::LEAVES_ONLY); ?> becomes: <?php use PEAR2::Pyrus::PackageFile::v2Iterator as i2; use ::RecursiveIteratorIterator as ri; // currently not possible in CVS but I have a patch... // ... return new i2::File( new i2::FileAttribsFilter( new i2::FileContents( $this->packageInfo['contents'], 'contents', $this)), ri::LEAVES_ONLY); ?> The argument has been made that namespaces can actually increase code size because class names are so rarely used. This is not true in my coding experience. As an example, a single file in PEAR2_Pyrus has 25 uses of the classname PEAR2_Pyrus_PackageFile_Exception. Any class making use of class constants used by another class will be using the full classname a lot. 3) Ability to use common short names (like File, Exception, Date) as class names within a namespace increases the logical descriptive qualities of the code, making it easier to understand without documentation (self-documenting code). For example, in the classname PEAR2::Pyrus::Config, the only part that really describes anything important for the user to know about is "Config", the rest is redundant. In terms of the danger of using namespaced classes with autoload, the biggest danger is this code: <?php namespace Blah; $a = new Exception('hi'); ?> In this case, the ::Exception class is instantiated. <?php namespace Blah; use Blah::Exception; $a = new Exception('hi'); ?> Now we guarantee the correct class is loaded. Although this seemed onerous to me at first, on reflection, there are very few internal classes that are likely to be overridden by a package (only 98 exist in my 5.3 build), probably at most 2 in any given package, so this is not a huge burden. A larger danger is this example: <?php namespace foo; echo strlen('hi'); include 'maliciousfile.php'; echo strlen('hi'); ?> maliciousfile.php: <?php namespace foo; function strlen($a) { // do bad stuff echo "I'm evil" return ::strlen($a); } ?> The above script outputs "2I'm evil2" - foo::strlen() is called for the second call to strlen(). Application authors like blogs that run plugins must be extremely cautious that plugins from third parties cannot contain redefinitions of core functions inside the blog's namespace. Of course, any third-party code is dangerous regardless of language construct, but this is a subtle vector that could be used and would not be caught by a code scanner unless consciously coded in to watch for a "namespace foo;" declaration. I hope this is helpful - by digging deeply into the source and talking to lots of devs, I have learned a lot about PHP's namespace implementation in the past 2 days, and I'm certain we have a much better implementation than I thought we did 2 days ago, and will be writing up a "how to namespace" and "how not to namespace" guide based on the pitfalls I've discovered in this quest as well as contributing to the documentation when things settle. Greg -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php