Pull to refresh

Comments 29

Однажды в рамках создания очередного очень важного компонента веб-сервиса нам понадобилось проверять уйму очень разных входных параметров (в данном случае, пришедших через $_REQUEST)

Что серьезно разбираете $_REQUEST?
$_GET/$_POST — ok.

Использование $_REQUEST ограничивает портативность кода, т.к. полученные скриптом данные будут зависеть от настроек request_order/variables_order.
В рамках статьи $_REQUEST означает — любые данные извне. Это не стоит внимания.
Сделал нечто похожее. Но все значения, которые требуют проверки, помещаются в экземпляр класса «опасных значений» — Values. При этом никаких модификаций со значениям не происходят, они просто изолируются таким образом, что получить их можно будет только с указанием правила на них. Поэтому, важно было разработать универсальный способ описания правил. В правиле указывается тип значения, требуемая величина, структура, обязательность существования с возможностью определять условия на элементы массива, ну и с возможностью добавлять свои фильтры. Подробнее написано в статье про правила
Мне нравится у вас использование __callStatic(). Это избавляет от начинания с runMatch(). Возможно, применю у себя такой подход.
Хотя мне нужно на вход так или иначе принять массив и флаги. AMatch::doc_id('', '!array', $params, $flags) — пожалуй не лучший подход. Всё-таки метод-конструктор тут более нагляден.
Да, так как правило и фильтр у вас вместе
Я бы с удовольствием избавился от stopMatch или, хотя бы, привёл их к коротким формам. Но резервировать горячие имена в общем пространстве — неблагодарное дело. Приходится терпеть лишние буквы :)
Необходимо продумать расширяемость методов проверки, тогда однозначно откажетесь от switch() и выносите проверку во внешние структуры (классы), и, может, не станет общего пространства. В обще, сложно судить, как лучше сделать, если хотите сохранить текущий принцип использования проверки. Я изначально всё упростил, потом колдовал с магическими методами)
Я про пространство имён самих входящих ключей. Например AMatch::run()->stop() нельзя сделать, т.к. имя «stop» может быть ключём валидируемого массива.
Вынести проверки во внешние классы — это возможное направление развития. Но всё-таки каждый такой новый шаг всё более и более усугубляет скорость и использование памяти. Мне тут больше нравится принцип callback для ситуаций, которые не попадают в типовые. А если типовых калбеков становится больше чем нужно — вносить их в основной код.
Были такие же мыли по поводу валидации, но ограничился только сопоставлением типов и вхождением ключа в массив. Думаю, AMatch мне пригодится. Спасибо большое! Очень продуманный инструмент, желаю процветания и побольше полезных коммитов :)
Спасибо. Было бы неплохо услышать отзыв про опытную эксплуатацию.
>> я не силён в английском
все же переведите комментарии и пример на английский. так на гитхабе делают многие неаглоязычные пользователи.
Возможно, когда появится немного времени и желания поупражняться в знаниях языка.
Рассматривали ли Вы Zend_Validate?

У Вас валидаторы реализованы методами класса, а там классами, как результат расширяемость библиотеки гораздо лучше там.

Было бы круто использовать замыкания вместо call_user_func, и принимать контекст функции вторым параметром, тогда появляется гораздо больше свободы в формировании логики валидации, нежели «from_topic(specialValidation(), true)»

Zend_Validate не использовал. Посмотрел сейчас примеры — всё-таки несколько другая основная идея там. В AMatch ключевая цель — ускорить написание типовых проверки, сократить запись для удобства восприятия. В случае Zend_Validate для реализации описанных примеров понадобится целое полотно кода на каждый параметр. Там как раз целевое назначиние — возможность написания различных «модулей» валидации с общим стилем.
По-поводу:
> использовать замыкания вместо call_user_func
— это вполне годная идея, и я не против её реализации. Но тут нужно сначала продумать возможные варианты. Я сначала для себя пишу пример желаемого использования, потом думаю — как его удобно можно сделать. Напишите, как вы видите синтаксически использование замыканий для внешних модулей?
> гораздо больше свободы в формировании логики валидации, нежели «from_topic(specialValidation(), true)»

Я тут подумал ещё насчёт этой фразы. Все-таки, использование замыканий удобнее в виде отдельной возможности, а ->key(true, true) или key->(false, false) может пригодиться само по себе, т.к. позволяет применить в валидации сторонний код, не адаптированный под этот стиль. Например, ->get_profile(is_admin(), true) — по сути, вызов глобальной функции is_admin() в данном случае либо разрешит, либо запретит получение профиля пользователя в зависимости от типа авторизации.
->subject_id(0, '!=') // Не равен нулю
->subject_id('', '!float') // Не float
->data('', 'array') // массив
->data('', '!empty') // не пустой

Йода-код. Прям как
if (42 != $your_value) {}


Ну в вашем примере ещё есть смысл так писать код, часто спасает от опечаток
Пример несколько лишен смысла, конечно же. Т.к. цель — показать разные возможности, а не решить практическую задачу.
По-поводу размещения условий «справа налево» — это особенность реализации. Увы, первый параметр более важный: ->key(42) означает «ключ существует и его значение равно 42». Если первым параметром принимать условие: ->key('array') — то уже не получится сделать короткие записи. Так что да, Йода-код, но позволяет значительно сократить текст.
существует такое понятие Pattern Matching, в PHP такое устроить сложнее, конечно, нужно ифки воротить или свичи, но вполне реализуемо. Можно будет и короткие записи делать по значению и короткие записи по условию и полные записи
На каждое условие «if» по бизнес-процессу полагается писать юнит-тест с различными граничными значениями. Представьте, сколько строк теста понадобится только на этот код.


AMatch не избавит от необходимости писать тесты. Кстати, тут очень выручают параметризованные тесты
Конечно не избавит. Но облегчит, т.к. сам AMatch уже тестами покрыт. Если развить идею и всё-таки вынести обработчики сопоставлений в отдельный класс и регистрировать их каким-то удобным быстрым способом — несколько стандартных протестированных классов сильно упростят жизнь. Думаю сделать все-таки возврат кодов ошибок вместо текста для упрощения локализаций. Тогда пишется маппинг, подгружаются свои тексты, один раз тестируются — и в остальных случаях в тестах достаточно будет проверить через dataProvider граничные значения сразу скопом. Это удобно.
Извините, а вы предлагаете в коде писать if ( $somevariable == self::SOME_CONSTANT ) return true,
а в тесте писать assertTrue( $obj->someFunction( self::SOME_CONSTANT ) )?
Такого я не предлагал. Я имел ввиду, что все равно негативные сценарии в тестах должны быть отражены, а параметризованные тесты в этом очень хорошо помогают.
Sign up to leave a comment.

Articles