Любой способ указать необязательные значения параметров в PHP?

Допустим, у меня есть функция PHP foo:

function foo($firstName = 'john', $lastName = 'doe') {
    echo $firstName . " " . $lastName;
}
// foo(); --> john doe

Есть ли способ указать только второй необязательный параметр?

Пример:

foo($lastName='smith'); // output: john smith

person zaczap    schedule 27.03.2009    source источник


Ответы (13)


PHP не поддерживает именованные параметры для функций как таковых. Однако есть несколько способов обойти это:

  1. Используйте массив в качестве единственного аргумента функции. Затем вы можете извлечь значения из массива. Это позволяет использовать именованные аргументы в массиве.
  2. Если вы хотите разрешить дополнительное количество аргументов в зависимости от контекста, вы можете использовать func_num_args и func_get_args, а не указывать допустимые параметры в определении функции. Затем на основе количества аргументов, длины строки и т. д. вы можете определить, что делать.
  3. Передайте нулевое значение любому аргументу, который вы не хотите указывать. Не совсем обойти это, но это работает.
  4. Если вы работаете в контексте объекта, вы можете использовать волшебный метод __call() для обработки этих типов запросов, чтобы вы могли направлять частные методы на основе переданных аргументов.
person Noah Goodrich    schedule 27.03.2009
comment
Хотя неявной поддержки нет, есть лучшие обходные пути, чем предлагает принятый ответ. См. решение Уолфа ниже. Если вам нужно, в моем решении есть более подробная информация (к которой, клянусь, я пришел независимо!) - person DJDave; 15.12.2015

Вариант метода массива, позволяющий упростить установку значений по умолчанию:

function foo($arguments) {
  $defaults = array(
    'firstName' => 'john',
    'lastName' => 'doe',
  );

  $arguments = array_merge($defaults, $arguments);

  echo $arguments['firstName'] . ' ' . $arguments['lastName'];
}

Использование:

foo(array('lastName' => 'smith')); // output: john smith
person ceejayoz    schedule 27.03.2009
comment
Для ассоциативных массивов $arguments += $defaults; чище. - person Walf; 11.12.2015

Вы можете немного реорганизовать свой код:

function foo($firstName = NULL, $lastName = NULL)
{
    if (is_null($firstName))
    {
        $firstName = 'john';
    }
    if (is_null($lastName ))
    {
        $lastName = 'doe';
    }

    echo $firstName . " " . $lastName;
}

foo(); // john doe
foo('bill'); // bill doe
foo(NULL,'smith'); // john smith
foo('bill','smith'); // bill smith
person Andrew G. Johnson    schedule 27.03.2009

Если у вас есть несколько необязательных параметров, одним из решений является передача одного параметра, который является хэш-массивом:

function foo(array $params = array()) {
    $firstName = array_key_exists("firstName", $params) ?
      $params["firstName"] : "";
    $lastName = array_key_exists("lastName", $params) ?
      $params["lastName"] : "";
    echo $firstName . " " . $lastName;
}

foo(['lastName'=>'smith']);

Конечно, в этом решении нет проверки того, что поля хеш-массива присутствуют или написаны правильно. Это все зависит от вас, чтобы подтвердить.

person Bill Karwin    schedule 27.03.2009

Нет. Обычно это делается с помощью некоторых эвристик, чтобы определить, какой параметр подразумевается, например, длина строки, тип и т. д.

Вообще говоря, вы должны написать функцию, которая принимает параметры в порядке от наиболее необходимых до наименее необходимых.

person Ryan Graham    schedule 27.03.2009
comment
Эвристика работает надежно только тогда, когда параметры безошибочно распознаются по типу, как это делает jQuery. Другие методы, такие как длина строки, — это авария, ожидающая своего часа. - person Walf; 11.04.2014

Как вы хотите: нет.

Вы можете использовать какую-то специальную метку, например NULL, чтобы отметить, что значение не указано:

function foo($firstName, $lastName = 'doe') {
    if (is_null($firstName))
        $firstName = 'john';
    echo $firstName . " " . $lastName;
}

