Share via:

PHP: Named Parameters

PHP does not have named parameters build-in, but in this tutorial you can learn how to simulate the functionality with a bit of extra code.

198 views

Edited: 2020-01-19 14:02

named parameters, PHP tutorial

The problem with function arguments in PHP is that you have to enter them in a specific order for them to work. Change the arguments a function accepts, and all existing code using the function will break. With named parameters, we do not have these problems.

Now, while PHP does not natively support named parameters, we can get very close by using an associative array instead of using positional parameters.

Ideally, we should choose carefully, and only use arrays for certain functions. It is also not necessary for a function to accept both named- and positional parameters—although it would be pretty neat.

Also, if a function only accepts a couple parameters, and we are fairly sure that the signature is not going to change, it might be best not to complicate it further by using arrays.

Benefits

1. One benefit is that we do not need to remember the order of the function arguments—what matters is the name of the argument.

2. Another benefit is that we no longer have to declare empty values before reaching the argument of interest, unlike in the bad example below:

my_function(false, '', 'this is what we want');

3. Finally, changing the parameter signature of a function will not break existing code using the function. This can be hugely beneficial in larger projects, since you do not have to update your global code just to introduce new function argument.

Disadvantages

Existing implementations of named parameters do not come entirely without disadvantages. While I will discuss some below, there might be more.

1. One of the main disadvantages is that our code editor will not be able to reveal the function arguments by default, and as a result we now need to know about the implementation of the function. I personally think this is a small price to pay, and you could actually fix this with a plugin if you really wanted.

Another possible solution is to reveal the $defined_arguments in an error message when a parameter is either used incorrectly or missing.

2. Finally, having extra code comparing $input_arguments with our $defined_arguments will effect performance a little. Most of the time, this is probably not going to matter much.

Arrays and named parameters

We can either choose to go with an easy solution, like this one:

say_hallo(['name' => 'Jacob', 'email' => 'jacob@example.com']);

function say_hallo($input_arguments=[]) {
    $name = null;
    $email = null;
    extract($input_arguments,EXTR_IF_EXISTS);

    echo 'Name: ' . $name  . PHP_EOL;
    if ($email !== null) {
        echo 'Email: ' . $email . PHP_EOL;
    }
}

Or, we can choose a more secure solution that also shows helpful error messages to the developer.

For example, just by writing little extra code, we can also support type hinting, block unknown parameters, and add support for required and optional parameters. To do so, we only need to define the parameters that is used by our functions.

Calling a function that accepts an array of parameters can be done like this::

// A function with named parameters
say_hallo(['name' => 'Jacob', 'email' => 'jacob@example.com']);

Implementation

In this section I will show how to use arrays as a substitute for named parameters in PHP.

As discussed earlier, when making a new function, we will first have to define the parameters that the function is going to accept. A direct benefit of doing this is that we can now block unknown- and misspelled parameters—something that PHP does not do by itself.

In addition, we can also decide if a parameter should be defined as required or optional, and we may even choose to validate the type of the parameter.

Note. This example uses simple procedural code, you may want to check out the php_helpers class for an object orientated implementation of this code.

In the below example, I am defining parameters in the $defined_arguments variable, which contains the definition of the parameters we wish to allow:

function say_hallo(array $input_arguments) {
  // Define allowed $input_arguments
  $defined_arguments = ['name' => ['required' => true, 'type' => 'string'], 'email' => false];

  // Check that the used parameters is allowed in defined parameters
  handle_arguments($input_arguments, $defined_arguments);
  

  // *****
  // *** Function code
  // ** *
  echo 'Name: ' . $input_arguments['name'] . PHP_EOL;

  // Output the e-mail address only if provided.
  if (isset($input_arguments['email'])) {
    echo 'Email: ' . $input_arguments['email'] . PHP_EOL;
  }
}

The arguments can either be defined as true for required, or false for optional. If you want to also check the type of the parameter, you simply use an array instead of a boolean value. Below is a few more valid examples:

