Эмуляция параметров именованных функций в PHP, хорошая или плохая идея? - PullRequest
20 голосов
/ 25 марта 2009

Параметры именованных функций можно эмулировать в 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:

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

Ответы [ 5 ]

35 голосов
/ 25 марта 2009

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

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. Таким образом, вы точно знаете, какие переменные у вас есть.

8 голосов
/ 25 марта 2009

Вот еще один способ сделать это.

/**
 * 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 делает его безопасным, просто извлекая ключи массива, которые уже существуют как переменные.

(Между прочим, создание значений по умолчанию из предоставленного вами примера не годится, потому что, если массив параметров представлен без индекса 'name', ваше значение по умолчанию будет потеряно.)

6 голосов
/ 25 марта 2009

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

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

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

При этом часто обе эти проблемы можно решить и с помощью рефакторинга.

2 голосов
/ 04 ноября 2011

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

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.

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

1 голос
/ 06 октября 2014

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

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

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

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

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