Table of Contents

PHP RFC: Alternative "use" syntax for Closures

Introduction

PHP users often say that they find defining the lexical-scope imports cumbersome, because they hate importing the variables explicitly.

My opinion on the matter is that what's actually annoying is not writing the variable names, but the syntax with which the imports are defined.

In ES we are required to declare variables but we don't find it annoying; in PHP we don't have to declare variables, but we are required to specify which one we want to import in Closures. It won't be much different if not for the syntax. It is very standard in ES, but irksome in PHP.

Specifically, it requires a lot of effort to write compared to normal expressions. Whitespace, parentheses, possible indentation on different depths. Also, being in the middle of the signature, it is very invasive, visually, as it separates the arguments from the return information:

    public function bar(){
        // ...
 
        $closure = function (
            ArgumentType $argument1,
            ArgumentType $argument2,
            ArgumentType $argument3,
            ArgumentType $argument4
        ) use ( // indent out
            // indent in
            $importVariable1,
            &$importVariable2,
            $importVariable3,
            &$importVariable4
        ): ReturnType { // indent out again
            // indent in again
        };
    }
 
    // or also
 
    public function bar(){
        // ...
 
        $closure = function (
            ArgumentType $argument1,
            ArgumentType $argument2,
            ArgumentType $argument3,
            ArgumentType $argument4
        ) use ( // indent out
            // indent in
            $importVariable1, &$importVariable2, $importVariable3, &$importVariable4
        ): ReturnType { // indent out again
            // indent in again
        };
    }

Proposal

This RFC proposes to provide an alternative syntax for use(), one resembling global or Python's nonlocal modifier:

    $closure = function (
        ArgumentType $argument1,
        ArgumentType $argument2,
        ArgumentType $argument3,
        ArgumentType $argument4
    ): ReturnType {
        use $importVariable1, &$importVariable2, $importVariable3, &$importVariable4;
 
        // ...
    };
 
    // or also
 
    $closure = function (
        ArgumentType $argument1,
        ArgumentType $argument2,
        ArgumentType $argument3,
        ArgumentType $argument4
    ): ReturnType {
        use $importVariable1, &$importVariable2;
        use $importVariable3, &$importVariable4;
 
        // ...
    };

Unlike global, which is allowed everywhere in a function body, use must only appear at the very top of the Closure's body. For example, the following code will cause a syntax error:

    $closure = function (
        ArgumentType $argument1,
        ArgumentType $argument2,
        ArgumentType $argument3,
        ArgumentType $argument4
    ): ReturnType {
        use $importVariable1, &$importVariable2; // ok
        echo 123;
        use $importVariable3, &$importVariable4;
        // ^ syntax error, as "use" can only be preceded by other "use" statements
    };

Exactly like the current use(), variables are imported by value, unless prefixed by &. A Closure can define multiple use; statements; they can go on multiple lines and they can be surrounded by whitespace and comments as with most of other expression statements.

Backward Incompatible Changes

None.

Proposed PHP Version(s)

Next PHP minor version

Proposed Voting Choices

Vote will require 2/3 majority