Webiny Framework. Перший погляд

Є жарт про типовою кар'єрі розробника:

  1. Не використовує фреймворки
  2. Виявляє фреймворки
  3. Пише свої фреймворки
  4. Не використовує фреймворки
Пункт 4, звичайно ж, посилається на новопридбані здатності використовувати Composer для побудови власних фреймворків, створюючи їх з різних компонентів третіх сторін.

Всі ми знаємо, що екосистема PHP не страждає від нестачі різних фреймворків, і тому я трохи здивувався, коли побачив ще один, створений порівняно недавно.

Webiny
Він називається Webiny, і, будучи упакованим до країв удосконаленнями і змінами, які вони вважають необхідними, фреймворк має деякі дійсно цікаві компоненти, на які варто поглянути. У цій вступній статті ми не будемо звертати увагу на структуру в цілому, але висвітлимо основні її компоненти —
StdLib
.

Webiny StdLib

Ні, не те щоб «std».
StdLib
лежить в основі всіх інших суб-компонентів фреймворка. Бібліотека служить допоміжним ланкою, таким же, як і впровадження залежностей або Symfony/Yaml в будь-якому іншому PHP проекті.

Серед іншого, StdLib, як і багато інших до неї, робить роботу зі скалярами значно простіше, додаючи вільний об'єктно-орієнтований інтерфейс і деякі допоміжні методи. Наприклад, є полегшений
URLObject
, який містить кілька методів для роботи з редиректами, схеми, порти і т. д. Крім допомоги з ГО обгортками, бібліотека також пропонує базову валідацію, методи, що допомагають при складанні інших Webiny-компонентів, простий трейт для реалізації Singleton і багато іншого.

Основна відмінність
StdLib
полягає в тому, що вона реалізована у вигляді набору трейтов — дуже сильно недооціненою частини сучасної розробки PHP. Наприклад, вищезгаданий
URLObject
створюється ось так:
$this->url('http://www.webiny.com/');
, тому що трейт
StdObject
може бути доданий до будь-якого класу, якому необхідна дана функціональність. Більшість інших компонентів Webiny також реалізовані на трейтах. Зважаючи ізольованого характеру фреймворку, команда вибрала такий підхід для спрощення ієрархії класів і деякого розширення можливостей.

StdLib
автономно, безпосередньо з Composer або ж витягнути її по залежностях в якості складової частини всього фреймворку. Давайте подивимося, що ж вона пропонує нам?

Особливості

Всередині StdLib складається з двох суб-бібліотек. Одна з них —
Exception
, використовується тільки якщо ви маєте намір створювати додаткові Webiny-компоненти. Інша —
StdObject
, тут і міститься функціонал, про який ми говорили.

Крім того, бібліотека містить трейты, які використовуються в цих суб-бібліотеках.

ComponentTrait
ComponentTrait
корисний тільки, якщо ви створюєте додаткові Webiny-компоненти, чого ми робити не будемо. Пропустимо.

FactoryLoaderTrait
FactoryLoaderTrait
корисний у виклику екземплярів класу, які були динамічно визначені. Наприклад:

$className = 'myNameSpace\myClass';
$argument1 = 'Hello';
$argument2 = 'World';
$mustBe = 'myNameSpace\mySubClass';

Щоб инстанцировать клас
myClass
з аргументами «Hello» і «World», переконавшись, що він є екземпляром
mySubClass
можна використовувати такі підходи:

// standard PHP

try {
$instance = new $className($argument1, $argument2);
if (!($instance instanceof $mustBe)) {
throw new Exception("Instances don't match!");
}
} catch (Exception $e) {
// Handle exception
}

// FactoryLoaderTrait approach

try {
$instance = $this->factory($className, $mustBe, [$argument1, $argument2]);
} catch (Exception $e) {
// Handle exception
}

Як бачите, другий спосіб трохи коротше. Згоден, корисність цього трейта не вселяє довіри, особливо з урахуванням того, що динамічні імена класів не така річ, яку використовують усі, але якщо взяти до уваги масштаби всієї організації/фреймворку, то плюси подібної стандартизації стають очевидні. Підхід з використанням цього трейта є просто «стандартом кодування» для процесу створення екземплярів динамічних класів, в деяких ситуаціях це може бути корисно.

SingletonTrait
SingletonTrait миттєво перетворює ваш клас у Singleton. В інтернеті є безліч великих дискусій про «поганий природі Singleton» — не просто стародавні топіки, але і дещо наших старих дискусій про це. На всяк випадок, якщо раптом не буде можливості реалізувати нормальний DI-контейнер, то тут то цей трейт нам і стане в нагоді.

