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


96

Хорошо, я совершенно забыл, как пропускать аргументы в PHP.

Допустим, у меня есть:

function getData($name, $limit = '50', $page = '1') {
    ...
}

Как мне вызвать эту функцию, чтобы средний параметр принимал значение по умолчанию (например, «50»)?

getData('some name', '', '23');

Правильно ли сказанное выше? Кажется, я не могу заставить это работать.


1
@ Чак, что именно ты ищешь? Как обходной путь или класс для реализации этого или ...?
Rizier123

@ Rizier123 Я прошу посмотреть, поддерживает ли текущая версия PHP пропуск аргументов (как это делают другие языки). Текущие ответы могут быть устаревшими. Из описания награды: «Текущим ответам пять лет. Изменила ли текущая версия PHP положение вещей?»
Чак Ле Батт

1
@Chuck Тогда ответ, вероятно, будет таким: ничего не изменилось; без обходного пути / кода вы не получите свою функциональность.
Rizier123

Все ответы здесь предполагают, что вы контролируете вызываемую функцию. Если эта функция является частью библиотеки или фреймворка, у вас нет другого выбора, кроме как указать что-то для аргумента 2, если вам нужен аргумент 3. Единственный вариант - поискать функцию в источнике и воспроизвести значение по умолчанию.
Snapey

Пропустить его невозможно, но можно передать аргумент по умолчанию, используя ReflectionFunction. Я отправил этот ответ в похожем вопросе.
Дэвид

Ответы:


56

Ваш пост правильный.

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


1
Вы можете привести пример?
я это я

2
@iamme сделать $limit = is_numeric($limit) ? $limit : '50';в начале GetData функции
Kamafeather

41

Нет другого способа «пропустить» аргумент, кроме как указать значение по умолчанию, например falseили null.

Поскольку в PHP отсутствует некоторый синтаксический сахар, вы часто будете видеть что-то вроде этого:

checkbox_field(array(
    'name' => 'some name',
    ....
));

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

Это дает максимальную гибкость, но в некоторых случаях может не понадобиться. По крайней мере, вы можете переместить все, что, по вашему мнению, в большинстве случаев не ожидается, в конец списка аргументов.


2
Я бы определил «что-то подобное» как «использование массивов для имитации именованных аргументов», FWIW. :)
хаос

3
Несмотря на большую гибкость, вы должны запомнить параметры, которые вы должны / можете установить (поскольку их нет в подписи). Так что, в конце концов, это может быть не так полезно, как кажется, но, конечно, это зависит от контекста.
Феликс Клинг

Итак, каковы были бы лучшие практики для такого сценария?
Адам Грант

39

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

Для этого было официальное предложение: https://wiki.php.net/rfc/skipparams , но оно было отклонено. На странице предложения есть ссылки на другие вопросы SO по этой теме.


Тогда как функции PHP могут иметь необязательные параметры больше / меньше?
я это я

2
PHP поддерживает значения параметров по умолчанию. Но эти параметры должны быть в конце списка.
Cristik

1
Вы можете найти примеры в документации PHP
Cristik

2
Рывки. Меня так злит. Если вам не нравится функция, которую запрашивает кто-то другой, просто не используйте ее. Интернет иногда бывает хуже всего.
Ли Саксон

@LeeSaxon проблема в удобочитаемости и переносимости. 1) Если кто-то решает пропустить параметры, а кто-то при отладке кода упускает этот факт, возникает большая путаница. 2) новый код не может работать на старых версиях без серьезной переделки, и вероятность ошибки будет очень высока.
Марк Уолш,

6

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

define('DEFAULT_DATA_LIMIT', '50');
define('DEFAULT_DATA_PAGE', '1');

/**
 * getData
 * get a page of data 
 *
 * Parameters:
 *     name - (required) the name of data to obtain
 *     limit - (optional) send NULL to get the default limit: 50
 *     page - (optional) send NULL to get the default page: 1
 * Returns:
 *     a page of data as an array
 */

function getData($name, $limit = NULL, $page = NULL) {
    $limit = ($limit===NULL) ? DEFAULT_DATA_LIMIT : $limit;
    $page = ($page===NULL) ? DEFAULT_DATA_PAGE : $page;
    ...
}

Это может быть вызвано следующим образом: getData('some name',NULL,'23');и любому, кто будет вызывать функцию в будущем, не нужно каждый раз запоминать значения по умолчанию или константу, объявленную для них.


1
Возможно, вам понадобится строгое сравнение ($ limit === null)
roelleor

4

Простой ответ - нет . Но зачем пропускать, когда перестановка аргументов достигает этого?

Ваше сообщение - « Неправильное использование аргументов функции по умолчанию » и не будет работать так, как вы ожидаете.

