Объект для хранения и передачи разнородных данных с проверкой типов и значений

от автора

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

  1. При разработке (даже в продвинутых IDE (системах разработки)), и при выполнении приложения отсутствует какой-либо контроль за структурой и типами данных в массиве.
  2. IDE ничем не может помочь при разработке, так что названия возможных ключей массивов придется вспоминать-печатать или где-то искать-копировать, что кроме неудобства и снижения производительности повышает возможность опечатки.
  3. Сложно контролировать где-как используются отдельные элементы этого массива и соответственно сложно рефакторить, даже несмотря на мощь современных IDE.

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

Один из известных подходов который позволяет решить проблемы 2 и 3 — создание объектов на основе этаких псевдоклассов расширяющих stdClass. Описанные в PHPDoc свойства класса и указание везде где используется объект его класса (в PHPDoc) позволяет работать автокомплиту и проверке возможных имен свойств, контролировать места применения свойств и при необходимости легко их рефакторить:

/** * @property integer   $integer * @property integer[] $integers */ class DataStorage extend stdClass{ } $ds=new DataStorage; $ds->integer=13; $ds->integers=[1,2,3]; 

Но проблема с контролем значений так не решается, данные пришедшие извне IDE точно не проконтролирует, не говоря уж о том, что предупреждение в IDE кто-то может и проигнорировать, в результате неверные данные могут привести к неожиданной ошибке в неожиданном месте. Кроме того, насколько мне известно, PHPDoc пока что не позволяет указывать список возможных значений свойств, что часто бывает важно.

StrictDataStorage

Не найдя подходящего решения этих проблем разработал класс который позволяет выполнять проверку данных на основе PHPDoc, при этом как дополнительный бонус проверяет значения и по списку возможных. Работает класс аналогично решению приведенному выше с stdClass — создается класс отнаследованный от StrictDataStorage и у него в PHPDoc через @property описываются типы свойств. При присвоение значений свойствам производится проверка значений на соответствие типа описанному и по результату проверки или происходит присвоение, или срабатывает обработчик ошибки — по умолчанию выбрасывается исключение. При проверке поддерживает все типы данных PHPDoc кроме callable, перечисление нескольких возможных типов через | и массивы описанные через [] в конце типа.
Кроме элемента @property обрабатывается @enum и @options.

@enum

Позволяет указывать список возможных значений свойства. Исследование имеющейся в интернете информации привело к выводу, что никакого стандарта и даже просто соглашения по этому поводу на настоящий момент не существует, поэтому пока что придумал свой вариант не конфликтующий с @property. Синтаксис @enum похож на @property — сначала идет описание возможных значений, потом название свойства. Возможные значения указываются двумя способами

  1. Списком в квадратных скобках — содержимое скобок разбирается через json_decode(), поэтому должно соответствовать по формату, в первую очередь это означает использование двойных кавычек для обрамления строк.
  2. Названием класса в котором хранятся значения, этот класс должен поддерживать интерфейс EnumArrayableInterface, который лежит в том же репозитории, можно в принципе сделать и поддержку SplEnum.
@options

Содержит настройки работы класса через запятую, в настоящий момент поддерживается два значения:

  • PhpDocNotRequired — по умолчанию если свойство не описано элементом @enum или @property, то ему невозможно присвоить значение, наличие PhpDocNotRequired в @options включает менее строгий режим проверки — свойство м.б. присвоено даже если не описано. Этот режим позволяет описывать только те свойства для которых важна проверка значения.
  • StrictNumberTypeCheck — по умолчанию если свойство описано как integer или float, то строки содержащие числа также проходят проверку, как это применяется в PHP повсеместно, добавление StrictNumberTypeCheck в @options активирует более строгий режим проверки чисел — строки не пройдут!
Пример

Класс StrictDataStorage выложен здесь github.com/Yannn/php-strict-data, как он работает проиллюстрирую на примере приведенного там же наследника SampleDataStorage:

<?php /** * @property integer $integer * @property Closure $callback * @property mixed $mixed * * @property integer[] $integers * @enum SampleEnum[] $integers * * @property float|string $enumFloat * @enum ["1.8","7","9"] $enumFloat * * @property string[] $enumArray * @enum ["one","two"][] $enumArray * * @options PhpDocNotRequired|StrictNumberTypeCheck */ class SampleDataStorage extends StrictDataStorage { } 

Т.к. указано @options PhpDocNotRequired у объекта класса SampleDataStorage можно присвоить значение любому свойству, но при присвоении свойств $integer, $callback, $mixed, $integers, $enumFloat, $enumArray значения будут проверятся по описанным в @property и @enum правилам:

  • Значение $integer д.б. обязательно целым числом, если убрать из @options значение StrictNumberTypeCheck, то можно будет присвоить строку содержащую целое число.
  • Значение $callback д.б. обязательно замыканием, т.е. объектом типа Closure.
  • Значение $mixed м.б. любым — тип не проверяется.
  • Значение $integers д.б. массивом целых чисел, при том список возможных значений элементов массива будет получен методом getEnumValues() из SampleEnum.
  • Значение $enumFloat д.б. дробным числом или строкой, при том возможные значения перечислены в списке ["1.8","7","9"].
  • Значение $enumArray д.б. массивом строк, при том возможные значения элементов массива перечислены в списке ["one","two"].

Буду рад замечаниям, предложениям и конечно же багрепортам.

ссылка на оригинал статьи http://habrahabr.ru/post/234433/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *