Использование аргументов по умолчанию в функции

Я путаюсь о значениях по умолчанию для функций PHP. Скажем, у меня есть такая функция:

function foo($blah, $x = "some value", $y = "some other value") { // code here! } 

Что делать, если я хочу использовать аргумент по умолчанию для $ x и установить другой аргумент для $ y?

Я экспериментировал по-разному, и я просто становлюсь более смущенным. Например, я пробовал эти два:

 foo("blah", null, "test"); foo("blah", "", "test"); 

Но оба из них не приводят к правильному аргументу по умолчанию для $ x. Я также попытался установить его по имени переменной.

 foo("blah", $x, $y = "test"); 

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

Я бы предложил изменить объявление функции следующим образом, чтобы вы могли делать то, что хотите:

 function foo($blah, $x = null, $y = null) { if (null === $x) { $x = "some value"; } if (null === $y) { $y = "some other value"; } code here! } 

Таким образом, вы можете сделать вызов как foo('blah', null, 'non-default y value'); и пусть он работает так, как вам хочется, где второй параметр $x прежнему получает значение по умолчанию.

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

Как указано в других ответах,

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

Если у меня есть метод, который может принимать различное количество параметров и параметры разных типов, я часто объявляю функцию, подобную ответу, показанному Райаном П.

Вот еще один пример (это не отвечает на ваш вопрос, но, надеюсь, информативным:

 public function __construct($params = null) { if ($params instanceof SOMETHING) { // single parameter, of object type SOMETHING } else if (is_string($params)) { // single argument given as string } else if (is_array($params)) { // params could be an array of properties like array('x' => 'x1', 'y' => 'y1') } else if (func_num_args() == 3) { $args = func_get_args(); // 3 parameters passed } else if (func_num_args() == 5) { $args = func_get_args(); // 5 parameters passed } else { throw new InvalidArgumentException("Could not figure out parameters!"); } } 

Дополнительные аргументы работают только в конце вызова функции. Невозможно указать значение для $ y в вашей функции, не указав также $ x. Некоторые языки поддерживают это через именованные параметры (например, VB / C #), но не PHP.

Вы можете эмулировать это, если вы используете ассоциативный массив для параметров вместо аргументов, т. Е.

 function foo(array $args = array()) { $x = !isset($args['x']) ? 'default x value' : $args['x']; $y = !isset($args['y']) ? 'default y value' : $args['y']; ... } 

Затем вызовите функцию следующим образом:

 foo(array('y' => 'my value')); 
 function image(array $img) { $defaults = array( 'src' => 'cow.png', 'alt' => 'milk factory', 'height' => 100, 'width' => 50 ); $img = array_merge($defaults, $img); /* ... */ } 

Фактически это возможно:

 foo( 'blah', (new ReflectionFunction('foo'))->getParameters()[1]->getDefaultValue(), 'test'); 

Если бы вы хотели сделать это, это еще одна история 🙂


ОБНОВИТЬ:

Причины этого избежать:

  • это (возможно) уродливое
  • он имеет очевидные накладные расходы.
  • так как другие ответы доказывают, есть альтернативы

Но это может быть полезно в ситуациях, когда:

  • вы не хотите / не можете изменить исходную функцию.
  • вы можете изменить функцию, но:

    • использование null (или эквивалента) не является вариантом (см. комментарий DiegoDD)
    • вы не хотите идти ни с ассоциативным, ни с func_num_args()
    • ваша жизнь зависит от экономии нескольких LOC

Что касается производительности, очень простой тест показывает, что использование Reflection API для получения параметров по умолчанию делает вызов функции в 25 раз медленнее , в то время как он по-прежнему занимает менее одной микросекунды . Вы должны знать, можете ли вы жить с этим.

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

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

 function foo($blah, $y = "some other value", $x = "some value") 

Затем вы можете вызвать foo как:

 foo("blah", "test"); 

Это приведет к:

 $blah = "blah"; $y = "test"; $x = "some value"; 

Вы не можете сделать это напрямую, но небольшая кодовая игра позволяет эмулировать.

 function foo($blah, $x = false, $y = false) { if (!$x) $x = "some value"; if (!$y) $y = "some other value"; // code } 

Это случай, когда объект лучше – потому что вы можете настроить свой объект на наличие x и y, настроить значения по умолчанию и т. Д.

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

Cerainly вы всегда можете сделать некоторые трюки, чтобы установить нуль или что-то вроде этого по умолчанию

Вы также можете проверить, есть ли пустая строка в качестве аргумента, чтобы вы могли называть:

foo('blah', "", 'non-default y value', null);

Ниже функции:

 function foo($blah, $x = null, $y = null, $z = null) { if (null === $x || "" === $x) { $x = "some value"; } if (null === $y || "" === $y) { $y = "some other value"; } if (null === $z || "" === $z) { $z = "some other value"; } code here! } 

Неважно, если вы заполните null или "" , вы все равно получите тот же результат.