Аспектно ориентированное программирование (АОП) — парадигма программирования, основанная на идее разделения функциональности для улучшения разбиения программы на модули. © Wiki. Проблема “сквозной”; функциональности: Как жаль, что мы не живем в идеальном мире! В идеальном мире разработчик занимался бы только программированием чистой бизнес логики приложения. К нашему превеликому сожалению, зачастую в бизнес логике появляются участки кода, не имеющие к решаемой задаче прямого отношения, например, авторизация, журналирование, транзакционный контроль и проч. Этот код нарушает стройность бизнес логики, делая ее более сложной для понимания и дальнейшей поддержки.  Большая статья с примерами и исходниками.

Почти настоящий “пример из жизни”;:

Не будем голословными и представим себе пример идеального кода контроллера, который создает объект новости:

class NewsController extends Controller {
 function create(){
   $news = new News();
   $news->setDate($this->request->get('date'));
   $news->setContent($this->request->get('content'));
   $news->save();
 }
}

На время отбросим детали того, каким образом приложение заставляет отрабатывать именно этот контроллер, вместо этого сосредоточимся на бизнес логике. А эта логика, к счастью, проста и читабельна. Беда лишь в том, что на практике все так просто никогда не бывает. Вместо приведенного выше кода скорее будет нечто похожее на следующее:

class NewsController extends Controller {
 function create(){
   $ctx = AppContext :: instance();
   if($ctx->isUserAuthorized())
   {
    $news = new News();
    $news->setDate($this->request->get('date'));
    $news->setContent($this->request->get('content'));

    $ctx->startTransaction();
    try {
       $news->save();
       $ctx->commitTransaction();
       $ctx->log("News created successfully");
     }
     catch(ValidationException $e) {
       $ctx->rollbackTransaction();
       $ctx->log("News validation error");
       throw $e;
     }
   }
   else
   {
    throw new AuthException();
    $ctx->log("Operation is not permitted");
   }
 }
}

Как видно, простая бизнес логика утопла в загромождении обслуживающего технического кода, который, по сути, затмил собой суть происходящего. Что именно собой представляет этот «технический код»?

  1. Происходит проверка на тот факт, если пользователь авторизован для выполнения действия(if($ctx>isUserAuthorized())).
  2. Если пользователь имеет достаточные права, перед сохранением объекта стартует транзакция БД($ctx>startTransaction()), иначе же заносится ошибка авторизации в журнал($ctx>log(«Operation is not permitted»)) и выбрасывается исключение подсистемы авторизации.
  3. После создания новости, если все прошло успешно, происходит запись транзакции($ctx>commitTransaction()).
  4. В журнал записывается успешность выполнения действия($ctx>log(«News created successfully»)).
  5. Если возникла, ошибка валидации новости, то происходит откат транзакции($ctx>rollbackTransaction()) и запись ошибки в журнал(ctx>log(«News validation errror»))

Очевидно, что из этого описания можно выделить три четко определенных функциональных роли:

  • Система авторизации и контроля доступа
  • Целостность данных при помощи транзакций
  • Система журналирования

Самое же печальное то, что этот же функционал в той или иной форме «разбросан» и в других модулях системы. Подобный стихийный функционал, сложно поддающийся четкой инкапсуляции, называют «сквозным». В английском языке есть очень удачный термин для этого – crosscutting concerns. Бесспорно, что «сквозная» функциональность приводит к рассредоточенному и запутанному коду.

Если посмотреть на приложение «с птичьего» полета, то картина взаимодействия «сквозного» функционала с модулями выглядит примерно так:

fetch

Основной целью АОП является именно организация контроля над «сквозным» функционалом с возможностью его выделения в отдельные сущности.

“AOP to the rescue”;

Попробуем переписать пример, приведенный выше, с использованием АОП принципов. Не пугайтесь, если что-то будет непонятно, более подробно базовые понятия и термины АОП будут приведены ниже.

Во-первых, оставим оригинальный «идеальный» код:

class NewsController extends Controller {
 function create(){
   $news = new News();
   $news->setDate($this->request->get('date'));
   $news->setContent($this->request->get('content'));
   $news->save();
 }
}

Во-вторых, напишем три аспекта, инкапсулирующих «сквозной» функционал:

Первым будет аспект авторизации пользователей:

aspect Authentication{
    pointcut controllerCreate:exec(public *Controller::create());
    around() : controllerCreate{
        $ctx = AppContext :: instance();
        if($ctx->isUserAuthorized())) {
          proceed();
        } else {
          $ctx->log("Operation is not permitted");
          throw new AuthException();
        }
    }
}

Что здесь происходит:

  1. Объявляется срез(pointcut controllerCreate) всех точек исполнения метода create() во всех классах, оканчивающихся на строку Controller. Срезы точек исполнения в терминах АОП имеют название «pointcut», а сами точки исполнения – «joinpoint».
  2. Объявляется «АОП метод»(around controllerCreate), который будет исполняться вместо (правильнее сказать «вокруг») всех точек среза controllerCreate. Подобные «методы» называются «advice» в терминах АОП. В теле advice происходит проверка авторизации пользователя, и если она успешна, контроль выполнения кода возвращается в «обернутую» точку исполнения вызовом метода proceed(). Иначе же происходит запись об ошибке доступа в журнал и выбрасывается исключение.

Вторым реализуем аспект, обеспечивающий транзакционный контроль:

aspect Transaction{
    pointcut save:call(public News->save());
    around(): save{
        $ctx = AppContext :: instance();
        $ctx->startTransaction();
        try {
          proceed();
        } catch (ValidationException $e) {
          $ctx->rollbackTransaction();
          $ctx->log("News validation error");
          throw $e;
        }
    }
}

Что здесь интересного:

  1. Объявляется срез «pointcut save», который выбирает все точки исполнения, в которых вызывается метод News::save(). Заметьте разницу между типами срезов – в этом случае используется «call»(вызов), в предыдущем примере использовался «exec»(исполнение). Call захватывает точку вызова метода объекта, тогда как exec захватывает непосредственно исполнение самого тела метода.
  2. Объявляется advice «around(): save», который «обворачивает» все точки среза save. В его теле стартует транзакция и вокруг точки исполнения устанавливается «try..catch..» блок, отлавливающий все ValidationException ошибки и откатывающий транзакцию в случае их появления с записью причины в журнал.

Реализуем последний аспект, отвечающий за журналирование:

aspect Logging{
    pointcut save:call(public News->save());
    after(): save{
        $ctx = AppContext :: instance();
        $ctx->log("News created successfully");
    }
}

