PHP:命名参数

命名参数(也称为命名参数)在 PHP 8.0 中可用,并允许我们使用参数名称将输入数据传递给函数,而不是依赖于函数参数本身的顺序。

这是一个很好的例子,说明如何在 PHP 中使用它们:

$data = file_get_contents(
  filename:'form.html',
  use_include_path: true
);
echo $data;

单个参数名称与函数输入变量的名称匹配。
如果我们有一个相当不错的代码编辑器,那么我们只需将鼠标悬停在函数名称上即可获得一个包含其定义的小弹出窗口,其中包括有关参数名称的信息;但是,由于这些相同的编辑器功能,使用参数名称的好处可能会受到限制。
命名参数很有用的一个地方是,当我们希望通过省略未使用的参数来提高代码的可读性时。

file_get_contents 函数的完整函数签名如下:

function file_get_contents(
    $filename,
    $use_include_path = false,
    $context = null,
    $offset = 0,
    $maxlen = null
) { }

当我们将函数名称悬停在编辑器中时,此信息通常可用。
我可以推荐 Eclipse 或者带有智能电话的 Visual Studio Code。

旧版本的 PHP 没有名称参数,但仍然可以使用数组和类型检查的组合来模拟功能——尽管我个人不推荐这样做——我尝试了一段时间,发现它只是让我的代码。

尽管如此,我的旧解决方案仍包含在以下部分中。

如果我们正在处理一个更大的项目,那么我强烈建议我们在整个项目中坚持为自定义函数和方法使用命名参数。
这样做可以让我们以后更轻松地更改函数或者方法定义,而无需更改项目中依赖于该函数的所有代码。

对于旧版本的 PHP

旧版本 PHP 中函数参数的问题在于,我们必须按特定顺序输入它们才能工作。
对于用户函数,当我们需要更改函数接受的参数时,这是有问题的,因为我们会破坏使用该函数的现有代码。
使用命名参数,我们没有这些问题。

现在,虽然 PHP 本身不支持命名参数,但我们可以通过使用关联数组而不是使用位置参数来非常接近。

理想情况下,我们应该谨慎选择,并且只对某些功能使用数组。
一个函数也没有必要同时接受命名和位置参数——尽管它会非常简洁。

此外,如果一个函数只接受几个参数,并且我们相当确定签名不会改变,最好不要使用数组进一步复杂化。

PHP命名参数的优点

  1. 一个好处是我们不需要记住函数参数的顺序——重要的是参数的名称。

  2. 另一个好处是我们不再需要在到达感兴趣的参数之前声明空值,这与下面的坏例子不同:

my_function(false, '', 'this is what we want');
  1. 最后,更改函数的参数签名不会破坏使用该函数的现有代码。
    这在大型项目中非常有益,因为我们不必为了引入新的函数参数而更新全局代码。

PHP命名参数的缺点

命名参数的现有实现并非完全没有缺点。
虽然我将在下面讨论一些,但可能还有更多。

  1. 主要缺点之一是我们的代码编辑器默认无法显示函数参数,因此我们现在需要了解函数的实现。
    我个人认为这是一个很小的代价,如果你真的想要,你实际上可以用一个插件来解决这个问题。

另一种可能的解决方案是,当参数使用不正确或者丢失时,在错误消息中显示 $defined_arguments。

  1. 最后,将 $input_arguments 与我们的 $defined_arguments 进行比较的另外代码会稍微影响性能。
    大多数情况下,这可能无关紧要。

数组和命名参数

我们可以选择使用一个简单的解决方案,如下所示:

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;
    }
}

或者,我们可以选择更安全的解决方案,同时向开发人员显示有用的错误消息。

例如,只需编写一些另外的代码,我们还可以支持类型提示、阻止未知参数,并添加对必需和可选参数的支持。
为此,我们只需要定义函数使用的参数。

调用一个接受参数数组的函数可以这样完成:

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

执行

在本节中,我将展示如何使用数组代替 PHP 中的命名参数。

如前所述,在创建新函数时,我们首先必须定义该函数将要接受的参数。
这样做的一个直接好处是我们现在可以阻止未知和拼写错误的参数——这是 PHP 本身无法做到的。

此外,我们还可以决定一个参数应该定义为必需的还是可选的,我们甚至可以选择验证参数的类型。

此示例使用简单的过程代码,我们可能需要查看 php_helpers 类以获得此代码的面向对象实现。

在下面的示例中,我在 $defined_arguments 变量中定义参数,该变量包含我们希望允许的参数的定义:

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;
  }
}

参数可以定义为 true 表示必需,或者 false 表示可选。
如果我们还想检查参数的类型,只需使用数组而不是布尔值。
下面是一些更有效的例子:

//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];

该函数将被 exit() 中断;如果未提供必需的参数。

为了处理参数定义,我们可以使用以下几个函数:

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_array($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;
    }
}

echo 语句仅用于演示目的。
我们可以使用自己的错误处理轻松替换它们。

现在,调用 say_hallo();函数,我们可以简单地这样做:

//A function with named parameters
say_hallo(['name' => 'Jacob', 'email' => 'jacob@example.com']);
日期:2020-06-02 22:15:41 来源:oir作者:oir