/** * Dispatches a handle action taking into account the routing parameters * * @return object|boolean */ public function dispatch() { if (is_object($this->_dependencyInjector) === false) { $this->_throwDispatchException('A dependency injection container is required to access related dispatching services', self::EXCEPTION_NO_DI); return false; } if (is_object($this->_eventsManager) === true) { if ($this->_eventsManager->fire('dispatch:beforeDispatchLoop', $this) === false) { return false; } } $numberDispatches = 0; $this->_finished = false; $handler = null; while (true) { //Loop until finished is false if ($this->_finished === true) { break; } ++$numberDispatches; //Throw an exception after 256 consecutive forwards if ($numberDispatches >= 256) { $this->_throwDispatchException('Dispatcher has detected a cyclic routing causing stability problems', self::EXCEPTION_CYCLIC_ROUTING); break; } $this->_finished = true; //If the current namespace is null we use the set in $this->_defaultNamespace if (is_null($this->_namespaceName) === true) { $this->_namespaceName = $this->_defaultNamespace; } //If the handler is null we use the set in $this->_defaultHandler if (is_null($this->_handlerName) === true) { $this->_handlerName = $this->_defaultHandler; } //If the action is null we use the set in $this->_defaultAction if (is_null($this->_actionName) === true) { $this->_actionName = $this->_defaultAction; } //Calling beforeDispatch if (is_object($this->_eventsManager) === true) { if ($this->_eventsManager->fire('dispatch:beforeDispatch', $this) === false) { continue; } //Check if the user made a forward in the listener if ($this->_finished === false) { continue; } } //We don't camelize the classes if they are in namespaces $p = strpos($this->_handlerName, '\\'); if ($p === false) { $camelizedClass = Text::camelize($this->_handlerName); } elseif ($p === 0) { //@note this only handles one leading slash $camelizedClass = substr($this->_handlerName, strlen($this->_handlerName) + 1); } else { $camelizedClass = $this->_handlerName; } //Create the complete controller class name prepending the namespace if (is_null($this->_namespaceName) === false) { if (strrpos($this->_namespaceName, '\\') === strlen($this->_namespaceName) - 1) { $handlerClass = $this->_namespaceName . $camelizedClass . $this->_handlerSuffix; } else { $handlerClass = $this->_namespaceName . '\\' . $camelizedClass . $this->_handlerSuffix; } } else { $handlerClass = $camelizedClass . $this->_handlerSuffix; } //Handlers are retrieved as shared instances from the Service Container if ($this->_dependencyInjector->has($handlerClass) === false) { //Check using autoloading if (class_exists($handlerClass) === false) { if ($this->_throwDispatchException($handlerClass . ' handler class cannot be loaded', self::EXCEPTION_HANDLER_NOT_FOUND) === false) { if ($this->_finished === false) { continue; } } break; } } //Handlers must be only objects $handler = $this->_dependencyInjector->getShared($handlerClass); if (is_object($handler) === false) { if ($this->_throwDispatchException('Invalid handler returned from the services container', self::EXCEPTION_INVALID_HANDLER) === false) { if ($this->_finished === false) { continue; } } break; } //If the object was recently created in the DI we initialize it $wasFresh = $this->_dependencyInjector->wasFreshInstance(); $this->_activeHandler = $handler; //Check if the method exists in the handler $actionMethod = $this->_actionName . $this->_actionSuffix; if (method_exists($handler, $actionMethod) === false) { //Call beforeNotFoundAction if (is_object($this->_eventsManager) === true) { if ($this->_eventsManager->fire('dispatch:beforeNotFoundAction', $this) === false) { continue; } if ($this->_finished === false) { continue; } } if ($this->_throwDispatchException('Action \'' . $this->_actionName . '\' was not found on handler \'' . $this->_handlerName . '\'', self::EXCEPTION_ACTION_NOT_FOUND) === false) { if ($this->_finished === false) { continue; } } break; } //Calling beforeExecuteRoute if (is_object($this->_eventsManager) === true) { if ($this->_eventsManager->fire('dispatch:beforeExecuteRoute', $this) === false) { continue; } //Check if the user made a forward in the listener if ($this->_finished === false) { continue; } } //Calling beforeExecuteRoute as callback and event if (method_exists($handler, 'beforeExecuteRoute') === true) { if ($handler->beforeExecuteRoute($this) === false) { continue; } //Check if the user made a forward in the listener if ($this->_finished === false) { continue; } } //Check if params is an array if (is_array($this->_params) === false) { if ($this->_throwDispatchException('Action parameters must be an Array', self::EXCEPTION_INVALID_PARAMS) === false) { if ($this->_finished === false) { continue; } } break; } //Call the 'initialize' method just once per request if ($wasFresh === true) { if (method_exists($handler, 'initialize') === true) { $handler->initialize(); } } //Call the method with/without exceptions if an events manager is present if (is_object($this->_eventsManager) === true) { try { //Call the method allowing exceptions $m = new ReflectionMethod($handler, $actionMethod); $value = $m->invokeArgs($handler, $this->_params); } catch (\Exception $exception) { //Copy the exception to rethrow it later if needed //Try to handle the exception if ($this->_handleException($exception) === false) { if ($this->_finished === false) { continue; } } else { //Exception wasn't handled, rethrow it throw new Exception($exception); } } //Update the latest value produced by the latest handler $this->_returnedValue = $value; } else { //Call the method handling exceptions as normal $this->_returnedValue = call_user_func_array(array($handler, $actionMethod), $this->_params); } $this->_lastHandler = $handler; //Calling afterExecuteRoute if (is_object($this->_eventsManager) === true) { if ($this->_eventsManager->fire('dispatch:afterExecuteRoute', $this) === false) { continue; } if ($this->_finished === false) { continue; } //Calling afetDispatch $this->_eventsManager->fire('dispatch:afterDispatch', $this); } //Calling afterExecuteRoute as callback and event if (method_exists($handler, 'afterExecuteRoute') === true) { if ($handler->afterExecuteRoute($this, $this->_returnedValue) === false) { continue; } if ($this->_finished === false) { continue; } } } //Call afterDispatchLoop if (is_object($this->_eventsManager) === true) { $this->_eventsManager->fire('dispatch:afterDispatchLoop', $this); } return $handler; }