// Required With Type Checking
$defined_arguments = ['name' => ['required' => true, 'type' => 'string'];

// Required Without Type Checking
$defined_arguments = ['name' => true];

// Optional With Type Checking
$defined_arguments = ['name' => ['required' => false, 'type' => 'string'];

// Optional Without Type Checking
$defined_arguments = ['name' => false];

The function will be interrupted with exit(); if a required parameter is not provided.

To handle the argument definitions, we can use a couple of function like this:

function handle_arguments($input_arguments, $defined_arguments) {
  // Check if parameter is defined, and validate the type (if provided)
  foreach ($input_arguments as $key => $value) {
    if (!isset($defined_arguments["$key"])) {
      echo 'Unknown function parameter: ' . $key . PHP_EOL;
      echo 'used in: ' . __FUNCTION__ . PHP_EOL;
      exit();
    }
    // Validate the type, if defined. A type is always defined in an array.
    // I.e.: array('required' => true, 'type' => 'object')
    if (is_array($defined_arguments["$key"])) {
      // Check if the developer remembered to define both "required" and "type"
      if ((!isset($defined_arguments["$key"]['required'])) || (!isset($defined_arguments["$key"]['type']))) {
        echo 'Missing argument definition "required" or "type".';
        exit();
      }
      if (!type_check($value, $defined_arguments["$key"]['type'])) {
        echo 'Invalid input type of: ' . $key . PHP_EOL;
        echo 'used in: ' . __FUNCTION__ . PHP_EOL;
        echo 'Expected ' . $defined_arguments["$key"]['type'] . ', ' . gettype($value) . ' given.' . PHP_EOL;
        exit();
      }
      // In case value was an array, make sure to add possible default elements.
      if ((isset($defined_arguments["$key"]['default'])) && (is_array($defined_arguments["$key"]['default']))) {
        $input_arguments["$key"] += $defined_arguments["$key"]['default'];
      }
    }
  }
  // --------------------------------------------------
  // Check for missing required parameters-------------
  // The "required" setting only needs to be checked when the parameter is missing
  // --------------------------------------------------
  $missing_parms = array_diff_key($defined_arguments, $input_arguments);
  foreach ($missing_parms as $key => $value) {
    if (!is_array($defined_arguments["$key"])) {
      // If no type validation
      if (true === $defined_arguments["$key"]) {
        echo 'Missing required parameter: ' . $key . PHP_EOL;
        echo 'used in: ' . __FUNCTION__ . PHP_EOL;
        exit();
      }
    } else {
      // Check if the developer remembered to define both "required" and "type"
      if ((!isset($defined_arguments["$key"]['required'])) || (!isset($defined_arguments["$key"]['type']))) {
        echo 'Missing argument definition "required" or "type". ';
        exit();
      }
      // If type was to be validated, check the "required" key
      if (true === $defined_arguments["$key"]['required']) {
        echo 'Missing required parameter: ' . $key . PHP_EOL;
        echo 'used in: ' . __FUNCTION__ . PHP_EOL;
        exit();
      }
      // Check if the parameter has a default value
      if (isset($defined_arguments["$key"]['default'])) {
        $input_arguments["$key"] = $defined_arguments["$key"]['default'];
      }
    }
  }
  return $input_arguments;
}
function type_check($input_value, $defined_type) {
    switch ($defined_type) {
        case 'string':
          return is_string($input_value);
        break;
        case 'str':
          return is_string($input_value);
        break;
        case 'integeer':
          return is_int($input_value);
        break;
        case 'int':
          return is_int($input_value);
        break;
        case 'object':
          return is_object($input_value);
        break;
        case 'array':
          return is_object($input_value);
        break;
        case 'bool':
          return is_bool($input_value);
        break;
        case 'resource':
          return is_resource($input_value);
        break;
        case 'null':
          return is_null($input_value);
        break;

        // Mixed type will allow anything
        case 'mixed':
          return true;
        break;

        // For unknown types we return false
        default:
          return false;
        break;
    }
}

Note. The echo statements is simply for demonstration purposes. You can easily replace them with your own error handling.

Now, to call the say_hallo(); function, we can simply do this:

// A function with named parameters
say_hallo(['name' => 'Jacob', 'email' => 'jacob@example.com']);

Links

  1. Named Parameters in PHP - phil.tech
  2. PHP RFC: Named Parameters - wiki.php.net

Comments