После первых двух аспектов здесь все довольно просто:

  1. Объявляется срез «pointcut save», который выбирает все точки исполнения, в которых вызывается метод News::save().
  2. Объявляется advice «after(): save», который исполняется после точки среза save. В теле advice происходит запись об успешном выполнении в журнал.

Диаграмма взаимодействия аспектов с кодом в рассмотренном примере будет примерно такой:

fetch2

На схеме видно, как advice методы «around():exec(public *Controller::create())» и «around(): call(public News>save())«обворачивают все точки вызова «*Controller::create» и «News>save()» соответственно. А advice метод «after():call(public News>save)» указывает на точку вызова после выполнения «News>save()».

Хотелось бы надеяться, что показанных примеров использования АОП было достаточно, чтобы заинтриговать читателя, однако чтобы продолжить, нам потребуется немного теории.

Общие понятия и термины АОП

Базовые идеи и принципы АОП были сформированы профессором Грегором Кицзалесом (Gregor Kiczales) из канадского университета The University of British Columbia. Под его руководством команда разработчиков из Xerox PARC разработала первую и самую популярную имплементацию АОП для Java — AspectJ. Реализация получилась настолько удачной, что на данный момент AspectJ является «lingua franca» для базовых АОП понятий. Рассмотрим их ниже.

JoinPoint — фундаментальное понятие АОП, под которым понимают любую четко идентифицируемую точку исполнения программы. Например, вызов метода, как и, собственно, тело метода являются такими точками. Даже присваивание или возврат значения переменной являются подобными точками. Добавьте к этому конструкторы, исключительные ситуации, условные проверки, операторы и проч. Как видно из описания, joinpoint являются точками возможной инъекции «сквозной» функциональности.

Все joinpoint точки обладают некоторым контекстом. Например, точка вызова метода в качестве своего контекста предоставляет a) объект, вызвавший метод, b) объект, чей метод был вызван, c) аргументы вызванного метода.

Рассмотрим небольшой пример:

class Account {
  function credit($amount) {
    $this->balance += $amount;
  }
}

oinpoint точками в приведенном коде являются: исполнение тела метода credit, доступ к атрибуту balance.

Pointcut — набор/срез joinpoint точек, удовлетворяющих определенному условию. Например, подобным срезом точек может является выполнение всех методов начинающихся с «set».

Пример pointcut среза, захватывающего исполнение метода credit:

exec(Account::credit(*))

Срезы pointcut могут быть как анонимными так и именованными. Пример анонимного pointcut был показан выше, именованные же pointcut правила позволяют повторно использовать срезы с помощью некоторого идентификатора. Рассмотрим простой случай:

poincut credit:exec(Account::credit(*))

Наверное, хорошим примером для понимания pointcut срезов является аналогия с SQL запросами. Если SQL мы используем для выборки данных из БД, то в случае pointcut мы выбираем joinpoint точки по определенным правилам.

Advice — код, выполняемый для каждой joinpoint точки, входящей в определённый pointcut. Advice может выполняться до (before), после (after) или вместо (around) joinpoint точки. Внешне advice очень схож с традиционным ООП методом.

Рассмотрим простой пример для приведенного выше анонимного pointcut среза:

before(): exec(Account::credit(*))  {
  echo("Сейчас будет выполнен метод credit");
}

Теперь посмотрим, как будет выглядеть этот пример с использованием именованного pointcut среза:

before(): credit  {
  echo("Сейчас будет выполнен метод credit");
}

Данный advice выведет сообщение перед каждым выполнением метода Account::credit().

Introduction – инструкция для изменения статической структуры классов, интерфейсов и аспектов. Применяется, например, для того, что бы добавить дополнительный интерфейс к существующему классу, или изменить цепь наследников.

Попробуем добавить метод setLog во все классы, которые начинаются со строки «Foo»:

public function Foo*::setLog(Log $log){
    $log->setLevel(Log::ALL);
    $this->log = $log;
}

Aspect — модуль в терминах АОП, некоторый аналог класса, который инкапсулирует в себе правила переплетения(weaving) некоторого сквозного функционала. Именно aspect содержит в себе pointcut, advice, introduction правила. Aspect схож функционально с классом и также позволяет объявлять и использовать обычные методы и атрибуты.

Все примеры, приведенные выше, мы можем теперь объединить в один простой aspect:

aspect ExampleAspect {
  before(): exec(Account::credit(*)) {
    echo("Сейчас будет выполнен метод credit");
  }
}

Weaving – процесс «вплетения» аспектов в логику приложения. Процесс «вплетения» может происходить на уровне исходных кодов или же на уровне виртуальной машины (в случае PHP, это уровень исполнения opcode инструкций). Процесс разбиения функциональных требований на аспекты с их последующим сплетением в конечный код приложения можно условно представить в виде двух призм:

fetch3

В первой призме монолитные функциональные требования разбиваются на узкоспециализированные аспекты. Если задуматься, то и бизнес логика, по сути, также является аспектом. Аспекты проходят этап имплементации и во второй призме сплетаются вместе в результирующее конечное приложение.

Средства АОП для PHP

