Хабрахабр

[recovery mode] Типобезопасная работа с массивами PHP

Всем привет, расскажу о собственном велосипеде для удобной работы с массивами в PHP.

Type hinting

В PHP7 появились подсказки типов (type hinting), что позволило IDE проводить более качественный статический анализ кода, качество нашего кода улучшилось (или правильно говорит "стало более лучше"? ).

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

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

Но кроме приятных возможностей type hinting накладывает и обязанности, то есть типы переменных действительно должны быть такими как указано в сигнатуре метода.

Если не проверять типы, то можно получить ошибки в методах и конструкторах (особенно радуют ошибки в конструкторах).

Писать проверки вручную утомительно, я решил это дело автоматизировать, но не через проверку, а через приведение к нужному типу.
В моей работе достаточно часто приходиться писать с нуля, обычно это или прототипы, или парсеры, или ETL для нового источника данных, по сути тоже парсер.

«Любимый» Bitrix не умеет возвращать данные иначе как в массиве. Работаешь конечно с массивами (например когда читаешь из *.csv), работать с базой можно через ORM, но для моих задач это слишком громоздко, мне удобно работать с базой через PDO, которое отдаёт тебе данные опять же в массивах.

Поэтому я написал обёртку для работы с массивами. Как ни крути приходиться извлекать данных из массивов.

Что бы не копипастить код из проекта в проект я оформил пакет для Composer:

composer require sbwerewolf/language-specific

ValueHandler

Первоё моё требование было — всегда знать значение какого типа я получу. Перед этим значение конечно надо бы ещё получить, наверное по индексу, так мы пришли к тому что нам нужен метод get().

И теперь нужны методы для приведения типа, типов в PHP не много, получились такие методы:

  1. int()
  2. str()
  3. bool()
  4. double()

Иногда попадаются массивы, поэтому пусть будет и для массивов:
Иногда надо просто получить элемент как он есть:
Иногда элемента с заданным индексом может не быть и тогда надо использовать значение по умолчанию:

ArrayHandler

Следующим требованием было получить возможность упростить массив из одного значения до ровно этого значения.

Покажу на примере из документации:

$connection = new PDO ($dsn,$login,$password); $command = $connection->prepare('select name from employee where salary > 10000');
$command->execute();
$data = $command->fetchAll(PDO::FETCH_ASSOC);
/*
$data = array ( 0 => array ( 'name' => 'Mike', ), 1 => array ( 'name' => 'Tom', ), 2 => array ( 'name' => 'Jerry', ), 3 => array ( 'name' => 'Mary', ) );
*/
$names = new ArrayHandler($data);
$result = $names->simplify(); echo var_export($result,true);
/*
LanguageSpecific\ArrayHandler::__set_state(array( '_data' => array ( 0 => 'Mike', 1 => 'Tom', 2 => 'Jerry', 3 => 'Mary', ),
))
*/

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

$response[] = $element[0];

, но мне так не нравится, пусть это происходит автоматически, так появился метод simplify().

Ну и раз уж у нас есть обёртка над массивом, то добавим метод для проверки наличия индекса — has(), если захочется пробежаться по элементам массива, то поможет метод next().

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

Выглядит это так:

$address = new ArrayHandler($item)->pull('metaDataProperty')->pull('GeocoderMetaData')->pull('Address')->asIs();

Можно конечно и так писать:

$address = $item['GeoObject']['metaDataProperty']['GeocoderMetaData']['Address'];

, но у меня в глазах рябит от количества квадратных скобок, мне удобней через pull().

Общие рассуждения

Когда код подключается из Композера это очень удобно, кроме того что вы избавляетесь от необходимости копипастить, вы одной командой получаете свою библиотеку и она всегда под рукой.

Перед тем как делать свой пакет я посмотрел аналоги и ни чего подобного не нашёл, есть несколько проектов, которые просто обёртка над array, и в этих проектах просто оборачивают многие методы для работы с массивами, а типобезопасности ни где нет.

Видимо написать (int) или (bool) перед именем переменной всем просто и удобно и ни кто не видит смысла заморачиваться с отдельным репозиторием под это дело.

Возможности библиотеки чуть шире описанных в статье и больше информации можно получить в документации (README.md).

2, отличается только тем что в сигнатуре у метода object() появляется тип возвращаемого значения — object. PHP5 ещё не редкость, поэтому у библиотеки есть отдельная версия для PHP5, отличается от версии для PHP7 названием нескольких методов и конечно весь type hinting только в коментах.
Есть версия библиотеки для PHP7.

Код полностью покрыт тестами, но в принципе так и ломать не чему 🙂

Пользуйтесь на здоровь!

Ещё один пример использования

foreach ($featureMember as $item) { $pointInfo = extract($item); $info = new ArrayHandler($pointInfo); $address = $info->get('formatted')->default('Челябинск')->str(); $longitude = $info->get('longitude')->default(61.402554)->double(); $latitude = $info->get('latitude')->default(55.159897)->double(); $undefined = !$info->get('formatted')->has(); $properties = ['longitude' => $longitude, 'latitude' => $latitude, 'address ' => $address ,'undefined'=>$undefined,]; $result = json_encode($properties); output($result);
}

Смотреть во время отладки на JSON в котором числа это числа, логические значения — логические, мне намного приятней чем только на строки.

image

А вам как?

Теги
Показать больше

Похожие статьи

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

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

Кнопка «Наверх»
Закрыть