Всем привет! Расскажу о большом обновлении в пакете sbwerewolf/language-specific
.
Для тех, кто не знаком с этим пакетом, коротко опишу его назначение.
Допустим, вы по API получили JSON с большой вложенностью, и вам нужно достать из JSON какое-то значение, которое зарыто поглубже. Что делать ? конечно преобразовать JSON строку в ассоциативный массив, а дальше что делать ? Конечно прописать все индексы до искомого элемента, получится что то такое:
$formatted = (string)$response["response"]["GeoObjectCollection"]["featureMember"]
[0]["GeoObject"]["metaDataProperty"]["GeocoderMetaData"]["Address"]["formatted"]
?? "Адрес не найден";
Получилась строка кода длиной 200 символов. Не очень удобное такое читать. Самое неприятное, что элемента с индексом 0
может и не быть, то есть сначала надо проверить, что он есть, а потом уже читать дальше, и каждый раз придётся приписывать весь это хвост из индексов, поэтому код будет загромождён индексами, работать с таким кодом не удобно.
Пакет sbwerewolf/language-specific
позволяет избавить код от сплошного перечисления индексов, замороченных выражений, скобочек и операторов ??
.
С ним можно будет написать так:
$data = (new AdvancedArrayFactory())->makeAdvancedArray($response);
$formatted = $data
->pull("response")
->pull("GeoObjectCollection")
->pull("featureMember")
->pull()
->pull("GeoObject")
->pull("metaDataProperty")
->pull("GeocoderMetaData")
->pull("Address")
->get("formatted")
->default("Адрес не найден")
->str();
// 1, Mohammed Bin Rashid Boulevard, Downtown Dubai, Dubai, United Arab Emirates
И никаких забот о том, что по пути какого-то элемента может не быть, или элемент "formatted"
может внезапно оказаться с типом bool вместо string.
Назначение пакета sbwerewolf/language-specific
Суть пакета в том, чтобы безопасно, без ошибок пройти по массиву до нужного элемента, и получить значение этого элемента с нужным типом, без бойлерплейта.
Что значит безопасно?
Вместо:
$value = $response["GeocoderMetaData"]["Address"]["formatted"] ?? 'Адрес не найден';
Пишем:
$data = (new AdvancedArrayFactory())->makeAdvancedArray($response);
$value = $data->pull("GeocoderMetaData")->pull("Address")->get("formatted");
$value->default("Адрес не найден");
Чем это лучше ? Это лучше тем, что стена текста из перечисления индексов разбиваеться на отдельные индексы, которые визуально чётко разделены, такой код легче читать.
Что значит с нужным типом, без бойлерплейта?
Это значит, что нам не надо писать код для преобразования типа на случай если тип элемента отличается от ожидаемого.
Вместо:
$formatted = (string)$value;
Пишем:
$formatted = $value->str();
В целом, разница будет такой: было две строчки кода:
$formatted = (string)$response["GeocoderMetaData"]["Address"]["formatted"]
?? 'Адрес не найден';
стало семь строчек:
$data = (new AdvancedArrayFactory())->makeAdvancedArray($response);
$formatted = $data
->pull("GeocoderMetaData")
->pull("Address")
->get("formatted")
->default("Адрес не найден")
->str();
Код получился более человекочитаемый, менее машиночитаемый (теперь IDE не понимает, что мы идём по индексам массива, и теперь IDE не может предсказать структуру массива).
Строчек кода стало больше, понятность выше. Вместо компактности получили понятность. По-моему стало лучше.
Многословный, но понятный код, намного лучше компактного кода, который работает не пойми как, пока его не запустишь, да можно ограничиться запуском у себя в голове, но зачем лишний раз напрягать мозг ? Это отвлекает внимание от основной задачи, не надо так делать, берегите своё "мыслетопливо"!
Предыстория
Я несколько лет назад опубликовал на Packagist composer-пакет sbwerewolf/language-specific
, и написал на Хабре два статьи об этом пакете (статья1, статья2).
С августа 2023 года пакет начали активно скачивать, в среднем за месяц 500 установок, на январь 2025 года уже 8 000 установок.
Альтернатива
В декабре 2024 на Хабре была опубликована статья "PHP Typed" про библиотеку которая помогает получить значения глубоко вложенных элементов PHP-массивов или глубоко вложенных свойств PHP-классов (stdClass). Это ровно то что делает мой пакет sbwerewolf/language-specific
.
Конечно мне было интересно сравнить эти два решения. Я написал бенчмарк, вывод: Typed
на простых случаях в два раза быстрей, в сложных, наверное будет больше, не проверял.
Я заглянул под капот в Typed
, посмотрел что у меня написано в ArrayHandler
.
Разница между Typed
и ArrayHandler
в том, что Typed
только выдаёт значение элемента в занном типе. ArrayHandler
не только выдаёт значение в заданном типе, но и позволяет принять решение в ситуации когда значения нет, или поверить фактический тип элемента, и в зависимости от этого провести дальнейшую обработку. Например если элемент это bool
со значением false
, то ни чего не делаем, а если элемент это array
, то проводим дальнейшую обработку всех элементов.
Typed
это расширение базового набора PHP для работы с массивами.
ArrayHandler
это работа с массивом в ООП стиле, ArrayHandler
объединяет в себе данные и методы для работы с ними.
Если вы уверены в структуре массива с которым работает - используйте Typed
.
Если структура массива имеет большую вариантивность и в зависмости от варианта должна быть обработана по разному - используйте ArrayHandler
.
Доработки
Когда я сравнивал Typed
и ArrayHandler
, я увидел что у ArrayHandler
покрытие тестами хромает: покрытие кода упало до 76%, потому что после добавления поддержки интерфейсов ArrayAccess, Iterator, JsonSerializable, код методов добавился, а код тестов - нет.
Я решил это дело исправить, хотел добавить пару простых тестов, понял что ArrayHandler
перегружен функционалом, поделил его на несколько классов, переименовал методы и пошло поехало. Добавил интерфейсы, добавил фабрики. Добавил пакеты для статического анализа, короче в Новый Год развлекался на полную.
Как установить
composer require sbwerewolf/language-specific
Как пользоваться
Собственно основной use-case был показан выше. Допустим нам из API пришёл большой JSON, и мы хотим из него часть информации записать себе в БД, для этого надо эту часть информации извлечь из JSON, конечно с обработкой вариантов когда JSON не содержит нужной нам информации.
Запросим в Яндекс Геокодер адрес для "Mohammed Bin Rashid Boulevard 1":
$response = file_get_contents('https://geocode-maps.yandex.ru/1.x/?apikey=d3893dc1-c136-4084-ab9c-4db26b00463e&geocode=Mohammed+Bin+Rashid+Boulevard+1&lang=en_US&format=json');
var_dump( $response);
/* string(3197) "{"response":{"GeoObjectCollection":{"metaDataProperty":{"GeocoderResponseMetaData":{"request":"Mohammed Bin Rashid Boulevard 1","results":"10","found":"2"}},"featureMember":[{"GeoObject":{"metaDataProperty":{"GeocoderMetaData":{"precision":"exact","text":"1, Mohammed Bin Rashid Boulevard, Downtown Dubai, Dubai, United Arab Emirates","kind":"house","Address":{"country_code":"AE","formatted":"1, Mohammed Bin Rashid Boulevard, Downtown Dubai, Dubai, United Arab Emirates","Components":[{"kind":"country","name":"United Arab Emirates"},{"kind":"province","name":"Dubai"},{"kind":"area","name":"Sector 3"},{"kind":"district","name":"Downtown Dubai"},{"kind":"district","name":"Downtown Dubai"},{"kind":"street","name":"Mohammed Bin Rashid Boulevard"},{"kind":"house","name":"1"}]},"AddressDetails":{"Country":{"AddressLine":"1, Mohammed Bin Rashid Boulevard, Downtown Dubai, Dubai, United Arab Emirates","CountryNameCode":"AE","CountryName":"United Arab Emirates","AdministrativeArea":{"AdministrativeAreaName":"Dubai","SubAdministrativeArea":{"SubAdministrativeAreaName":"Sector 3","Locality":{"DependentLocality":{"DependentLocalityName":"Downtown Dubai","DependentLocality":{"DependentLocalityName":"Downtown Dubai","Thoroughfare":{"ThoroughfareName":"Mohammed Bin Rashid Boulevard","Premise":{"PremiseNumber":"1"}}}}}}}}}}},"name":"1, Mohammed Bin Rashid Boulevard","description":"Downtown Dubai, Dubai, United Arab Emirates","boundedBy":{"Envelope":{"lowerCorner":"55.270141 25.193445","upperCorner":"55.278352 25.200915"}},"uri":"ymapsbm1://geo?data=CgoyMTE3NTQxODgxEocB2KfZhNil2YXYp9ix2KfYqiDYp9mE2LnYsdio2YrYqSDYp9mE2YXYqtit2K_YqSwg2KXZhdin2LHYqSDYr9io2YosINmI2LPYtyDZhdiv2YrZhtipINiv2KjZiiwg2KjZiNmE2YrZgdin2LHYryDZhdit2YXYryDYqNmGINix2KfYtNivLCAxIgoN1BhdQhXVk8lB","Point":{"pos":"55.274247 25.19718"}}},{"GeoObject":{"metaDataProperty":{"GeocoderMetaData":{"precision":"street","text":"Mohammed Bin Rashid Boulevard, Downtown Dubai, Dubai, United Arab Emirates","kind":"street","Address":{"country_code":"AE","formatted":"Mohammed Bin Rashid Boulevard, Downtown Dubai, Dubai, United Arab Emirates","Components":[{"kind":"country","name":"United Arab Emirates"},{"kind":"province","name":"Dubai"},{"kind":"area","name":"Sector 3"},{"kind":"district","name":"Downtown Dubai"},{"kind":"street","name":"Mohammed Bin Rashid Boulevard"}]},"AddressDetails":{"Country":{"AddressLine":"Mohammed Bin Rashid Boulevard, Downtown Dubai, Dubai, United Arab Emirates","CountryNameCode":"AE","CountryName":"United Arab Emirates","AdministrativeArea":{"AdministrativeAreaName":"Dubai","SubAdministrativeArea":{"SubAdministrativeAreaName":"Sector 3","Locality":{"DependentLocality":{"DependentLocalityName":"Downtown Dubai","Thoroughfare":{"ThoroughfareName":"Mohammed Bin Rashid Boulevard"}}}}}}}}},"name":"Mohammed Bin Rashid Boulevard","description":"Downtown Dubai, Dubai, United Arab Emirates","boundedBy":{"Envelope":{"lowerCorner":"55.277606 25.195668","upperCorner":"55.279807 25.199027"}},"uri":"ymapsbm1://geo?data=Cgo1ODA2NzkyODQ1EkpVbml0ZWQgQXJhYiBFbWlyYXRlcywgRHViYWksIERvd250b3duIER1YmFpLCBNb2hhbW1lZCBCaW4gUmFzaGlkIEJvdWxldmFyZCIKDXQdXUIVGZTJQQ,,","Point":{"pos":"55.278765 25.197311"}}}]}}}" */
$object = json_decode(json: $response, associative: true, flags: JSON_THROW_ON_ERROR);
$data = new AdvancedArrayFactory()->makeAdvancedArray($object);
$formatted = $data
->pull("response")
->pull("GeoObjectCollection")
->pull("featureMember")
->pull()
->pull("GeoObject")
->pull("metaDataProperty")
->pull("GeocoderMetaData")
->pull("Address")
->get("formatted")
->default("Адрес не найден");
$val = $formatted->str();
var_dump( $val);
/*
string(77)
"1, Mohammed Bin Rashid Boulevard, Downtown Dubai, Dubai, United Arab Emirates"
*/
$isReal = $formatted->isReal();
var_dump( $isReal);
/* bool(true) */
Распарсим ответ:
преобразуем строковое представление JSON в массив
из массива с помощью фабрики делаем экземпляр
AdvancedArray
AdvancedArray::pull()
- получаем экземплярAdvancedArray
для вложенного массива, делаем так пока не дойдём до нужного элементаAdvancedArray::get()
- получаем экземплярCommonValue
для элементаformatted
(не массив)CommonValue::str()
- получаем значение элемента:"1, Mohammed Bin Rashid Boulevard, Downtown Dubai, Dubai, United Arab Emirates"CommonValue::isReal()
- поверяем что значение действительное, а не подставлено по умолчанию: true - значение действительное
Запросим в Яндекс Геокодер адрес для "Адский остров" (данные отсутствуют):
$response = file_get_contents('https://geocode-maps.yandex.ru/1.x/?apikey=d3893dc1-c136-4084-ab9c-4db26b00463e&geocode=%D0%B0%D0%B4%D1%81%D0%BA%D0%B8%D0%B9+%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B2&lang=en_US&format=json');
var_dump( $response);
/*
string(172)
"{"response":{"GeoObjectCollection":{"metaDataProperty":{"GeocoderResponseMetaData":
{"request":"адский остров","results":"10","found":"0"}},"featureMember":[]}}}"
*/
$object = json_decode(json: $response, associative: true, flags: JSON_THROW_ON_ERROR);
$data = (new AdvancedArrayFactory())->makeAdvancedArray($object);
$formatted = $data
->pull("response")
->pull("GeoObjectCollection")
->pull("featureMember")
->pull()
->pull("GeoObject")
->pull("metaDataProperty")
->pull("GeocoderMetaData")
->pull("Address")
->get("formatted")
->default("Адрес не найден");
$value = $formatted->str();
var_dump( $value);
/* string(28) "Адрес не найден" */
$isReal = $formatted->isReal();
var_dump( $isReal);
/* bool(false) */
Распарсим ответ:
преобразуем строковое представление JSON в массив
из массива с помощью фабрики делаем экземпляр
AdvancedArray
AdvancedArray::pull()
- получаем экземплярAdvancedArray
для вложенного массива, делаем так пока не дойдём до нужного элементаAdvancedArray::get()
- получаем экземплярCommonValue
для элементаformatted
(не массив)CommonValue::str()
- получаем значение элемента:"Адрес не найден"CommonValue::isReal()
- поверяем что значение действительное, а не подставное: false - значение не действительное, подставлено значение по умолчанию.
Как видите код абсолютно линеен, нет ни каких ветвлений и хитростей: получили значения, взяли нужное, передали дальше в обработку куда нам надо было, за соответствие типов не переживаем.
Разобраться в том как происходит парсинг легко - это важно для понимания и поддержки кода.
Алгоритм очень простой:
Последовательным вызовом метода
AdvancedArray::pull($index)
получить нужный массивМетодом
AdvancedArray::get($index)
получить значение элементаС помощью соответствующего метода приведения типа, получить значение с нужным типом
Дополнительные возможности
Дополнительные возможности пакета sbwerewolf/language-specific
это возможность проверки наличия элемента по индексу и встроенная возможность проверить тип элемента, так же можно получить значение элемента без преобразования типа, то есть получить значение как есть.
Кроме того можно получить все "элементы-значения" (не массивы), и соответствено, можно получить все элементы-массивы.
Проверить наличие элемента (существование)
$data = (new AdvancedArrayFactory())->makeAdvancedArray($array);
$element = $data
->pull("response")
->pull("GeoObjectCollection")
->pull("featureMember")
->pull()
->pull("GeoObject")
->pull("metaDataProperty")
->pull("GeocoderMetaData")
->pull("Address")
$hasElement = $element->has("formatted"); // bool
Можно проще:
$data = (new AdvancedArrayFactory())->makeAdvancedArray($array);
$formatted = $data
->pull("response")
->pull("GeoObjectCollection")
->pull("featureMember")
->pull()
->pull("GeoObject")
->pull("metaDataProperty")
->pull("GeocoderMetaData")
->pull("Address")
->get("formatted");
$isReal = $formatted->isReal(); // bool
Получить тип
$data = (new AdvancedArrayFactory())->makeAdvancedArray($array);
$formatted = $data
->pull("response")
->pull("GeoObjectCollection")
->pull("featureMember")
->pull()
->pull("GeoObject")
->pull("metaDataProperty")
->pull("GeocoderMetaData")
->pull("Address")
->get("formatted");
$type = $formatted->type();
// NULL если элемента нет и если элемент есть, то "1, Mohammed Bin Rashid ..."
В общем случае будет выдан тип элемента, в случае с ГеоКодером это string.
Если элемент отсутствует, то тип будет NULL.
Если задано значение по умолчанию и элемент отсутствует, то будет выдан тип значения по умолчанию.
Если задано значение по умолчанию и элемент присутствует, то будет выдан тип значения элемента и не факт что он будет совпадать с типом значения по умолчанию.
Получить значение как есть, без приведения типа
$data = (new AdvancedArrayFactory())->makeAdvancedArray($array);
$formatted = $data
->pull("response")
->pull("GeoObjectCollection")
->pull("featureMember")
->pull()
->pull("GeoObject")
->pull("metaDataProperty")
->pull("GeocoderMetaData")
->pull("Address")
->get("formatted")
;
$value = $formatted->asIs();
// NULL если элемента нет, если элемент есть, то "1, Mohammed Bin Rashid ..."
Значение элемента будте выдано как есть, если элемента нет, то будет выдано значение null
, если задано значение по умолчанию, то будет выдано значение по умолчанию.
Получить все элементы-значения
$data = [1, ['first', 'next',],
2, ['next','last']];
$fabric = new AdvancedArrayFactory();
$handler = (new AdvancedArrayFactory())->makeAdvancedArray($data);
$index = 0;
$item = $fabric::makeDummyAdvancedArray();
foreach ($handler->values() as $item) {
var_dump( $item->asIs());
}
/*
int(1)
int(2)
*/
Получить все элементы-массивы
$data = [1, ['first', 'next',],
2, ['next','last']];
$fabric = new AdvancedArrayFactory();
$handler = (new AdvancedArrayFactory())->makeAdvancedArray($data);
$index = 0;
$item = $fabric::makeDummyAdvancedArray();
foreach ($handler->arrays() as $item) {
var_dump( $item->raw());
}
/*
array(2) {
[0]=>
string(5) "first"
[1]=>
string(4) "next"
}
array(2) {
[0]=>
string(4) "next"
[1]=>
string(4) "last"
}
*/
Состав пакета sbwerewolf/language-specific
Пакет состоит из 4 классов:
CommonValue
предоставляет значение элемента, тип значения будет приведён к заданномуBaseArray
обёртка над массивом, реализация интерфейсовIterator
иJsonSerializable
CommonArray
класс наследник отBaseArray
, реализация интерфейсаArrayAccess
AdvancedArray
класс наследник отCommonArray
, работа с вложенными массивамиТри фабрики для создания экземпляров классов надлежащим образом, это:
CommonValueFactory
,ArrayFactory
,AdvancedArrayFactory
один вспомогательный класс и один класс DTO, интерфейсы на всё (больше ради "православного" тестирования)
CommonValue
Этот класс является сутью пакета, именно этот класс обеспечивает предоставление значения с заданным типом, класс гарантирует, что:
значение будет приведено к заданному типу,
при необходимости будет подставлено значение по умолчанию,
и маленький бонус, всегда можно понять, что предоставленное значение являться или подставным, или действительным, то есть появляться возможность завязать на это какую то логику без дополнительных телодвижений
Методы класса CommonValue
CommonValue::default()
задать значение по умолчаниюCommonValue::isReal()
проверяет, что значение является действительным (исследовательский метод)CommonValue::type()
получить действительный тип значения (исследовательский метод)CommonValue::asIs()
предоставить значение как есть, без приведения типаCommonValue::double()
предоставить значение приведённое к типуfloat
, методы называется double, потому что вызовgettype((float)null)
вернётdouble
CommonValue::str()
вернёт значение приведённое к типуstring
CommonValue::int()
вернёт значение приведённое к типуint
CommonValue::bool()
вернёт значение приведённое к типуbool
CommonValue::array()
вернёт значение приведённое кarray
CommonValue::object()
вернёт значение приведённое кobject
BaseArray
BaseArray
возвращает элементы массива только как экземпляры CommonValueInterface
, то есть перебрать вложенные массивы не получиться.
Класс является реализаций интерфейсов Iterator
и JsonSerializable
.
Можно применить json_encode()
, массив будет конвертирован в JSON-строку.
Можно прогонять экземпляр через forech
, можно вручную перебрать элементы массива (с помощью методов next()
key()
current()
rewind()
).
Пример с foreach
:
$data = (new AdvancedArrayFactory())->makeAdvancedArray([2,3,4,[5,'q'],'w',['e','r']]);
foreach ($data as $element){ var_dump($element->asIs()); }
/*
int(2)
int(3)
int(4)
array(2) {
[0]=>
int(5)
[1]=>
string(1) "q"
}
string(1) "w"
array(2) {
[0]=>
string(1) "e"
[1]=>
string(1) "r"
}
*/
До версии 8.4 при переборе элементов массива с помощью
foreach
возвращалось собственно значение, в версии 8.4 поведение было изменено, теперь значение возвращаеться как экземпляр класса с интерфейсомCommonValueInterface
, для получения значения без приведения типа служит методCommonValueInterface::asIs()
С помощью метода BaseArray::raw():array
можно вернуть исходный массив целиком.
В целом ни чего интересного, класс сделан что бы в основном классе AdvancedArray
не было "неинтересного кода".
Для создания экземпляров класса BaseArray
можно использовать фабрику ArrayFactory
.
CommonArray
Ещё один неинтересный класс, расширяет функционал класса BaseArray
методами интерфейса ArrayAccess
(работа с элементами массив через квадрантные скобочки) при этом вызов методов для изменения или удаления элементов массива приведёт к вызову исключений ListIsImmutableException
и ValueIsImmutableException
, значения элементов менять запрещено, удалять элементы запрещено.
Класс добавляет два метода аналогичных методам интерфейса ArrayAccess
:
CommonArray::has
(string|int|float|bool|null $key = null): bool проверить существование элемента по индексу, если вызвать без аргументов, то вернётtrue
если в массиве есть хотя бы один элемент, метод являтьеся аналогомArrayAccess::offsetExists()
CommonArray::get
(string|int|float|bool|null $key = null) получить элемент по индексу (элемент будет предоставлен как экземплярCommonValue
), если индекс не задавать, то будет возвращён случайный элемент (если в массиве есть хотя бы один элемент), если в массиве нет элементов, то метод вернётCommonValue
с недействительным значением (CommonValue::isReal()
=>false
), метод являтьеся аналогомArrayAccess::offsetGet()
Для создания экземпляров класса CommonArray
можно использовать фабрику ArrayFactory
.
AdvancedArray
Класс реализует доступ к элементам вложенных массивов, является наследником от CommonArray
.
Методы класса AdvancedArray
AdvancedArray::isDummy()
проверяет, что массив является подставным (заглушка для не существующих массивов) (исследовательский метод)AdvancedArray::pull
(int|bool|string|null|float $key = null) вытягивает вложенный массив, то есть возвращает новый экземплярAdvancedArrayInterface
для вложенного массива, если элемент с заданным индексом не являеться массивом, то будет возвращёнAdvancedArrayInterface::isDummy()
=>True
, если индекс не задавать, то будет возвращён новый экземплярAdvancedArrayInterface
от первого попавшегося вложенного массива, если вложенных массивов нет, то будет возвращёнAdvancedArrayInterface::isDummy()
=>True
AdvancedArray::arrays()
метод последовательно выдаёт все элементы-массивы, будет возвращёнGenerator
, который выдаёт новый экземплярAdvancedArrayInterface
для всех вложенных массивов (следует использовать как foreach (AdvancedArray::arrays()
as$k
=>$v
) )AdvancedArray::values()
метод последовательно выдаёт все элементы-значения (не массивы), будет возвращёнGenerator
, который выдаёт новый экземплярCommonValueInterface
для всех элементов не являющимися массивами (следует использовать как foreach (AdvancedArray::values()
as$k
=>$v
) )
Для создания экземпляров класса AdvancedArray
можно использовать фабрику AdvancedArrayFactory
.
Ведение версий пакета
Версии пакета привязаны к минимальной версии PHP. Semantic Versioning не соблюдается.
Версии пакета это минимальная версия PHP, нет разделения на major и minor. Изменение в minor могут нарушать обратную совместимость. Это важное обстоятельство, если будите использовать пакет sbwerewolf/language-specific
, то пожалуйста указывайте точную версию, что бы у вашего кода не было проблем при обновлении пакетов (composer update).
Заключение
Прошу делиться в коментах вашими способами "типобезопасной работы" с массивами в PHP. Своим способом я поделился в этой статье. Может быть есть аккие то более зрелые способы ? или все моим опасения устарели и я отстал от жизни ? Пишите комментарии. Но без оффтопика.
Написанного выше более чем достаточно, что бы понять возможности sbwerewolf/language-specific
, если у вас остались вопросы о том как этот пакет можно применить в конкретных use-case, то пишите мне в личку в ВК, потому что в коментах я могу отвечать один раз в сутки (спасибо всем кто ставит минусы, спасибо Хабру за лимиты на общение), и вы просто не дождётесь моего ответа.
спасибо за чтение.
Комментарии (7)
TsarS
19.01.2025 07:34Вот если бы каждый описывал свою библиотеку так же в Readme — мир стал бы лучше
karrakoliko
19.01.2025 07:34яй бы смотрел в сторону jsonpath-подобного синтаксиса, чтобы вместо цепоски pull'ов передавать строку типа "key?.nestedkey?.l2_nestedkey?"
разворачивать обратно в голове цепочку pull'ов весьма не удобно
SbWereWolf Автор
19.01.2025 07:34Такой подход реализован в Typed, и в принципе это стандартный способ записи, только вопросики ни кто не пишет, и так понятно, что элемента может не быть.
Но у моей библиотеки другое назначение, я не просто получаю значение, я его ещё и анализирую, было не было, и если было, то какого типа. Редкий сценарий использования, но как то с моим универсальным подходом получилось так. У Typed более практичный подход.
karrakoliko
19.01.2025 07:34Такой подход реализован в Typed
киньте в меня ссылкой, пожалуйста.
решительно не получается найти https://github.com/search?q=php typed&type=Everything&repo=&langOverride=&start_value=1
koleso_O
19.01.2025 07:34Всего 2 вопроса:
1) Чем это решение лучше/хуже symfony serialiser?
2) Есть ли возможность частичной десериализации (например, преобразовать часть json-строки в ассоциативный массив только обнаружения нудного ключа в исходной json-строке?
fo_otman
Отличная работа!