PHP: я смешиваю событийное Программирование с сигнальными интерфейсами (сигнал и слоты / шаблон наблюдателя)?

Я видел много людей, говорящих, что Symfony2, Zend Framework 2 и другие управляются событиями.

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

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

С другой стороны, есть аналогичная концепция: архитектура, управляемая событиями.

здесь вы можете читать как:

http://en.wikipedia.org/wiki/Event-driven_programming

http://en.wikipedia.org/wiki/Event-driven_architecture

и здесь:

http://en.wikipedia.org/wiki/Signal_programming

сигнал-это уведомление процесса о том, что произошло событие. Сигналы иногда описываются как программные прерывания. Сигналы аналогично аппаратным прерываниям в том, что они прерывают нормальное поток выполнения программы; в большинстве случаев невозможно точно предсказать, когда придет сигнал.

  • тег Stackoverflow [singals] описание

более того, то, что я называл управляемым событиями, кажется более связанным с сигналами и шаблонами слотов, введенными Qt (реализация шаблона наблюдателя)

в качестве примера есть структура Prado, которая утверждает, что она управляется событиями:

http://www.pradosoft.com/demos/quickstart/?page=Fundamentals.Applications (Жизненные Циклы Приложений раздел)

http://www.pradosoft.com/docs/manual/System/TApplication.html#methodonEndRequest

IIRC, это не приложение, управляемое событиями, а просто подключаемые крючки (сигналы и слоты), используемые классами, которые реализуют observable Interface. Я имею в виду, учитывая, как настольные приложения используют события и как приложения без состояния используют события (как plugi-ns): первые используют события для всего приложения, включая представления, последние только для операции на стороне сервера.

один больше связан с аспектно-ориентированным программированием (с сигналами и слотами), а другой специально не привязан к общие вопросы / AOP. Другими словами, это больше связано с состоянием приложения.

Итак, что же на самом деле связь между этими терминами и как они отличаются друг от друга?

  1. программирование на основе событий
  2. архитектура, управляемая событиями
  3. сигналы и Шаблон Слоты

эти термины просто общие шаблоны? Следовательно, все, что реализует шаблон наблюдателя, можно считать управляемым событиями?

обновление

Zend Framework 2

