Greg,

I've been thinking about this as well and I also concluded that #3 is the way to go. If I "use" a class with the same name as an internal one, then I want to reference that class, I don't care about the internal class, and I don't want an error either. If a new PHP internal class is added to the future, then having the option of overriding internal class names guarantees me that my code won't break in an upgrade.

If at the time of writing my code, I know that I am "using" a class with the same name as an internal one, AND I need to use both, then I can alias the classes appropriately to avoid clashes, e.g.

Zend/Exception.php
<?php
namespace Zend;

class Exception extends ::Exception
{
// ...
};
?>

test1.php
<?php
use Zend::Exception;
throw new Exception( 'Zend exception occurred' );
?>

test2.php
<?php
use Zend::Exception as ZendException;

if( $someCondition )
    throw new ZendException( 'Zend exception occurred' );
else
    throw new Exception( 'unknown generic exception' );
?>



Regards,

Jessie

Gregory Beaver wrote:
Dangit, I wasn't finished when that sent (apparently Ctrl-S sends a
message - I was trying to Ctrl-V to paste)

Gregory Beaver wrote:
Hi again,

I am not sure this will make it through the noise *sigh* but I'll give a
try :)

Derick Rethans wrote:
Hei,

now some patches by Greg and Dmitry made it into CVS, some of the things in my mail have been resolved, however, we still need to come up with a solution to the 1st and most important issue:

On Tue, 4 Dec 2007, Derick Rethans wrote:

1. As it is impossible to do "use XXX as NativeClass" we get to the point where we'd have to namespace new internal native classes
   otherwise we might introduce BC breaks. AFAIK, we've always said that
   PHP reserves the globals space for it's own use (see also:
   http://www.php.net/manual/en/userlandnaming.rules.php). In case we do
   add new classes (an example could be DateTimeSpan), they should of
   course follow the same format as already existing classes in the
   extension (DateTime, DateTimeZone). However introducing the new class
   DateTimeSpan might break people's code that do things like:

        <?php
        use myNamespace::DateTimeZone as DateTimeZone;
        ?>

   feature versions of people would then show:

Fatal error: Cannot use myNamespace::DateTimeZone as DomDocument because the name is already in use.

   It would be silly to require to have to do this:

        - Create a class PHP::Date::TimeSpan
        - In your scripts:
          use PHP::Date::TimeSpan

  But with the current implementation, this seems to be the only non-BC
  breaking solution. If we chose *not* to require this silly namespacing
  of internal classes, users instead have to do this:

        <?php
        use myNamespace::DateTimeZone as myDateTimeZone;
        ?>

  Basically prefixing the classnames... This you can already do just
  fine without namespaces.
I know Greg has some ideas with __php__ as recommendation, but I find it a bit clumsy still. Perhaps Greg can summarize how it will address the above issue?
I've been thinking quite a bit about several suggestions (surprise
surprise).  First of all, Marcus Boerger suggested on IRC that "__php__"
was not the best name, and proposed "__user__" as a more logical choice
for the convention, which appeals to me more for the obvious reason that
it doesn't have the chance of confusing with "PHP" or "php" should
either of these be adopted by the core folks ever.

I am going to use some shorthand.  "internal class is used first" means
this code:

<?php
namespace hithere;
$a = new Exception('hi');
?>

would use ::Exception unles hithere::Exception already exists.

In my inimitable style, for the larger suggestions I've been thinking
about I will list them:

1) having an implicit namespace for unnamespaced code
2) never accessing internal classes inside a namespace (tough to
summarize I explain below)
3) allowing override of internal classes with "use" in the global scope
4) keep things the way they work now, and recommend using "namespace
__user__" for userspace code.

1) having an implicit namespace for unnamespaced code
=====================
This is an interesting idea that will break autoloaders unless the
implicit namespace is automatically stripped on a call to autoload,
get_class() and reflection.

Pros:
use myNamespace::DateTimeZone as DateTimeZone; will always work as intended

Cons:
slight performance hit on every autoload caused by a strstr() check on
classname for "__auto__::" and same hit on get_class(), ReflectionClass
stuff
large number of potential "oops" spots for the implicit namespace to
slip into code and break everything horrendously.
the patch would be huge and very dangerous for the above reason until
kinks are worked out.
doesn't solve the "internal class is used first" issue.

2) never accessing internal classes inside a namespace
=====================

This basically means changing the autoloading rules to the following
(using the hithere namespace/Exception example from above):

1) does hithere::Exception exist?
2) if not, autoload it

Pros:
it is not necessary to "use hithere::classname" for every class you
might autoload.
1 hash lookup reduced per autoloaded class (very minor performance gain,
hash lookup is O(1).  profiling might not even detect a difference.)

Cons:
to use internal classes, you would need to explicitly use ::Classname;
for each of them.
Code that does not use autoload would *also* have to explicitly use
::Classname;
code that uses internal classes that wishes to become namespaced would
need to go through and figure out which internal classes are used and
add "use ::Classname" at the top of the file, making porting to
namespaces much more difficult.

3) allowing override of internal classes with "use" in the global scope
=====================

This would be surprisingly easy to implement, currently the only thing
preventing it from happening is a 5-10 line check in zend_do_use() to
see if the name requested would conflict with an internal class.  Remove
that check and it becomes possible

Pros:
Derick's main issue goes away, name conflicts with internal classes are
never possible when "use"ing them
it becomes possible to change which class is used in a file with 1 line
of code:

<?php
use myNamespace::Exception as Exception;
?>

As opposed to two:

<?php
namespace __user__;
use myNamespace::Exception as Exception;
?>

Cons:
It is then possible to accidentally override an internal classname used
in the code.
it encourages continuing to declare classes/functions in the global
namespace, resulting in potential name conflicts a la "Date"

4) keep things the way they work now, and recommend using "namespace
__user__" for userspace code.

Pros:
explicit declaration of namespace can make code clearer

Cons:
users using global namespace and "use" will get burned.

=========
As a reality check, in the entire lifetime of PHP, there has been only 1
name collision between new PHP classes and pre-existing major
classnames.  SplFileInfo was originally a conflict, but that was solved
with a simple prefix to the classname of "Spl."  This is an issue, but
until PHP starts adding billions of classes to handle core elements, it
is not a large one.  However, it can be easily and simply solved, so I
say why not :).

After enumerating the pros/cons of each point, my personal opinion is
that #1 and #2 are not feasible from either a user or a core developer
standpoint, and would create more problems than they would solve.  #3,
however, looks much more appealing than I thought it would.  I think
this is a good idea as an addition.  I might be inclined to ask for an
E_STRICT warning that "use My::Exception;" is overriding an internal
class, but that's up to what you all think.  Implementing #3 would
remove the con of #4.

To answer Derick's call for clarification on #4, The convention of using
the __user__ namespace for all code that uses namespaced code would
force users to do two things:

1) explicitly "use" classnames they wish to shorten by importing them
from another namespace
2) add "namespace __user__" to the top of their files.

These two things would mean

1) autoload always works as intended
2) classes from external projects (dependencies not found in the current
file) are always documented prominently at the top of the file,
increasing maintainability if the project passes on to another developer.
3) the presence of "namespace __user__" would also immediately document
the fact that this code is intended to be executed and is not library
code (does not declare classes/functions and only uses them)

Point #1 does not matter to people who don't use autoload, but #2 and #3
both increase the ease of understanding the purpose of code.  I've often
had trouble sifting through source code to find the main files that are
executed.  If I could have done a simple "grep -e namespace __user__" it
would make things a lot easier that way.

Thanks,
Greg

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

Reply via email to