[recovery mode] Типобезопасная работа с массивами PHP, часть 2
Всем привет, прошёл почти год с публикации первой части. Обсуждение в комментариях было жарким, выводы я для себя сделал, изменения в либу внёс почти сразу, но написать об этом времени не нашлось.
На днях расширил функционал парой методов, и хочу поделиться с вами этими новостями.
И конечно я напишу о работе над ошибками.
Для тех кто не знал и забыл, что такое ArrayHandler
Spoiler
Ответим на вопрос: «что такое типобезопасная работа с массивами в PHP?»
Типобезопасная это:
Когда мы можем получить элемент массива без опасности получить эксепшен о не существующем индексе;
Когда мы можем передать полученный элемент в метод и мы точно не получим эксепшен несоответствия типов;
И последние в списке, но не последнее по значению, типобезопасный код — это когда нам приятно писать хороший код. Для меня это наверное даже самое важное, потому что я устал от бесконечных:
$a = 0;
if (key_exists($key, $collection))
{
$a = (int) $collection[$key];
}
И даже когда я узнал о современном способе писать через:
$a = (int) $collection[$key] ?? 0;
Мне от этого не полегчало, потому что вложенность у массивов бывает «мама не горюй».
Кроме этих трёх преимуществ, ArrayHandler даёт иммутабельность, то есть мы можем спокойно пробрасывать наш ArrayHandler, через слои абстракций и ни кто случайно не поменяет элементы внутри исходного массива. Для того что бы поменять значение, надо создать новый экземпляр ArrayHandler — и такое пропустить на код ревью сложнее, чем пропустить запись нового значения в элемент массива.
Не буду копипастить примеры работы с либой, их можно посмотреть в первой части или можно почитать документацию.
Либа устанавливается через Композер:
composer require sbwerewolf/language-specific
Есть версии для PHP 5.6 / 7.0 / 7.2 .
Это было долгое вступление теперь по существу.
Обновления
Пару дней назад мне было и грустно и скучно, захотелось сделать что ни будь хорошее, например сделать так что бы, когда пробегаешь по элементам с помощью foreach (), можно было не только получить элемент (ValueHandler), но индекс этого элемента.
Я с энтузиазмом принялся за работу и уже написав тонны кода, наткнулся на комент в документации к PHP, который сделал весь новый код бесполезным.
Оказывается можно делать так:
yield $key => $value;
И в foreach () будет возвращён индекс элемента. Эврика!
Теперь IArrayHandler: pulling () возвращает и новый IArrayHandler от элемента массива, и индекс этого элемента. Я был счастлив, кажется теперь ArrayHandler стал идеальной библиотекой для работы с массивами (в том виде как я обозначил в начале статьи).
На эмоциях я запилил ещё два метода. То есть ещё один метод — IArrayHandler: getting (), плюс я добавил поддержку интерфейса Iterator и теперь экземпляр ArrayHandler можно использовать в foreach () как обычный массив.
Теперь IArrayHandler: pulling () возвращает ArrayHandler для каждого вложенного массива (будут проигнорированы элементы исходного массива, которые не являются массивами). Название метода «pulling» образовалось от названия другого метода — IArrayHandler: pull (), с помощью которого можно получить экземпляр ArrayHandler от элемента массива.
IArrayHandler: getting () возвращает IValueHandler для всех элементов массива, не являющимися массивами. Название метода «getting» образовалось от названия другого метода — IArrayHandler: get (), с помощью которого можно получить экземпляр IValueHandler от элемента массива.
Теперь у нас IArrayHandler: pulling () для массивов, и IArrayHandler: getting () для всех остальных типов.
Более наглядно:
$data = new ArrayHandler(
[
'first' => ['A' => 1],
'next' => ['B'=>2],
'last' => ['C'=>3],
4=>[5,6],
7,
8,
9
]);
echo 'arrays'.PHP_EOL;
foreach ($data->pulling() as $key => $value) {
echo "[$key] => class is ".get_class($value).' '.PHP_EOL;
}
echo 'values'.PHP_EOL;
foreach ($data->getting() as $key => $value) {
echo "[$key] => {$value->asIs()} , class is ".get_class($value).' '.PHP_EOL;
}
Вывод скрипта:
arrays
[first] => class is LanguageSpecific\ArrayHandler
[next] => class is LanguageSpecific\ArrayHandler
[last] => class is LanguageSpecific\ArrayHandler
[4] => class is LanguageSpecific\ArrayHandler
values
[5] => 7 , class is LanguageSpecific\ValueHandler
[6] => 8 , class is LanguageSpecific\ValueHandler
[7] => 9 , class is LanguageSpecific\ValueHandler
Если нам надо пробежаться по всем элементам исходного массива, мы используем обычный foreach ():
$data = new ArrayHandler(
[
'first' => ['A' => 1],
'next' => ['B'=>2],
'last' => ['C'=>3],
4=>[5,6],
7,
8,
9
]);
echo 'ALL'.PHP_EOL;
foreach ($data as $key => $value) {
$type = gettype($value->asIs());
echo "[$key] => value type is $type , class is ".get_class($value).PHP_EOL;
}
Вывод скрипта:
ALL
[first] => value type is array , class is LanguageSpecific\ValueHandler
[next] => value type is array , class is LanguageSpecific\ValueHandler
[last] => value type is array , class is LanguageSpecific\ValueHandler
[4] => value type is array , class is LanguageSpecific\ValueHandler
[5] => value type is integer , class is LanguageSpecific\ValueHandler
[6] => value type is integer , class is LanguageSpecific\ValueHandler
[7] => value type is integer , class is LanguageSpecific\ValueHandler
Соответственно, если мы какие то элементы хотим обработать как элементы, а какие то как массивы, то мы в foreach (), делаем так:
foreach ($data as $key => $value) {
/* @var \LanguageSpecific\ValueHandler $value */
if($value->type() === 'array'){
$handler = new ArrayHandler($value->array());
/* some code */
}
}
Работа над ошибками
Метод IValueHandler: default () перестал быть статическим, его опасность до меня пытался донести @GreedyIvan, до меня дошло через неделю, спасибо.
Метод ArrayHandler: simplify () был удалён, потому что на самом деле
Зачем ArrayHandler→simplify (), когда есть array_column? © @olegmar
Cпасибо @olegmar.
Метод IArrayHandler: next () был заменён на IArrayHandler: pulling (), этот метод перебирает все вложенные массивы (первый уровень вложенности). Не то что бы комент от @Hett меня прямо убедил, но к размышлениям подтолкнул.
Спасибо @ReDev1L за поддержку в коментах.
Был добавлен метод IArrayHandler: raw (), что бы можно было получить исходный массив. Раньше, когда не было возможности получить индекс элемента, приходилось перебирать исходный массив, сейчас по опыту использования, бывает необходимость добавить/убавить элементы массива и создать из изменённого массива новый ArrayHandler.
На этом всё. Спасибо за чтение.