Re: Not autoloading functions

php.internals

Net Mo

9 years ago
An alternative (which I haven't properly developed yet). Thoughts? ``` define_autoload(AUTOLOAD_NS, function($ns){ if($ns === '\A\B\C'){ echo "LOADING ABC\n"; require(__DIR__ . $ns . '\functions_and_constants.php'); } }); define_autoload(AUTOLOAD_NS, function($ns){ if($ns === '\A\B'){ echo "LOADING AB\n"; require(__DIR__ . $ns . '\functions_and_constants.php'); } }); define_autoload(AUTOLOAD_NS, function($ns){ if($ns === '\B'){ echo "LOADING A\n"; require(__DIR__ . $ns . '\functions_and_constants.php'); } }); \A\B\someFunction(); // prints: // LOADING A\n // LOADING AB\n \A\B\someFunction(); // tries no autoload, because both namespaces A and A\B were // attempted to be loaded already. // in other words, autoload is attempted only once // for any given namespace \A\B\C\someOtherFunction(); // prints: // LOADING ABC\n // because contrarily to super namespaces, A\B\C wasn't // attempted to be loaded yet ```

Rowan Collins

9 years ago
On 27 January 2017 09:23:38 GMT+00:00, Wes <netmo.php@gmail.com> wrote:
>An alternative (which I haven't properly developed yet). Thoughts? >\A\B\someFunction(); >// prints: >// LOADING A\n >// LOADING AB\n
As I understand it, PHP namespaces aren't really hierarchies, they just have a hierarchical naming scheme; so namespace \A is not actually being referenced here.
>\A\B\someFunction(); >// tries no autoload, because both namespaces A and A\B were >// attempted to be loaded already. >// in other words, autoload is attempted only once >// for any given namespace
The tricky case that stalled the discussion of https://wiki.php.net/rfc/function_autoloading is using unqualified function names: if you're already inside the same namespace as the function, or alias it with "use function". I'm not sure how this mechanism would work for those cases. Finally, suggestions of a single include per namespace haven't been universally popular in previous threads, because some projects genuinely prefer each function in its own file (think plugin functions for Smarty / Wordpress / etc). It's an idea worth exploring though, IMO. Regards,
-- Rowan Collins [IMSoP]

Net Mo

9 years ago
Importing functions from a static class would equally require to have all functions in the same file. I was proposing an alternative to that, not "symbol to file" autoloading. Though, I know that problem well, sadly. I've suggested PHP should just deprecate the fallback to root namespace, and remove the feature altogether in PHP8. Imho it's the only way to actually solve this. Some say it would be a huge BC break, but in reality old applications could maintain compatibility only by adding a simple autoloader that would alias the global function/constant into the calling namespace. Roave wrote this https://github.com/Roave/FunctionFQNReplacer , PHPStorm is supporting the no-fallback referencing: https://youtrack.jetbrains.com/issue/WI-27425 (recently implemented) https://youtrack.jetbrains.com/issue/WI-34446 (recently implemented) https://youtrack.jetbrains.com/issue/WI-34858 So... it could be the right time to deprecate the fallback to root namespace, before PHP8 gets too close.

Rasmus Schultz

9 years ago
> old applications could maintain compatibility only > by adding a simple autoloader that would alias the global
function/constant
> into the calling namespace
So as a side-effect of calling any function, my namespace would get polluted with all sorts of random names? That sounds like a messy work-around. Okay, so take it back for a moment. The problem case is this, right? namespace Foo; bar(); If the function Foo\bar() has not already been loaded, we fall back to bar() in the global namespace. With a function-autoloader in place, we would need to trigger this for every function-call from any namespace, and that's unacceptable. Okay, well, existing code does not expect function autoloading and wouldn't benefit from it without changes, right? So what if, instead of trying to autoload everything by default, we make it opt-in? Something like: namespace Foo; use namespace Foo; bar(); Now, if function Foo\bar() has not been defined yet, and function \bar() doesn't exist, then start trying trying to autoload them from used namespaces. Okay, that may seem a bit redundant - having to explicitly indicate I'd like to be able to call functions from within the file's own namespace. But it'll make more sense if you put your functions in a functions-only namespace: namespace Foo; use namespace Foo\Functions; bar(); This call would prefer Foo\Functions\bar() if it is able to autoload that, otherwise will try \bar(). Or if you have several function-namespaces: namespace Foo; use namespace Click, Clack; bar(); This would prefer Click\bar(), then Clack\bar(), if any of those can autoload, otherwise will try \bar(). Now, this is still a lot of autoload queries, e.g. one for every function called in every file. It also doesn't address the issue of autoloading constants. So. What if we don't autoload functions at all? What if we autoload namespaces instead? That reduces the number of autoload queries from one per function to one per namespace, which might be more acceptable?? It does mean that individual functions and constants cannot be autoloaded - the smallest unit that can autoload will be a namespace. I think that's an okay limitation? For the most part, packages will likely want to ship a set of functions (and/or constants) as a single file anyway. If a vendor has too many functions and don't want the overhead of loading them as a single file, they likely can/should be organized into several (sub) namespaces anyhow, so maybe that's acceptable. So by default, there would be no namespace-autoloading for the file's own namespace - we don't query the namespace-autoloader by default. As for use function: namespace Foo; use function Blip\bar(); bar(); This would trigger namespace auto-loading for Blip when bar() is invoked. So existing code with function imports could switch to namespace-autoloading. Code that currently uses include/require to load functions into a file-namespace will need to remove their include/require statements, so needs changes anyhow to use namespace-autoloading - if this code also needs an added "use namespace" statement to achieve namespace-autoloading, that's probably okay? So yeah, this form of autoloading is a bit inconsistent with the name resolution rules - but it doesn't require any change as drastic as changing the function name resolution rules, it's pretty simple, and (I think?) backwards-compatible. It could also potentially save you a lot of explicit "use function" statements, since you can replace ranges of function-imports (from the same namespace) with a single namespace-import. When new functions are added to imported namespaces, they would be immediately callable, without needing to add more imports first. One drawback or risk of this approach, is of files importing multiple namespaces, e.g.: use Foo, Bar; baz(); If you're currently calling Bar\baz() and then a function Foo\baz() is introduced, unwittingly you would now be calling that. That's a pretty marginal issue though - function name collisions probably aren't very likely to happen, especially within the same file. Thoughts? On Fri, Jan 27, 2017 at 4:29 PM, Wes <netmo.php@gmail.com> wrote:

Net Mo

9 years ago
> > old applications could maintain compatibility only > > by adding a simple autoloader that would alias the global > function/constant > > into the calling namespace > >
So as a side-effect of calling any function, my namespace would get
> polluted with all sorts of random names? That sounds like a messy > work-around. > >
Obviously it would be just a temporary workaround.
> Okay, so take it back for a moment. > > The problem case is this, right? > > namespace Foo; > bar(); > > If the function Foo\bar() has not already been loaded, we fall back to > bar() in the global namespace. > > With a function-autoloader in place, we would need to trigger this for > every function-call from any namespace, and that's unacceptable. >
I'm sorry, it's unacceptable for who? Only for not defined function it would be called. If I call the same function a thousand times, the autoload will be attempted just the first time, and if it doesn't work then the script would die, exactly like classes. Any project I know that uses functions define them one per file, and each file is required one by one. How is that different? Plus, there's opcache: function autoloading is not really autoloading but actually about "not having to maintain lists of require()s". Also, if you really think having one function per file is too much, you can always require everything in one go: ``` function autoloadFunction($functionFQN){ // $functionFQN = "\\foo\\bar\\functionName"; $namespace = substr($functionFQN, 0, strrpos($functionFQN, '\\')); require($namespace . '\\all_the_functions_and_constants.php'); } ```
> So what if, instead of trying to autoload everything by default, we make > it opt-in? Something like: >
People clearly need this and I wouldn't mind if this was the solution. But, this is a workaround imho, and contrarily to what I've suggested, this wouldn't be temporary.

Niklas Keller

9 years ago
> > I'm sorry, it's unacceptable for who? Only for not defined function it > would be called. If I call the same function a thousand times, the autoload > will be attempted just the first time, and if it doesn't work then the > script would die, exactly like classes. Any project I know that uses > functions define them one per file, and each file is required one by one. >
Our observations are pretty different. I don't know any projects that use one function per file. I usually see a functions.php per namespace. Regards, Niklas

Net Mo

9 years ago
I suppose that is because they use just few functions. I don't think you would want to have 20-30 functions in the same file :P

Niklas Keller

9 years ago
2017-01-28 21:37 GMT+01:00 Wes <netmo.php@gmail.com>:
> I suppose that is because they use just few functions. I don't think you > would want to have 20-30 functions in the same file :P >
I wouldn't call these just a few functions: - https://github.com/nikic/iter/blob/master/src/iter.php - https://github.com/amphp/amp/blob/master/lib/functions.php You might want to have a look at https://github.com/search?p=1&q=extension%3Ajson+functions+autoload&ref=searchresults&type=Code&utf8=%E2%9C%93 Regards, Niklas

Tony Marston

9 years ago
"Niklas Keller" wrote in message news:CANUQDCi945M2TDdztY_tUuhdqN1Xj9WjGsL_EqbYqr5F7_Xing@mail.gmail.com...
> >> >> I'm sorry, it's unacceptable for who? Only for not defined function it >> would be called. If I call the same function a thousand times, the >> autoload >> will be attempted just the first time, and if it doesn't work then the >> script would die, exactly like classes. Any project I know that uses >> functions define them one per file, and each file is required one by one. >> > >Our observations are pretty different. I don't know any projects that use >one function per file. I usually see a functions.php per namespace. > >Regards, Niklas
I agree. My framework contains over 100 functions, and the idea of putting each one into a separate file is something that I would never do. The overhead of loading 100 small files is much large that loading 1 large file. Besides, when I am stepping through code with the debugger in my IDE it shows each file of a separate tab, and being forced to jump around from one tab to another would drive me crazy.
-- Tony Marston

Net Mo

9 years ago
Curious that those who are posting here put all functions in the same file. So, why don't you just require_once('Namespace/functions.php') ?

Rowan Collins

9 years ago
On 29/01/2017 15:37, Wes wrote:
> Curious that those who are posting here put all functions in the same file. > So, why don't you just require_once('Namespace/functions.php') ?
By that argument, why not just require_once('Namespace/ClassName.php)? It turns out that many people find autoloading a much more convenient and manageable solution. In particular, the standardisation of autoloading provided by PSR-0 and PSR-4 has been central to the success of Composer, and the ecosystem of reusable components that it promotes. Autoloading functions, either one by one, or namespace by namespace, is a logical extension of that, with all the same benefits. Unfortunately, it has some unique complications due to the way PHP resolves namespaced vs global function names. Please have a look through the archives of the list if you want to know more; there's been too much discussion already to re-hash it all here. Regards,
-- Rowan Collins [IMSoP]

Net Mo

9 years ago
Hi Rowan, I probably wasn't clear enough. How is having to write use namespace Foo\Bar\Baz; better than require_once("../Foo/Bar/Baz/functions.php");

Tony Marston

9 years ago
"Rowan Collins" wrote in message news:ae15f883-bdba-89ea-9e6a-6e3b09745288@gmail.com...
> >On 29/01/2017 15:37, Wes wrote: >> Curious that those who are posting here put all functions in the same >> file. >> So, why don't you just require_once('Namespace/functions.php') ? > >By that argument, why not just require_once('Namespace/ClassName.php)? It >turns out that many people find autoloading a much more convenient and >manageable solution. > >In particular, the standardisation of autoloading provided by PSR-0 and >PSR-4 has been central to the success of Composer, and the ecosystem of >reusable components that it promotes. > >Autoloading functions, either one by one, or namespace by namespace, is a >logical extension of that, with all the same benefits. Unfortunately, it >has some unique complications due to the way PHP resolves namespaced vs >global function names. Please have a look through the archives of the list >if you want to know more; there's been too much discussion already to >re-hash it all here. > >Regards, >
While it has already been good practice to put each class in its own file, it has never been good practice (at least with all the online tutorials and examples that I have read since 2002) to put each function into its own file. Autoloading classes might be practical and easily implemented, but autoloading functions is a can of worms, especially when you throw in namespaces. I neither need nor want it in my code.
-- Tony Marston

Tony Marston

9 years ago
"Wes" wrote in message news:CAOv67gtxm0muzuKrQyizgkM9Pf0hn7E-iTBXpq1VV9D8aUWM=w@mail.gmail.com...
> >Curious that those who are posting here put all functions in the same file. >So, why don't you just require_once('Namespace/functions.php') ?
I do, but without using namespaces. I actually group my functions together in three files, not one.
-- Tony Marston