0

PHP 8.0 — Named arguments

Beginner5 min read·php-09-003

Concept

Named arguments (PHP 8.0) were covered in depth in the OOP section (php-08-015). This lesson focuses on their interaction with PHP 8's other new features and the practical impact on daily PHP development.

Named arguments work with all callable types: built-in functions, user-defined functions, methods, and constructors. PHP 8.0 also added named argument support to nearly all standard library functions.

Key everyday wins:

  • array_slice with preserve_keys: array_slice($arr, 0, 10, preserve_keys: true) vs the old positional array_slice($arr, 0, 10, true).
  • str_pad with pad_type: str_pad($s, 10, pad_type: STR_PAD_LEFT) skips the padding string parameter.
  • htmlspecialchars with explicit encoding: htmlspecialchars($s, flags: ENT_QUOTES|ENT_HTML5, encoding: 'UTF-8').

Named arguments in combination with spread: fn(...$params) when $params is an associative array creates named argument calls. This enables dynamic function calls with parameter names from configuration.

Impact on API design: Named arguments make it safer to add optional parameters to existing functions without breaking callers. Previously, adding a parameter in the middle would break any caller using positional arguments. With named arguments, new optional parameters can be placed anywhere and existing callers remain unbroken (as long as they use named args).

Code Example

php
<?php
declare(strict_types=1);

// Standard library: skip optional parameters cleanly
$arr = ['a', 'b', 'c', 'd', 'e'];
$sliced = array_slice($arr, offset: 1, length: 3, preserve_keys: true);
// [1 => 'b', 2 => 'c', 3 => 'd']

// str_pad — skip the padding string
$padded = str_pad('42', length: 6, pad_type: STR_PAD_LEFT);
// '    42' (padded with spaces on the left)

// Custom padding character, left-pad
$padded2 = str_pad('42', 6, '0', STR_PAD_LEFT);
// '000042'

// With named args in combination with PHP 8.0 constructor promotion
class PaginationOptions
{
    public function __construct(
        public readonly int $page    = 1,
        public readonly int $perPage = 15,
        public readonly string $sortBy   = 'created_at',
        public readonly string $sortDir  = 'desc',
    ) {}
}

// Only override what you need — clear intent
$options = new PaginationOptions(page: 3, sortBy: 'name');
echo $options->page;    // 3
echo $options->perPage; // 15 (default)
echo $options->sortBy;  // 'name'
echo $options->sortDir; // 'desc' (default)

// Dynamic named argument dispatch
function configureHttp(
    string $baseUrl,
    int    $timeout   = 30,
    bool   $verify    = true,
    array  $headers   = [],
): array {
    return compact('baseUrl', 'timeout', 'verify', 'headers');
}

$params = ['baseUrl' => 'https://api.example.com', 'timeout' => 60, 'verify' => false];
$config = configureHttp(...$params); // spread as named args

// Named args in attributes
#[\Attribute]
class Column
{
    public function __construct(
        public string $name,
        public string $type   = 'string',
        public bool   $nullable = false,
        public ?int   $length   = null,
    ) {}
}

class User
{
    #[Column(name: 'user_email', type: 'string', length: 255)]
    private string $email;
}