3 июня 2010 в 01:22

Передача аргументов функции по имени в PHP

PHP*
При рефакторинге кода возникла одна небольшая идея относительно вызова методов. Иногда возникает потребность передать функции аргумент по имени. Например тогда, когда невозможно (или неудобно) передать список в нужном порядке. Такими случаями могут быть вызовы динамических блоков из шаблонизаторов: в шаблоне у нас {{mymodule action=foo second=124322 fourth='catalog' first='name' third='foo'}} и в коде есть следуйщая синатура функции — function foo($first, $second, $third, $fourth). подобный подход используеться в системе Magento для вызова блоков из лей-аутов; или нужно передать методу даные на основе фильтра в каком-нибуть асоциативном масиве. В PHP4 возможным решением было помещение всего списка аргументов в масив. В PHP версии 5 есть же Reflection API, с помощью которого возможно проделать подобное. Perl, Python (, ...) могут, так почему ж ето должно быть невозможно в PHP?:)

UPD: код на pastebin затерся, вот метод-хелпер для етого (вызов вида $object->__named('methodNameHere', array('arg3' => 'three', 'arg1' => 'one')))
  /**
   * Pass method arguments by name
   *
   * @param string $method
   * @param array $args
   * @return mixed
   */
  public function __named($method, array $args = array())
  {
    $reflection = new ReflectionMethod($this, $method);

    $pass = array();
    foreach($reflection->getParameters() as $param)
    {
      /* @var $param ReflectionParameter */
      if(isset($args[$param->getName()]))
      {
        $pass[] = $args[$param->getName()];
      }
      else
      {
        $pass[] = $param->getDefaultValue();
      }
    }

    return $reflection->invokeArgs($this, $pass);
  }


* This source code was highlighted with Source Code Highlighter.
Сергей Смертин @nfx
карма
6,9
рейтинг 0,0
Самое читаемое Разработка

Комментарии (18)

  • 0
    имхо такой же костыль как и

    function foo(array $args)
    {
      extract($args);
    }


    только extract() проще
    • –1
      да, но если надо вызвать в такой способ уже существующий код?
      • 0
        прав. да. я использовал ReflectionAPI для получения public функций кучи классов и это довольно медленная процедура.

        а рефакторить нельзя в вашем случае так, чтобы получить именно массив на вход, а не отдельно каждый параметр?
        • 0
          в одних местах — возможно, в конкретно моем случае — нет.
          еще одно распостраненное использование — движок лей-аутов в етом нисколько не ведомом продукте: для подключения внешних css и js файлов, та и для других целей в описании шаблона там используеться именно метод такого типа.

          а для какой цели вам нужно было получать список public методов? get_class_methods() не подходил? или использовать php-tokenizer для разбора и обернуть его кешированием в файл типа <?php return array( ... );?
          • 0
            get_class_methods() даёт так же публичные методы родительских классов, а мне про них совсем не надо знать. tokenizer не пробовал, да и на выходе он давал бы слишком много лишних данных.
            • 0
              tokenizer скорее для анализа и генерации кода больше подходит. даже для генерации SOAP WSDL приходилось его как-то использовать.
      • 0
        call_user_func_array()
        список аргументов предварительно отлично сортируется и фильтруется по маске.
        • –1
          в своем примере я показал как сделать ето только с использованием reflection. до написания тестов на производительность пока не доходили руки. вечером может как-то добавлю. вопросов с производительностю на момент написания не возникало. ето как раз тот случай, где она неважна.
          • 0
            Я в курсе как это сделать с рефлексией :)
            Но для меня в варианте рефлексии многовато писать лишнего кода.

            А с массивами как-то проще. array_intersec_key, array_multisort, call_user_func_array и вуаля.
            Минус только в том, что таки необходимо иметь массив-маску хотя бы вида array(param_name1 => null, ...., param_name2 => null)
            • –1
              а здесь — не надо :)
  • 0
    Хорошая идея. Также перечень аргументов в сигнатуре лучше переносится в phpDoc
  • +1
    Это нововведение уже обсуждается в @internals для будущих версий PHP. С помощью Reflection можно получить нужный результат, но оправдано ли такое удобство с точки зрения производительности?
    • –1
      о, спасибо, за введение в @internals не знал.
      а оправдано ли? все относительно: если таких вызовов немного — очень удобно и расширяемо, если их очень много (вродь как в Magento) — за производительность все прекрасно знаем (правда еще не вникал какой оверхед именно от етой возможности там). тоесть ету возможность не стоит пихать повсеместно, а только там, где даст много удобства в разработке и других полезных вещей.
    • 0
      wtf @internals?
  • 0
    добавил код в топик
  • –1
    Давно (года 3 точно) использую массивы в качестве параметров в функциях.
    Описываю их так
    function __name ($a = null) {}

    тогда вызов без параметров не даст ошибку.

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