Как стать автором
Обновить

Типобезопасная работа с массивами PHP, часть 2

Время на прочтение5 мин
Количество просмотров4K

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

На днях расширил функционал парой методов, и хочу поделиться с вами этими новостями.

И конечно я напишу о работе над ошибками.

Для тех кто не знал и забыл, что такое 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.

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

Post scriptum

Добавил поддержку JsonSerializable, теперь получить JSON стало проще:

$data = new ArrayHandler(
    [
        'first' => ['A' => 1],
        'next' => ['B'=>2],
        'last' => ['C'=>3],
        4=>[5,6],
        7,
        8,
        9
    ]);

echo json_encode($data);

Вывод:

#raw
{"first":{"A":1},"next":{"B":2},"last":{"C":3},"4":[5,6],"5":7,"6":8,"7":9}

#formatted
{
  "first": {
    "A": 1
  },
  "next": {
    "B": 2
  },
  "last": {
    "C": 3
  },
  "4": [
    5,
    6
  ],
  "5": 7,
  "6": 8,
  "7": 9
}

Добавил поддержку ArrayAccess (специально для любителей квадратных скобочек):

echo 'Use array access to all '.PHP_EOL;
foreach ($data as $key => $value) {
    $element = $data[$key];
    $type = gettype($element);
    echo "[$key] => value type is $type";
    if($type!== 'array'){
        echo ", element value is $element";
    }
        echo PHP_EOL;
}

Вывод:

Use array access to all
[first] => value type is array
[next] => value type is array
[last] => value type is array
[4] => value type is array
[5] => value type is integer, element value is 7
[6] => value type is integer, element value is 8
[7] => value type is integer, element value is 9

Теги:
Хабы:
+11
Комментарии5

Публикации

Истории

Работа

PHP программист
157 вакансий

Ближайшие события

Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн