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

предположим, у меня есть функция PHP foo:

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

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

пример:

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

12 ответов


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

  1. использовать массив в качестве единственного аргумента для функции. Затем вы можете извлечь значения из массива. Это позволяет использовать именованные аргументы в массив.
  2. Если вы хотите разрешить необязательное количество аргументов в зависимости от контекста, вы можете использовать func_num_args и func_get_args вместо указания допустимых параметров в определении функции. Затем, исходя из количества аргументов, длины строк и т. д. Вы можете определить, что делать.
  3. передайте значение null любому аргументу, который вы не хотите указывать. Не очень-то получается, но работает.
  4. Если вы работаете в контексте объекта, то вы можете использовать метод magic __call() для обработки этих типов запросов, чтобы вы могли маршрутизировать частные методы на основе того, какие аргументы были переданы.

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

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

вы могли бы немного рефакторинг кода:

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

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

function foo(array $params = array()) {
    echo $params['firstName'] . " " . $params['lastName'];
}

foo(array('lastName'=>'smith'));

конечно, в этом решении нет никакой проверки, что поля хэш-массива или правильно. Все зависит от тебя.


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

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


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

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

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

foo(null, 'smith');

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

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 в этом примере) и предотвращает создание любых переменных, которые не имеют значения по умолчанию. Это значит, что никто не может случайно перезаписать другие параметры или другие переменные внутри функции.


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

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

$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']);

Я хотел бы, чтобы это решение было включено, когда я начал использовать 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'));

результаты:

собака стояла на осел!--4-->

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

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


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

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. И это не самый сексуальный способ закодировать его, с супер-метапрограммированием или чем-то еще, но какое решение вы предпочли бы встретить в процессе обслуживания?


к сожалению, то, что вы пытаетесь сделать, не имеет "синтаксического сахара". Все они являются различными формами 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, потому что вы можете сделать, просто фу( имя => 'Джон' );


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

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

foo(array('lastName'=>'smith'));