Майте на увазі одну річ —
__construct
кофлікти. Трейт не реалізує свою имплиментацию методу
__construct
, він може бути використаний як в класах, які мають свою реалізацію методу, так і в тих, які використовують інші реалізації
Singleton
. Але не забувайте, якщо ви вирішили використовувати цей трейт, ви повинні адаптувати свій конструктор класу у відповідності з паттерном.

Якщо ви новачок в роботі з синглтонами, цей пост може допомогти.

Необхідно також відзначити той факт, що в даній реалізації імплементуються публічний
init
і захищений
_init
методи, і виконуються вони саме в такому порядку після створення екземпляра.

Це корисно, так як ви можете використовувати його в якості механізму пост-ініціалізації для подальшого використання в якості Singeton-об'єкта без необхідності покладатися на свій конструктор. Можливо, ваш клас все ж має свій конструктор, і все що вам потрібно — перетворити його в Singleton, але така магія потребує більш тонкої настройки. Як раз для таких випадків ідеально підходить цей трейт.

StdLibTrait
Трейт є поєднанням
StdObjectTrait
та
ValidatorTrait
. Сам по собі
StdLibTrait
містить деякі функції допомогою кодування/декодування JSON, але вони досить убогі на даний момент, а як сериализовывать в JSON знають і інші PHP-аналоги. Можливо в цьому місці краще використовувати Serializer з Symfony — компонент, добре випробуваний співтовариством і має підтримку мультиформатов.

ValidatorTrait
Почнемо з того, що
ValidatorTrait
— проста колекція рідних для PHP методів перевірки типу, загорнута в трейт. Я не впевнений у його доцільності, і причини, якими керувалися автори, т. к. API залишається практично ідентичним (
self::isArray()
vs
is_array()
), але припущу, що це якось пов'язано з можливістю розширювати компонент, перевантажувати методи, реалізуючи власний функціонал.

В одному місці валідатор використовує
StdObjectWrapper
для забезпечення плавного інтерфейсу перевірки цих типів, який у свою чергу є частиною подбиблиотеки
StdObject
, що використовується в Webiny у якості об'єктно-орієнтованої обгортки для скаляров і форматування URL.

StdObjectTrait
Це ядро компонента
StdLib
, основна його частина. Цей трейт забезпечує базовими можливостями стандартного класу такі класи, як
ArrayObject
,
UrlObject
,
StringObject
та
DateTimeObject
.

StringObject
Мабуть, найпростіший з зв'язки. Цей об'єкт дозволяє використовувати рядки у якості об'єктів.

$string = $this->str("My string");

Примірник надає можливість зручної зміни кодування за замовчуванням в кодуванні UFT8) і деякі допоміжні методи, такі як
length
,
wordCount
та
subStringCount
, а також обгортки деяких вбудованих PHP функцій. Все це досить корисно мати під рукою, прямо всередині класу. Через
ManipulatorTrait
, загальним для всіх
StdObjects
,
StringObject
також є доступ до методів, які змінюють його: наприклад,
trim
— сама частоиспользуемая і корисна фіча при роботі з рядками.

Знову ж, величезною перевагою роботи з трейтами є не тільки можливість мати ланцюжок викликів над
string
-об'єктом, але і автопідстановка в IDE:


$string = $this->str("This is a string");
echo $string->hash()->padLeft(45, "testing");

Ось так, в одну сходинку ми взяли хеш і додали зліва
testing
до набору 45 символів. І ось результат —
testif72017485fbf6423499baf9b240daa14f5f095a1
. Навряд чи ви зможете зробити це ще простіше і зрозуміліше, ніж в прикладі вище.

В якості іншого прикладу, подивіться на цей код:

$string = new StringObject('Some test string.');
$string->caseUpper()->trimRight('.')->replace(' test'); // STRING SOME

Звичайно, об'єкт реалізує метод
__toString
для безпосередньої підготовки його до використання у вигляді рядка. Всі інші об'єкти StdLib також мають імплементацію цього методу для безпосереднього виводу на друк. На виході ви завжди зможете отримати примітив для подальшої роботи.

Ці методи занадто численні, щоб їх перераховувати; для отримання більш детальної інформації просто подивіться самі в исходниках.

ArrayObject
Аналогічно
StringObject
,
ArrayObject
пропонує зручний інтерфейс для роботи з масивом. Природно, він підходить для ітерацій через foreach і, взагалі, веде себе майже як рідний масив.

$array = new ArrayObject(['one', 'two', 'three']);
$array->first(); // StringObject 'one'
$array->append('four')->prepend('zero'); // ['zero', 'one', 'two', 'three', 'four']