foo(null, 'smith');
person Milan Babuškov    schedule 27.03.2009

Я бы хотел, чтобы это решение было на SO, когда я начал использовать PHP 2,5 года назад. Он отлично работает в созданных мной примерах, и я не понимаю, почему он не должен быть полностью расширяемым. Он предлагает следующие преимущества по сравнению с теми, которые были предложены ранее:

(i) весь доступ к параметрам внутри функции осуществляется с помощью именованных переменных, как если бы параметры были полностью объявлены, а не требовался доступ к массиву

(ii) очень быстро и легко адаптировать существующие функции

(iii) для любой функции требуется только одна строка дополнительного кода (в дополнение к неизбежной необходимости определения ваших параметров по умолчанию, которые вы бы в любом случае сделали в сигнатуре функции, но вместо этого вы определяете их в массиве). Кредит на дополнительную линию полностью принадлежит Биллу Карвину. Эта строка идентична для каждой функции.

Метод

Определите свою функцию с ее обязательными параметрами и необязательным массивом

Объявите свои необязательные параметры как локальные переменные

Суть: замените предварительно объявленное значение по умолчанию любых необязательных параметров на те, которые вы передали через массив.

extract(array_merge($arrDefaults, array_intersect_key($arrOptionalParams, $arrDefaults)));

Вызовите функцию, передав ее обязательные параметры и только те необязательные параметры, которые вам нужны

Например,

function test_params($a, $b, $arrOptionalParams = array()) {

$arrDefaults = array('c' => 'sat',

                     'd' => 'mat');

extract(array_merge($arrDefaults, array_intersect_key($arrOptionalParams, $arrDefaults)));

echo "$a $b $c on the $d";

}

а затем назовите это так

test_params('The', 'dog', array('c' => 'stood', 'd' => 'donkey'));
test_params('The', 'cat', array('d' => 'donkey'));
test_params('A', 'dog', array('c' => 'stood'));

Результаты:

Собака встала на осла

Кот сел на ослика

Собака стояла на коврике

person DJDave    schedule 19.08.2015
comment
Подтверждено - я видел это две недели назад и дал вам балл. Признаюсь, я не перечитал все решения, прежде чем опубликовать свое. Если мне будет позволено, я добавлю комментарий к принятому ответу, указывающий на ваше решение. - person DJDave; 15.12.2015

PHP 8 представил именованные аргументы, поэтому теперь можно указать параметры, передаваемые функции.

до PHP 8:

htmlspecialchars($string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);

после PHP 8:

htmlspecialchars($string, double_encode: false);

Справочник

person Ratto    schedule 18.12.2020

Здесь упоминается несколько реализаций стиля «опций». Пока что ни один из них не является очень пуленепробиваемым, если вы планируете использовать их в качестве стандарта. Попробуйте этот шаблон:

function some_func($required_parameter, &$optional_reference_parameter = null, $options = null) {
    $options_default = array(
        'foo' => null,
    );
    extract($options ? array_intersect_key($options, $options_default) + $options_default : $options_default);
    unset($options, $options_default);

    //do stuff like
    if ($foo !== null) {
        bar();
    }
}

Это дает вам локальные переменные функции (только $foo в этом примере) и предотвращает создание любых переменных, у которых нет значения по умолчанию. Это делается для того, чтобы никто не мог случайно перезаписать другие параметры или другие переменные внутри функции.

person Walf    schedule 11.04.2014

Аргументы необходимо передавать по порядку, вы не можете пропустить параметр как таковой; вам нужно будет указать значение параметра по умолчанию, чтобы пропустить его. Возможно, это противоречит цели того, чего вы пытаетесь достичь.

Не переписывая свою функцию, чтобы принимать параметры по-другому, вот способ обойти это во время вызова:

$func = 'foo';
$args = ['lastName' => 'Smith'];

