Використання аргументів за замовчуванням у функції


177

Мене бентежить щодо значень за замовчуванням для функцій 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");   

Я повністю очікував, що щось подібне спрацює. Але це не працює, як я взагалі очікував. Схоже, що б я не робив, мені все одно доведеться вводити аргументи за замовчуванням, кожного разу, коли я викликаю функцію. І я, мабуть, пропускаю щось очевидне.


68
Ви, мабуть, нічого не пропускаєте, розробники PHP, мабуть, пропустили це.
Матті Вірккунен

Це цікаве питання. Я ніколи насправді не мав цього робити, як ви заявляли, оскільки я використовую аргументи за замовчуванням лише для розширення функцій, успадкованих іншими програмістами, коли я не впевнений, що залежить від оригіналу. Мені цікаво відповісти.
Кай Цін

3
Одне з найкращих питань, яке я бачив за деякий час! Ви нічого не пробували? foo("blah", , "test");?
Привид

2
Правило полягає в тому, що аргументи за замовчуванням можуть наслідувати лише аргументи. Тобто у вас може бути foo ("blah") і x, y за замовчуванням або foo ("Blah", "xyz"), а y - за замовчуванням.
Мишача їжа

1
Очікувана поведінка. Див. Розділ "Значення аргументів за замовчуванням" у: php.net/manual/en/functions.arguments.php - нульове значення та порожня рядок приймаються як фактичні аргументи функції
jmdavalos

Відповіді:


164

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

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

Чому не $ x = набір ($ x)? $ x: 'значення за замовчуванням'; ?
zloctb

@zloctb - це чиста і читаемая перевірка нуля. Схоже, це налаштування розробника. Чи $ x = набір ($ x)? $ x: 'значення за замовчуванням'; або $ x = is_null ($ x)? 'значення за замовчуванням': $ x; буде працювати так само добре, як це рішення. Це вибір переваги щодо читабельності та обслуговування.
DeveloperWeeks

9
Просто для уточнення для майбутніх читачів. Перше рішення очевидно унеможливлює присвоєння nullпараметру реального значення, оскільки кожного разу воно призначатиме значення за замовчуванням. Навіть у випадку, якщо у вас було інше спеціальне значення для того, коли ви насправді хочете нуль (наприклад, коли $ x == "use_null" складає $ x = null), ви не зможете призначити таке спеціальне значення, як буквальне значення параметр (у цьому випадку рядок "use_null"). Тому просто не забудьте використовувати унікальний, ніколи не бажаний "ключ", коли ви хочете використовувати значення за замовчуванням (і ви хочете nullбути дійсним варіантом)
DiegoDD

37

Необов’язкові аргументи працюють лише в кінці виклику функції. Неможливо вказати значення для $ 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'));

1
Ваша умова має бути справді такою isset($args['x']), оскільки вона наразі замінить порожній рядок, порожній масив або falseзначення за замовчуванням.
Тім Купер

@TimCooper lol Я пішов змінити, виходячи з вашого першого коментаря, що це має бути "=== null", пішов і змінив свою відповідь на використання isset, а потім повернувся, щоб побачити, що ви теж змінили свій коментар. : D
Ryan P

Ах, драт! Мені це зовсім не подобається ... Ну добре, ти не можеш мати все. Я вважаю, що мені доведеться трохи більше подумати над порядком моїх аргументів. Дякую!
прогноз

На жаль, ви не зможете призначити нуль за допомогою цієї відповіді. Це кращий варіант: $x = !array_key_exists('x',$args) ? 'default x value' : $args['x'];
Франк Форте

20

Насправді це можливо:

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

Чи хотіли б ви це зробити - це інша історія :)


ОНОВЛЕННЯ:

Причини уникати цього рішення:

  • це (певно) потворно
  • у неї очевидні накладні витрати.
  • як інші відповіді на підтвердження, існують альтернативи

Але насправді це може бути корисно в ситуаціях, коли:

  • ви не хочете / не можете змінити початкову функцію.

  • Ви можете змінити функцію, але:

    • використання null(або еквівалент) не є варіантом (див. коментар DiegoDD )
    • ви не хочете йти ні з асоціативом, ні з func_num_args()
    • ваше життя залежить від збереження пари МІС

Щодо продуктивності, дуже простий тест показує, що використання API відбиття для отримання параметрів за замовчуванням робить виклик функції в 25 разів повільнішим , в той час як вона все ще займає менше однієї мікросекунди . Ви повинні знати, чи можете ви з цим жити.

