Dear all,
this RFC started by me having a problem on these subjects (not only me
I think) :
- namespaced functions and constants loading, or rather not-autoloading,
- interfaces loading (this time, autoloading).
About namespaced functions and constants, the fact that they are not
autoloaded makes them useless for any code that wants some good
packaging. For example I looked at the Doctrine2 code, and (tell me if
I'm wrong) although it's one of the most recently released high
quality code available today, they don't use a single namespaced
function nor constant. Rather, they use public static methods for
namespaced functions, and class consts for namespaced consts. This
way, they get both "namespaced" and autoloaded features. Impossible to
achieve by using native namespaced functions and consts, no downside.
I also have a second strong grief against namespaced functions and
constants : due to their at runtime namespace resolution, static code
analysis is now very hard, even impossible in the general case! It is
impossible to parse a single PHP file and be sure that a call to
"strlen" is really this "strlen" in the global namespace or an
"strlen" defined in the local namespace, but nobody knows where
without scanning the full project's source code, if that's possible.
Of course, your static code analyser could rely on heuristics which
may work for you, but what about a more generic static code
analyser?!...
So, my first proposal for namespaced functions and constants is :
- Remove namespaced functions and constants at all - promote
namespaced public static methods and class consts instead.
This would fix static code analysis, and the discussion about how to
autoload (I may say "how to package") namespaced functions and
constants would vanish. About backward compatibility, who as written
code relying on namespaced functions or constants? They are useless!
:) To be more realistic, what about triggering E_DEPRECATED? An other
mitigated solution might be namespace initializers, see below.
Now about interfaces in an autoloading world: used on the web, PHP has
this wonderfully successful "run and forget" execution model. That
means that the autoload machinery is generally triggered on every
single request as needed. But for interfaces, this autoloading model
seams too heavy to me: because their name is what matters most to
keep their promise (think of type hints and "instanceof"), their
definition is generally very short. So, on one side, interfaces really
adds semantics to the code, but on the other side, the most interfaces
you add to your code, the most autoload call you will make, for very
small source files. Because intrinsic performance is important, and
because I can generally find other ways around, I tend to dismiss
interfaces. But what's a shame, on the semantic level of my code! When
I really need interfaces, as a workaround, I break my class/interface
naming convention (PEAR style) and put interfaces definitions in one
single file which I know is loaded before they are needed; but this is
an ad hock hack, nothing more. The same reflexion could be made for
small classes, when their name is their most important feature.
Here comes namespace initializers!
My second proposal is :
- At runtime, when a namespaced identifier is used (lets say
\my\sub\ns\class_interface_constant_or_function), take the namespace
part of the identifier and if any, autoload it as if it were a class,
do not generate any error if the given class name is not found. (ie,
trigger something like class_exists('my\sub\ns', true);).
Benefits :
- reuses existing autoload packaging code to autoload functions and
constants, namespace by namespace (fixes my first point), - creates a standard source file were all functions and constants
should be defined (mitigates my second point), - creates a standard source file were all small (and bigger if wanted)
classes and interfaces definition are stored, so autoload is then
bypassed for them (fixes my third point), - compatible with existing code base: if class "my\sub\ns" already
exists, it is autoloaded - so what :)
Cons :
- none
- well, one : I don't speak C, so I don"t have any patch...
What do you think about that?
Happy New Year!
Nicolas Grekas
http://pa.tchwork.com/
<snip>Dear all,
this RFC started by me having a problem on these subjects (not only me
I think) :
- namespaced functions and constants loading, or rather not-autoloading,
- interfaces loading (this time, autoloading).
So, my first proposal for namespaced functions and constants is :
- Remove namespaced functions and constants at all - promote
namespaced public static methods and class consts instead.This would fix static code analysis, and the discussion about how to
autoload (I may say "how to package") namespaced functions and
constants would vanish. About backward compatibility, who as written
code relying on namespaced functions or constants? They are useless!
Dear god no! Functions are already close to being second-class citizens in
PHP at this point. The project I work on (Drupal) is currently debating how
to leverage function namespaces for our next version and there are several
ways that we could do so effectively; mostly they come down to module ==
namespace, and then a "hook" (magic function callback) can be placed within a
namespace. That would actually make for clearer code than our current model.
Class == namespace is wrong. Plain and simple. It is a broken assumption
based on broken understanding and the use of them that way is broken. That
approach is utterly useless if you want any sort of flexibility.
My second proposal is :
- At runtime, when a namespaced identifier is used (lets say
\my\sub\ns\class_interface_constant_or_function), take the namespace
part of the identifier and if any, autoload it as if it were a class,
do not generate any error if the given class name is not found. (ie,
trigger something like class_exists('my\sub\ns', true);).
I am not entirely sure I follow. Are you suggesting that:
use Stuff\Things as Bar;
$foo = new Bar\Baz\Foo()
should trigger:
autoload('Stuff\Things\Baz\Foo');
AND
autoload('Stuff\Things\Baz')?
In the current approach I don't know how you'd properly resolve the "namespace
portion" of the extended class/function/whatever name. Honestly, combined
with the previous proposal(s) for function autoloading (which I support in
general) it seems to me that we need to have separate but parallel pathways
for autoloading different things; that could lead to better performance, too.
If we can properly separate them I can see a use for "clustered" autoloading,
certainly, and namespace is a not-unreasonable definition of cluster.
Cons :
- none
- well, one : I don't speak C, so I don"t have any patch...
I sadly have the same problem. :-)
--Larry Garfield
Hi!
About namespaced functions and constants, the fact that they are not
autoloaded makes them useless for any code that wants some good
packaging. For example I looked at the Doctrine2 code, and (tell me if
This is not true. You can always load stuff manually - in fact, many
languages don't have any autoloading at all and are just fine.
I also have a second strong grief against namespaced functions and
constants : due to their at runtime namespace resolution, static code
analysis is now very hard, even impossible in the general case! It is
In general case, it always was so. Think of $foo->$bar() - what is being
called?
So, my first proposal for namespaced functions and constants is :
- Remove namespaced functions and constants at all - promote
namespaced public static methods and class consts instead.
This won't happen, for obvious BC reasons. Please re-read the
discussions on the list about namespaced functions to know what are
reasons for their existence.
- At runtime, when a namespaced identifier is used (lets say
\my\sub\ns\class_interface_constant_or_function), take the namespace
part of the identifier and if any, autoload it as if it were a class,
do not generate any error if the given class name is not found. (ie,
trigger something like class_exists('my\sub\ns', true);).
Autoloading for functions/constancs don't seem to have enough benefit to
justify the complications. As for classes, you can always make you
autoloader do anything you want with the name it receives.
--
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
(408)454-6900 ext. 227
at runtime namespace resolution, static code analysis is now very hard
In general case, it always was so. Think of $foo->$bar()
Maybe I was too strong about the term "general case". What I though
about is that although variable calls have always been there, they are
both statistically rare and their real variability is most often very
limited, so analyzing the code won't go very wrong. With namespaced
functions, this: rare variability => quite good understandability is
reversed to: large variability (any function call is potentially a
source of variability) => much lower understandability (much higher
risk of going completely wrong).
- Remove namespaced functions and constants at all
This won't happen, for obvious BC reasons.
I agree with that, please forget about this part. The idea came up
while writing the mail but that was not my first idea, expressed in
the other parts of my message...
Going back on static call analysis, given that namespaced functions
and constants exists, the next step to analyzing them correctly is for
me to add some heuristics to the parser - for example: if function
exists in the global space assume is has not been overwritten. This
can go wrong but will work most of the time. Maybe enough for some
rough analysis. If I want to go further, I do have to inform the
analyser of some coding convention, to enhance it's capability to know
were functions are defined. My RFC for namespace initializers helps on
this part : giving a path for developers on where to put these
definitions, exactly as autoload does for classes.
Autoloading for functions/constancs don't seem to have enough benefit
I agree, autoloading functions for the sake of autoloading functions
isn't worth the pain. As you've understood, I mostly don't use
functions, so personnaly I'm ok with the current state of PHP on this
point. But my proposal isn't restricted solely to solving function
autoloading.
To sum up my point, it tries to address 3 points :
- functions and constants autoloading
- helping static code analyzers
- cluster functions, constants, classes and interfaces autoloading
If you don't care for one point, maybe one of the other two may be enough ?
Personally : I don't really care for 1. but I read that others may be
interested - I do code analysis so 2. is good - and 3. seems good for
performance, because it reduces autoload overhead and helps opcode
caches concerning this NOP vs. FETCH_CLASS optimization (thinking
about this: http://sb2.info/alternative-php-cache-and-autoload-interoperability/
- not sure for this opcode part - am I wrong ?)
So, to get back on the second part of my RFC, taking quotes from Larry
Garfield's answer :
- At runtime, when a namespaced identifier is used, take the namespace
part of the identifier and if any, autoload it as if it were a class
Are you suggesting that:use Stuff\Things as Bar;
$foo = new Bar\Baz\Foo()should trigger:
autoload('Stuff\Things\Baz\Foo');
AND
autoload('Stuff\Things\Baz')?
Well, yes, but I was thinking about autoloading the namespace before the class.
To be more precise, I was thinking about this :
when (class/interface Ns\Foo is need)
{
// clustered Ns autoloading
autoload('Ns', true)// like today
if (class/interface Ns\Foo still not exists) autoload('Ns\Foo', null)if (class/interface Ns\Foo still not exists) fatal_error
}
when (function/constant Ns\Foo is need)
{
// clustered Ns autoloading
autoload('Ns', true)if (function/constant Ns\Foo still not exists) fatal_error
}
A second parameter to autoload would be need for coders to decide
between clustered autoload and regular class. This is necessary to be
able to handle nested namespace and clustered autoload recursivity (it
being wanted or not wanted by implementations).
This led me too this reflexion:
this "Ns" then "Ns\Foo" order has one side effect: "Ns\Foo" isn't
available while loading "Ns". This may be seen as a BC break because
code needing this will get a fatal error. Unlikely is practice ? I
don't know.
The benefit for autoloading Ns before Ns\Foo is that we give a
guarantee to developers: "Ns" is always initialized before any usage
of any Ns\Foo. This is the reason I choose this loading order in the
first place - and never really though about the reverse order... But
this guarantee comes at a price: autoload is now called more often:
one more time per namespace.
So thinking seriously about the reverse order : autoloading "Ns\Foo" then "Ns".
I see it a little bit more complicated, but really interesting, with
this algorithm :
// as today:
when (class/interface Ns\Foo is need)
{
autoload('Ns\Foo', null)// clustered Ns autoloading, before today's fatal error
if (class/interface Ns\Foo still not exists) autoload('Ns', true)if (class/interface Ns\Foo still not exists) fatal_error
}
when (function/constant Ns\Foo is need)
{
// clustered Ns autoloading
autoload('Ns', true)if (function/constant Ns\Foo still not exists) fatal_error
}
This algo doesn't have the previous guarantee but as it plugs before
current fatal errors, it is exempt from any BC break, event in term of
performance, existing code won't trigger a single more autoload call.
Personally, I now prefer this second approach!
What about you ?
--
Nicolas Grekas
http://pa.tchwork.com/
One of the first things I tried when I seriously started exploring
namespaces was the ideal of dynamic runtime loading. Consider class \A\Foo.
I have a project that has \B\Foo extends \A\Foo. For that project though I
want the autoloader to pull an unqualified request for "Foo" from \B\Foo
even if this comes up in the A namespace. That currently isn't possible -
the autoloader receives the argument "\A\Foo" from the A namespace whether
new Foo or new A\Foo or new \A\Foo was the origin code.
This leads to my second problem though - even if the autoloader gets to know
the difference and reacts accordingly, what happens if \A\Foo gets
explicitly asked for in the \A namespace after "Foo" was asked for. I don't
know how to resolve this either. So I gave up.
Anyway, My only real gripe with namespaces is that they have no affect on
variables. This I feel is a major implementation error because it runs
counter to how all other languages I've encountered handle namespaces. I've
lost count of how many people I've had to point this out to on sitepoint -
the expectation for programmers coming to PHP is that namespaces will
segregate variables.
It also means that "static" classes become the key means to keep registries
for those that use them. Coupled with the current situation with functions
that can't be autoloaded, this will lead to a lot more var/function hives in
code bases. It's not a particularly testable pattern due to the hidden
dependencies that are created, and becomes a headache over time. Encouraging
the pattern at a language level isn't the worst thing that's ever occurred
(that would be magic_quotes and register_globals) but it's almost in the
same ballpark in my opinion.
I don't see the harm in having a function autoloader and don't understand
the rationale behind the resistance to it. The resistance is futile anyway -
all that's really happening is people move to making functions members of
static classes as a kludge.
All that said, I'm not going to worry myself too much about it as it doesn't
affect my coding approach. I don't use global functions and my only static
class is a bootstrap factory which gets the rest of the code working and
isn't accessible from that code once it starts.