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
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]
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.
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?
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.
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.
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 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
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
"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
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') ?
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]
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");
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");
OK, your last question just mentioned putting multiple functions in one
file, not that you were addressing that particular proposal. Those are
very different parts of the conversation. In an ideal world, just
calling "\Foo\Bar\Baz\frobnicate()" would trigger an autoloader, and
that autoloader could decide to require_once one big file if that's what
the library author wanted.
To an extent, I agree that requiring you to import a namespace in order
to autoload it is perhaps too much of a compromise. There is some
advantage over a plain require_once, though, in that you don't need to
hard-code into your application the directory layout of some other
library; it would be more akin to having a global function like
"load_function_definitions($namespace);"
Regards,
--
Rowan Collins
[IMSoP]
"Rowan Collins" wrote in message
news:ae15f883-bdba-89ea-9e6a-6e3b09745288@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') ?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
"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