Skip to content

RFC: Support for Options Array and PHP 8.0 Named Parameters #325

@bshaffer

Description

@bshaffer

RFC for supporting both named parameters in PHP 8 and options arrays for previous versions of PHP

Given class Foo with two "options" in its constructor, string cacheKey and int cacheLifetime, this would give us the following behavior in PHP 8.0:

new Foo(
    cacheKey: 'foo',
    cacheLifetime: 123
);

And the following behavior in PHP 7.4 and below:

new Foo([
    'cacheKey' => 'foo',
    'cacheLifetime' => 123
]);

For this case, the class constructor would look like:

class Foo 
{
    public function __construct(
        array $options = null,
        string $cacheKey = null,
        int $cacheLifetime = 1500
    ) {
        // ...
    }
}

The following cases would throw exceptions:

// provide both options array and named parameters
new Foo(
    ['cacheKey' => 'foo', 'cacheLifetime' => 123],
    cacheKey: 'bar',
    cacheLifetime: 456
);
// provide both options array and ordered parameters
new Foo(
    ['cacheKey' => 'foo', 'cacheLifetime' => 123],
    'bar',
    456
);
// provide options as a named parameter with other named parameters
new Foo(
    options: ['cacheKey' => 'foo', 'cacheLifetime' => 123],
    cacheKey: 'bar',
    cacheLifetime: 456
);
// provide empty options array as a named parameter with other parameters
new Foo(
    options: [],
    cacheKey: 'bar',
    cacheLifetime: 456
);
// provide empty options array as the first argument with other parameters
new Foo(
    [],
    cacheKey: 'bar',
    cacheLifetime: 456
);

It's not possible (AFAICT) to throw exceptions in the following non-standard scenarios:

// provide null options as a named parameter
new Foo(
    options: null,
    cacheKey: 'bar',
    cacheLifetime: 456
);
// provide null options as a named parameter
new Foo(
    null,
    cacheKey: 'bar',
    cacheLifetime: 456
);
// provide options as a named parameter by itself
new Foo(
    options: ['cacheKey' => 'foo', 'cacheLifetime' => 123],
);

This behavior is done by checking if $options is an array and calling func_num_args(). Using a trait, we can guarantee standard behavior across all our classes:

trait OptionsArrayWithAdditionalParametersTrait
{
    private function verifyOptionsArrayWithAdditionalParameters(
        ?array $options,
        int $numArgs
    ) {
        if (is_array($options) && func_num_args() > 1) {
            throw new LogicException(
                'Cannot supply options array with additional parameters'
            );
        }
    }
}
class Foo 
{
    use OptionsArrayWithAdditionalParametersTrait;

    public function __construct(
        array $options = null,
        string $cacheKey = '',
        int $cacheLifetime = 1500
    ) {
        $this->verifyOptionsArrayWithAdditionalParameters(
            $options,
            func_num_args()
        );

        $this->cacheKey = $options['cacheKey'] ?? $cache;
        $this->cacheLifetime = $options['cacheLifetime'] ?? $cacheLifetime;
    }
}

Pros

  • Named parameters can be used in PHP 8
  • This allows for typehinting and cleaner syntax
  • There is still a usable interface for earlier PHP versions.

Cons

  • Some of the possible constructor cases are non-standard
  • this behavior may be considered hacky or strange
  • it allows for classes to be constructed without typehints.

Metadata

Metadata

Assignees

No one assigned

    Labels

    type: feature request‘Nice-to-have’ improvement, new feature or different behavior or design.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions