Передача аргументов функции по имени в 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.
    Поделиться публикацией
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 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) {}

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

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