Что такое генераторы и как их использовать php
Генераторы в действии
Небольшое вступление
Не так давно я решил для себя, что пора восполнить большой пробел в знаниях и решил прочитать про переходы между версиями PHP, т.к. понимал, что остался где-то между 5.2 и 5.3 и этот пробел необходимо как-то устранить. До этого я читал про namespaces, traits и т.д, но дальше чтения не уходило. И вот тут я заметил генераторы, почитал документацию, одну из статей на хабре на этот счет и после этого возникла мысль — а как раньше без них жили-то?
Данным переводом хочу помочь хотя бы новичкам, поскольку на php.net документация по генераторам на английском и, на мой взгляд, должным образом не раскрывает всю идею и места применения. Текста много, кода чуть меньше, картинок нет. Потребуются общие знания, например, про итераторы. Очевидный код комментировать не буду, а вот сложные для понимания примеры постараюсь объяснить в силу своих знаний.
UPD1: Изменил расплывчатую формулировку, про которую говорили в комментариях.
UPD2: Добавил решение с принудительным break.
Теория
Сразу скажу главную вещь — генераторы никоим образом не позволят сделать что-то новое, чего нельзя было сделать раньше, поскольку генераторов до PHP 5.5 нет. Это лишь новая возможность, которая несколько меняет обычное поведение языка. Везде, где используются генераторы, можно также использовать итераторы. Теперь, зная об этом, сразу взглянем на пример. Скажем, нам необходимо пройтись по строкам в файле. В процедурном стиле это можно сделать как-то так:
Это обычное решение, ничего странного тут нет. Но что если нам нужно что-то более абстрактное? Скажем, генерировать строки из абстрактного источника. Да, сегодня это может быть файл, но завтра мы решим, что более удачным решением будет база данных или вообще что-то иное.
Сейчас у нас есть два пути решения данной задачи — мы можем вернуть массив или итератор. Но возвращая массив есть несколько проблем: во-первых мы не знаем сколько нам нужно памяти (вдруг файл у нас размером 30 гб?), а во-вторых, возможно, мы и вовсе не сможем описать наш источник как массив (например, мы можем возвращать бесконечные порции данных и попробуй угадай когда этот поток закончится, если ты клиент).
Итак, остаются итераторы. Наш пример очень просто описать через итератор. Тем более, что в PHP уже есть готовый класс для этого — SPLFileObject. Но давайте оставим его и напишем что-то свое.
Совсем просто, не так ли? Хорошо, не совсем, но уже что-то. Хотя если мы взглянем на пример внимательнее, то увидим, что мы не совсем точно описали итератор, поскольку двойной вызов метода current() не даст нам ожидаемый результат в виде одного и того же значения.
Я (автор статьи, не «переводчик») сделал это специально, чтобы показать, что замена процедуры на итератор не всегда является простой задачей, поскольку в реальных ситуациях все куда сложнее. Давайте сделаем правильный итератор для нашего файла.
Боже, как много всего для, казалось бы, простой задачи типа обхода файла, да и основная работа все равно спрятана внутри функций работы с файлами. Теперь, представим, что нам нужно сделать реализовать более сложный алгоритм. Если продолжать текущий подход, то он может стать еще сложнее и понять его работу будет труднее. Давайте решим нашу проблему с помощью генераторов.
Намного проще! Да, это почти как первый пример с функцией, только появилось исключение и ключевое слово yield.
Итак, как оно работает?
Очень важно понимать, что в примере выше изменяется возвращаемое значение функции. Это не null, как может показаться с первого взгляда. Наличие yield говорит о том, что PHP вернет нам специальный класс — генератор. Генератор ведет себя также, как и итератор, поскольку он реализует его. И использовать генератор можно аналогично итераторам.
Вся фишка здесь в том, что мы можем писать код как угодно и просто выбрасывать (yield, йелднуть, йелдануть… не знаю как перевести правильнее, когда есть бросание исключений) каждый раз новое значение когда нам это надо. Итак, как же оно работает? Когда мы вызываем функцию getLines(), PHP выполнит код до первой встречи ключевого слова yield, на котором он запомнит это значение и вернет генератор. Затем, будет вызов метода next() у генератора (который описан нами или итератором), PHP снова выполнит код, только начнет его не с самого начала, а начиная с прошлого значения, которое мы благополучно выбросили и забыли о нем, и опять, до следующего yield или же конца функции, или return. Зная этот алгоритм, теперь можно сделать полезный генератор:
Возможно, с первого взгляда не совсем понятно что это, да и вообще бесконечный цикл все испортит. Да, эта функция и будет работать как бесконечный цикл. Но посмотрите внимательнее — это ведь числа Фибоначчи.
Нужно отметить, что генераторы не являются заменой итераторам. Это лишь простой путь их получения. Итераторы по-прежнему являются мощным инструментом.
Сложный пример
Нам нужно сделать собственный ArrayObject. Вместо того, чтобы делать итератор, сделаем небольшой трюк с генератором. Интерфейс IteratorAggregate требует от нас всего один метод — getIterator(). Так как генератор возвращает объект, реализующий итератор, то мы можем переопределить этот метод таким образом, чтобы он возвращал генератор. Все просто:
В точку! Теперь мы можем перебрать все свойства нашего массива через генератор или через обычный синтаксис обращения по ключу.
Отправляем данные обратно
Генераторы позволяют отправлять себе данные, используя метод send(). В некоторых случаях это может быть очень удобно. Например, когда надо сделать какой-то лог-файл. Вместо того, чтобы писать целый класс для него, можно просто воспользоваться генераторами:
Довольно просто и быстро. Чтобы немного усложнить задачу, посмотрим пример, где функции работают совместно, перекидывая управление между собой при помощи генераторов. Нам нужно построить очередь, которая получает и отправляет данные пакетами. Иногда такие задачи появляются, когда мы читаем бинарный поток и нужно контролировать размер пакета.
Немного сложно, но, надеюсь, вы поняли как это работает. Мы разделили обработку и получение данных определенного размера в нужный момент + остается возможность повторного использования кода.
Нужно боольше примеров!
Вообще генераторы можно применять во многих задачах. Одна из них — симуляция потоков. Сначала мы определяем каждый поток как генератор. Затем выбрасываем сигнал управления родителю, чтобы тот смог передать сигнал для работы следующему потоку. Построим такую систему, которая работает с разными источниками данных (работаем с неблокирующим вводом-выводом). Вот пример такой системы:
Заключение
Генераторы — ОЧЕНЬ мощная штука. Они позволяют очень сильно упростить код. Подумайте только, вы можете написать функцию для диапазона чисел в одну строчку кода:
Коротко и просто. Легко читается, легко понять как работает и очень производительно — быстрее, чем с итератором.
Оригинал статьи — Anthony Ferrara @ blog.ircmaxell.com
В комментариях возник популярный вопрос о том, что делать, когда генератор (вернее сказать, его перебор через foreach) принудительно завершает свою работу, например, через break. В таком случае, если мы имеем дело с перебором файла, как из первого примера, то есть риск того, что никогда не сработает fclose, так как генератор попросту «забывает» о нем. Одно из самых верных решений предложил weirdan (#) — использовать конструкцию try <… >finally <… >, где в блоке finally очищаем открытые ресурсы. Данный блок сработает всегда при завершении перебора генератора, но есть маленький нюанс: если перебор генератора отработал до конца (без break) нормально, то выполнится и код после блока finally.
Кратко о генераторах
— Не добавляют нового функционала в язык
— Быстрее*
— Возобновление работы генератора происходит с последнего «выброса» yield
— В генератор можно отправлять значения и исключения (через метод throw())
— Генераторы однонаправлены, т.е. нельзя вернуться назад
— Меньше кода в большинстве случаев, более простые для понимания конструкции
* Основываясь на этих результатах.
При больших масштабах перебора — генераторы быстрее. Примерно в 4 раза быстрее чем итераторы и на 40% быстрее обычного перебора. При небольшом количестве элементов могут быть медленнее обычного перебора, но все еще быстрее итераторов.
Если сообщество одобрит перевод и посчитает его хорошим (а главное, не утверждающим чепуху и не меняющим суть кода), мне будет интересно иногда переводить другие статьи.
Думаю, не будет лишним перевести и собрать в кучку статьи, публикуемые сейчас на phpmaster про структуры данных.
Также буду рад любым замечаниям, наставлениям, комментариям про ошибки как в тексте с кодом, так и в самом переводе.
Что такое генераторы в PHP
При вождении автомобиля – скорость это далеко не все. Но в WEB все решает скорость. Чем быстрее ваше приложение, тем лучше пользовательский опыт. Хорошо, эта статья о генераторах в PHP, так почему же мы говорим о скорости? Как вы увидите вскоре, генераторы привносят большие изменения по части скорости и потреблении памяти приложением.
Что такое PHP генераторы?
Добавленные в PHP в версии 5.5, генераторы представляют собой функции, обеспечивающие простой механизм для циклической обработки данных, без необходимости создавать массив данных в памяти. Все еще не понимаете о чем речь? Тогда давайте посмотрим на PHP генераторы в действии.
Создаем файл generator_test.php со следующим содержанием:
Затем в папке где у нас лежит этот файл открываем консоль и пишем следующее
Дальше открываем браузер и идем по следующему адресу:
Результат будет такой:
Данные 1
Данные 2
….
Данные 15
Код выше достаточно прост. Однако, давайте сделаем небольшое изменение в нем:
Теперь диапазон генерируемых чисел находится в пределах от 0 до константы PHP_INT_MAX, которая представляет собой наибольшее целое число, которое способен представить интерпретатор PHP. После этого опять идем в браузер и обновляем страницу. Однако на этот раз, вместо обычного текста получаем сообщение о том, что превышен объем доступной памяти, вследствие чего работа скрипта была аварийно завершена.
Что за досада – у PHP закончилась память! Первое что приходит на ум – это редактировать настройку memory_limit в php.ini. Но давайте спросим себя – действительно ли это так эффективно? Неужели мы хотим, чтобы какой-то единственный скрипт занимал всю доступную память?
Используем генераторы
Давайте напишем ту же самую функцию, что и выше, вызовем ее с тем же значением PHP_INT_MAX и запустим снова. Но в этот раз мы создадим функцию-генератор.
Определяя функцию getRange на этот раз, мы всего лишь проходим по значениям и генерируем вывод. Ключевое слово yield похоже на инструкцию return тем, что возвращает значение из функции, но единственное отличие заключается в том, что yield возвращает значение только тогда, когда это необходимо и не пытается вместить весь массив данных в память за один раз. Перейдя к браузеру, вы должны увидеть данные, отображаемые на странице. Обратите внимание на тот факт, что генераторы в PHP могут быть использованы только лишь из функции.
Зачем нужны генераторы?
Время от времени возникают такие задачи, когда нам необходимо обработать большие объемы данных (например, файлы логов), выполнить вычисления на больших выборках из базы и т.д. И мы отнюдь не хотим, чтобы эти операции занимали всю доступную память, так мы должны стараться сохранять память насколько это возможно. Данные не обязательно должны быть большими – PHP генераторы эффективны вне зависимости от размера данных. И не забывайте, что наша цель – сделать приложение быстрым и при этом таким, чтобы оно потребляло как можно меньше памяти.
Возврат ключей
Бывают случаи, когда нам необходимо возвращать не просто значение, а пару ключ-значение. При использовании генераторов, мы можем генерировать пары ключ-значение следующим образом.
Использовать данную функцию мы можем также как и простой массив:
Отсылка значений генераторам
Генераторы также могут принимать значения. Под этим подразумевается, что генераторы позволяют нам вставлять значения, которое может представлять собой подобие команды или еще что-то. Например, мы можем отправить значение в наш генератор, которое сигнализирует о необходимости остановки исполнения или изменения выходных данных. Далее пример кода:
Отмечу, что использование инструкции return в функции-генераторе приведет к немедленному выходу из этой функции.
Кстати, о генераторах я подробно рассказываю в моем курсе PHP и MySQL с Нуля до Гуру 2.0. Там есть и примеры и задания, которые помогут лучше усвоить материал.
Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)!
Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/myrusakov.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk.com/rusakovmy.
Если Вы не хотите пропустить новые материалы на сайте,
то Вы можете подписаться на обновления: Подписаться на обновления
Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.
Порекомендуйте эту статью друзьям:
Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):
Комментарии ( 2 ):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.
Copyright © 2010-2021 Русаков Михаил Юрьевич. Все права защищены.
Что такое генераторы и как их использовать php
В этом разделе помещены уроки по PHP скриптам, которые Вы сможете использовать на своих ресурсах.
Фильтрация данных с помощью zend-filter
Когда речь идёт о безопасности веб-сайта, то фраза «фильтруйте всё, экранируйте всё» всегда будет актуальна. Сегодня поговорим о фильтрации данных.
Контекстное экранирование с помощью zend-escaper
Обеспечение безопасности веб-сайта — это не только защита от SQL инъекций, но и протекция от межсайтового скриптинга (XSS), межсайтовой подделки запросов (CSRF) и от других видов атак. В частности, вам нужно очень осторожно подходить к формированию HTML, CSS и JavaScript кода.
Подключение Zend модулей к Expressive
Expressive 2 поддерживает возможность подключения других ZF компонент по специальной схеме. Не всем нравится данное решение. В этой статье мы расскажем как улучшили процесс подключение нескольких модулей.
Совет: отправка информации в Google Analytics через API
Предположим, что вам необходимо отправить какую-то информацию в Google Analytics из серверного скрипта. Как это сделать. Ответ в этой заметке.
Подборка PHP песочниц
Подборка из нескольких видов PHP песочниц. На некоторых вы в режиме online сможете потестить свой код, но есть так же решения, которые можно внедрить на свой сайт.
Совет: активация отображения всех ошибок в PHP
При поднятии PHP проекта на новом рабочем окружении могут возникнуть ошибки отображение которых изначально скрыто базовыми настройками. Это можно исправить, прописав несколько команд.
Агент
PHP парсер юзер агента с поддержкой Laravel, работающий на базе библиотеки Mobile Detect.
Реальные примеры использования генераторов в PHP
Несмотря на то, что генераторы доступны с версии PHP 5.5, они до сих пор используются крайне редко. На самом деле, большинство разработчиков, которых я знаю, понимают, как работают генераторы, но не видят, когда они могут быть полезны в реальных ситуациях.
«Да, генераторы, безусловно, выглядят здорово, но знаете. за исключением вычисления последовательности Фибоначчи, я не вижу, чем они могут быть мне полезны.»
И в чем-то такие люди правы, ведь даже примеры в документации PHP о генераторах довольно упрощены. Они только показывают, как эффективно реализовать диапазон или итерацию по строкам файла.
Но даже из этих простых примеров мы можем понять суть генераторов: это просто упрощенные итераторы.
Генератор позволяет вам написать код, который использует foreach для итерации по набору данных без необходимости создавать массив в памяти.
Учитывая это, я попытаюсь объяснить, почему генераторы являются потрясающими, используя некоторые примеры, взятые из реальных моих приложений, над которыми я работаю.
Немного контекста
У меня был один проект, направленный на чтение электронных книг. Книга проходит весь путь от получения файлов электронных книг от издателей до представления ее на сайте и предоставления конечному покупателю возможности читать их онлайн (с помощью веб-ридера) или на электронной читалке.
В большинстве примеров кода, которые будут показаны ниже, я буду ссылаться на эти метаданные под именем электронной книги.
Итерация больших наборов данных
Для первого примера предположим, что у меня есть большая коллекция электронных книг, и я хочу отфильтровать те, которые можно читать в веб-читалке.
Традиционно я бы написал что-то вроде этого:
Агрегирование данных с нескольких источников
Мы можем написать простой метод для объединения этих двух источников:
Но опять же, объем памяти, используемый этим методом, слишком сильно зависит от количества электронных книг, которые мы имеем в базе данных и в Elasticsearch.
Мы могли бы начать с использования генераторов для получение мерджа этих данных с различных источников:
Это лучше, но у нас все еще есть проблема: наш метод getBooks выполняет слишком много работы! Мы должны разделить эти две обязанности (чтение из базы данных и вызов Elasticsearch) на два отдельных метода:
Вы заметите использование оператора yield from (доступного с PHP 7.0), который позволяет делегировать использование генераторов. Это идеально подходит, например, для объединения нескольких источников данных, использующих генераторы.
Используя это ключевое слово, мы могли бы объединить несколько источников данных всего несколькими строками кода:
Сложная гидратация данных для строк базы данных со связями
Другим примером использования генераторов, который я нашел, была реализация гидратации данных, которая могла обрабатывать связи.
Наличие, и строки товаров заказа было необходимым условием для того, что нам нужно было сделать, поэтому я должен был написать метод, который мог бы возвращать объединенные данные по заказам, не будучи слишком медленным и не занимая много памяти.
Идея довольно наивна: соединить заказ и соответствующие строки, сгруппировать заказ и строки заказа вместе в цикле.
Генераторы при парсинге больших объемов данных
В своей работе и личных проектах мне часто приходится писать различные парсеры. И когда дело доходит до парсинга больших объемов данных, то тут очень выручают генераторы и асинхронные запросы.
В документации Guzzle есть отличные примеры использования генераторов с их библиотекой для параллельных запросов. Но я приведу свой личный код из персонального проекта.
У меня есть проект по сбору бесплатных прокси с многих источников и проверка их на валидность. Задача, по факту, делится на 2 этапа: сбор прокси и проверка на валидность. Сбор прокси условно быстрая операция. На один сайт, откуда мы парсим список прокси мы делаем от 1 до 5 запросов. Если таких источников будет 15-20, то даже в этом случае эти запросы можно выполнить синхронно где-то в очереди.
Проблемы начинаются, когда нам нужно проверить весь список спаршенных прокси на валидность. Потому что тут два нюанса: к-во прокси после первого этапа равно где-то от 15-20к. Проверка должна быть достаточно быстрой, чтобы в этом была целесообразность. Если мы будем в синхронном режиме ждать проверки каждого из прокси-сервера с максимальным тамймаутом в 5 секунд. То в худшем случае, 100 прокси проверится за 500 секунд. А проверка всего списка займет больше 27 часов.
Потому, нужно сформировать пулл из прокси и парралельно проверять их валидность. И как только один прокси проверен, в пулл добавляется новое значение. То есть, в один момент времени будет проверяться N-значений.
Если интересно, пишите на почту, и я обязательно напишу подробную статью как я писал проект на Laravel по многопоточной проверке прокси, проверки ответа, скорости соединения и это все на PHP.
Subscribe to Блог php программиста: статьи по PHP, JavaScript, MySql
Get the latest posts delivered right to your inbox
Как я писал кодогенератор на PHP и что из этого получилось
В этой статье я вам расскажу о том как я писал кодогенератор на php. Расскажу о пути, который он прошел от генерации простых таблиц, до довольно полноценного генератора html и css кода. Приведу примеры его использования и покажу уже сделанные проекты.
В этом семестре на одном из предметов можно было использовать только PHP.
После бесконеного ренейма проекта Проект получил имя MelonPHP. Чтобы люди думали о еде когда произносили его имя? Но у нас тут статья не о генерации бреда, поэтому давайте я вам расскажу о причине его создания.
Написать надо было много, но это не проблема. Основная проблема заключалась в выводе HTML кода через PHP. Я постараюсь объяснить проблему ниже.
Например вот вывод текста через всем знакомое echo:
Выглядит просто и понятно. Давайте возьмем отрезок кода моего друга, где он генерирует таблицу:
Это страшный код демонстрирует проблемы, которые я хотел решить:
Стоит отметить, что я относительно давно пишу на Flutter и мне очень нравится идея заложенная в его основе, связанная с написанием интерфейса с помощью постройки дерева из виджетов. Я решил позаимствовать оттуда идею с нодами (виджетами).
Я был уверен, что быстрее будет написать небольшой кодогенератор, чем терпеть это.
Изначально генератор занимался генерацией таблиц через функции. Но потом перерос в нечто более масштабное.
Основными идеями были следующими:
Особенности MelonPHP
Архитектура
Очень печально, что в PHP отсутствует передачу параметров по имени, и по этому я решил использовать вместо них функции. Я уверен что это не менее удобный аналог (Какого было мое удивление, когда Microsoft показали MAUI, который использует ту же идею с функциями).
Все классы в MelonPHP наследуется от Node. Это простой класс, который имеет только 2 функции: Generate(), static Create().
Element
Element — это более высокоуровневый класс, который нужен для более комфортного написания своих элементов.
Элемент в основном занимается генерацией чистого html кода.
Элементами в Фреймворк являются такие сущности как контейнер, кнопка, таблица и тд.
Component
Основная идея компонента в том, что этот класс управляет, и состоит из дерева элементов в нем. Компонент наследуется от элемента (бредовая идея).
Компонентами могут быть например дисплеи (аналог страниц в MelonPHP), списки, карточки, навигационные меню и тд.
Компонент удобен тем, что вам не надо генерировать строки; чем занимается Element. Так же в нем есть удобные функции для использования логики.
Попробуем написать простой компонент. Создадим класс ListItem, наследуемый от Component.
Перезапишем функции Initialize() и Build().
Initialize() вызывается при создание компонента. В ней например можно инициализировать переменные или обработать логику.
Build() вызывается при генерации элемента. В ней обязательно должен возвращаться элемент. Обязательная для перезаписи.
Detach() вызывается при удалении компонента.
В Build() возвратим контейнер, а в качестве его ребенка элемент текста и присвоим ему текст из переменной класса $Text.
В Initialize() пропишем значение $Text по умолчанию.
Добавим функцию Text(string) в которой будет записываться значение пользователя в переменную $Text.
DisplayComponent
DisplayComponent — это компонент, который может выводит сгенерированный код на страницу. Для генерации нужно вызвать функцию Display.
Попробуем написать пример простого дисплея.
В функции Build() возвратим Document и присваиваем ему Title(string).
В DisplayComponent, в функции Build() всегда должен возвращаться Document. Document — это класс, который генерирует стандартную разметку HTML5.
Создадим функцию BuildList(), в которой через цикл заполним колонку созданными выше ListItem.
В качестве ребенка документа вызовем BuildList() функцию. Разделение дерева из нод на функции не дает ему превратиться в макаронного монстра.
Если будет ситуация что надо выполнить какую-то логику прямо в дереве, то для того есть класс Builder. Но так лучше не делать.
После тела класса вызовем функцию Diplay(), которая при переходе на данный файл, на сайте cгенерирует его и выведит.
Макеты
Одна из целей преследуемых при создании фреймворка — удобство при создание макетов сайта.
Container
Container — это макет, который используется для декоративных целей.
Может содержать только одного ребенка.
Column и Row
У большинства элементов есть дети. Если у элемента доступен метод Child то он может иметь только одного ребенка, а если Children то у него может быть больше одного ребенка.
Так же Child перезаписывает переменную ребенка в то время как Children добавляет аргументы в верх стека.
Если в Children один элемент в аргументе, то не обязательно его заносить в массив.
Тоесть вместо Children([Text::Create()]) можно написать Children(Text::Create())
Column — это макет, который выравнивает его детей вертикально.
Обращаясь к функциям CrossAlign и MainAlign можно выравнивать детей внутри колонки.
Row идентичен Column, но выравнивает детей горизонтально.
Stack
Stack — это макет, дети которого не позиционированы. Это полезно если вы хотите чтобы ноды пересекались, для создания более красивого дизайна.
ScrollView, HorizontalScrollView, VerticalScrollView
Эти контейнеры который является областью для скороллинга.
HorizontalScrollView — в этом контейнере можно скроллить только по горизонтальной оси.
VerticalScrollView — в этом контейнере можно скроллить только по вертикальной оси.
ScrollView — в этом контейнере можно скроллить по всем осям.
Стилизация
Долга думая как лучше встроить css в фреймворк я пришел к идее с константами.
Например у нас в css есть «background-color». Я строку записываю в константу и в php коде можно будет использовать без «». Это намного удобнее.
Что касается например такой конструкции «34».Px. Тут идея с константами выглядит не читабельно. По этому я решил в таких ситуациях использовать функции для css — например Px(34). Выглядит понятно и вписывается в пхп код.
Простая стилизация
Для простой стилизации в элементе функция ThemeParameter(. ). Первый аргумент — это название параметра, а второй аргумент — это или массив из значений/значение.
В первом параметре мы изменим цвет фона на #f0f0f0.
Во втором параметре мы добавим отступы. Сверху и снизу 20px, справа и слева 15px.
Значения в массиве генерируются через пробел. Если вы хотите чтобы генерация происходила через запятую, то для этого есть функция CommaLine().
Как видно все очень просто и удобно, но если нам понадобятся модификаторы (hover например)? Для этого сделаны темы.
Темы в этом Фреймворк — это более продвинутый css, с media, keyframes, и модификаторами.
Напишем тему для контейнера с модификатором hover и active.
Для того надо создать класс темы и добавить в него ThemeBlock через метод ThemeBlocks.
Блоку темы нужно присвоить ключ / ключи. Я назову ключ my_container.
Дальше в блок темы можно добавить модификаторы. Я добавил: StandartModifier, HoverModifier, ActiveModifier. И задал для них параметры через метод Parameter(. ). Parameter работает так же как ThemeParameter.
Дальше контейнеру я присвоил ключ (имя класса в css) темы через метод ThemeKeys. Но для того чтобы тему можно было использовать ее надо добавить в документ через метод Themes.
После можно запустить дисплей и увидеть что тема по ключу применилась.
Продвинутые анимации
Для продвинутой анимации есть keyframes.
Для того чтобы добавить в тему keyframe, используйте метод FrameBlocks.
Добавим уже в существующую тему FrameBlock.
В FrameBlock есть метод Frames. Вызовем его и добавим несколько фреймов, так же для каждого фрейма надо указывать Value. Оно может быть в процентах (используйте функцию Pr(value)) или может быть константой From или To.
Каждому кадру можно добавлять параметры, которые будут уникальны и плавно изменятся между другими кадрами.
Так же добавим блок темы, к которому будет применяться эта анимация и назовем его shake_text.
Создадим текст в предыдущем дисплее, в контейнере и присвоим ему ключ темы.
Так же на примере видно, что в параметры можно использовать и обычные строки.
Адаптивность
Создадим еще 2 темы. Одна будет для мобильных девайсов, а вторая для пк. У темы есть функции: MinWidth, MaxWidth, MinHeight, MaxHeight, объявив которые вы можете указать на каком размере будет работать тема.
Теме для телефонов зададим MinWidth 800px.
Теме для пк зададим MaxWidth 800px.
Создадим блок темы где в стандартном модификаторе для мобильной версии будет присваиваться цвет фона зелёный, а на пк версии — желтый. Назовём блок adaptive_color.
Добавим обе темы в документ дисплея.
Добавим ключи темы к контейнеру.
Логика
Попробуем написать простой кликкер.
Для начала нам надо создать класс и наследовать его от DisplayComponent.
Создадим функцию Build() и возвратим в ней Document.
Добавим колонку в качестве ребенка документа.
Так же в качестве детей колонки добавим текст и кнопку.
Результат будет следующим.
Далее добавим простые ThemeParameter, чтобы сделать наш пример красивее.
Выглядит куда лучше в несколько простых строчек.
Теперь можно добавить логику.
Для начала нужно инициализировать функцию Initialize() и создать приватную переменную TapCount.
Аналог form в фреймворке — это Action.
Добавим Action в наше дерево элементов. Action тип пусть будет Post. В качестве детей укажем нашу колонку, где находится наша кнопка.
Далее добавим click_count переменную в Action. А в качестве её значение присвоим TapCount.
В Initialize() через Action::GetValue(name, standart_value, action_type) получим наше переменную. В качестве значения по умолчанию укажем 0, а в качестве типа укажем Post.
Добавим инкремент для нашей переменной.
Все, простой кликкер готов.
Мне удалось написать простой, но достаточно мощный кодогенератор.
Он прошел путь от генерации простых таблиц до полноценного генератора html и css, на котором можно удобно верстать проекты и совмещять верстку с логикой.
На данном фреймворке я написал курсовой проект (скриншоты ниже), использовал его на экзамене и делал на нём учебные задания.