Зверніть увагу, що при отриманні елементів цього масиву повертаються примірники
StdObject
, а не фактичні скаляри. Обчислене значення завжди буде мати тип
StdObjectAbstract
. Рядки виробляють
StringObject
, масиви виробляють
ArrayObject
. Цифри, що може бути дещо непослідовною, виробляють примірники
StdObjectWrapper
властивості
_value
, в якому задано наше число. Якщо елемент — це клас, то цей клас також буде обгорнутий. Наприклад:

$array = $this->arr([1, "2", new myClass(1, 2)]);
var_dump($array->last());

Ось що ми отримаємо:

image
Я не впевнений в своїх відчуттях з приводу цього. З одного боку — це суперпоследовательно, ми завжди отримуємо
StdObjectWrapper
в тій чи іншій формі. Але з іншого боку, якщо я маю справу з масивом класів, я не можу просто забути про обгортці і працювати як раніше: щоразу потрібно приділяти належну увагу цьому аспекту. Звичайно, є спосіб отримати реальні значення з будь-якого
StdLib
-об'єкта шляхом виклику методу
val
, який витягує базове значення, що міститься всередині.

Для різноманітних маніпулятивних прийомів з масивами, що базуються на
ArrayObject
, дивіться на відповідний трейт. Я знаходжу синтаксис за принципом ланцюжка особливо корисним, адже можна спуститися в багатовимірний масив вказавши
key1.key2.key3
в якості індексу, що, в свою чергу, є досить значною економією часу:

$array = [
'k1' => 'test',
'k2' => [
'k3' => [
'k4' => 'deepest'
]
]
];

$a = new ArrayObject($array);
$a->key('k2.k3.k4', 'webiny');
$a->key('k2.k3.k5', 'anotherElement');

echo $a>key('k2.k3.k4'); // webiny
echo $a>key('k2.k3.k5'); // anotherElement

URLObject
Цей об'єкт допомагає зробити синтаксис коду, який працює з URL, чистіше. Він підтримує деякі поширені коди відповіді, методи підпису запитів, роботу з портами та іншими змінними, а також може легко маніпулювати — перемикати схеми, хости, домени і т. д. за допомогою одного рядка надійного коду.

Дивно, що
URLObject
не успадковується від
StringObject
. Для мене наявність спадкування мало б сенс, по суті, обидва вони б виграли від маніпулятивних прийомів один одного.

Існують бібліотеки для роботи з URL-адресами й краще, так що я не бачу особливих переваг саме в цій. Перший приклад, який приходить на розум — чудова PHP League URL library, яка робить все це і багато чого іншого, має відмінну якість коду, який повністю протестований і активно підтримується більш ніж десятком висококваліфікованих розробників.

DateTimeObject
Нарешті, є
DateTimeObject
. Це обгортка над об'єктом datetime, забезпечує вільний і природний інтерфейс роботи, а також допомагає вирішити більшу частину проблем, що виникають при роботі з часом:

$dt = new DateTimeObject('3 months ago');
echo $dt; // 2013-02-12 17:00:36
$dt->add('10 days')->sub('5 hours'); // 2013-02-22 12:00:36

Само собою,
DateTimeObject
буде дуже зручний, якщо ви ще не використовуєте якусь бібліотеку для роботи з часом, але для більш складних маніпуляцій з datetime я б порекомендував Carbon, трохи приправлений бібліотекою Period.

Не можна сказати, що
DateTimeObject
зовсім марний — він за замовчуванням підтримує роботу з часовими поясами, у нього зручний формат виведення дати, прості і легкі diffs як альтернанивы
Period
, удобочитаемое «x часу назад», залежне від часових поясів та багато іншого.

Висновок

Маючи все це, Webiny-фреймворк дозволяє розробникам входити в проект зі своїм нативним кодом PHP. Всі обгортки ведуть себе саме так, як очікує розробник. Занадто амбіційно? Можливо. Але я розглядаю це як цікавий підхід, який визначає стандарти кодування перед початком роботи над проектом — він дозволяє обійтися без проблем, що виникають в ході подальшого проектування, заощаджуючи час в довгостроковій перспективі. Єдиний недолік, який я бачу — необхідність використовувати use в кожному окремому класі, в якому ви хочете мати підтримку StdObject.

Проект Webiny масштабний і цікавий. Фреймворк вражає, його компоненти охоплюють обширні області. Питання про те, чи це дійсно необхідно, я залишаю вас. Якщо вам цікаво, чому я так багато уваги йому приділяю уваги, і навіщо весь цей пост, то відповідь буде простим — мені подобається грати з новими речами; мені трохи цікаво як вони працюють зсередини.

В наступних постах ми розглянемо інші Webiny компоненти і навіть зробимо приклад подійно-орієнтованого додатки з використанням фреймворку, а також оцінимо його ефективність і гнучкість. А поки, попбробуйте самі та розкажіть про свій досвід.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.