Примечание из документации PHP:

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

Обратите внимание на следующее:

function getData($name, $limit = '50', $page = '1') {
    return "Select * FROM books WHERE name = $name AND page = $page limit $limit";
}

echo getData('some name', '', '23');   // won't work as expected

Результат будет:

"Select * FROM books WHERE name = some name AND page = 23 limit"

Правильное использование аргументов функции по умолчанию должно быть таким:

function getData($name, $page = '1', $limit = '50') {
    return "Select * FROM books WHERE name = $name AND page = $page limit $limit";
}

echo getData('some name', '23');  // works as expected

Результат будет:

"Select * FROM books WHERE name = some name AND page = 23 limit 50"

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

Изменить: установка его, nullкак предлагают другие, может работать и является другой альтернативой, но может не соответствовать тому, что вы хотите. Он всегда будет устанавливать значение по умолчанию на null, если он не определен.


Это лучший ответ, поскольку он дает четко иллюстрированные синтаксические объяснения! Спасибо!
Christian

2

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

(Установка на null, если параметр по умолчанию - '' или аналогичный или наоборот, вызовет у вас проблемы ...)


2

Как уже упоминалось выше, вы не сможете пропустить параметры. Я написал этот ответ, чтобы добавить добавление, которое было слишком большим, чтобы помещать его в комментарий.

@Frank Nocke предлагает вызвать функцию с параметрами по умолчанию, например, имея

