Child pages
  • Create bridge between Symfony's Event Dispatcher and In-Portal [5.3.0-B1]
Skip to end of metadata
Go to start of metadata

The Event Dispatcher is core of every major framework today and In-Portal isn't not an exception. The way how Event Dispatchers should work isn't standardized, which can create problems in these cases:

  • components from one framework needed to be used in another framework
  • event dispatcher from one framework needed to be used in another framework

In particular the Event Dispatcher from Symfony Components (http://symfony.com/doc/current/components/event_dispatcher/introduction.html) works using radically different logic, then one at In-Portal. This is a problem, because most of Symfony Components are communicating between themselves using Symfony's Event Dispatcher.

To use Symfony's Components without limits I'm proposing to:

  1. allow In-Portal to subscribe to events, triggered by Symfony's Event Dispatcher (e.g. "kernel.request" from Symfony's HttpKernel component)
  2. allow In-Portal to trigger events, registered within Symfony's Event Dispatcher
  3. allow Symfony's Event Dispatcher to trigger events in In-Portal 

Solution

Part 1: Triggering In-Portal events from Symfony

WORKFLOW (in Symfony):

  1. create "kEvent" class instance (the In-Portal event)
  2. create "InPortalEvent" class instance and pass above created event to it
  3. call the "$eventDispatcher->dispatch" method on above created event
  • Create "InPortalEvent" class that would:
    • extend "Symfony\Component\EventDispatcher\Event" class of Symfony's Event Dispatcher
    • allow to specify "kEvent" class instance in it's constructor through "$wrapped_event" parameter
    • allow wrapped event to be retrieved via "getWrappedEvent($class_name = null)" method
    • when $class_name argument is given, then confirm, that wrapped event is of given class
    • when wrapped event is of wrong class immediately throw an exception
  • the "kEventManager" class will:
    • create "Symfony\Component\EventDispatcher\EventDispatcher" class instance via "new ..." (because it's not class from In-Portal factory)
    • store reference to created object locally in the protected property
    • call "addListener" method on created event dispatcher, that will add "in-portal.event" event listener, that once fired will call "EventManager::handleSymfonyEvent($event_name, InPortalEvent $event)" method
  • add the "EventManager::handleSymfonyEvent($event_name, InPortalEvent $event = null)" method would:
    • use "$event->getWrappedEvent()" to get wrapped event
    • call the "$this->Application->HandleEvent($wrapped_event)" to process event via In-Portal event dispatcher

Part 2: Listening for Symfony events in In-Portal

WORKFLOW (in In-Portal):

  1. create In-Portal event, that handle for Symfony event
  2. register that event as a hook to "sf:symfony.event.name.here" event (hook type doesn't matter)
  3. once in Symfony event will be fired our event will be executed and Symfony event can be accessed via these methods:
    1. $event->MasterEvent->getWrappedEventName()
    2. $event->MasterEvent->getWrappedEvent()

 

  • create "SymfonyEvent" class, that would:
    • extend "kEvent" class of In-Portal's Event Dispatcher
    • have "$SkipBeforeHooks" and "$SkipAfterHooks" properties set to "false" by default
    • allow to specify following in it's constructor:
      • "$wrapped_event_name" parameter - event name
      • "$wrapped_event" parameter - "Symfony\Component\EventDispatcher\Event" class instance
    • allow wrapped event name to be retrieved via "getWrappedEventName()" method
    • allow wrapped event to be retrieved via "getWrappedEvent($class_name = null)" method
    • when "$class_name" argument is given, then confirm, that wrapped event is of given class
    • when wrapped event is of wrong class immediately throw an exception
  • to listen for Symfony Event just add a hook, in which:
    • unit prefix would be "sf"
    • the event name would be the same as in Symfony (e.g. "kernel.request")
  • create unit "sf" unit, that will:
    • create "SymfonyEventHandler" class by sub-classing "kEventHandler" (not "kDBEventHandler"), because not dealing with DB in any way
    • set "SymfonyEventHandler" as event handler of this unit
    • both "EventHandler::CheckPermission" and "EventHandler::checkItemStatus" will always return "false", because isn't allowed to be executed from Web Request directly
    • the "EventHandler::processEvent" method won't do anything, because it doesn't have any own events
  • add "kHookManager::symfonyEvents" property that will be stored/retrieved as part of unit config cache
  • during unit config cache building in case when "HookToPrefix" is "sf" additionally store "HookToEvent" into "kHookManager::symfonyEvents" array
  • add "kHookManager::handleSymfonyEvent($symfony_event_name, Event $symfony_event = null)" method, that will:
    • create "SymfonyEvent" class instance:
      • with "$symfony_event_name" as wrapped event name
      • with "$symfony_event" as wrapped event
      • set "$SkipBeforeHooks" and "$SkipAfterHooks" event properties to "true"
    • call the "$this->Application->HandleEvent($event)" method to make all hooks to be called
  • make "kHookManager" implement "Symfony\Component\EventDispatcher\EventSubscriberInterface" interface
  • add "kHookManager::getSubscribedEvents" to make above interface work, that would:
    • take Symfony events names from "kHookManager::symfonyEvents" array
    • if none, then return an empty array
    • if some found, then build array that would make each of events to be handled via "kHookManager::handleSymfonyEvent" method
  • after Symfony Event Dispatcher was created call the "addSubcriber" method on it with "kHookManager" class instance

Part 3: Triggering Symfony events from In-Portal

WORKFLOW (In-Portal):

  1. create "kEvent" class instance for event named as "sf:symfony.event.name.here"
  2. use "$this->Application->HandleEvent($event)" to fire it
  3. wait for Symfony to handle it
  • change the "SymfonyEventHandler::processEvent" method to:
    • get wrapped event and wrapped event name
    • call "dispatch" method of Symfony's event dispatcher stored within EventManager with that event
    • tricky moment: don't get into recursion loop, when firing Symfony event we'll be hitting own hooks that would process it twice
  • create instance of "SymfonyEvent" class, by specifying "$event_name" and "$event" (optional) parameters
  • call "$this->Application->HandleEvent($symfony_event)" to get it processed

Related Tasks