Подтвердить что ты не робот

Эмуляция названных параметров функции в PHP, хорошая или плохая идея?

Именованные функциональные параметры могут быть эмулированы в PHP, если я пишу такие функции, как это

function pythonic(array $kwargs)
{
    extract($kwargs);
    // .. rest of the function body
}

// if params are optional or default values are required
function pythonic(array $kwargs = array('name'=>'Jon skeet'))
{
    extract($kwargs);
    // .. rest of the function body
}

Помимо потери intellisense в IDE, каковы другие возможные недостатки этого подхода?

Edit:

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

4b9b3361

Ответ 1

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

function myFunc(array $args) {
    echo "Hi, " . $args['name'];
    // etc
}

Есть несколько причин для этого. Глядя на эту функцию, вы вполне можете ясно видеть, что я имею в виду один из аргументов, переданных в функцию. Если вы их извлечете и не заметите extract(), вы (или следующий парень) будете там, царапая голову, задаваясь вопросом, откуда взялась эта переменная "$name". Даже если вы знаете, что вы извлекаете аргументы для локальных переменных, это до некоторой степени догадывается.

Во-вторых, он гарантирует, что другой код не перезаписывает аргументы. Возможно, вы написали свою функцию, ожидая, что будут иметь только аргументы с именем $foo и $bar, поэтому в вашем другом коде вы, например, определяете $baz = 8;. Позже вы можете расширить свою функцию, чтобы принять новый параметр под названием "baz", но забудьте изменить другие переменные, поэтому независимо от того, что передается в аргументах, $baz всегда будет иметь значение 8.

Есть несколько преимуществ использования массива (они одинаково применимы к методам извлечения или ухода в массиве): вы можете настроить переменную в верхней части каждой функции под названием $defaults:

function myFunc (array $args) {
    $default = array(
        "name" => "John Doe",
        "age" => "30"
    );
    // overwrite all the defaults with the arguments
    $args = array_merge($defaults, $args);
    // you *could* extract($args) here if you want

    echo "Name: " . $args['name'] . ", Age: " . $args['age'];
}

myFunc(array("age" => 25)); // "Name: John Doe, Age: 25"

Вы даже можете удалить все элементы из $args, которые не имеют соответствующего значения $default. Таким образом, вы точно знаете, какие переменные у вас есть.

Ответ 2

Здесь вы можете сделать это другим способом.

/**
 * Constructor.
 * 
 * @named string 'algorithm'
 * @named string 'mode'
 * @named string 'key'
 */
public function __construct(array $parameter = array())
{
    $algorithm = 'tripledes';
    $mode = 'ecb';
    $key = null;
    extract($parameter, EXTR_IF_EXISTS);
    //...
}

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

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

Ответ 3

По моему опыту, этот подход действительно полезен, если одна из двух вещей истинна.

  • По каким-либо смягчающим причинам ваша подпись аргументов является большой. Я как бы прохожу до 6 как максимум - не по какой-то конкретной причине, хотя это кажется правильным, но я свободно признаю, что это число произвольно.
  • Все или многие из ваших аргументов являются необязательными, и иногда вам нужно только установить значение для пятого или какого-то такого. Досадно писать someFunc( null, null, null, null, 1 );

Если одно из них верно для вас, подделка с именем params с ассоциативным массивом может быть правильной реализацией. Помимо того, что, зная, когда не извлекать (или вообще избегать его), я не могу сразу думать о других недостатках.

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

Ответ 4

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

function someFunc($requiredArg, $arg1 = "default11", $arg2 = "default2") {

Чтобы моделировать это поведение при передаче всего в массиве, вам нужно будет написать больше кода, а "подпись функции" будет менее "понятной и очевидной".

function someFunc($requiredArg, $optionalArgs) {
    // see other answers for good ways to simulate "named parameters" here

Мне интересно, будет ли это хорошей идеей PHP для решения этой проблемы в будущей версии, возможно, для аргументов функции может быть что-то похожее на синтаксис Pascal или VB.

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

Ответ 5

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

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

Да и нет. Код, который вы написали, может (в зависимости от того, всегда ли вы инициализируете переменные после этого вызова) перезаписывает ваши вары. Пример:

function pythonic(array $kwargs = array('name'=>'Jon skeet'))
{
    $is_admin = check_if_is_admin();  // initialize some variable...

    extract($kwargs);

    // Q: what is the value of $is_admin now? 
    // A: Depends on how this function was called... 
    // hint: pythonic([ 'is_admin' => true ])
}

Что делает этот код "своего рода безопасным", так это то, что вы тот, кто его вызывает, поэтому пользователь не может предоставить произвольные параметры (если вы, конечно, не перенаправляете POST-вары;).

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