function a($b=0, $c=NULL, $d=''){ //...

ты должен использовать

$var = a(0, NULL, 'ddd'); 

который функционально будет таким же, как если бы не были первые два ( $bи $c) параметра.

Неясно, какие из них являются значениями по умолчанию ( 0набирается для предоставления значения по умолчанию или это важно?).

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

Некоторым обходным решением может быть определение некоторых глобальных констант, например, DEFAULT_A_Bкоторые будут «значением по умолчанию для параметра B функции A» и «опустить» параметры следующим образом:

$var = a(DEFAULT_A_B, DEFAULT_A_C, 'ddd');

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

class MyObjectClass {
  const DEFAULT_A_B = 0;

  function a($b = self::DEFAULT_A_B){
    // method body
  }
} 
$obj = new MyObjectClass();
$var = $obj->a(MyObjectClass::DEFAULT_A_B); //etc.

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

Ясность этого решения, конечно, лучше, чем предоставление исходных значений по умолчанию (например NULL, 0и т.д.), которые ничего не говорят читателю.

(Согласен, что позвонить в лайк $var = a(,,'ddd');будет лучшим вариантом)


2

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

function myfunction($array_param)
{
    echo $array_param['name'];
    echo $array_param['age'];
    .............
}

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

myfunction(array("name" => "Bob","age" => "18", .........));

0

Как уже говорили все, то, чего вы хотите, будет невозможно в PHP без добавления каких-либо строк кода в функцию.

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

foreach((new ReflectionFunction(debug_backtrace()[0]["function"]))->getParameters() as $param) {
    if(empty(${$param->getName()}) && $param->isOptional())
        ${$param->getName()} = $param->getDefaultValue();
}

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

В цикле я просто проверяю, является ли аргумент функции empty()И аргумент «необязательным» (означает, что он имеет значение по умолчанию). Если да, я просто присваиваю аргументу значение по умолчанию.

Demo


0

Установите ограничение на ноль

function getData($name, $limit = null, $page = '1') {
    ...
}

и вызовите эту функцию

getData('some name', null, '23');

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

getData('some name', 50, '23');

0

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

Возможно, ваша функция слишком много работает:

// first build context
$dataFetcher->setPage(1);
// $dataFetcher->setPageSize(50); // not used here
// then do the job
$dataFetcher->getData('some name');

Некоторые параметры можно сгруппировать логически:

$pagination = new Pagination(1 /*, 50*/);
getData('some name', $pagination);
// Java coders will probably be familiar with this form:
getData('some name', new Pagination(1));

В крайнем случае, вы всегда можете ввести специальный объект параметра :

$param = new GetDataParameter();
$param->setPage(1);
// $param->setPageSize(50); // not used here
getData($param);

(который является просто прославленной версией менее формальной техники массива параметров )

Иногда сама причина сделать параметр необязательным - неверна. В этом примере $pageдействительно подразумевается, что это необязательно? Действительно ли сохранение пары персонажей имеет значение?

// dubious
// it is not obvious at first sight that a parameterless call to "getData()"
// returns only one page of data
function getData($page = 1);

// this makes more sense
function log($message, $timestamp = null /* current time by default */);

0

Этот фрагмент:

    function getData ($ name, $ options) {
       $ по умолчанию = массив (
            'limit' => 50,
            'page' => 2,
        );
        $ args = array_merge ($ по умолчанию, $ параметры);
        print_r ($ args);
    }

    getData ('foo', array ());
    getData ('foo', array ('limit' => 2));
    getData ('foo', array ('limit' => 10, 'page' => 10));

Ответ:

     Массив
    (
        [лимит] => 50
        [page] => 2
    )
    Массив
    (
        [лимит] => 2
        [page] => 2
    )
    Массив
    (
        [лимит] => 10
        [page] => 10
    )


2
Ницца. Но небольшое объяснение было бы полезно.
showdev

Хороший ответ, но если вы хотите опустить необязательный параметр, сделайте это так: function getData ($ name, $ args = array ()) {$ defaults = array ('limit': => 50, 'page' => 2) ; $ args = array_merge ($ по умолчанию, $ args); } Теперь эту функцию можно вызывать только с первым параметром, например: getData ('foo');
EchoSin

0

Вот что бы я сделал:

<?php

    function getData($name, $limit = '', $page = '1') {
            $limit = (EMPTY($limit)) ? 50 : $limit;
            $output = "name=$name&limit=$limit&page=$page";
            return $output;
    }

     echo getData('table');

    /* output name=table&limit=50&page=1 */

     echo getData('table',20);

    /* name=table&limit=20&page=1 */

    echo getData('table','',5);

    /* output name=table&limit=50&page=5 */

    function getData2($name, $limit = NULL, $page = '1') {
            $limit = (ISSET($limit)) ? $limit : 50;
            $output = "name=$name&limit=$limit&page=$page";
            return $output;
    }

    echo getData2('table');

    // /* output name=table&limit=50&page=1 */

    echo getData2('table',20);

    /* output name=table&limit=20&page=1 */

    echo getData2('table',NULL,3);

    /* output name=table&limit=50&page=3 */

?>

Надеюсь, это кому-то поможет


0

Это своего рода старый вопрос с рядом технически грамотных ответов, но он требует одного из современных шаблонов проектирования в PHP: объектно-ориентированного программирования. Вместо того, чтобы внедрять набор примитивных скалярных типов данных, рассмотрите возможность использования «внедренного объекта», который содержит все данные, необходимые функции. http://php.net/manual/en/language.types.intro.php

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

Мы можем напечатать введенный объект, чтобы выявить ошибки перед развертыванием. Некоторые идеи суммированы в этой статье, появившейся несколько лет назад.

https://www.experts-exchange.com/articles/18409/Using-Named-Parameters-in-PHP-Function-Calls.html


0

Мне пришлось создать Factory с необязательными параметрами, мой обходной путь заключался в использовании оператора объединения null:

public static function make(
    string $first_name = null,
    string $last_name = null,
    string $email = null,
    string $subject = null,
    string $message = null
) {
    $first_name = $first_name ?? 'First';
    $last_name  = $last_name ?? 'Last';
    $email      = $email ?? 'foo@bar.com';
    $subject    = $subject ?? 'Some subject';
    $message    = $message ?? 'Some message';
}

Применение:

$factory1 = Factory::make('First Name Override');
$factory2 = Factory::make(null, 'Last Name Override');
$factory3 = Factory::make(null, null, null, null 'Message Override');

Не самая красивая вещь, но может быть хорошим шаблоном для тестов на фабриках.



-1

Вы не можете пропустить средний параметр в вызове функции. Но вы можете обойти это:

function_call('1', '2', '3'); // Pass with parameter.
function_call('1', null, '3'); // Pass without parameter.

Функция:

function function_call($a, $b='50', $c){
    if(isset($b)){
        echo $b;
    }
    else{
        echo '50';
    }
}

Я не уверен, почему у вас есть голоса против, кроме $ c, также требующего значения по умолчанию (у вас не может быть никаких обязательных аргументов после необязательного аргумента).
Фрэнк Форте

-2

Как отметил @IbrahimLawal. Лучше всего просто установить для них nullзначения. Просто проверьте, соответствует ли переданное значение nullзаданным вами значениям по умолчанию.

<?php
define('DEFAULT_LIMIT', 50);
define('DEFAULT_PAGE', 1);

function getData($name, $limit = null, $page = null) {
    $limit = is_null($limit) ? DEFAULT_LIMIT : $limit;
    $page = is_null($page) ? DEFAULT_PAGE : $page;
    ...
}
?>

Надеюсь это поможет.


Вы просто продублировали ответ. Скорее, вы могли бы проголосовать за него или прокомментировать его.
Ибрагим Лаваль

-6
getData('some name');

просто не передавайте их и значение по умолчанию будет принято

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.