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

Я путаюсь о значениях по умолчанию для функций 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");   

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

Ответ 1

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

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!");
    }
}

Ответ 2

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

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

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

Ответ 3

function image(array $img)
{
    $defaults = array(
        'src'    => 'cow.png',
        'alt'    => 'milk factory',
        'height' => 100,
        'width'  => 50
    );

    $img = array_merge($defaults, $img);
    /* ... */
}

Ответ 4

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

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

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


UPDATE:

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

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

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

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

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

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

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

Ответ 5

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

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

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

foo("blah", "test");

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

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

Ответ 6

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

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

  // code
}

Ответ 7

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

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

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

Ответ 8

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

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 или "", вы все равно получите тот же результат.