Типобезопасная работа с массивами 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? (c) @olegmar

Cпасибо @olegmar.

Метод IArrayHandler::next() был заменён на IArrayHandler::pulling(), этот метод перебирает все вложенные массивы (первый уровень вложенности). Не то что бы комент от @Hett меня прямо убедил, но к размышлениям подтолкнул.

Спасибо @ReDev1L за поддержку в коментах.

Был добавлен метод IArrayHandler::raw(), что бы можно было получить исходный массив. Раньше, когда не было возможности получить индекс элемента, приходилось перебирать исходный массив, сейчас по опыту использования, бывает необходимость добавить/убавить элементы массива и создать из изменённого массива новый ArrayHandler.

На этом всё. Спасибо за чтение.

Источник: https://habr.com/ru/post/522348/


Интересные статьи

Интересные статьи

Предлагаю ознакомиться с ранее размещенными материалами по проекту Starlink (SL): Часть 1. Рождение проекта ‣ Часть 2. Сеть SL ‣ Часть 3. Наземный комплекс ‣ Часть 4. Абонентский те...
Часть 1 ‣ Часть2 ‣ Часть 3 ‣ Часть 4 ‣ Часть 5 ‣ Часть 6 ‣ Часть 7 Монтаж и включение абонентского терминала Несмотря на известнейший твит Илона Маска про Plug and Play («включил и ...
Позиции историков при оценке деятельности КГБ делятся на те что за и те что против. Собранные материалы и факты от анонимных источников или из воспоминаний ветеранов спецслужб можно поддать сомне...
Оптимизирующие компиляторы — основа современного ПО: они позволяют программистам писать код на понятном для них языке, затем преобразуя его в код, который сможет эффективно исполняться оборудов...
Этой статьей мы начинаем серию публикаций о неуловимых малвари. Программы для взлома, не оставляющие следов атаки, известные также как fileless («бестелесные», невидимые, безфайловые), как пр...