На Хабре, было несколько статей о Yaml, но мне кажется все они однобоки и не раскрывают его истинную природу. Я попробую это исправить и рассказать о Yaml в положительном контексте. Не буду вновь описывать детали синтаксиса стандартного Yaml, в Интернете есть много материалов на эту тему. Их можно найти и на Хабре, в том числе, по ссылкам из этой статьи. Материал ориентирован на тех, кто знаком с Yaml, но возможно чувствует неприязнь к формату.
За последние 30 лет активно развивающего IT, устоялись три основных формата для представления иерархических данных, т.е. деревьев:
-
XML/HTML — особенность формата в том, что узлами являются объекты, а это архи необходимо, в первую очередь, для кодирования богатых «человеческих визуализаций». Отсюда и постфикс «ML» — язык разметки. Этот формат используется для веб-сайтов или, например, для хранения данных MS Excel. Узлы представляются объектами из-за наличия атрибутов тегов, а также необходимостью прохода по дереву во всех направлениях. В начале 2000-х годов, XML более широко использовался для передачи данных между удаленными хостами, но часто был вытеснен JSON, так как последний намного более прост, а мощь XML (как и сложность) оказались не востребованы.
-
JSON — также представляет иерархические данные, но узловыми ключами не могут быть объекты, а только скаляры: строки или целые числа. Особенность формата в том, в том, что он имеет минимальное количество правил, необходимых для практической реализации парсеров. Отсюда его популярность, в том числе, для обмена сообщениями между удаленными хостами. JSON часто не читают люди, а кодирование и декодирование происходит автоматически.
-
YAML — также не может содержать в узловых ключах объекты, а только скаляры. Этот формат включает, как подмножество, JSON, часто используется для конфигураций, и ориентирован в большей степени для чтения-записи человеком, чем JSON. В этой статье, сравниваются два последних формата, но имхо, нужно акцентировать, что Yaml предназначен для человека, а JSON не только. И ставить вопрос «кто круче» не очень корректно.
Ситуация с Yaml, наверно самая сложная и запутанная, например в этом посте-нытье рассказывается как всё плохо. Но негативные обстоятельства можно и нужно обратить вспять. Для этого следует всё расставить по своим местам. С момента появления Yaml прошло 23 года, но он активно используется в различных языках программирования для конфигураций, в том числе, флагманском PHP-framework Symfony. По-моему этого факта достаточно, чтобы утверждать, что Yaml имеет свой «рай», просто его нужно понять. Наверно до Yaml нужно дорасти, я например, написал свой парсер Yaml и многое понял из того о чём пишу, только в прошлом году, хотя занимаюсь программированием уже давно.
Я считаю, в стандартной архитектуре Yaml, действительно присутствует удивительный факап. Первичная идея — гениальная, но конечные детали — неудовлетворительны. Как говорится «начали за здравие, закончили за упокой»… Тем не менее, за 23 года жизни Yaml устоялся факт его локального применения. Жизнестойкость и локальность объясняется так: он действительно красив, минималистичен и выразителен, но его парсер всегда будет более сложен, объёмен и менее эффективен чем у JSON. Производительность — не проблема, так как компилированные данные всегда можно поместить в кэш. Я назвал Yaml королем мета-описаний, потому что, считаю синтаксис Yaml таким, который вобрал в себя все самые мощные способы представления иерархических структур, которые будут выглядеть красиво и выразительно для человека. По сути, Yaml удобно использовать не только для конфигураций, но и для программирования. Но об этом позже.
Я не нашел в Wikipedia четкого термина для «мета-описаний», поэтому опишу, что я под этим подразумеваю. Иногда, в программировании, бывает эффективно придумать какой-то собственный формат представления данных, который будет отражать алгоритмические особенности парсера, работающего с таким форматом. Данные в таком произвольном формате, я и называю мета-описаниями. Например, среди моих прошлых работ, имеется PHP имплементация CSS framework Tailwind. Для генерации всех(!) утилит Tailwind используется до 200 мета-описаний. Это пример одного мета-описания для генерации 42-х утилит «Grid Column Start / End»:
- MetaName: col #grid ForMenu: 2col span-start-end Body: | auto|span-full|start-auto|end-auto|@num grid-column: auto .auto grid-column: 1 / -1 .span-full grid-column-start: auto .start-auto grid-column-end: auto .end-auto span-~num!=: span {0} / span {0}|start-~num!=-start: {0}|end-~num!=-end: {0} grid-column{@} .@num
В консоли можно протестировать как Vesper (это моя имплементация Tailwind) генерирует утилиты, вот три примера:
>php vendor/bin/sky venus tw col-auto .col-auto { grid-column: auto; } >php vendor/bin/sky venus tw col-span-2 .col-span-2 { grid-column: span 2 / span 2; } >php vendor/bin/sky venus tw col-start-2 .col-start-2 { grid-column-start: 2; }
Кстати, эта статья включает работу программиста на PHP. Для тех, кто хочет подробнее ознакомиться с моей работой, доступно к установке пустое демо-приложение «Hole». Предостережение: Coresky — это экспериментальный фреймворк, подробнее в спойлере:
Hidden text
Это значит, что я использую фреймворк только для разработки новых архитектурных решений и не рекомендую его использовать для публичной части Интернета. Проделана огромная работа благодаря только лишь эндорфинам и удовольствию творчества. Много кропотливой, не творческой работы не сделано и вы легко найдете ошибки. Отчасти они присутствуют из-за того, что я всё еще продолжаю делать обратно несовместимые изменения новых версий к старым. Тем не менее то, о чём я пишу в этой статье — отлично работает. Большая часть кода имеет статус release-candidate.
>composer create-project coresky/hole # и можно сразу запустить PHP-DEV-WEB сервер: >cd hole >php vendor/bin/sky s
Исправим стандартный Yaml
Смотрите, для передачи данных между хостами существуют JSON и XML, а Yaml мы будем использовать только локально. Поэтому нет проблем, чтобы написать свой собственный парсер. Давайте исправим архитектурный факап, чтобы минимизировать досадные «подводные камни» и с другой стороны, раскроем всю потенциальную мощь Yaml. Конечно, необходимо стараться максимально сохранить стандартный Yaml, это упростит изучение Coresky модификации. Пакет Composer symfony/yaml как и многие другие популярные имплементации также не поддерживают стандарт полностью. Например, обычно, не поддерживается множественный Yaml документ. В Coresky, парсер Yaml это один файл, содержащий 577 строк. Я использую технику разбивки на токены, тогда как в Symfony используются регулярные выражения.
Неявная типизация. С проблемой Норвегии — это ерунда какая-то.. Давайте оставим рабочими только три литерала, которые являются стандартными в JSON и соответствуют по типу в PHP: true, false, null. Целые числа оставим только десятичные, а также с плавающей точкой float, в полном соответствии с синтаксисом PHP. Двоичные, шестнадцатеричные INT и всю остальную неявную типизацию убираем. Для этого дела лучше применить функции трансформации или операторы. Об этом ниже.
Явная типизация. Здесь, нужно расширить стратегию и использовать соответствующие термины «оператор» или «функция преобразования». В Coresky имеется собственный компилятор представлений — Jet, который является потомком Laravel/Blade, в нём используются операторы, начинающиеся с символа амперсанд. Используем такой же подход и в Yaml. Для имплементации большинства функций трансформации требуется буквально пару строк кода на PHP. То как трансформируются значения записано в нотации стандартного JSON:
# @base64 тоже что и !!binary в стандартном Yaml img: @base64 | R0lGODdhDQAIAIAAAAAAANnZ2SwAAAAADQAIAAACF4SDGQ ar3xxbJ9p0qa7R0YxwzaFME1IAADs= array: @csv(:) a:b:c # трансформируется в массив: ["a", "b", "c"] hi: @hex2bin > # трансформируется в строку: "Hello word!" 48 65 6C 6C 6F 20 77 6F 72 64 21 # десятичные числа в спец-нотации: card: @dec 1234_5678_9012_3456 # >>> 1234567890123456 big-number: @dec 1 000 000 000 # >>> 1000000000 phone: @dec 2-777-222-22-22 # >>> 27772222222 # двоичное число: five: @bin 101 # >>> 5 # Search-And-Replace, используется функция PHP preg_replace(..) sar: @sar(|=+| is ) a========1, b==2 # >>> "a is 1, b is 2" # @left(string), @right(string) - дописать "string" слева или справа key: @left(left-) value # >>> "left-value" # и так далее ..
Операторы можно указывать только вначале значений Yaml или JSON нотаций. Для одного значения, можно указать несколько операторов и они могут применяться каскадно для всех значений под-массива. Порядок выполнения — справа налево и снизу вверх. Для частной отмены каскадного выполнения, можно использовать @deny:
one: @right( RR) two: @bin - 0b11 - 101 # можно и без префикса 0b - @deny @left(LL ) 1010 three: four color: @left(#) @each @bang(. ) @sar(| +| ) > # not in cascade aliceblue.f0f8ff antiquewhite.faebd7 beige.f5f5dc bisque.ffe4c4 # для кодирования цветов мы указали только информационную "соль" # вот почему Yaml - король метаопределений
{ "one": { "two": ["3 RR", "5 RR", "LL 1010"], "three": "four RR" }, "color": { "aliceblue": "#f0f8ff", "antiquewhite": "#faebd7", "beige": "#f5f5dc", "bisque": "#ffe4c4" } }
Якоря и ссылки. Не будем расширять синтаксис, можно использовать функцию трансформации @path:
one: two: [1, 3] three: @path(one.two.1) # this value will eq. to 3
Множественный Yaml. Должен иметь именованные части! Тогда можно будет свободно компоновать Yaml-данные и применять принцип DRY. Компилятор Jet поддерживает маркеры частей файлов. Используем эту же стратегию и в Coresky Yaml:
#.run ========================= - @inc(.test) # @inc это include - 2 #.run #.test ========================= + 123 #.test
<?php # функция yml(..) выполняет inline-yaml, может кешировать компилированный PHP # и передавать переменные, подобно как это делается в Jet (Blade) print_r(yml('+ @inc(run) filename.yml')); # stdout: array(0 => 123, 1 => 2)
Гибридные массивы PHP. В стандартной Yaml нотации есть одна неразбериха: для того чтобы указать значение со строковым ключом на нижнем уровне иерархии — обязательно нужен отступ, а для перечислений отступ необязателен:
aaa: bbb: # { "aaa": { "bbb": null } } отступ ==> глубина aaa: bbb: # { "aaa": null, "bbb": null } <--- здесь глубина не изменилась aaa: - bbb # { "aaa": [ "bbb" ] } отступ ==> глубина aaa: - bbb # { "aaa": [ "bbb" ] } <--- здесь глубина изменилась хотя нет отступа!!! # Исправим синтаксис для предыдущего примера в Coresky вот так: aaa: - bbb # { "aaa": null, "0": "bbb" }
Yaml компилируется в PHP массив, а массивы в PHP могут быть гибридными. Разрешим совмещать в Yaml нотации строковые ключи и ключи перечислений (дефис). Это расширит потенциал использования Yaml. Ниже представлен действующий код для генерации HTML формы системной конфигурации Coresky для Root-Admin-Section:
#.system - <fieldset><legend>Primary settings</legend> - ['', [[<b><u>Production</u></b>, li]]] trace_root: [Debug mode on production for `root` profile, chk] trace_cli: [Use X-tracing for CLI, chk] error_403: [Use 403 code for `die`, chk] empty_die: [Empty response for `die`, chk] gate_404: [Gate errors as 0.404 (soft), chk] log_error: [Log ERROR, radio, [Off, On]] log_crash: [Log CRASH, radio, [Off, On]] - [Hard cache, { cache_act: ['', radio, [Off, On]], cache_sec: ['Default TTL, seconds', number, style="width:100px", 300] }] - </fieldset> - <fieldset><legend>"Visitor's & users settings"</legend> - [Cookie name, { c_name: ['', '', '', sky], c_upd: ['Cookie updates, minutes', number, style="width:100px", 60] }] visit: ['One visit break after, off minutes', number, '', 5] reg_req: [Users required for registrations, radio, [Both, Login, E-mail]] - </fieldset> #.system
Coresky Yaml — младший брат Jet
В Coresky имеется функционал для генерации HTML форм из массивов PHP. Теперь формы можно определять с помощью Yaml. Добавим операторы @php и @preflight, ниже пример кода Yaml и соответствующий компилированный PHP кэш-файл:
#.test ======================================= + @preflight($v_1, &$v_2) | return SomeClass::method_1($v_1, $v_2); - string - @php OtherClass::method_2($__return) - {a: b, c: @php(OtherClass::method_3([1, $v_3])), x: y} # json notation #.test
<?php # preflight code $__return = call_user_func(function() use ($v_1, &$v_2) { return SomeClass::method_1($v_1, $v_2); }); # other yaml after compile return array( 0 => 'string', 1 => OtherClass::method_2($__return), 2 => array( 'a' => 'b', 'c' => OtherClass::method_3([1, $v_3]), 'x' => 'y' ), );
В операторе @php, код PHP можно указать или в параметре (в скобках) или в значении (после оператора). В первом примере выше, в последней строке, подсветка синтаксиса Yaml неверно сработала: всё что указано в скобках оператора @php, не является данными JSON нотации, а является кодом PHP. Если @preflight имеет скобки, то код будет выполняться в изолированной области видимости, которая организуется с помощью Closure и call_user_func, а если скобок нет — то без изоляции.
Вызов такого кода, и передачу переменных, можно выполнить с помощью функции Coresky yml(..):
<?php $array = yml('cache_filename', '+ @inc(test) filename.yaml', [ 'v_1' => $var_1, 'v_2' => $var_2, 'v_3' => $var_3, ]);
В примерах выше, область применения Coresky Yaml, определенно коррелирует с компилятором представлений Jet. Представьте бухгалтерское приложение с огромным количеством форм. Теперь, рядом с папкой mvc, можно сделать папку forms и разместить в ней все формы приложения. Имена файлов содержащих yaml-формы, можно назвать в соответствии с именами контроллеров и разместить по несколько штук в одном файле разделив маркерами, в соответствии с именами действий, где используются эти формы. Представляете как это разгрузит код моделей, контроллеров и представлений? Как сказал автор статьи «Некоторые приемы YAML«, да пребудет с нами KISS и DRY, а еще актуально: «разделяй и властвуй».
Формы на Yaml это не панацея, посмотрите, например этот файл. Довольно часто бывает нужно «прожонглировать» кусками кода и данных, чтобы сгенерировать финальный код. Раньше в подобных случаях, я использовал возможности Jet. Теперь это удобнее делать в Yaml. В приложении «Hole», которое вы можете установить с помощью Composer, есть несколько примеров форм на Yaml, в том числе, с передачей переменных в компилированный кэш.
Вы вероятно заметили «плюс» в Yaml нотации, в примерах выше, в местах где обычно бывает дефис. Это еще одно новшество в синтаксисе Coresky Yaml. Есть и другие нововведения, читайте подробнее в документации. Я старался показать хорошую сторону и скрытый потенциал Yaml, хотя, действительно в стандарте есть недостатки. Часто написать веб-приложение можно и без Yaml, но, например, в этом проекте без него было бы туго. Yaml в Coresky — это еще один мощный механизм для программирования и упрощения кода. Если у вас в проекте имеется более-менее значительный массив данных, который поместить в БД не рационально, у вас есть возможность использовать Yaml.
Другие интересные идеи в Coresky
Я напишу пару слов о каждой и помещу текст в спойлеры, так как это немного не по теме статьи. Если не интересно — вы можете пропустить эту часть.
Планы и продукты:
Hidden text
Нет велосипедам! Система продуктов в Coresky — это попытка сделать так, чтобы повторное использование функционального кода было максимально простым. Продукты бывают трёх типов: prod(функциональный код), dev(это по сути просто плагины для инструментов разработчика), view(дополнительные наборы шаблонов Jet и стилей CSS). Имя продукта основного приложения всегда main. Продукты похожи на модули Zend/Laminas, но в последнем нет никаких специальных средств для их интеграции в приложение, а также специальных системных средств для их поддержки. Продукты устанавливаются с помощью веб-интерфейса инструментов разработчика, в это время может произойти специальная инициализация продукта: создание таблиц в БД и т.д.
Планы — это надстройка над сущностями приложений. Стандартные планы, которые имеются в любом Sky-приложении: app, cache, mem, gate, jet. Управляет планами, по сути, движок абстрактного кэша.
Sky Gate — небесные врата
Hidden text
В Coresky нет привычной системы роутинга. SkyGate — это утилита с веб-интерфейсом, в которой настраиваются входные внешние данные для контроллеров. Первая часть адреса запроса определяет контроллер, а вторая действие в нем. Если нужна нестандартная адресация, необходимо использовать Coresky Rewrites.
Компилятор преставлений Jet
Hidden text
За основу была взята идея Laravel/Blade. В Jet много новшеств: препроцессор, бинарная идея блоков, включающая операторы @block, @use, #use, маркеры частей файла, три типа генерации визуализаций: top, sub, block. Имеется ввиду, что каждая визуализация запускает действие в контроллере, подготавливает переменные и генерирует визуализацию с помощью компилированного шаблона Jet.
Заключение
-
Если вы работаете со значительным объемом, особенно иерархических данных и не сильно воспринимаете Yaml: потрудитесь его изучить получше. Ему уже 23 года и он прекрасно себя чувствует, несмотря на плохие слухи. Если это так, значит он кому-то нужен, может нужен и вам? Когда вы, как Джек Салли скажете «Yaml, я тебя вижу..», вас от него уже будет не оторвать.
-
У стандартного Yaml есть скрытый потенциал, и я в этой статье старался показать как его раскрыть.
-
Не забывайте про подводные камни, если вы «не видите Yaml», будьте осторожны.
ссылка на оригинал статьи https://habr.com/ru/articles/834270/
Добавить комментарий