Source of file Forward.php
Size: 8,623 Bytes - Last Modified: 2014-03-12T23:21:18+01:00
/home/theseer/Downloads/ZendFramework-2.3.0/library/Zend/Mvc/Controller/Plugin/Forward.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 | <?php /** * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ namespace Zend\Mvc\Controller\Plugin; use Zend\EventManager\SharedEventManagerInterface as SharedEvents; use Zend\Mvc\Controller\ControllerManager; use Zend\Mvc\Exception; use Zend\Mvc\InjectApplicationEventInterface; use Zend\Mvc\MvcEvent; use Zend\Mvc\Router\RouteMatch; class Forward extends AbstractPlugin { /** * @var ControllerManager */ protected $controllers; /** * @var MvcEvent */ protected $event; /** * @var int */ protected $maxNestedForwards = 10; /** * @var int */ protected $numNestedForwards = 0; /** * @var array */ protected $listenersToDetach = null; /** * @param ControllerManager $controllers */ public function __construct(ControllerManager $controllers) { $this->controllers = $controllers; } /** * Set maximum number of nested forwards allowed * * @param int $maxNestedForwards * @return Forward */ public function setMaxNestedForwards($maxNestedForwards) { $this->maxNestedForwards = (int) $maxNestedForwards; return $this; } /** * Get information on listeners that need to be detached before dispatching. * * Each entry in the array contains three keys: * * id (identifier for event-emitting component), * event (the hooked event) * and class (the class of listener that should be detached). * * @return array */ public function getListenersToDetach() { // If a blacklist has not been explicitly set, return the default: if (null === $this->listenersToDetach) { // We need to detach the InjectViewModelListener to prevent templates // from getting attached to the ViewModel twice when a calling action // returns the output generated by a forwarded action. $this->listenersToDetach = array(array( 'id' => 'Zend\Stdlib\DispatchableInterface', 'event' => MvcEvent::EVENT_DISPATCH, 'class' => 'Zend\Mvc\View\Http\InjectViewModelListener', )); } return $this->listenersToDetach; } /** * Set information on listeners that need to be detached before dispatching. * * @param array $listeners Listener information; see getListenersToDetach() for details on format. * @return void */ public function setListenersToDetach($listeners) { $this->listenersToDetach = $listeners; } /** * Dispatch another controller * * @param string $name Controller name; either a class name or an alias used in the controller manager * @param null|array $params Parameters with which to seed a custom RouteMatch object for the new controller * @return mixed * @throws Exception\DomainException if composed controller does not define InjectApplicationEventInterface * or Locator aware; or if the discovered controller is not dispatchable */ public function dispatch($name, array $params = null) { $event = clone($this->getEvent()); $controller = $this->controllers->get($name); if ($controller instanceof InjectApplicationEventInterface) { $controller->setEvent($event); } // Allow passing parameters to seed the RouteMatch with & copy matched route name if ($params !== null) { $routeMatch = new RouteMatch($params); $routeMatch->setMatchedRouteName($event->getRouteMatch()->getMatchedRouteName()); $event->setRouteMatch($routeMatch); } if ($this->numNestedForwards > $this->maxNestedForwards) { throw new Exception\DomainException("Circular forwarding detected: greater than $this->maxNestedForwards nested forwards"); } $this->numNestedForwards++; // Detach listeners that may cause problems during dispatch: $sharedEvents = $event->getApplication()->getEventManager()->getSharedManager(); $listeners = $this->detachProblemListeners($sharedEvents); $return = $controller->dispatch($event->getRequest(), $event->getResponse()); // If we detached any listeners, reattach them now: $this->reattachProblemListeners($sharedEvents, $listeners); $this->numNestedForwards--; return $return; } /** * Detach problem listeners specified by getListenersToDetach() and return an array of information that will * allow them to be reattached. * * @param SharedEvents $sharedEvents Shared event manager * @return array */ protected function detachProblemListeners(SharedEvents $sharedEvents) { // Convert the problem list from two-dimensional array to more convenient id => event => class format: $formattedProblems = array(); foreach ($this->getListenersToDetach() as $current) { if (!isset($formattedProblems[$current['id']])) { $formattedProblems[$current['id']] = array(); } if (!isset($formattedProblems[$current['id']][$current['event']])) { $formattedProblems[$current['id']][$current['event']] = array(); } $formattedProblems[$current['id']][$current['event']][] = $current['class']; } // Loop through the class blacklist, detaching problem events and remembering their CallbackHandlers // for future reference: $results = array(); foreach ($formattedProblems as $id => $eventArray) { $results[$id] = array(); foreach ($eventArray as $eventName => $classArray) { $results[$id][$eventName] = array(); $events = $sharedEvents->getListeners($id, $eventName); foreach ($events as $currentEvent) { $currentCallback = $currentEvent->getCallback(); // If we have an array, grab the object if (is_array($currentCallback)) { $currentCallback = array_shift($currentCallback); } // This routine is only valid for object callbacks if (!is_object($currentCallback)) { continue; } foreach ($classArray as $class) { if (is_a($currentCallback, $class)) { $sharedEvents->detach($id, $currentEvent); $results[$id][$eventName][] = $currentEvent; } } } } } return $results; } /** * Reattach all problem listeners detached by detachProblemListeners(), if any. * * @param SharedEvents $sharedEvents Shared event manager * @param array $listeners Output of detachProblemListeners() * @return void */ protected function reattachProblemListeners(SharedEvents $sharedEvents, array $listeners) { foreach ($listeners as $id => $eventArray) { foreach ($eventArray as $eventName => $callbacks) { foreach ($callbacks as $current) { $sharedEvents->attach($id, $eventName, $current->getCallback(), $current->getMetadatum('priority')); } } } } /** * Get the event * * @return MvcEvent * @throws Exception\DomainException if unable to find event */ protected function getEvent() { if ($this->event) { return $this->event; } $controller = $this->getController(); if (!$controller instanceof InjectApplicationEventInterface) { throw new Exception\DomainException('Forward plugin requires a controller that implements InjectApplicationEventInterface'); } $event = $controller->getEvent(); if (!$event instanceof MvcEvent) { $params = array(); if ($event) { $params = $event->getParams(); } $event = new MvcEvent(); $event->setParams($params); } $this->event = $event; return $this->event; } } |