В городе появился новый игрок, и он принес новые игрушки: PHP World приветствует FLOW3 , инфраструктуру корпоративных приложений, созданную и поддерживаемую сообществом TYPO3 CMS . FLOW3 может использоваться в качестве автономной среды полного стека для ваших приложений. Это интересно, потому что в нем представлены некоторые концепции разработки программного обеспечения, которые ранее не были адаптированы к PHP.
Среди этих новых концепций — «Аспектно-ориентированное программирование». Мы взглянем на теорию паттернов, настроим базовое приложение FLOW3 и сплелись в своем собственном аспекте!
Почему это должно меня волновать?
Если новый фреймворк выйдет стабильным, может возникнуть отличный вопрос: что он может сделать, чего не могут инструменты, которые я люблю?
У PHP уже есть армада превосходных фреймворков, и большинство из них утверждают, что они написаны с учетом разделения интересов. Так же как и FLOW3.
С точки зрения разработки программного обеспечения это означает, что классы, реализующие логику вашего приложения, должны заботиться только об одном. Например, класс почтовой программы должен отправлять письма. Он не должен извлекать потенциальных получателей из базы данных.
Все современные фреймворки (включая FLOW3) внедряют в программный стек множество шаблонов, которые отлично справляются с разделением задач вашей бизнес-логики; среди них знаменитый MVC, который разделяет вашу логику на разные слои.
Однако приложение основано не только на бизнес-логике. По мере роста вы можете захотеть реализовать дополнительные сервисы, функции, плагины или плагины плагинов. Вы, конечно же, не хотите этого в своей бизнес-логике! Но каковы ваши варианты?
Допустим, вы хотите реализовать службу ведения журналов, которая записывает некоторые вещи в текстовый файл каждый раз, когда определенный набор удаляется из базы данных. Естественно, служба журналирования не является частью вашего уровня базы данных. Но для того, чтобы он заработал, вам нужно разместить код прямо здесь, например так:
1
2
3
4
5
6
7
8
9
|
/**
* Delete a user
* @param UserModel $user
* @return void
*/
public function deleteUser(UserModel $user) {
$this->loggingService->log(‘removing user ‘ . $user->getName());
$this->users->remove($user);
}
|
Ужасно, не правда ли? Посмотрим правде в глаза: служба регистрации добавляет грязи в ваш код. Вам нужно дотронуться до исходного кода, чтобы реализовать его, и вы добавляете дополнительную зависимость, в данном случае экземпляр LoggingService.
Следовательно, вы добавляете сложность. И вам, вероятно, придется добавить все это в ваше приложение (в зависимости от того, что еще вы хотите войти).
Вы можете придумать шаблон типа диспетчера событий или систему перехвата, как это реализовано в Drupal и WordPress, и затем ваш регистратор будет прослушивать события:
1
2
3
4
5
6
7
8
9
|
/**
* Delete a user
* @param UserModel $user
* @return void
*/
public function deleteUser(UserModel $user) {
$this->eventDispatcher->dispatch(self::USER_DELETED);
$this->users->remove($user);
}
|
Это лучше, но все равно не круто. Ваш код по-прежнему осведомлен о зависимостях: он имеет некоторый код с единственной целью информировать другие стороны о том, что что-то происходит.
Это имеет некоторые плохие побочные эффекты: что, если вы хотите что-то зарегистрировать, но в это время событие не отправляется? Что если вы не написали этот код, потому что вы пишете плагин? Вам придется изменить исходный код — не один раз, но каждый раз, когда доступно обновление.
Давайте назовем сервис журналирования аспектом и посмотрим, как FLOW3 решает эту проблему.
Аспект Ткачество
Наш сервис регистрации интересуется только одной вещью, касающейся существующего кода: он хочет перехватить его в некоторых классах в различных точках (например, когда что-то удалено). В реализации, описанной выше, существующий код контролирует точки выполнения.
Разве не было бы неплохо, если бы служба могла определить набор правил, и в этот момент она захотела бы внедрить себя в класс?
Это концепция разработки программного обеспечения, названная Inversion of control. Он отделяет классы от другого, потому что нет необходимости изменять существующий код при изменении службы. Сервис позаботится об этом сам.
Допустим, мы просто хотим выполнить ведение журнала для каждого метода в каждом классе, который начинается с delete, например deleteUser()
, deleteEntry()
или чего-либо еще. Удобным способом было бы написать правило в службу ведения журнала и позволить инфраструктуре позаботиться о правильном выполнении кода. Это очень просто в FLOW3:
1
2
3
4
5
6
7
8
|
/**
* An aspect that is executed on all methods that start with «delete».
*
* @FLOW3\Before(«method(.*->delete.*())»)
*/
public function logginMethod() {
// Do some logging here.
}
|
FLOW3 выполняет некоторые действительно продвинутые методы PHP под капотом.
Правило называется pointcut в AOP, и есть несколько простых в освоении способов их определения. В приведенном выше примере FLOW3 вводит код регистрации в каждом классе перед каждым методом, который начинается с delete.
Это делается с помощью ключевого слова метода и путем добавления подстановочных знаков (*) для класса и для частей имени метода. Если вы добавляете этот код в Aspect Class (как мы это сделаем позже), то все готово. Вы также сможете получить доступ к свойствам целевого класса. Целевой класс сам по себе остается нетронутым. Возможно, он еще не записан — если он будет добавлен в ваше приложение в будущем, тогда правило будет соответствовать, и код будет введен.
Может быть, вы прочитали вышеупомянутый абзац дважды и спрашиваете себя: это волшебство? Что за чертов трюк? — Я уверен, что сделал, когда я впервые познакомился с АОП!
Ну, АОП уже некоторое время существует в мире Java. FLOW3 в значительной степени вдохновлен реализацией AOP Spring, Java Framework. На улице было слово, что шаблон нельзя перенести на интерпретируемые языки, потому что он требует компиляции.
Хитрость в том, что фреймворк — или некоторые инструменты, такие как JAspect — перехватывает процесс компиляции (от Java-кода до байтового кода, который может быть выполнен во время выполнения Java) и выполняет анализ определенных аспектов. Всякий раз, когда набор правил (так называемый pointcut) соответствует методу, он включается непосредственно в скомпилированный код. Следовательно, фреймворк заботится о слиянии частей кода:
FLOW3 адаптировал эту концепцию к миру PHP, представив расширенный кеш. В то время как большинство фреймворков предлагают кэширование для повышения производительности, FLOW3 дополнительно использует кеш для объединения аспектов в исходные классы.
Если вы запускаете свое приложение, FLOW3 поставляет кэшированные классы вместо тех, которые вы написали. Следовательно, ваш оригинальный код остается нетронутым.
Это звучит сложно — и это так: FLOW3 выполняет некоторые действительно передовые методы PHP под капотом, которые стоит изучить! Но: для вас, как для разработчика приложений, это совсем не сложно. Вам не нужно беспокоиться, если вы не хотите: FLOW3 отслеживает ваши файлы на предмет изменений и, если вы их вносите, FLOW3 автоматически фиксирует их в кеше при вашем следующем запросе. Если аспект соответствует методу, он автоматически включается — дальнейшая настройка не требуется.
Это сложный шаблон удобно к вашим услугам!
Несколько слов о кеше
Каждая интересная функция имеет свои недостатки — как и FLOW3. На данный момент мы только что коснулись того, на что способен механизм кэширования FLOW3, но я хочу дать вам идею.
Посмотрите на следующий код:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
/**
* @FLOW3\Inject
* @var \YourApp\MailerServiceInterface
*/
protected $mailer;
/**
* Sends a mail.
*
* @param \YourApp\Model\User $user
* @param string $message
*/
protected function sendMail(\YourApp\Model\User $user, $message) {
// Sends a mail.
}
|
Следствием этого является то, что PHP в некоторой степени больше не является интерпретируемым языком.
Это может выглядеть как некоторый код, который хорошо документирован. Но это гораздо больше.
Первая аннотация свойства (FLOW3 \ Inject) говорит FLOW3 искать некоторый класс в вашем приложении, который реализует MailerServiceInterface и внедряет его здесь (и, возможно, настраивает его раньше). Эта модель называется инъекцией зависимостей .
Второй класс определяет некоторые определения типов в аннотациях комментариев, и FLOW3 использует их. Если вы предоставляете методу некоторые данные, которые можно использовать для создания объекта User вашей модели (например, массив с чистыми строками из HTML-формы), FLOW3 автоматически преобразует данные в объект User и применяет некоторую безопасность и правила проверки для второго параметра (в данном случае строка).
Это (среди некоторых других приемов) делается путем вставки некоторого кода в кеш.
Следствием этого является то, что PHP в некоторой степени больше не является интерпретируемым языком. Процесс кеширования похож на компиляцию и имеет те же недостатки: вы не можете просто сохранить и обновить свой браузер, вам нужно подождать, пока кеш прогреется во время разработки. Если вы фиксируете код в своей производственной среде, вы должны очистить кеш, чтобы увидеть изменения.
Кроме того, FLOW3 добавляет некоторую проверку типа во время выполнения: вы не можете передать целое число в качестве второго аргумента в приведенном выше примере — это вызовет ошибку. Мне это очень нравится, потому что у меня есть опыт работы в строго типизированных языках, таких как Java и ActionScript. Это также повышает безопасность вашего приложения. Но если вы являетесь пуристом PHP, это может быть тем, что вам не нужно.
Настройка Flow3
Хорошо, достаточно с теорией. Давайте испачкаем руки.
Настройка FLOW3 довольно проста, если у вас уже есть сервер с поддержкой PHP, такой как XAMP или MAMP. Просто возьмите копию фреймворка с сайта загрузки FLOW3 (или клонируйте с git ) и распакуйте ее в свой каталог htdocs.
Вы уже должны быть сделаны сейчас на машинах Windows. Системы на основе Unix, такие как MAC или Linux, должны предоставить необходимые права для построения кэша. FLOW3 поставляется с мощным инструментом командной строки, который упаковывает эту задачу в одну строку:
1
2
3
4
|
# On linux:
./flow3 core:setfilepermissions chris www-data www-data
# On Mac:
./flow3 core:setfilepermissions chris _www _www
|
Пожалуйста, замените Крис своим именем пользователя! Этого должно быть достаточно. Тем не менее, я установил FLOW3 на
различные машины и столкнулись с некоторыми проблемами. Наиболее распространенные из них:
- Если вы используете MAC и FLOW3 выдает сообщение «index.php not found», откройте .htaccess в веб-каталоге и добавьте # перед строкой Rewrite Base / «-
- Если вы работаете в Windows и инструмент командной строки не работает, откройте файл Settings.yaml.example в каталоге конфигурации FLOW3, раскомментируйте и измените переменную phpBinaryPathAndFilename на правильный путь (например, C: \ xampp \ php \ php.exe) и сохранить файл как Settings.yaml
- На некоторых машинах, если FLOW3 очень медленный, вам нужно увеличить лимит памяти PHP в php.ini
Если вы столкнулись с проблемами, которых здесь нет, пожалуйста, оставьте их в комментариях!
Теперь запустите ваш любимый браузер и перейдите по адресу http: // localhost / flow3 / Web! FLOW3 должен приветствовать вас с вводным экраном:
Это приветственный пакет. Каждое приложение в FLOW3 (и сам FLOW3) представляет собой пакет, и пакеты могут обмениваться кодом друг с другом. Например, когда новая версия TYPO3 будет закончена, вы можете добавить ее в виде пакета, и тогда ваше приложение будет иметь готовую к использованию CMS!
Давайте начнем наш собственный пакет. Это простая задача, потому что инструмент командной строки FLOW3 справится с большей частью работы! Введите следующую команду:
1
2
3
4
|
# On linux / MAC
./flow3 kickstart:package Nettuts.AspectDemo
# On Windows
flow3 kickstart:package Nettuts.AspectDemo
|
Это создаст готовый к работе пакет с именем Nettuts.AspectDemo
в папке Packages / Application FLOW3. Если вы просмотрите структуру файла, вы найдете много папок, которые соответствуют определенному соглашению.
Важными из них являются:
- Классы: Дом вашего PHP-кода, первый Контроллер уже создан в Папке Классов / Контроллеров: Стандартный Контроллер!
- Ресурсы / Частные: Дом всех шаблонов, которые запускаются во Fluid, мощном шаблонизаторе FLOW3. Он также содержит некоторые другие вещи, такие как файлы локализации.
- Ресурсы / Общедоступные: это еще не было создано, но каждый файл в папке Ресурсы / Общедоступный будет напрямую доступен из Интернета. Это хорошее место для изображений, CSS, Javascript и т. Д.
Создание полнофункционального приложения выходит за рамки данного руководства, поэтому мы перейдем к настройкам по умолчанию. Пожалуйста, откройте следующий URL: http://localhost/flow3/Web/index.php/Nettuts.AspectDemo
Это немного, но это начало. Мы хотим что-то записать в лог-файл, каждый раз, когда этот сайт вызывается. Мы делаем это, вплетая один аспект в StandardController!
Написание первого аспекта
Посмотрите на StandardController в папке Classs / Controller в вашем Nettuts.AspectDemo
.
Он реализует метод с именем indexAction()
. FLOW3 автоматически настроил наше приложение таким образом, что этот метод внутри StandardController вызывается, когда мы вводим указанный выше URL.
На данный момент мы видим, что этот метод назначает несколько строк представлению:
01
02
03
04
05
06
07
08
09
10
|
/**
* Index action
*
* @return void
*/
public function indexAction() {
$this->view->assign(‘foos’, array(
‘bar’, ‘baz’
));
}
|
Если вы откроете файл index.html этого представления в Resources / Private / Templates / Standard внутри нашего пакета, вы увидите, как эти строки обрабатываются механизмом шаблонов.
Однако наша цель состоит в том, чтобы перехватить выполнение кода до вызова этого метода.
Сначала создайте пустой файл с именем Access.log в папке журнала FLOW3 в файле flow3 / Data / Logs. Вы можете сохранить этот файл где угодно, но это хорошее место для файлов журнала.
Затем создайте папку с именем Service в папке Nettuts.AspectDemo / Classes и внутри нее, создайте другую папку и назовите ее Logging. Затем создайте файл PHP с именем LoggingAspect.php внутри него. Он должен иметь следующий контент:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
<?php
namespace Nettuts\AspectDemo\Service\Logging;
use TYPO3\FLOW3\Annotations as FLOW3;
/**
* @FLOW3\Aspect
*/
class LoggingAspect {
/**
* Log a message if this site is called.
*
* @param \TYPO3\FLOW3\AOP\JoinPointInterface $joinPoint
* @FLOW3\Before(«method(Nettuts\AspectDemo\Controller\StandardController->indexAction())»)
* @return void
*/
public function logSiteAccess(\TYPO3\FLOW3\AOP\JoinPointInterface $joinPoint) {
$filePath = ‘/home/chris/www/flow3/Data/Logs/Access.log’;
$message = ‘The site has been accessed at ‘ .
file_put_contents($filePath, $message, FILE_APPEND);
}
}
?>
|
Это очень простой класс, и если вы хотите иметь продвинутый регистратор, вы можете отделить протоколирование от перехватывающего аспекта.
Что тут происходит? Сначала мы определяем пространство имен, которое представляет собой имя вашего пакета и путь к фактическому классу (без самой папки класса). Далее мы импортируем пространство имен аннотации FLOW3. Таким образом, мы можем использовать мощный анализатор аннотаций FLOW3 для настройки нашего класса.
Это именно то, что мы делаем в комментариях к классу. Мы определяем класс как аспект со следующей аннотацией:
1
2
3
|
/**
* @FLOW3\Aspect
*/
|
Отныне FLOW3 знает, что этот класс может определять правила для перехвата потока кода, и отслеживает нашу кодовую базу на предмет совпадений.
Это правило:
1
|
@FLOW3\Before(«method(Nettuts\AspectDemo\Controller\StandardController->indexAction())»)
|
Мы могли бы использовать регулярные выражения или некоторые другие методы для сопоставления нескольких методов или классов, но мы точно знаем, куда мы хотим пойти: перед indexAction()
в StandardController — и это именно то, что мы говорим FLOW3.
Обратите внимание на параметр $jointInterface
метода logSiteAccess()
, предоставленный нам FLOW3. Мы не используем его здесь, но он содержит некоторые полезные данные о реальном контексте. Например, если перехваченный метод будет иметь аргументы, которые мы можем захотеть использовать, мы можем получить к ним доступ следующим образом:
1
|
$argument = $joinPoint->getMethodArgument(‘argumentName’);
|
Код в нашем logSiteAccess()
сам по себе довольно прост. Просто настройте путь к Access.log в вашей среде, и все готово!
Теперь снова откройте http://localhost/flow3/Web/index.php/Nettuts.AspectDemo
— ничего не должно было измениться. Но когда вы открываете файл Access.log, вы должны увидеть следующую запись:
1
|
The site has been accessed at 1335715244.
|
Если вы можете прочитать это, аспект был сплетен в кеш. Давайте посмотрим на магию!
FLOW3 хранит кэш в flow3/Data/Temporary/Development/Cache/Code/FLOW3_Object_Classes
.
Вы найдете класс с именем Nettuts_AspectDemo_Controller_StandardController_Original.php
. Это оригинальный класс из нашей кодовой базы.
Но есть и другой класс: Nettuts_AspectDemo_Controller_StandardController.php
. Этот класс расширяет наш исходный класс и создается исключительно из сгенерированного кода. Среди множества строк вы найдете что-то вроде этого:
1
2
3
4
5
6
7
8
9
|
$this->FLOW3_Aop_Proxy_targetMethodsAndGroupedAdvices = array(
‘indexAction’ => array(
‘TYPO3\FLOW3\Aop\Advice\BeforeAdvice’ => array(
new \TYPO3\FLOW3\Aop\Advice\BeforeAdvice(
‘Nettuts\AspectDemo\Service\Logging\LoggingAspect’, ‘logSiteAccess’
$objectManager, NULL),
),
),
);
|
Это — то, где FLOW3 инициирует аспект, и это вплетено непосредственно в ваш класс. Аспект выполняется именно там, где мы хотим, не касаясь оригинального контроллера.
Фрагмент кода инициирует экземпляр класса BeforeAdvice
с нашим аспектом, соответствующим методом и $objectManager
качестве аргументов. Диспетчер объектов содержит необходимую информацию для создания jointInterface
, которая передается аспекту.
Если вам интересны подробности, взгляните на папку AOP в пакете FLOW3!
Вывод
Аспектно-ориентированный подход имеет много преимуществ, ожидающих открытия.
Аспектно-ориентированный подход имеет много преимуществ, ожидающих своего открытия. Например, FLOW3 использует AOP для перехвата собственной начальной загрузки для создания брандмауэра, если вы решите использовать инфраструктуру безопасности FLOW3. Кроме того, мы еще не обсуждали конкретную терминологию, которая обычно используется в АОП.
Однако FLOW3 поставляется с некоторыми другими уникальными концепциями, такими как Domain-Driven-Design. Он имеет мощную интеграцию с Doctrine и умный движок шаблонов. Мы видели только верхушку айсберга!
Каково твое мнение? Вы хотите узнать больше о философии FLOW3? Каково ваше впечатление от АОП? Вы взволнованы иметь надежный порт для мира PHP или думаете, что это слишком много для интерпретируемого языка?