1. PHP / Говнокод #4256

    +161

    1. 1
    2. 2
    3. 3
    4. 4
    5. 5
    6. 6
    7. 7
    8. 8
    $id = $fInt->filter($this->_getParam('id'));
            $sort = $fInt->filter($this->_getParam('sort'));
            $c_name = $this->mbtrim($fStr->filter($this->_getParam('c_name')));
            $c_shortname = $this->mbtrim($fStr->filter($this->_getParam('c_shortname')));
            $email = $this->mbtrim($fStr->filter($this->_getParam('email')));
            $d_firstname = $this->mbtrim($fStr->filter($this->_getParam('d_firstname')));
            $d_lastname = $this->mbtrim($fStr->filter($this->_getParam('d_lastname')));
    ...

    И так около 20 строк. Какие есть хорошие варианты фильтровать множество разнотипных данных?

    Запостил: asfalanaft, 15 Сентября 2010

    Комментарии (29) RSS

    • Если это ZF, то почему бы не сделать это формой и намутить к ней фильтров?
      Ответить
      • Данные из extJS приходят
        Ответить
        • Все равно я сделал бы валидацию формой.
          Ответить
          • Подскажите хороший пример связи extJS и Zend_Form
            Ответить
            • Хорошего примера не знаю, но я сделал бы так. Наследнику Zend_Form добавил бы все элементы с такими же именами, как приходят от extJS. На каждое поле повесил бы нужный Zend_Filter. Если нужного фильтра нет — они пишутся очень просто. И этим наследником делается только фильтрация и валидация, но не отображение формы. Как-то так.

              Да, и фильтрация выглядела бы, как с обычной формой:
              if( $form->isValid( $this->_getParams() ) ) {
                  $sort = $form->getValue( 'sort' );
                  // ... 
              }
              Ответить
              • Хм, можно попробовать. Как я понимаю, вы предлагаете реализовать что-то подобное тому, что описано здесь: http://framework.zend.com/manual/ru/zend.form.advanced.html ? Но если имена полей и валидаторы к ним будут жестко "зашиты" в коде, чем этот способ принципиально лучше?
                Ответить
                • Как минимум тем, что если еще где-то придется обрабатывать такой же запрос от extJS, то придется продублировать только пару строк, а не 20. Еще тем, что код контроллера будет чище.
                  Ответить
          • Вы доверяете данным, которые отправил пользователь? Ну, не знаю. Может я параноик, но я всегда считаю, что все пользователи хотят как-нибудь да обмануть мою программу. :D То есть, даже если бы была валидация формой, я бы продублировал её в обработчике. Даже для безопасных данных.
            Ответить
    • ну хотя бы вынести повторяющийся код в функции (для int-ов, строк и т.д.), чтобы не было такой копипасты
      Ответить
      • Какой именно? Вызов функции внутри функции? Мало что изменится.
        Ответить
        • Минусующие, поясните тогда, что имеете ввиду
          Ответить
          • ну смотри (синтаксис примерный, ибо пхп не знаю):
            function parseInt ($param) {
               return $fInt->filter($this->_getParam($param));
            }
            function parseString ($param) {
               return $this->mbtrim($fStr->filter($this->_getParam($param)));
            }
            
            # use
            $id = parseInt('id');
            $sort = parseInt('sort');
            $c_name = parseString('c_name');
            $c_shortname = parseString('c_shortname');
            $email = parseString('email');
            $d_firstname = parseString('d_firstname');
            $d_lastname = parseString('d_lastname');

            Короче и нагляднее же! нет?
            Ответить
            • Не спорю, это я и так сделал. Я думал, что вы знаете способ сократить эти бесчисленные парсинги.
              Ответить
    • Массивы + циклы
      foreach($params as $param)
          {
          $fields[$param] = $this->mbtrim($fStr->filter($this->_getParam($param)));
          }

      ?
      Ответить
      • Попробуйте читать описание под кодом - данные РАЗНОТИПОВЫЕ
        Ответить
        • Насколько я помню, PHP позволяет хранить разнотипные данные в хеше. Или Вы не об этом?
          Ответить
          • Я о том, что по имени поля, которое пришло в запросе к серверу, я не знаю, КАКОЙ тип данных в этом поле. Предлагаете завести справочник и заюзать паттерн "стратегия"?
            Ответить
            • $int_params = array('id', 'sort');
              $str_params = array('c_name', 'email');
              foreach ($int_params as $p)
                  $fields[$p] = $fInt->filter($this->_getParam($p));
              foreach ($str_params as $p)
                  $fields[$p] = $this->mbtrim($fStr->filter($this->_getParam($p)));
              Ответить
              • У меня сейчас и так этот вариант, только строк в массивах побольше и убрана двойная обертка фильтрации, слита в одну функцию. Код все равно пованивает в этом варианте...
                Ответить
              • еще можно вместо двух массивов один хеш с ключом "имя" и значением "тип", а потом тернарный оператор или даже свичкейс какой-нить. Хотя, может, это и избыточно, смотря что вам нужно
                Ответить
    • define ( S, 's' );
      define ( I, 'i' );
      
      class UniFilter 
        {  
          private $lthis;
          private $fInt;
          private $fStr;
          
          function __construct ( $lthis, $int_filter, $str_filter )
            {
              if ( ! is_object ( $lthis ) || ! is_object ( $int_filter ) || ! is_object ( $str_filter ) )
                {
                  throw new Exception ( 'Bad object' );
                }  
      
              $this->lthis = $lthis;
              $this->fInt = $int_filter;
              $this->fStr = $str_filter;                
            }
          
          function unifilter ( )
            {
              $argc = func_num_args ( );
              if ( $argc % 2 )
                {
                  throw new Exception ( 'Wrong arg count' );
                }
              $args = func_get_args ( );
              
              $lthis = $this->lthis;
              $fInt  = $this->fInt;
              $fStr  = $this->fStr;
               
              $oarr = array ( );
              for ( $cnt = 0; $cnt < $argc; $cnt += 2 )
                {
                  $value = $lthis->_getParam ( $args [ $cnt ] );
                  switch ( $args [ $cnt + 1 ] )
                    {
                      case S :
                        {
                          $oarr [ ] = $lthis->mbtrim ( $fStr->filter ( $value ) );
                          break;
                        }     
                      case I :
                        {
                          $oarr [ ] = $fInt->filter ( $value );
                          break;
                        }
                      default :
                        {
                          throw new Exception ( 'Unknown type ' . $args [ $cnt + 1 ] );
                        }
                    }
                }
              return $oarr;
            }
      };
      $GetFilter = new Unifilter ( $obj, $fInt, $fStr );
      list ( $id, $name, $intval ) = $GetFilter->unifilter ( 'id', I, 'username', S, 'foobar', I );

      Неоптимально, расширябельно, ООП.
      Ответить
      • Спасибо, этот способ, как и множество советов выше, в очередной раз подтвердили мои мысли - загвоздку с разными типами никак не обойти. Эх, жаль.
        Ответить
        • >загвоздку с разными типами никак не обойти

          какая загвоздка, вот λPω в хаскелле - это загвоздка, ага, а тут просто динакомикоговно.
          Ответить
          • Вы такой крутой, наверное, еще и ногти не подстригаете.
            Ответить
        • На самом деле, нет никакой загвоздки. Нам просто нужно фильтровать определённый тип. Например, фильтрование типа int более жёсткое (только цифры, знак и точка-разделитель), нежели чем строки. Или наоборот. Соответственно, человеку надо указать тип для конкретной переменной.
          В моём варианте я постарался разнести всё на части. Все типы определяются в одном месте, рядом с данными. Обработка идёт в другом. Ввести новый тип - не проблема. $this с собой таскать не надо, он в объекте.
          Главный плюс, как мне кажется - аргументов у функции может быть хоть сорок, но следить за ними не столь сложно.
          Если такое решение кажется слишком запутанным при использовании, можно продумать автоматизацию. Например, явно указать тип в названии переменной. Типа: i_int, s_nickname, i_somevalue. Обработка упрощается, тип жёстко привязан, нет необходимости хранить отдельно.
          Можно пойти ещё дальше. Например, если параметр является допустимым числом, то к нему применяется только обработка на числа. Иначе - на строки. Впрочем, я придерживаюсь мнения, что входные переменные, откуда бы они не пришли, должны жёстко иметь заданный программистом тип.
          Неудобно, конечно, что типы динамические и чаще всего привязаны к строковому. От пользователя из $_GET, насколько я помню, приходят только строки.
          Просто так сразу и из головы писать код глупо. Код с копипастой неэффективен. За счёт процедур можно объединять куски в блоки, дабы не было копипасты. Объекты разделяют данные меж своими методами. Но основой основ является инженерная идея (как бы ни звучало смешно это касательно php). Без понимания того, что и как будет работать, какие данные и как мы хотим увидеть - толку не будет.
          Ответить
          • Поверьте, я понимаю эти концепции и даже, более того, рассматривал вариант решения, аналогичный вашему. Соль в том, что, как вы совершенно верно заметили, "Неудобно, конечно, что типы динамические и чаще всего привязаны к строковому. От пользователя из $_GET, насколько я помню, приходят только строки."
            Ответить

    Добавить комментарий