На данный момент существует несколько проектов, пытающихся реализовать поддержку АОП в PHP. Откровенно говоря, все они находятся либо в глубокой альфе, либо не являются по-настоящему АОП решениями (если брать за идеал AspectJ). Перечислим кратко самые известные:

  1. **phpAspect **(http://phpaspect.org) – юная, но наиболее перспективная, на мой взгляд, попытка Вильяма Кандилона(Willliam Candillon) сделать аналог AspectJ для PHP. Библиотека получила «вторую жизнь» во время GSOC2006(Google Summer of Code), когда она была переписана на C, а для трансформации кода стал использоваться XSLT. Процесс вплетения аспектов происходит статически непосредственно в исходный код приложения, который копируется в отдельную директорию. Об этой библиотеке мы поговорим подробнее ниже. Последняя версия на момент написания – 0.1.1-alpha.
  2. **AspectPHP **(http://www.sebastian-bergmann.de/AspectPHP) – ранняя попытка Себастьяна Бергмана(Sebastian Bergmann) добавить поддержку АОП в PHP, используя аннотации. В данный момент работа не ведется, т.к. автор, осознав тщетность затеи, переключился на phpAspect, точнее даже так – Бергман был ментором у Вильяма Кандилона во время GSOC2006.
  3. **aoPHP **(http://www.aophp.net) – изначально написанная на Java(версии 1.0 и 2.0), имплементация некоторого подобия АОП. Вплетение аспектов происходит довольно нетривиальным образом на «лету»: все запросы к PHP скриптам на сервере перенаправляются на Java weaver, который изменяет код и перенаправляет его снова PHP интерпретатору. Разрабатываемая версия(3.0 и выше) написана на C++, однако вплетение аспектов происходит опять же на лету путем перенаправления запросов сервера, однако авторы обещают однажды сделать Apache модуль, значительно упрощающий процесс установки.
  4. **aspectPHP **(http://www.cs.toronto.edu/~yijun/aspectPHP) – форк ранней версии aoPHP, написанный на C и доступный в виде патча к PHP-4.3.10. Библиотека не обновлялась с февраля 2005 года. Представляет интерес, пожалуй, только в качестве «музейного экспоната».
  5. **AOP Library for PHP **(http://www.phpclasses.org/browse/package/2633.html) – крайне спорная эмуляция АОП средствами PHP, написанная Дмитрием Шейко. Требует явного изменения исходного кода приложения чем, по сути, сводит на нет все преимущества АОП.
  6. **runkit **(http://php.net/runkit) — PECL модуль, разработанный Сарой Големон(Sara Golemon), позволяющий «на лету» изменять классы и функции, назначать новые значения для констант и многое другое. Очень жаль, что не является частью core PHP.

Из всего вышеперечисленного, пожалуй, наиболее удачным (но еще далеко не законченным) решением и близким по духу к AspectJ является phpAspect. Так как. объем доклада не позволяет подробно остановиться на других проектах, то именно рассмотрением этой библиотеки мы и займемся.

phpAspect

Установка

phpAspect использует Parse_Tree PECL модуль для анализа исходного кода PHP(автор этого модуля все тот же Вильям Кандилон, кстати), поэтому необходимо его установить. Проще всего это сделать из командной строки:

# pecl install -f Parse_Tree

Кроме этого потребуются PEAR модули PHP_Beautifier и Console GetOpt, опять же они легко устанавливаются из командной строки:

# pear install PHP_Beautifier
# pear install Console_GetOpt

После этого следует скачать phpAspect. На данный момент можно использовать последний релиз 0.1.1-alpha (http://code.google.com/p/phpaspect/downloads/list), либо выбрать исходники непосредственно из Subversion репозитория (http://phpaspect.googlecode.com/svn/trunk/).

В поставке phpAspect идет, собственно, сама библиотека, примеры использования и скрипт phpaspect.php для статического переплетения исходного кода.

Принцип работы

Схематично работу по вплетению аспектов в phpAspect можно изобразить так:

fetch4

  • На вход phpAspect компилятора поступает исходный код программы вместе с аспектами
  • При помощи Parse_Tree PECL модуля происходит анализ исходных кодов программы и аспектов с последующей генерацией синтаксических деревьев в виде XML.
  • Для переплетения исходного и аспектного деревьев используется XSLT трансформация.

Использование

Для вплетения аспектов используется консольная утилита phpaspect.php, базовое использование которой такое:

$ php phpaspect.php <путь/до/исходников> <путь/до/аспектов> <конечная/директория>

Простой пример использования:

$ php phpaspect.php –d src src bin

Здесь мы предполагаем, что аспекты хранятся вместе с исходным кодом в директории src. Переплетенный код будет сохранен в директории bin. Ключ –d предварительно очищает директорию с вплетенным кодом.

Основные возможности phpAspect

Рассмотрим основные АОП возможности **phpAspect**.

Joinpoints

На данный момент phpAspect поддерживает три вида типизированных(kinded) joinpoint точек (однако разработчики обещают поддержку большего количества в будущем):

  • **method execution** — выполнение метода, сигнатура: **exec(method_modifiers ClassName::MethodName(ParametersNumber))**
poincut callFoo:exec(public Foo::action(*));
  • **method call** — вызов метода, сигнатура: **call( ClassName>MethodName(ParametersNumber))**
pointcut callFoo:call(Foo->action(*))
  • **object construction** — конструирование объекта, сигнатура: **new(ClassName(ParametersNumber))**
pointcut newFoo:new(Foo(*));

Также существуют два вида нетипизированных(non-kinded) joinpoint точек:

  • **file** – предикат, позволяющий наложить ограничение на расположение точки в определенном файле, сигнатура: **file(file_name)**
pointcut fooInBar:call(*->bar(*)) && file('Bar.php');
  • **within** – предикат, накладывающий ограничение на расположение точки в определенном классе, сигнатура: **within(class_name)**
pointcut fooInBar:call(*->bar(*)) && within(Bar);

Pointcuts

**phpAspect** позволяет комбинировать joinpoint точки в именованные и анонимные срезы pointcuts. При комбинировании joinpoint точек можно использовать следующие операторы: || (or), && (and) и ! (not).

Пример именованного среза user:

<pre class="brush: php; gutter: false; first-line: 1">pointcut user:(new(User(0)) || exec(public User::*(*))) 

&& !exec(public User::logout(*));

<p>
  В этом срезе мы захватываем все точки конструирования объекта класса User, выполнение любых его методов, кроме выполнения метода logout.
</p>

<p>
  Анонимные срезы применяются без идентификатора, т.е. их нельзя повторно использовать.
</p>

<h4>
  Advices
</h4>

<div>
  <p>
    Для работы со срезами pointcut точек исполнения используются advice методы. Поддерживаются следующие виды advice методов:
  </p>

  <ul>
    <li>
      <div>
        **before** – выполняется перед **joinpoint** точкой:
      </div>
    </li>
  </ul>

  <pre class="brush: php; gutter: false; first-line: 1">&lt;?php
before(): call(HelloWorld::say(0)){
    echo "Before saying Hello World\n";
}

?>

  <ul>
    <li>
      **after** – выполняется после **joinpoint** точки:
    </li>
  </ul>

  <pre class="brush: php; gutter: false; first-line: 1">&lt;?php
after(): call(HelloWorld::say(0)){
    echo "After saying Hello World\n";
}

?>

  <ul>
    <li>
      **around** – выполняется вместо **joinpoint** точки:
    </li>
  </ul>

  <pre class="brush: php; gutter: false; first-line: 1">&lt;?php
around(): call(HelloWorld::say(0)){
    echo "50% chance to say Hello World\n";
    if(rand(0, 1)){
        proceed();
    }
}

?>

  <div>
    <h4>
      Introductions
    </h4>

    <p>
      При помощи правил introductions, используя **phpAspect**, можно добавлять в классы новые атрибуты, методы и константы:
    </p>

    <ul>
      <li>
        <div>
          **attribute introduction** – добавление нового атрибута:
        </div>
      </li>
    </ul>
  </div>

  <pre class="brush: php; gutter: false; first-line: 1">//добавить $log и $debug аттрибуты во всех классах с префиксом Ba.

private Ba::$log, Ba::$debug=false;
//добавить статический $instance аттрибут в Foo и Bar классах.
private static Foo,Bar::$instance = null;

  <ul>
    <li>
      **method introduction** – добавление нового метода:
    </li>
  </ul>

  <pre class="brush: php; gutter: false; first-line: 1">//добавить метод setLog во все классы с префиксом Ba

public function Ba*::setLog($log){
$this->log = $log;
}

  <ul>
    <li>
      **constant introduction:**
    </li>
  </ul>

  <pre class="brush: php; gutter: false; first-line: 1">//добавить константы в класс Log 

Log::LEVEL_NOTICE=1, Log::LEVEL_ERROR=2;

  <div>
    <h4>
      Wildcards
    </h4>

    <p>
      Как видно из примеров, **phpAspect** поддерживает wildcard символы при описании правил. Поддерживаются следующие символы:
    </p>

    <ul>
      <li>
        <div>
          * — используется в **joinpoint** правилах, обозначает любое количество символов или аргументов:
        </div>
      </li>
    </ul>
  </div>

  <pre class="brush: php; gutter: false; first-line: 1">new(*(*));

exec(* *::addItem(2));

  <p>
    + — используется в **joinpoint** правилах, обозначает конкретный класс/интерфейс и всех его потомков:
  </p>

  <pre class="brush: php; gutter: false; first-line: 1">call(ActiveRecord+-&gt;save(0));</pre>

  <p>
    @ — используется в **introduction** правилах для обозначения конкретного класса/интерфейса и всех его потомков:
  </p>

  <pre class="brush: php; gutter: false; first-line: 1">private Bar@::$foo = 'bar';</pre>

  <h4>
    Introspection
  </h4>

  <p>
    phpAspect предоставляет средства для анализа контекста <em>joinpoint</em> точки во время выполнения advice метода. Точка исполнения доступна в advice методе в виде объекта $thisJoinPoint. В зависимости от конкретной точки исполнения $thisJoinPoint может быть трех типов:
  </p>

  <div>
    <ul>
      <li>
        <div>
          ExecJoinPoint –точка исполнения типа method execution
        </div>
      </li>

      <li>
        <div>
          CallJoinPoint –точка исполнения типа method call
        </div>
      </li>

      <li>
        <div>
          NewJoinPoint –точка исполнения типа object construction
        </div>
      </li>
    </ul>

    <p>
      ExecJoinPoint и CallJoinPoint обладают схожим интерфейсом, предоставляя следующие методы:
    </p>
  </div>

  <pre class="brush: php; gutter: false; first-line: 1">getKind()

getSignature()
getLine()
getFile()
getArgs()
getArg($i)
getTarget()
getSource()
getClassName()
getMethodName()

  <p>
    Пример использования:
  </p>

  <pre class="brush: php; gutter: false; first-line: 1">after():call(*-&gt;*(*)){
echo "After a call of " .
$thisJoinPoint-&gt;getClassName() .
" :: " . 
$thisJoinPoint-&gt;getMethodName() . "()";

}

  <p>
    У NewJoinPoint практически такой же интерфейс за исключением нескольких несущественных отличий.
  </p>

  <h4>
    Aspects
  </h4>

  <div>
    <p>
      Все вышеперечисленные компоненты используются в контексте аспектов. <em>Аспект</em>, как говорилось выше, инкапсулирует в себе некоторый «сквозной» функционал и является аналогом класса в ООП. В аспектах также можно использовать обычные методы и атрибуты. Пример простого аспекта в **phpAspect**:
    </p>
  </div>

  <pre class="brush: php; gutter: false; first-line: 1">aspect Trace{    

pointcut all:call(->(*));
after(): all{
echo $this->formatMessage($thisJoinPoint);
}

function formatMessage($point) {
echo “After a call of ” .
$point->getClassName() .
“ :: “ .
$point->getMethodName() . “()”;
}
}

  <p>
    Все аспекты **phpAspect** обязаны храниться в отдельных файлах с именем в формате «*.aspect.php». На данный момент в **phpAspect** нет поддержки наследования аспектов.
  </p>

  <h4>
    HelloWorld!
  </h4>

  <p>
    Рассмотрим простейший пример работы **phpAspect**. Предположим, у нас есть следующий примитивный скрипт hello.php в директории hello/src:
  </p>

  <pre class="brush: php; gutter: false; first-line: 1">&lt;?php

class HelloWorld {
function say() {
echo “Hello!\n”;
}
}
$hello = new HelloWorld();
$hello->say();
?>

  <p>
    Попробуем выполнить его:
  </p>

  <pre class="brush: shell; gutter: false; first-line: 1">$ php hello/src/hello.php

Hello!

  <p>
    Теперь напишем простой аспект, который производит трассировку работы объекта HelloWorld. Таким аспектом будет hello/src/trace.aspect.php(<em>phpAspect</em> использует соглашение о том, что все аспекты хранятся в файлах с маской *.aspect.php):
  </p>

  <pre class="brush: php; gutter: false; first-line: 1">&lt;?php

aspect Trace{
pointcut traceNew:new((0));
pointcut traceSay:call(
->say(0));
after(): traceNew{
echo “After a construction of ” .
get_class($thisJoinPoint->getObject()) .
“\n”;
}
before(): traceSay{
echo “Before a saying of ” .
get_class($thisJoinPoint->getTarget()) .
“\n”;
}
around(): traceSay{
echo “Around a saying of ” .
get_class($thisJoinPoint->getTarget()) .
“\n”;
$res = proceed();
echo “\nend around\n”;
return $res;
}
after(): traceSay{
echo “After a saying of ” .
get_class($thisJoinPoint->getTarget()) .
“\n”;
}
}
?>

  <p>
    В аспекте мы трассируем следующие точки исполнения: a) конструирование любых объектов, b) выполнение любых методов say().
  </p>

  <p>
    Вплетем аспекты в код, выполнив команду:
  </p>

  <pre class="brush: shell; gutter: false; first-line: 1">$ php phpaspect.php hello/src hello/src hello/bin</pre>

  <p>
    Попробуем выполнить сплетенный код из директории hello/bin:
  </p>

  <pre class="brush: shell; gutter: false; first-line: 1">$ php hello/bin/hello.php

After a construction of HelloWorld
Before a saying of HelloWorld
Around a saying of HelloWorld
Hello!
end around
After a saying of HelloWorld

  <h4>
    "За кулисами"; phpAspect
  </h4>

  <p>
    Попробуем посмотреть на работу **phpAspect** «изнутри», для этого рассмотрим код после вплетения аспектов для приведенного выше примера.
  </p>

  <pre class="brush: php; gutter: false; first-line: 1">&lt;?php

$current_dir = dirname(FILE);if(!function_exists(‘isType’)){ require_once ‘_phpaspect/functions.php’;
require_once ‘_phpaspect/joinpoint.class.php’;
require_once ‘_phpaspect/newjoinpoint.class.php’;
require_once ‘_phpaspect/calljoinpoint.class.php’;
require_once ‘_phpaspect/execjoinpoint.class.php’;
require_once ‘_phpaspect/aspect.interface.php’;}
require_once $
current_dir.‘/_phpaspect/trace.class.php’;

?><?php
class HelloWorld {
function say() {
$return_result = $this->phpaspectsay();
return $__return_result;
}
function __phpaspectsay() {
echo “Hello!\n”;
}
}
$phpaspect_70 = new HelloWorld();
if (true) {
$thisJoinPoint = new NewJoinPoint(“, LINE, FILE, array(), $phpaspect_70);
function __phpaspectba49ac85769ed0c6e09c7c12487053d2($thisJoinPoint) {
echo “After a construction of ” . get_class($thisJoinPoint->getObject()) . “\n”;
unset($thisJoinPoint);
}
__phpaspectba49ac85769ed0c6e09c7c12487053d2($thisJoinPoint);
}
$phpaspect_56 = &$hello;
$phpaspect_56 = $phpaspect_70;
$phpaspect_87 = &$hello;
if (isCallType($phpaspect_87, ‘’, ‘say’, ‘say’)) {
$thisJoinPoint = new CallJoinPoint(“, LINE, FILE, array(), $phpaspect_87, ‘say’);
function __phpaspectff7205121179f7e637a085e06b4bef62($thisJoinPoint) {
echo “Before a saying of ” . get_class($thisJoinPoint->getTarget()) . “\n”;
unset($thisJoinPoint);
}
__phpaspectff7205121179f7e637a085e06b4bef62($thisJoinPoint);
}
if (isCallType($phpaspect_87, ‘
’, ‘say’, ‘say’)) {
$thisJoinPoint = new CallJoinPoint(“, LINE, FILE, array(), $phpaspect_87, ‘say’);
echo “Around a saying of ” . get_class($thisJoinPoint->getTarget()) . “\n”;
$res = $phpaspect_87->say();
echo “\nend around\n”;
$return_result = $res;
unset($thisJoinPoint);
} else {
$phpaspect_104 = &$phpaspect_87->say();
$
return_result = $phpaspect_104;
}
$phpaspect_104 = $__return_result;
if (isCallType($phpaspect_87, ‘*‘, ‘say’, ‘say’)) {
$thisJoinPoint = new CallJoinPoint(“, LINE, FILE, array(), $phpaspect_87, ‘say’);
function __phpaspecte2600e1d66b7ca11ec71f56332b62ade($thisJoinPoint) {
echo “After a saying of ” . get_class($thisJoinPoint->getTarget()) . “\n”;
unset($thisJoinPoint);
}
__phpaspecte2600e1d66b7ca11ec71f56332b62ade($thisJoinPoint);
}
$phpaspect_104;
?>

  <p>
    Приведенный код может смутить в начале, однако при детальном изучении все, в принципе, встает на свои места. Как видно из кода, **phpAspect** добавляет вспомогательные функции до(before) и после(after) указанных точек исполнения, замещает around точки специальными врапперами, а также добавляет объекты интроспекции $thisJoinPoint, позволяющие получить детальную информацию о контексте.
  </p>

  <h3>
    Выводы
  </h3>

  <p>
    **phpAspect** представляет из себя достойную внимания попытку сделать нечто похожее на AspectJ для **<acronym title="Hypertext Preprocessor">PHP</acronym> **разработчиков. Для продукционного использования библиотека пока явно не готова, сказывается и «сырость» кода (версия 0.1.1-alpha все-таки), и ошибки работы (некоторые примеры, идущие в поставке с библиотекой, не работают), да и функционально еще многих вещей не хватает (например, работа с аннотациями, расстановка приоритетов применения аспектов, наследование аспектов – продолжать можно долго). К тому же, возникает вопрос цены использования аспектов – сплетенный код довольно сложен и возможное падение производительности очевидно.
  </p>

  <div>
    <p>
      Хотелось бы надеяться, что ближе к стабильной версии все эти недостатки будут в той или иной степени исправлены, и мы получим в руки отличного помощника для борьбы со «сквозным» функционалом.
    </p>

    <h4>
      Достоинства АОП
    </h4>
  </div>

  <div>
    <ul>
      <li>
        <div>
          На данный момент <em>АОП</em> является, пожалуй, единственной методологией, которая заботится о проблеме «сквозного» функционала на качественно ином уровне.
        </div>
      </li>

      <li>
        <div>
          <em>АОП</em> облегчает повторное использование кода — слабо связанные между собой аспекты легко взаимозаменять. Например, мы можем использовать более продвинутый механизм кэширования без добавления единой строчки кода в бизнес логику приложения.
        </div>
      </li>

      <li>
        <div>
          <em>АОП</em> теоретически позволяет отложить принятие спорного решения, касающегося работы всего приложения, на «потом», сохраняя, таким образом, основную бизнес логику приложения простой и легкой для поддержки. Это также позитивно сказывается на сроках выпуска первой рабочей версии приложения.
        </div>
      </li>
    </ul>

    <h4>
      Недостатки АОП
    </h4>

    <ul>
      <li>
        <div>
          Неочевидность происходящего (слишком много «магии»). В системе с дюжиной аспектов сложно с уверенностью сказать, в каком порядке и каким образом применяются аспекты.
        </div>
      </li>

      <li>
        <div>
          Аспекты сложно (невозможно?) протестировать отдельно от сплетенного кода.
        </div>
      </li>

      <li>
        <div>
          <em>АОП</em> мощный, но в то же время и опасный инструмент. Использование <em>АОП</em> магическим образом не сделает ваш код лучше и чище. В реальных задачах код аспектов может быть довольно сложным для понимания и поддержки, поэтому при написании аспектов используются свои собственные специфические паттерны проектирования.
        </div>
      </li>
    </ul>
  </div>

  <h2>
    Альтернативы
  </h2>

  <p>
    Так же необходимо рассмотреть существующие решения проблем сквозной функциональности.
  </p>

  <h3>
    Декораторы
  </h3>

  <p>
    Декораторы — первое, что приходит на ум. <a href="http://ru.wikipedia.org/wiki/%D0%94%D0%B5%D0%BA%D0%BE%D1%80%D0%B0%D1%82%D0%BE%D1%80_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">Декоратор</a> — структурный шаблон проектирования, предназначенный для динамического подключения дополнительного поведения к объекту. Шаблон Декоратор предоставляет гибкую альтернативу практике создания подклассов с целью расширения функциональности.
  </p>

  <p>
    Когда разговор заходит об **<a title="Аспектно-Ориентированное Программирование" href="http://ru.wikipedia.org/wiki/%D0%90%D1%81%D0%BF%D0%B5%D0%BA%D1%82%D0%BD%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5">АОП</a>**, первый вопрос, который обычно задают ООП-программисты — почему не использовать обычный декоратор? И это правильно! Потому что декоратором можно сделать почти все то, что делается с помощью **АОП**, но… Контр-пример: что если мы сделаем LoggingDecorator поверх CachingDecorator, а последний, в свою очередь, поверх основного класса? Сколько однотипного кода будет в этих декораторах? Сколько различных классов декораторов будет во всей системе?
  </p>

  <p>
    Легко прикинуть, что если у нас 100 классов, реализующих 100 интерфейсов, то добавление кэширующих декораторов добавит нам в систему еще 100 классов. Конечно, это не проблема в современном мире (загляните в папку cache любого большого фреймворка), но зачем нам нужны эти 100 однотипных классов? Непонятно, согласитесь?
  </p>

  <p>
    Тем не менее, умеренное использование декораторов полностью оправданно.
  </p>

  <h3>
    Прокси-классы
  </h3>

  <p>
    Прокси-классы — второе, что приходит на ум. <a href="http://ru.wikipedia.org/wiki/Proxy_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)">Прокси</a> — шаблон проектирования, предоставляет объект, который контролирует доступ к другому объекту, перехватывая все вызовы (выполняет функцию контейнера).
  </p>

  <p>
    Не очень хорошее решение с моей точки зрения, но у всех разработчиков на слуху кэширующие прокси, поэтому их так часто можно встретить в приложениях. Основные недостатки: падение скорости работы (часто используется __call, __get, __callStatic, call_user_func_array), а также ломается тайпхинтинг, потому что вместо реального объекта приходит прокси-объект. Если попытаться завернуть кэширующий прокси поверх логирующего, а тот, в свою очередь, поверх основного класса, то скорость упадет на порядок.
  </p>

  <p>
    Но есть и плюс: в случае 100 классов мы можем написать один кэширующий прокси на все классы. Но! Ценой отказа от тайпхинтинга по 100 интерфейсам, что категорически неприемлемо при разработке современных приложений.
  </p>

  <h3>
    События и паттерн Наблюдатель
  </h3>

  <p>
    Трудно не вспомнить такой замечательный паттерн, как Наблюдатель. <a href="http://ru.wikipedia.org/wiki/%D0%9D%D0%B0%D0%B1%D0%BB%D1%8E%D0%B4%D0%B0%D1%82%D0%B5%D0%BB%D1%8C_(%D0%BC%D0%BE%D0%B4%D0%B5%D0%BB%D1%8C)">Наблюдатель</a> (Observer) — поведенческий шаблон проектирования. Также известен как «подчинённые» (Dependents), «издатель-подписчик» (Publisher-Subscriber).
  </p>

  <p>
    Во многих известных фреймворках разработчики сталкиваются со сквозной функциональностью и необходимостью со временем расширять логику некоторого метода. Было испробовано много идей, и одной из самых удачных и понятных стала модель событий и подписчиков на эти события. Добавляя или удаляя подписчиков на события, мы можем расширять логику основного метода, а изменяя их порядок с помощью приоритетов — выполнять логику обработчиков в нужном порядке. Весьма неплохо, почти **АОП**!
  </p>

  <p>
    Надо отметить, что это максимально гибкий шаблон, так как на его основе вы можете спроектировать систему, которая будет расширяться очень легко и будет понятной. Если бы не было **АОП**, это был бы самый лучший способ расширять логику методов, не изменяя при этом исходного кода. Не удивительно, что многие фреймворки используют события для расширения функциональности, например ZF2, Symfony2. На сайте Symfony2 есть <a href="http://symfony.com/doc/master/cookbook/event_dispatcher/class_extension.html">отличная статья</a> о том, как можно расширить логику метода, не используя наследование.
  </p>

  <p>
    Тем не менее, несмотря на все плюсы, есть несколько больших минусов, которые иногда перевешивают плюсы. Первый минус заключается в том, что вы должны заранее знать, что и где может расширяться в вашей системе. К сожалению, зачастую это неизвестно. Второй минус заключается в том, что необходимо писать код особым образом, добавляя шаблонные строки генерации события и его обработки (пример из Symfony2):
  </p>

  <pre class="brush: php; gutter: false; first-line: 1">class Foo

{
// …

public function __call($method, $arguments)
{
    // create an event named 'foo.method_is_not_found'
    $event = new HandleUndefinedMethodEvent($this, $method, $arguments);
    $this-&gt;dispatcher-&gt;dispatch('foo.method_is_not_found', $event);

    // no listener was able to process the event? The method does not exist
    if (!$event-&gt;isProcessed()) {
        throw new \Exception(sprintf('Call to undefined method %s::%s.', get_class($this), $method));
    }

    // return the listener returned value
    return $event-&gt;getReturnValue();
}

}

  <h3>
    Сигналы и слоты
  </h3>

  <p>
    Этот <a href="http://ru.wikipedia.org/wiki/%D0%A1%D0%B8%D0%B3%D0%BD%D0%B0%D0%BB%D1%8B_%D0%B8_%D1%81%D0%BB%D0%BE%D1%82%D1%8B">паттерн</a>, по своей сути, является реализацией паттерна Наблюдатель, но позволяет сократить количество повторяющегося кода.
  </p>

  <p>
    Из самых интересных реализаций этого паттерна я бы отметил ядро фреймворка Lithium, изучение которого может дать много нового даже продвинутым разработчикам. Если вкратце, то Lithium позволяет навешивать функции-фильтры обратного вызова на любые важные методы в системе и проводить дополнительную обработку. Желаете писать лог запросов к базе в файл в отладочном режиме — нет ничего проще:
  </p>

  <pre class="brush: php; gutter: false; first-line: 1">use lithium\analysis\Logger;

use lithium\data\Connections;

// Set up the logger configuration to use the file adapter.
Logger::config(array(
‘default’ => array(‘adapter’ => ‘File’)
));

// Filter the database adapter returned from the Connections object.
Connections::get(‘default’)->applyFilter(’_execute’, function($self, $params, $chain) {
// Hand the SQL in the params headed to _execute() to the logger:
Logger::debug(date(“D M j G:i:s”) . “ ” . $params[‘sql’]);

// Always make sure to keep the filter chain going.
return $chain-&gt;next($self, $params, $chain);

});

  <p>
    Настоятельно рекомендую ознакомиться с <a href="http://lithify.me/docs/manual/lithium-basics/filters.wiki">системой фильтров</a>, потому что реализация фильтров в Lithium максимально приближает разработку к аспектно-ориентированному программированию и может стать для вас тем толчком, который позволит окунуться в мир <em>АОП</em> окончательно.
  </p>

  <h2>
    И опять к Аспектно-ориентированному программированию
  </h2>

  <p>
    Итак, фильтры в Lithium позволяют подключать дополнительные обработчики почти куда угодно, что дает возможность вынести код кэширования, логирования, проверки прав доступа в отдельные замыкания. Казалось бы, вот она, серебряная пуля. Но все не так уж гладко. Во-первых, для использования фильтров нам нужно подключить весь <em>фреймворк</em>, так как отдельной библиотеки для этого нет, а жаль. Во-вторых, фильтры-замыкания (в терминах **АОП** — советы) разбросаны повсюду и за ними очень сложно следить. В-третьих, код должен быть написан определенным образом и реализовывать специальные интерфейсы, чтобы можно было использовать фильтры. Эти три минуса значительно ограничивают возможность использовать фильтры в качестве **АОП** в других приложениях и <em>фреймворках</em>.
  </p>

  <p>
    Вот здесь у меня и появилась идея — написать библиотеку, которая дала бы возможность использовать **АОП** в любом приложении на **PHP**. Дальше была битва с **PHP**, изучение техник ускорения кода, борьба с багами опкод-ускоритиелей и много-много интересного. В результате родилась библиотека **Go! AOP PHP**, которая может внедриться в существующее приложение, перехватить доступные методы во всех классах и вынести из них сквозную функциональность на несколько тысяч строк кода в пару десятков строк советов.
  </p>

  <h3>
    Библиотека Go! AOP PHP
  </h3>

  <p>
    Основные отличия от всех существующих аналогов — это библиотека, не требующая никаких расширений <em>PHP</em>, не призывающая на помощь черную магию runkit-a и <em>php-aop</em>. Она не использует eval-ов, не завязана на DI-контейнер, не нуждается в отдельном компиляторе аспектов в конечный код. Аспекты представляют собой обычные классы, органично использующие все возможности ООП. Формируемый библиотекой код с вплетенными аспектами — очень чистый, его можно легко отлаживать с помощью XDebug-а, причем как сами классы, так и аспекты.
  </p>

  <p>
    Самое ценное в этой библиотеке то, что теоретически ее можно подключить в любое приложение, потому что для добавления новой функциональности с помощью АОП не нужно менять код приложения вообще, аспекты вплетаются динамически. Для примера: с помощью десяти-двадцати строк кода можно перехватить все публичные, защищенные и статические методы во всех классах при запуске стандартного ZF2-приложения и выводить при вызове метода на экран имя этого метода и его параметры.
  </p>

  <p>
    Проработан вопрос работы с опкод-кэшерем — в боевом режиме вплетение аспектов происходит только раз, после чего код достается из опкод-кэшера. Используются Doctrine-аннотации для классов аспектов. В общем, много чего интересного внутри.
  </p>

  <h3>
    Рефакторинг сквозного кода с использованием АОП
  </h3>

  <p>
    Чтобы разжечь интерес к **АОП** побольше, я решил выбрать интересную тему, о которой можно найти мало информации — рефакторинг кода к <em>аспектам</em>. Дальше будет два примера того, как можно сделать свой код чище и понятнее с использованием аспектов.
  </p>

  <h3>
    Выносим логирование из кода
  </h3>

  <p>
    Итак, представим, что у нас есть логирование всех выполняемых публичных методов в 20 классах, находящихся в неймспейсе Acme. Выглядит это как-то так:
  </p>

  <pre class="brush: php; gutter: false; first-line: 1">namespace Acme;

class Controller
{

public function updateData($arg1, $arg2)
{
    $this-&gt;logger-&gt;info("Executing method " . __METHOD__, func_get_args());
    // ...
}    

}

  <p>
    Давайте возьмем и отрефакторим этот код с использованием аспектов! Легко заметить, что логирование выполняется __перед__ кодом самого метода, поэтому сразу выбираем тип совета — Before. Дальше нам нужно определить точку внедрения — выполнение всех публичных методов внутри неймспейса Acme. Это правило задается выражением execution(public Acme\*->*()). Итак, пишем LoggingAspect:
  </p>

  <pre class="brush: php; gutter: false; first-line: 1">use Go\Aop\Aspect;

use Go\Aop\Intercept\MethodInvocation;
use Go\Lang\Annotation\Before;

/**
* Logging aspect
*/
class LoggingAspect implements Aspect
{
/** @var null|LoggerInterface */
protected $logger = null;

/** ... */
public function __construct($logger) 
{
    $this-&gt;logger = $logger;
}

/**
 * Method that should be called before real method
 *
 * @param MethodInvocation $invocation Invocation
 * @Before("execution(public Acme\*-&gt;*())")
 */
public function beforeMethodExecution(MethodInvocation $invocation)
{
    $obj    = $invocation-&gt;getThis();
    $class  = is_object($obj) ? get_class($obj) : $obj;
    $type   = $invocation-&gt;getMethod()-&gt;isStatic() ? '::' : '-&gt;';
    $name   = $invocation-&gt;getMethod()-&gt;getName();
    $method = $class . $type . $name;

    $this-&gt;logger-&gt;info("Executing method " . $method, $invocation-&gt;getArguments());
}

}

  <p>
    Ничего сложного, обычный класс с обычным на вид методом. Однако это — <em>аспект</em>, определяющий совет beforeMethodExecution, который будет вызван перед вызовом нужных нам методов. Как вы уже заметили, **Go!** использует аннотации для хранения метаданных, что давно уже стало обычной практикой, так как это наглядно и удобно. Теперь мы можем зарегистрировать наш аспект в ядре **Go!** и выкинуть из кучи наших классов все логирование! Убрав ненужную зависимость от логера, мы сделали наш код классов чище, он стал больше соблюдать принцип единой ответственности, потому что мы вынесли из него то, чем он не должен заниматься.
  </p>

  <p>
    Более того, теперь мы легко можем менять формат логирования, потому что теперь он задается в одном месте.
  </p>

  <h3>
    Прозрачное кэширование
  </h3>

  <p>
    Думаю, всем знаком шаблонный код метода с использованием кэширования:
  </p>

  <pre class="brush: php; gutter: false; first-line: 1"> /** ... */
public function cachedMethod()
{
    $key = __METHOD__;
    $result = $this-&gt;cache-&gt;get($key, $success);
    if (!$success) {
        $result = // ...
        $this-&gt;cache-&gt;set($key, $result);
    }
    return $result;
}</pre>

  <p>
    Несомненно, все узнают этот шаблонный код, так как таких мест всегда достаточно. Если у нас большая система, то таких методов может быть очень много, вот бы сделать так, чтобы они сами кэшировались. А что, идея! Давайте помечать аннотацией те методы, которые должны кэшироваться, а в поинткате зададим условие — все методы, помеченные определенной аннотацией. Так как кэширование «оборачивает» код метода, то и нам нужен подходящий тип совета — Around, самый могущественный. Этот тип совета сам принимает решение о необходимости выполнения исходного кода метода. А дальше все просто:
  </p>

  <pre class="brush: php; gutter: false; first-line: 1">use Go\Aop\Aspect;

use Go\Aop\Intercept\MethodInvocation;
use Go\Lang\Annotation\Around;

class CachingAspect implements Aspect
{

/**
 * Cache logic
 *
 * @param MethodInvocation $invocation Invocation
 * @Around("@annotation(Annotation\Cacheable)")
 */
public function aroundCacheable(MethodInvocation $invocation)
{
    static $memoryCache = array();

    $obj   = $invocation-&gt;getThis();
    $class = is_object($obj) ? get_class($obj) : $obj;
    $key   = $class . ':' . $invocation-&gt;getMethod()-&gt;name;
    if (!isset($memoryCache[$key])) {
        $memoryCache[$key] = $invocation-&gt;proceed();
    }
    return $memoryCache[$key];
}

}

  <p>
    В этом совете самое интересное — вызов оригинального метода, который осуществляется с помощью вызова proceed() у объекта MethodInvocation, содержащего информацию о текущем методе. Легко заметить, что если у нас есть данные в кэше, то мы не производим вызов оригинального метода. При этом, ваш код не изменяется никак!<br /> Имея такой аспект, мы можем перед любым методом поставить аннотацию Annotation\Cacheable и этот метод будет кэшироваться благодаря <em>АОП</em> автоматически. Проходимся по всем методам и вырезаем логику кэширования, заменяя ее на аннотацию. Теперь шаблонный код метода с использованием кэширования выглядит просто и изящно:
  </p>

  <pre class="brush: php; gutter: false; first-line: 1">/** 
 * @Cacheable
 */
public function cachedMethod()
{
    $result = // ...
    return $result;
}</pre>

  <p>
    Этот пример можно также найти внутри папки demos библиотеки **Go! AOP PHP**, а также посмотреть на <a href="https://github.com/lisachenko/go-aop-php/commit/75cc05ea0472100b102e590170aed85c14cefd4b">коммит</a>, реализующий вышесказанное в действии.
  </p>

  <h2>
    Заключение
  </h2>

  <p>
    <em>Аспектно ориентированное программирование</em> — довольно новая парадигма для PHP, но у нее большое будущее. Развитие метапрограммирования, написание Enterprise-фреймворков в **PHP** — все это идет по следам <em>Java</em>, а **АОП** в <em>Java</em> живет уже очень давно, так что нужно готовиться к **АОП** уже сейчас.
  </p>

  <p>
    **Go! AOP PHP** — одна из немногих библиотек, которая работает с **АОП** и в некоторых вопросах она выгодно отличается от аналогов — возможность перехватывать статические методы, методы в финальных классах, обращения к свойствам объектов, возможность отладки исходного кода и кода аспектов. Go! использует массу техник для обеспечения высокого быстродействия: компиляция вместо интерпретации, отсутствие медленных техник, оптимизированный код выполнения, возможность использовать опкод-кэшер — все это дает свой вклад в общее дело. Одним из удивительных открытий было то, что Go! в некоторых аналогичных условиях может работать быстрее C-экстеншена **PHP-AOP**. Да-да, это правда, которая имеет простое объяснение — экстеншен вмешивается в работу всех методов в **PHP** в рантайме и делает небольшие проверки на соответствие поинткату, чем больше таких проверок, тем медленнее вызов каждого метода, тогда как Go! делает это один раз при компиляции кода класса, не влияя на скорость работы методов в рантайме.
  </p>

  <h5>
    Ссылки
  </h5>

  <ol>
    <li>
      Исходный код <a href="https://github.com/lisachenko/go-aop-php">https://github.com/lisachenko/go-aop-php</a>
    </li>
    <li>
      Презентация SymfonyCampUA-2012 <a href="http://www.slideshare.net/lisachenko/php-go-aop">http://www.slideshare.net/lisachenko/php-go-aop</a>
    </li>
    <li>
      Видео SymfonyCampUA-2012 <a href="http://www.youtube.com/watch?v=ZXbREKT5GWE">http://www.youtube.com/watch?v=ZXbREKT5GWE</a>
    </li>
    <li>
      Пример перехвата всех методов в ZF2 (после клонирования устанавливаем зависимости через composer) <a href="https://github.com/lisachenko/zf2-aspect">https://github.com/lisachenko/zf2-aspect</a>
    </li>
    <li>
      Интересная статья по теме: <a href="http://www.mwop.net/blog/251-Aspects,-Filters,-and-Signals,-Oh,-My!.html">Аспекты, фильтры и сигналы — о, боже! (en)</a>
    </li>
    <li>
      Источник 1: <a href="http://wiki.agiledev.ru/doku.php?id=aop:aop_php" target="_blank">Аспектно Ориентированное Программирование (АОП) в PHP</a>
    </li>
    <li>
      Источник 2: <a href="http://habrahabr.ru/post/165329/" target="_blank">Избавляемся от дублирования сквозного кода в PHP: рефакторинг кода с АОП</a>
    </li>
  </ol>
</div>


Комментарии

comments powered by Disqus