Аргументы функции PHP - использовать массив или нет?

мне нравится создавать мои PHP-функции, используя пары key=>value (массивы) в качестве аргументов вместо отдельных параметров.

например, я предпочитаю:

function useless_func($params) {
    if (!isset($params['text'])) { $params['text'] = "default text"; }     
    if (!isset($params['text2'])) { $params['text2'] = "default text2"; }   
    if (!isset($params['text3'])) { $params['text3'] = "default text3"; }   
    echo $params['text'].$params['text2'].$params['text3'];
    return;
}

и мне не нравится:

function useless_func($text = "default text", $text2 = "default text2", $text3 = "default text3") {
        echo $text.$text2.$text3;
    return;
}

Я впервые увидел, как это делается в кодовой базе Wordpress.

причина, по которой я предпочитаю массивы:

  • аргументы функции могут быть представлены в любом порядке
  • легче читать код / более нагляден (по-моему)
  • меньше подвержен ошибкам, потому что при вызове функции я должен исследовать соответствующие ключи блока

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

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

9 ответов


Ну, это вроде полезно. Но для некоторых аргументов, которые проходят всегда, лучше использовать классический проход, как function some($a1, $a2). Я делаю это в своем коде:

function getSome(SomeClass $object, array $options = array())
{
    // $object is required to be an instance of SomeClass, and there's no need to get element by key, then check if it's an object and it's an instance of SomeClass

    // Set defaults for all passed options
    $options = array_merge(array(
        'property1' => 'default1',
        'property2' => 'default2',
        ... => ...
    ), $options); 
}

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


Не делай этого!

передача всего в массиве-плохая идея большую часть времени.

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

похоже, что наоборот инъекции в функции что это по необходимости.

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

у меня нет таких предпочтений. Я не понимаю этой необходимости.

легче читать код / больше самостоятельного документирования (на мой взгляд)

большинство IDEs представит вам различные аргументы, необходимые функции. Если вы видите объявление функции, например foo(Someclass $class, array $params, $id) очень ясно, что нужно функции. Я не согласен с тем, что один аргумент param проще читать и нагляден.

меньше подвержен ошибкам, потому что при вызове функции я должен исследовать соответствующие ключи блока

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


Если проблема, которую вы пытаетесь решить, является слишком большим количеством аргументов, правильным решением является рефакторинг ваших функций в меньшие, а не скрытие зависимостей функций за массивом.


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

Если вы единственный человек, который когда-либо будет работать над вашим кодом, то вы можете делать то, что вам нравится. Однако, передавая все значения аргументов через массив, кому-то еще придется больше работать, чтобы понять, что делает функция и почему / как они могут ее использовать, особенно если они используют IDE с автозаполнением для имен функций и т. д. Они не называют это" сигнатурой функции " просто так.

Я бы рекомендовал зарезервировать параметры массива либо для элементов, где вы не знаете, сколько их будет (например, ряд элементов данных), либо для групп связанных параметров / настроек (которые могут быть тем, что происходит в Примере Wordpress, который вы упомянули?).

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


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


границы культ карго программирования. Вы говорите, что это более читабельным и самодокументируемыми. Я бы спросил, как? Чтобы узнать, как использовать вашу функцию/метод, я должен прочитать сам код. Нет никакого способа узнать, как использовать его из самой подписи. Если вы используете любую полу-достойную IDE или редактор, который поддерживает подпись метода, намекая, что это будет настоящая Пита. Кроме того, вы не сможете использовать синтаксис намеков на тип PHP.

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


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

function useless_func(array $params = array())
{
    $params += array(
        'text' => 'default text',
        'text2' => 'default text2',
        'text3' => 'default text3',
    );
}

Читайте также: функция передачи массива в определенный ключ

несколько вещей, которые вы не получите при использовании массивов в качестве аргументов функции:

  1. проверка типа (применима только к объектам и массивам, но может быть полезна и в некоторых случаях ожидаемый.)
  2. интеллектуальные (er) текстовые редакторы имеют функцию code insight, которая покажет аргументы, которые понимает функция; использование массивов отнимает эту функцию, хотя вы можете добавить возможные ключи в функцию docblock.
  3. из-за #2 он на самом деле становится более подверженным ошибкам, потому что вы можете ввести ключ массива.

ваш сотрудник сумасшедший. Вполне допустимо передать массив в качестве аргумента функции. Он распространен во многих приложениях с открытым исходным кодом, включая Symfony и Doctrine. Я всегда следовал правилу 2 аргументов, если функции требуется более двух аргументов, или вы думаете, что в будущем она будет использовать более двух аргументов, используйте массив. IMO это обеспечивает максимальную гибкость и уменьшает любые дефекты вызывающего кода, которые могут возникнуть при передаче аргумента неправильно.

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

вот фрагмент кода, отображающий необходимые vs необязательные параметры, чтобы дать вам представление:

// Class will tokenize a string based on params
public static function tokenize(array $params)
{
    // Validate required elements
    if (!array_key_exists('value', $params)) {
        throw new Exception(sprintf('Invalid $value: %s', serialize($params)));
    }        

    // Localize optional elements
    $value            = $params['value'];
    $separator        = (array_key_exists('separator', $params)) ? $params['separator'] : '-';
    $urlEncode        = (array_key_exists('urlEncode', $params)) ? $params['urlEncode'] : false;
    $allowedChars     = (array_key_exists('allowedChars', $params)) ? $params['allowedChars'] : array();
    $charsToRemove    = (array_key_exists('charsToRemove', $params)) ? $params['charsToRemove'] : array();

....

@Mike, вы также можете "извлечь()" свой аргумент $params в локальные переменные, например:

// Class will tokenize a string based on params
public static function tokenize(array $params)
{
    extract($params);
    // Validate required elements
    if (!isset($value)) {
        throw new Exception(sprintf('Invalid $value: %s', serialize($params)));
    }

    // Localize optional elements
    $value         = isset($value) ? $value : '';
    $separator     = isset($separator) ? $separator] : '-';
    $urlEncode     = isset($urlEncode) ? $urlEncode : false;
    $allowedChars  = isset($allowedChars) ? $allowedChars : array();
    $charsToRemove = isset($charsToRemove) ? $charsToRemove : array();

....

та же реализация, но короче.


я использовал массивы для замены длинного списка параметров во многих случаях, и он работал хорошо. Я согласен с теми, кто в этом посте упоминал о редакторах кода, которые не могут предоставить подсказки для Аргументов. Проблема в том, что если у меня есть 10 аргументов, а первые 9 пустые/нулевые, он просто становится громоздким при вызове этой функции.

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

function ($a1, $a2, ... $a10){

        if($a1 == "Y"){$clause_1 = " something = ".$a1." AND ";}
        ...
        if($a10 == "Y"){$clause_10 = " something_else = ".$a10." AND ";}

        $sql = "
        SELECT * FROM some_table 
        WHERE
        ".$clause_1." 
        ....
        ".$clause_10." 
        some_column = 'N'
        ";

        return $sql;
    }

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