Звичайно, якщо ви маєте намір використовувати його в циклі, попередньо слід отримати значення за замовчуванням.


1
Яка причина того, що цього не хочуть робити? Здається, досить зручно.
Брайан

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

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

2
Я щось пропускаю? Схоже, це зовсім не відповідає на питання, але в ньому є 7 підсумків. Можливо, додавання трохи пояснень допоможе?
Шон Бін

3
@SeantheBean: правда, пояснення було б краще, але його суть є: оскільки PHP не має аргументів, використовуйте асоціативний масив як його єдиний параметр.
MestreLion

1
Більш корисно використовувати приклад, наведений у питанні, наприклад$defaults = [ 'x' => 'some value', 'y' => 'some other value'];
Frank Forte

4

Єдиний спосіб, коли я це знаю, - це опустити параметр. Єдиний спосіб опустити параметр - це переставити список параметрів таким чином, щоб той, який ви хочете опустити, був після тих параметрів, які ВИ ХОТИ встановити. Наприклад:

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

Тоді ви можете зателефонувати foo на зразок:

foo("blah", "test");

Це призведе до:

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

3

Нещодавно у мене виникла ця проблема і я знайшов це питання та відповіді. Хоча вищезазначені питання працюють, проблема полягає в тому, що вони не показують значення за замовчуванням для IDE, які його підтримують (наприклад, PHPStorm).

введіть тут опис зображення

якщо ви користуєтеся, nullви не знатимете, яке значення буде, якщо ви залишите його порожнім.

Я вважаю за краще, щоб значення визначення за замовчуванням було вказано також у визначенні функції:

protected function baseItemQuery(BoolQuery $boolQuery, $limit=1000, $sort = [], $offset = 0, $remove_dead=true)
{
    if ($limit===null) $limit =1000;
    if ($sort===null) $sort = [];
    if ($offset===null) $offset = 0;
    ...

Єдина відмінність полягає в тому, що мені потрібно переконатися, що вони однакові - але я думаю, що це мала ціна, щоб заплатити за додаткову ясність.


Внутрішнє використання ReflectionFunction на собі врятувало б ціну!
ТемаДельта

2

Ви не можете це зробити безпосередньо, але невелике зміцнення коду дозволяє наслідувати.

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

  // code
}

6
Це би спрацювало, але я вважаю, що використання nullв цьому випадку концептуально акуратніше, ніж false.
TRiG

Додавання до коментаря TriG false- це більш ризикований вибір, ніж null, оскільки php - це не сувора мова. !$xбуде вірно для декількох різних входів, що може здивувати програміста: 0, ''. Тоді як з null, ви можете використовувати !issetяк суворіший тест.
ToolmakerSteve

1
<?php
function info($name="George",$age=18) {
echo "$name is $age years old.<br>";
}
info();     // prints default values(number of values = 2)
info("Nick");   // changes first default argument from George to Nick
info("Mark",17);    // changes both default arguments' values

?>

1

Мої 2 копійки з нульовим оператором злиття ?? (з PHP 7)

function foo($blah, $x = null, $y = null) {
    $varX = $x ?? 'Default value X';
    $varY = $y ?? 'Default value Y';
    // ...
}

Ви можете переглянути більше прикладів на моїй repl.it


0

Це випадок, коли об'єкт кращий - тому що ви можете налаштувати свій об'єкт для розміщення x і y, налаштування за замовчуванням тощо.

Підхід з масивом знаходиться поруч із створенням об'єкта (насправді, об'єкт - це купа параметрів і функцій, які працюватимуть над об'єктом, а функція, що приймає масив, буде працювати над деякими параметрами групи ov)

Cerainly ви завжди можете зробити деякі хитрощі, щоб встановити нуль або щось подібне як за замовчуванням


0

Ви також можете перевірити, чи є у вас аргумент порожній рядок, щоб ви могли дзвонити як:

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або "", ви все одно отримаєте той же результат.


0

Передайте масив функції замість окремих параметрів та використовуйте оператор з’єднання нуля (PHP 7+).

Нижче я передаю масив з 2-ма елементами. Всередині функції я перевіряю, чи встановлено значення для item1, чи не присвоєний оберіг за замовчуванням.

$args = ['item2' => 'item2',
        'item3' => 'value3'];

    function function_name ($args) {
        isset($args['item1']) ? $args['item1'] : 'default value';
    }

Ви можете використовувати $args['item1'] = $args['item1']??'default value'; в PHP 7+. якщо $ args ['item1'] є null, то default valueрядок буде призначений $ args ['item1']
Sadee
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.