статья о AOP, которую я связал выше (http://mwop.net/blog/251-Aspects, - фильтры, - и-сигналы, - о, - мой!.html ) был написан Мэтью Вейером О'Финни (ZF Leader). Если не ошибаюсь, это не есть упоминания о "event-driven", просто сигнал и слоты.

Symfony 2

С Использованием Symfony2 EventDispatcher описание компонента не содержит упоминаний о для "событийной" приложений: http://symfony.com/doc/current/components/event_dispatcher/introduction.html Он содержит только ссылки на" события " (которые, действительно, обрабатываются сигналом и слотами).

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

2 ответов


отказ от ответственности: это длинный ответ, но я думаю, что это стоит читать с все его ссылки. И ИМХО это приводит к окончательному ответу.

я боролся на эту тему последние пару дней, и, если я все правильно прочитал, ответ:

Event-driven !== Запрос-driven

"[...] Я нахожу это самое интересное различие в событии сотрудничества перефразируя Джона Уделла: запрос управляемого программного обеспечения говорит когда говорят, программное обеспечение, управляемое событиями, говорит, когда ему есть что сказать.

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

Мартин Фаулер - сотрудничество событий (раздел запросов)

основываясь на этом утверждении, IIRC, современные фреймворки PHP реализуют шаблон Observer + перехватывающие фильтры + Singal и слоты, чтобы вызвать некоторые события во время цикла запроса.

но, несмотря на то, что он принимает некоторые идеи event-driven архитектуры, похоже, не поддерживают, что вся структура управляется событиями (т. е. Symfony2-это управляемый событиями framewrok).

мы привыкли делить программы на несколько компонентов, которые сотрудничать вместе. (Я использую неопределенное слово "компонент" здесь намеренно, так как в этом контексте я имею в виду много вещей: в том числе объекты в программе и несколько процессов, взаимодействующих между собой сеть.) Наиболее распространенным способом заставить их сотрудничать является стиль запроса / ответа. Если объект customer хочет получить некоторые данные из объект salesman, он вызывает метод на объекте salesman, чтобы задать его за эти данные.

другой стиль сотрудничества-это совместные события. В этом стиле у вас никогда нет одного компонента, который просит другого что-либо сделать, вместо каждый компонент сигнализирует о событии, когда что-то меняется. Другой компоненты слушают это событие и реагируют так, как они хотят. Этот известный шаблон Observer является примером сотрудничества событий.

Мартин Фаулер - фокус на событиях (раздел: Использование событий для сотрудничать)

Я думаю, что PHP-приложения более тесно связаны с событиями, чем с запросами только когда внимание сосредоточено на событиях. Если эти приложения / фреймворки используют события только для межсекторальных проблем (AOP), то это не зависит от событий. Точно так же вы не назвали бы это тест-драйв или управляемый доменом только потому, что у вас есть некоторые объекты домена и модульные тесты.

примеры реального мира

Я выбрал несколько примеров, чтобы показать, почему эти рамки не полностью управляются событиями. Несмотря на события AOP, все запрос-driven:

Примечание: хотя, он может быть адаптирован для управления событиями

Zend Framework 2

рассмотрим \В Zend\MVC В\Приложения компонент:

он реализует \Zend\EventManager\EventManagerAwareInterface и полагается на \Zend\Mvc\MvcEvent который описывает возможные события:

class MvcEvent extends Event
{
    /**#@+
     * Mvc events triggered by eventmanager
     */
    const EVENT_BOOTSTRAP      = 'bootstrap';
    const EVENT_DISPATCH       = 'dispatch';
    const EVENT_DISPATCH_ERROR = 'dispatch.error';
    const EVENT_FINISH         = 'finish';
    const EVENT_RENDER         = 'render';
    const EVENT_ROUTE          = 'route';

    // [...]
}

The \В Zend\MVC В\Приложения сам компонент управляется событиями, потому что он не взаимодействует напрямую с другими компонентами, но вместо этого он просто запускает события:

/**
 * Run the application
 *
 * @triggers route(MvcEvent)
 *           Routes the request, and sets the RouteMatch object in the event.
 * @triggers dispatch(MvcEvent)
 *           Dispatches a request, using the discovered RouteMatch and
 *           provided request.
 * @triggers dispatch.error(MvcEvent)
 *           On errors (controller not found, action not supported, etc.),
 *           populates the event with information about the error type,
 *           discovered controller, and controller class (if known).
 *           Typically, a handler should return a populated Response object
 *           that can be returned immediately.
 * @return ResponseInterface
 */
public function run()
{
    $events = $this->getEventManager();
    $event  = $this->getMvcEvent();

    // Define callback used to determine whether or not to short-circuit
    $shortCircuit = function ($r) use ($event) {
        if ($r instanceof ResponseInterface) {
            return true;
        }
        if ($event->getError()) {
            return true;
        }
        return false;
    };

    // Trigger route event
    $result = $events->trigger(MvcEvent::EVENT_ROUTE, $event, $shortCircuit);
    if ($result->stopped()) {
        $response = $result->last();
        if ($response instanceof ResponseInterface) {
            $event->setTarget($this);
            $events->trigger(MvcEvent::EVENT_FINISH, $event);
            return $response;
        }
        if ($event->getError()) {
            return $this->completeRequest($event);
        }
        return $event->getResponse();
    }
    if ($event->getError()) {
        return $this->completeRequest($event);
    }

    // Trigger dispatch event
    $result = $events->trigger(MvcEvent::EVENT_DISPATCH, $event, $shortCircuit);

    // Complete response
    $response = $result->last();
    if ($response instanceof ResponseInterface) {
        $event->setTarget($this);
        $events->trigger(MvcEvent::EVENT_FINISH, $event);
        return $response;
    }

    $response = $this->getResponse();
    $event->setResponse($response);

    return $this->completeRequest($event);
}

это событие-действие: вы понятия не имеете, глядя на код, из которого будут использоваться маршрутизатор, диспетчер и визуализатор, вы знаете только, что эти события будут инициированы. Вы можете подключить практически любой совместимый компонент для прослушивания и обработки событий. Между компонентами нет прямой связи.

но, есть одна важная вещь, чтобы отметить: это уровень представления (контроллер + вид). Уровень домена действительно может управляться событиями, но это не относится почти ко всем приложениям, которые вы видите там. ** Есть смесь между управляемыми событиями и запросами:

// albums controller
public function indexAction()
{
    return new ViewModel(array(
        'albums' => $this->albumsService->getAlbumsFromArtist('Joy Division'),
    ));
}

компонент контроллера не управляется событиями. Он напрямую взаимодействует с компонентом service. вместо этого службы должны подписаться на события, поднятые контроллерами, который является частью уровня представления. (Я укажу ссылки на то, что будет моделью домена, управляемой событиями, в конце этого ответа).

Symfony 2

теперь давайте рассмотрим то же самое на Symfony2 Приложения/FrontController: \В Symfony\Компонент\HttpKernel\HttpKernel

он действительно имеет основные события во время запроса:Symfony\Component\HttpKernel\KernelEvents

/**
 * Handles a request to convert it to a response.
 *
 * Exceptions are not caught.
 *
 * @param Request $request A Request instance
 * @param integer $type    The type of the request (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST)
 *
 * @return Response A Response instance
 *
 * @throws \LogicException If one of the listener does not behave as expected
 * @throws NotFoundHttpException When controller cannot be found
 */
private function handleRaw(Request $request, $type = self::MASTER_REQUEST)
{
    // request
    $event = new GetResponseEvent($this, $request, $type);
    $this->dispatcher->dispatch(KernelEvents::REQUEST, $event);

    if ($event->hasResponse()) {
        return $this->filterResponse($event->getResponse(), $request, $type);
    }

    // load controller
    if (false === $controller = $this->resolver->getController($request)) {
        throw new NotFoundHttpException(sprintf('Unable to find the controller for path "%s". Maybe you forgot to add the matching route in your routing configuration?', $request->getPathInfo()));
    }

    $event = new FilterControllerEvent($this, $controller, $request, $type);
    $this->dispatcher->dispatch(KernelEvents::CONTROLLER, $event);
    $controller = $event->getController();

    // controller arguments
    $arguments = $this->resolver->getArguments($request, $controller);

    // call controller
    $response = call_user_func_array($controller, $arguments);

    // view
    if (!$response instanceof Response) {
        $event = new GetResponseForControllerResultEvent($this, $request, $type, $response);
        $this->dispatcher->dispatch(KernelEvents::VIEW, $event);

        if ($event->hasResponse()) {
            $response = $event->getResponse();
        }

        if (!$response instanceof Response) {
            $msg = sprintf('The controller must return a response (%s given).', $this->varToString($response));

            // the user may have forgotten to return something
            if (null === $response) {
                $msg .= ' Did you forget to add a return statement somewhere in your controller?';
            }
            throw new \LogicException($msg);
        }
    }

    return $this->filterResponse($response, $request, $type);
}

но помимо того, что он "способен к событиям", он напрямую взаимодействует с компонентом ControllerResolver, поэтому он не полностью управляется событиями с начала процесса запроса, хотя он запускает некоторые события и позволяет некоторым компонентам быть подключаемыми (что не относится к ControllerResolver, который вводится в качестве параметра конструктора).

вместо этого, чтобы быть полностью управляемым событиями компонентом, он должен быть, как в компоненте приложения ZF2:

    // Trigger dispatch event
    $result = $events->trigger(MvcEvent::EVENT_DISPATCH, $event, $shortCircuit);

Прадо

у меня недостаточно времени, чтобы исследовать исходный код, но сначала он не кажется встроенным в твердый путь. В любом случае, что такое контроллер на MVC-подобных фреймворках, Prado называет его TPage (не уверен пока):

http://www.pradosoft.com/demos/blog-tutorial/?page=Day3.CreateNewUser

и он действительно напрямую связывается с компонентами:

class NewUser extends TPage
{
    /**
     * Checks whether the username exists in the database.
     * This method responds to the OnServerValidate event of username's custom validator.
     * @param mixed event sender
     * @param mixed event parameter
     */
    public function checkUsername($sender,$param)
    {
        // valid if the username is not found in the database
        $param->IsValid=UserRecord::finder()->findByPk($this->Username->Text)===null;
    }
    [...]
}

Я понимаю, что TPage является прослушивателем событий и может быть подключаемым. Но это не делает вашу модель домена управляемой событиями. Поэтому я думаю, что в какой-то степени это ближе к предложению ZF2.

событийная примеры

чтобы закончить этот длинный ответ, вот что будет иметь полномасштабное приложение, управляемое событиями:

развязка приложений с событиями доменов http://www.whitewashing.de/2012/08/25/decoupling_applications_with_domain_events.html

событие поиске http://martinfowler.com/eaaDev/EventSourcing.html

Шаблон События Домена http://martinfowler.com/eaaDev/DomainEvent.html

событие Сотрудничество http://martinfowler.com/eaaDev/EventCollaboration.html

Перехват События http://martinfowler.com/bliki/EventInterception.html

Конечная Точка Сообщения http://www.enterpriseintegrationpatterns.com/MessageEndpoint.html

... и так далее


PHP не является апатридом, HTTP. Проще говоря, мы, по сути, построили слой поверх безгосударственной технологии, на которой мы можем реализовать государственные проекты. Взятые вместе, PHP и ваше хранилище данных имеют все инструменты, необходимые для создания дизайна приложения на основе шаблонов, управляемых событиями, посредством токенизации сеансов.

в очень обобщенном виде вы можете думать о HTTP как о том, что для интернета BIOS был для настольных компьютеров. На самом деле, возьмите это еще немного, и вы легко увидите неявный, управляемый событиями характер сети. Вы сказали: "это не событие, а совершенно новый запрос", и я возвращаюсь с " совершенно новым запросом is событие", и я имею в виду, что в Схема смысле этого слова. Он имеет конкретное семантическое значение, связанное с взаимодействием пользователя с вашим приложением.

по существу, через шаблоны, такие как MVC и Front Controller (и по механизму http cookies и PHP сеансы), мы просто восстанавливаем состояние сеанса, а затем реагируем на событие, соответствующим образом изменяя это состояние.

Мне нравится созерцать сущность покоя: передача репрезентативного состояния... но я бы добавил, что мы не должны забывать о неявном подразумевании, что состояние передается только тогда, когда произошло событие UI. Таким образом, мы поддерживаем контракты с HTTP, которые мы "говорим" только в "репрезентативных состояниях" нашей модели (т. е. документа, JSON и т. д.), Но это только наши диалект. Другие системы выбирают говорить в координатах холста, db сигнала, etc.

редактировать/больше мыслей

поэтому я размышлял над этим некоторое время, и я думаю, что есть концепция, которая иллюстрирует немного двусмысленности при обсуждении этих шаблонов в области PHP через HTTP: determinism. В частности, после получения запроса путь выполнения PHP является детерминированным, и именно поэтому чрезвычайно сложно рассматривать " управляемый событиями" архитектура в PHP. Я считаю, что мы должны рассмотреть один уровень выше, чем PHP, до более крупного "сеанса" взаимодействия с пользователем.

в настольных вычислениях мы используем runloops и контекст состояния для "ожидания" событий. Однако я буду утверждать, что интернет на самом деле является улучшением по сравнению с этой архитектурой (в большинстве случаев), но в конечном итоге тот же шаблон. Вместо состояния runloop и бесконечной длительности мы загружаем наше состояние когда событие происходит и затем обработать это событие. И вместо того, чтобы просто хранить это состояние в памяти и ждать следующего события, мы архивируем это состояние и закрываем ресурсы. Его можно считать менее эффективным в одном смысле (что нам нужно загружать состояние при каждом "событии"), но его также можно назвать более эффективным в том, что никогда состоянии ожидания в памяти без причины. Мы загружаем только состояние, которое фактически потребленной/манипулировать

Так в этом кстати, подумайте о PHP через HTTP как о событии, управляемом в макрос уровень, при этом будучи уверенным, что любое данное выполнение действительно детерминировано и на самом деле не управляется событиями. Однако мы реализуем шаблон front-controller и MVC, чтобы предоставить разработчикам приложений знакомые структура даже управляемых Крюков. Когда вы работаете с достойной структурой, вы просто говорите: "Я хочу знать, когда пользователь регистрируется, и пользователь должен быть доступен меня модифицировать в то время". Это событийное развитие. Вас не должно беспокоить, что фреймворк загрузил среду для (почти) единственной цели вызова вашего крючка (по сравнению с более традиционным представлением о том, что среда уже была там, и Вы были просто уведомления событие). Это то, что значит разрабатывать PHP с учетом событий. Контроллеры определяют (на основе запроса), какие событие происходит и использует whichever механизм он предназначен для использования (т. е. шаблон наблюдателя, архитектура крючка и т. д.), чтобы позволить вашему коду обрабатывать событие, реагировать на событие или любую номенклатуру, наиболее подходящую для семантики вашей конкретной структуры.