On Fri, Jan 3, 2020 at 12:22 PM Rasmus Schultz <ras...@mindplay.dk> wrote:

> On Fri, Jan 3, 2020 at 10:35 AM Nikita Popov <nikita....@gmail.com> wrote:
>
>> On Fri, Jan 3, 2020 at 2:51 AM tyson andre <tysonandre...@hotmail.com>
>> wrote:
>>
>> > After a quick search, it turns out I've mostly reinvented
>> > https://wiki.php.net/rfc/autofunc which I hadn't remembered.
>> > The `spl_autoload_*()` changes it mentions is what I had in mind
>> >
>> > There's been changes to php since then, it's been 7 years,
>> > and this adds thoughts on some implementation details.
>> >
>> > It's possible to support function / const autoloading
>> > in a way that keeps existing performance.
>> > Some of the objections at the time no longer apply.
>> >
>> > - `SOME_CONSTANT` no longer falls back to the literal string in php 8.
>> >   It throws an Error.
>> > - APC (used by some implementations) is no longer supported.
>> > - Many of the objections were against the available implementations
>> >   based on `function __autoload()`, not the concept of autoloading.
>> >
>> >   https://www.mail-archive.com/internals@lists.php.net/msg52307.html
>> > - There wasn't much traction from leads for the concept
>> >   or a Proof of Concept to vote on for that RFC (I think).
>> >
>> > What if an ambiguous `function_name()` or const outside the global
>> > namespace would attempt to autoload functions in the global namespace
>> > if it would throw an Error, but not the current namespace?
>> >
>> > `function_exists()` or `defined()` would not autoload,
>> > to preserve the behavior/performance of current programs.
>> > Anything with the callable type hint (e.g. `array_map`) would also need
>> to
>> > be modified,
>> > but I think it already needs to do that for method arrays and
>> > 'MyClass::method'.
>> >
>> > - Same for global constants
>> > - That should avoid the performance hit - this autoloading would only be
>> > triggered
>> >   when php would previously throw an Error or warning
>> >   for undefined functions/constants.
>> > - This would also avoid the need to load polyfill files (e.g. mbstring)
>> >   or test framework files when their functions/constants are unused.
>> > - One blocker for other autoload proposals
>> >   seemed to be performance if I understood correctly
>> >   (e.g. attempting to autoload NS\strlen() every time strlen was
>> called).
>> >
>> >   https://externals.io/message/54425#54616 and
>> > https://externals.io/message/54425#54655
>> >   detail this - choosing a different order of checks avoids the
>> > performance hit.
>> > - In addition to the RFC, changing the signatures to `defined(string
>> > $name, bool $autoload = false)`
>> >   and `function_exists($name, bool $autoload = false)`
>> >   might be useful ways to allow users to choose to autoload when
>> checking
>> > for existence.
>> >
>> > And there'd have to be a hash map, flag, or other check
>> > to avoid recursion on the same constant/function.
>> >
>> > Background: A function call or constant usage is
>> > ambiguous if it could look for the function in two namespaces,
>> > as described by the rules documented in the below link.
>> > It's unambiguous if it ends up looking in only one namespace.
>> >
>> > https://www.php.net/manual/en/language.namespaces.rules.php
>> > The only type of ambiguous function call or global constant use is (7.):
>> >
>> > > 7. For unqualified names,
>> > >    if no import rule applies and the name refers to a function or
>> > constant
>> > >    and the code is outside the global namespace, the name is resolved
>> at
>> > runtime.
>> > >    Assuming the code is in namespace A\B, here is how a call to
>> function
>> > foo() is resolved:
>> > >    1. It looks for a function from the current namespace: A\B\foo().
>> > >    2. It tries to find and call the global function foo().
>> >
>> > My approach should be in line with the current name resolution
>> > for unqualified names outside the global namespace.
>> >
>> > 1. It (first) looks for a function from the current namespace:
>> A\B\foo().
>> > 2. It (next) tries to find and call the global function foo().
>> > 3. (Optional) NEW ADDITION: Next, if both functions were undefined,
>> >    the autoloader(s) attempt to autoload the function A\B\foo()
>> >    (and call it instead of throwing if found) before proceeding to step
>> > (4.)
>> > 4. NEW ADDITION: If both functions were undefined,
>> >    the autoloader(s) attempt to autoload the global function foo()
>> >    (and call it instead of throwing if found) before throwing an Error
>> >
>> > And for unambiguous calls, find and autoload the only name it has.
>> >
>> > The fact that one of the two possible functions gets cached by the php
>> VM
>> > in CACHED_PTR
>> > (for the lifetime of the request) the first time the function is found
>> > in either namespace will remain unchanged with this proposal.
>> >
>>
>> I believe the problem here is this: The fact that the global function is
>> being cached, and continues to be cached when a namespaced function is
>> later defined, is a bug. See for example
>> https://bugs.php.net/bug.php?id=64346. We've never prioritized fixing
>> this
>> issue, but implementing the proposed autoloading behavior would make
>> fixing
>> it essentially impossible, as we certainly can't perform an autoloader
>> invocation on each call.
>>
>> Nikita
>>
>
> I will just briefly mention an alternative idea I've brought up before.
> (there's no RFC for this.)
>
>     use function MyClass::myStaticFunction;
>
>     myStaticFunction();
>
> This would allow importing a static function and calling it just like a
> flat function.
>
> The way most people normally write code in PHP, they're favoring static
> functions over flat functions, since aggressively preloading flat functions
> (e.g. "file" in "composer.json") doesn't scale, and using manual
> include/require statements is clumsy and error-prone. (and not really
> possible with Composer.)
>
> Many existing PHP codebases already have "classes" that are really just
> "pseudo-namespaces" for collections of functions, so this approach takes
> into account the existing ecosystem.
>
> It also implicitly addresses autoloading, which would just automatically
> work with Composer (or any class auto-loader) with no further changes.
>
> It also bypasses any learning curve surrounding function name resolution
> rules, if this were to become more complex by adding more rules and details
> with regards to resolving and autoloading flat functions.
>
> Is there a really good reason to encourage the entire community to migrate
> all of their existing codebases from static classes to flat functions?
> Expecting Composer to add support for it. Expecting package vendors to list
> all function names in package metadata files, and so on.
>
> In many cases, migrating from static to flat functions also means
> migrating any constants from class constants to namespaced constants - so
> this likely leads down the rabbit hole to autoloading constants as well,
> and/or having to use some mix of class constants and flat functions which
> isn't very desirable.
>
> Is there any practical return on that investment?
>
> Static functions aren't really any different from flat functions.
>
> (if you're thinking "state", that's really up to the author - your flat
> functions could depend on global state just like a static function could
> depend on static properties...)
>
> Just my five cents on the matter :-)
>

Hey Rasmus,

One issue I see with important static methods in this manner, is that PHP
has no concept of a static method call -- rather, it performs scoped calls,
that may refer to static or instance methods.

use function A::foo;

class B extends A {
    public function test() {
        foo(); // What does this do?
    }
}

If the call to foo() is simply treated equivalently to a call to A::foo(),
then this may have some quite surprising behavior: If A::foo() is an
instance method, then this call to foo() will inherit $this, which is
something that normally does not happen with free-standing function calls.
Possibly this would need a new call type that enforces that the method is
actually static?

Overall though, I do tend to agree that the use of static methods is at
this point more idiomatic than the use of free-standing functions, and it
might make more sense to go in that direction.

Nikita

Reply via email to