$ref = new ReflectionFunction($func);
$ref->invokeArgs(array_map(function (ReflectionParameter $param) use ($args) {
    if (array_key_exists($param->getName(), $args)) {
        return $args[$param->getName()];
    }
    if ($param->isOptional()) {
        return $param->getDefaultValue();
    }
    throw new InvalidArgumentException("{$param->getName()} is not optional");
}, $ref->getParameters()));

Другими словами, вы используете отражение для проверки параметров функции и сопоставления их с доступными параметрами по имени, пропуская необязательные параметры с их значением по умолчанию. Да, это некрасиво и громоздко. Вы можете использовать этот образец для создания такой функции, как:

call_func_with_args_by_name('foo', ['lastName' => 'Smith']);
person deceze♦    schedule 26.08.2015

Если это используется очень часто, просто определите новую специализированную функцию:

function person($firstName = 'john', $lastName = 'doe') {
    return $firstName . " " . $lastName;
}

function usualFirtNamedPerson($lastName = 'doe') {
    return person('john', $lastName);
}

print(usualFirtNamedPerson('smith')); --> john smith

Обратите внимание, что вы также можете изменить значение по умолчанию $lastname в процессе, если хотите.

Когда новая функция оценивается как раздутая, просто вызовите функцию со всеми параметрами. Если вы хотите сделать это более понятным, вы можете предварительно сохранить свои литералы в именованной переменной fin или использовать комментарии.

$firstName = 'Zeno';
$lastName = 'of Elea';
print(person($firstName, $lastName));
print(person(/* $firstName = */ 'Bertrand', /* $lastName = */ 'Russel'));

Хорошо, это не так коротко и элегантно, как person($lastName='Lennon'), но похоже, что вы не можете использовать его в PHP. И это не самый сексуальный способ закодировать это, с помощью супертрюка метапрограммирования или чего-то еще, но какое решение вы бы предпочли найти в процессе обслуживания?

person psychoslave    schedule 15.11.2016

К сожалению, то, что вы пытаетесь сделать, не имеет синтаксического сахара. Все они разные формы WTF.

Если вам нужна функция, которая принимает неопределенное количество произвольных параметров,

function foo () { 
     $args = func_get_args(); 
     # $args = arguments in order 
}

Будет делать свое дело. Старайтесь не использовать его слишком часто, потому что для Php это немного волшебно.

Затем вы можете при желании применить значения по умолчанию и делать странные вещи на основе количества параметров.

function foo() { 
   $args = func_get_args();
   if (count($args) < 1 ) { 
       return "John Smith"; 
   }
   if (count($args) < 2 ) { 
       return "John " . $args[0];
   }
   return $args[0] . " " . $args[1];
}

Кроме того, вы можете дополнительно эмулировать параметры стиля Perl,

function params_collect($arglist){ 
    $config = array();
    for ($i = 0; $i < count($arglist); $i+=2) { 
        $config[$i] = $config[$i+1];
    }
    return $config; 
}
function param_default($config, $param, $default){ 
    if (!isset($config[$param])) { 
        $config[$param] = $default;
    }
    return $config;
}

function foo() { 
   $args = func_get_args();
   $config = params_collect($args); 
   $config = param_default($config, 'firstname', 'John'); 
   $config = param_default($config, 'lastname', 'Smith'); 
   return $config['firstname'] . ' ' . $config['lastname'];   
}

foo('firstname', 'john', 'lastname', 'bob'); 
foo('lastname', 'bob', 'firstname', 'bob'); 
foo('firstname', 'smith'); 
foo('lastname', 'john');

Конечно, здесь было бы проще использовать аргумент массива, но у вас есть выбор (даже плохой способ) действий.

примечательно, что в Perl это лучше, потому что вы можете сделать просто foo( firstname =› 'john' );

person Kent Fredric    schedule 27.03.2009

Нет, но вы можете использовать массив:

function foo ($nameArray) {
    // Work out which values are missing?
    echo $nameArray['firstName'] . " " . $nameArray['lastName'];
}

foo(array('lastName'=>'smith'));
person Teifion    schedule 27.03.2009