rfc:use-static-function

PHP RFC: Static "use function"

Introduction

This RFC proposes an extension to the use function statement, providing a means of aliasing public static functions from classes as file-local functions.

At present, functions cannot be autoloaded - as a consequence, developers have been forced to either preload all functions (not knowing if they get called or not) - or put them in (abstract) classes as public static functions (the more popular approach in Composer packages) and qualify every call with a class-name (or alias.)

While function autoloading has been proposed to address this issue, this RFC has been stalled several times due to problems with name-resolution, to which no good solution has been proposed.

This RFC offers an arguably much simpler solution to the problem, by allowing the use of classes as “pseudo-namespaces”, which would immediately work with existing libraries of public static functions, and additionally would work with Composer (or any auto-loader) as-is.

Furthermore, existing classes with public static functions would be immediately supported without modification - e.g. without having to port all existing legacy code from public static to flat functions.

Proposal

This RFC proposes the following extensions to the “use function” statement.

Assume for the following examples a class as follows:

class Foo
{
    public static function bar();
    public static function baz();
}

The following code imports and calls the Foo::bar() function by introducing an alias:

use Foo::bar;
 
bar(); // calls Foo::bar()

Note that aliases (consistent with class-aliases) have a file-level scope - the function Foo::bar() will be callable as bar() only within the context of this file.

The following code imports and calls the Foo::bar() function by introducing a named alias:

use Foo::bar as plum;
 
plum(); // calls Foo::bar()

The following code imports and calls two functions by introducing two aliases:

use function Foo::{ bar, baz };
 
bar();
baz();

The following code imports two functions by introducing named aliases for both:

use function Foo::{ bar as blip, baz as plum };
 
blip();
plum();

Assuming another class Nib with a public static function bar, the following code imports both Foo::bar() and Nib::bar() with two distinct aliases:

use function Foo::bar as a, Nib::bar as b;
 
a(); // calls Foo::bar()
b(); // calls Nib::bar()

How it works

The net effect of a statement such as use Foo::bar is virtually equivalent to:

function bar(...$args) {
    return Foo::bar(...$args);
}

However, this operates at the file-level only - an imported function does not pollute the namespace, just as imported classes do not pollute the actual namespace. Function aliases are available only within the file they were declared in.

In terms of error-handling and stack-traces, a call to an alias bar() is literally equivalent to calling the static method - no evidence of the local alias is visible in a stack-trace or anywhere else, similar to how a class-aliases are not literal symbols.

In terms of reflection, a statement such as new ReflectionFunction(“bar”) would fail, because this alias represents a static method and not a function. A statement such as new ReflectionMethod(“bar”) is also not expect to work. To obtain a method-reflection, one needs to use new ReflectionMethod(Foo::class, “bar”) as normal.

In terms of dynamic resolution, the following are all expected to work:

use Foo::bar;
 
call_user_func("bar");
 
array_map($callback, "bar");

Alternative Approach

Alternatively to the above, and perhaps simpler, the use statements simply introduce a literal function, in the current namespace, that delegates calls to a given static function.

The advantage is perhaps more obvious use (and/or simpler implementation) of call_user_func(), array_map() et al. - as well as e.g. new ReflectionFunction(“bar”) actually returning a reflection of the delegate function.

The downside is these function-aliases would likely need to appear in stack-traces.

Another drawback is that these function aliases of course cannot trigger auto-loading if you attempt to invoke them from outside the file that defined it.

This is probably largely an unattractive prospect, but it's described here for completeness.

Backward Incompatible Changes

None.

Proposed PHP Version(s)

Next PHP 7.x.

RFC Impact

To SAPIs

None.

To Existing Extensions

TBD

To Opcache

TBD

Future Scope

TBD: how would this play along with an eventual implementation of function autoloading?

TBD: the introduction of “static classes” might provide a means of grouping functions in such a way that the containing class cannot have state, can only have static methods, cannot extend another class, cannot be extended, and cannot implement interface or use traits.

Proposed Voting Choices

TBD

Patches and Tests

None yet.

Implementation

None yet.

References

TODO

Rejected Features

TODO

rfc/use-static-function.txt · Last modified: 2017/09/22 13:28 (external edit)