/** * Dispatches the Controller * * @param Next\Application\Application $application * Application to Configure * * @param stdClass $data * Data to Configure Application * * @return Next\HTTP\Response * Response Object * * @throws Next\Controller\Dispatcher\DispatcherException * ReflectionException was caught */ public function dispatch(Application $application, \stdClass $data) { try { $this->setDispatched(TRUE); // Adding Request Params $application->getRequest()->setQuery($data->params); // Calling Action from Controller at defined Application $reflector = new \ReflectionMethod($data->class, $data->method); $reflector->invoke(new $data->class($application)); return $application->getResponse(); } catch (\ReflectionException $e) { throw DispatcherException::reflection($e); } catch (ControllerException $e) { /** * @internal * ControllerException's came from Application's Controllers * and, as part of Standardization Concept, should be thrown when * something is wrong * * E.g.: Database Query results in FALSE instead of a Recordset Object * * Doesn't matter the level of DEVELOPMENT MODE Constant, we'll * try to create a Template Variable and virtually re-send the Response, * by re-rendering the View * * Now, in Template View, a special variable will be available with * Exception Message * * If the assignment or rendering fails, the Production Handler * will be used as fallback */ try { $application->getView()->assign('__EXCEPTION__', $e->getMessage())->render(); } catch (ViewException $e) { Handlers::production($e); } } catch (ViewException $e) { /** * @internal * Catching ViewException grants a nice view for any sort of * errors triggered by Next View Class, specially when they come from Magic Methods * which is directly related to Template Variables usage. * * And by forcing a Development Handler we warn lazy * programmers they are doing the wrong thing, like trying to hide the error ^^ */ if (ob_get_length()) { // We want ONLY the Exception Template ob_end_clean(); } Handlers::development($e); } }
/** * Find the Template View File from FileSpec * * @return string * Template View FilePath from defined FileSpec * * @see * Next\Controller\Router\Router::getController() * Next\Controller\Router\Router::getAction() */ private function findFileBySpec() { // Known Replacements $application = $this->_application->getApplicationDirectory(); $controller = $this->_application->getRouter()->getController(); $action = $this->_application->getRouter()->getAction(); /** * @internal * Finding Default SubPath * * Default SubPath is built by removing: * * - Application Directory, * - 'Controller' Keyword * - Controller ClassName * * from Controllers Name */ $subpath = str_replace(array($application, self::CONTROLLERS_KEYWORD, basename($controller)), '', $controller); // Windows, Windows, Windows... <_< $subpath = str_replace('\\\\', '\\', $subpath); $subpath = trim($subpath, '\\'); // Cleaning Controller Class to find its "Real Name" $controller = str_replace('Controller', '', basename($controller)); // Cleaning known Action suffixes $action = str_replace(array(self::ACTION_METHOD_SUFFIX_VIEW, self::ACTION_METHOD_SUFFIX_ACTION), '', $action); // Replacing known matches $spec = trim(str_replace(array(self::APPLICATION, self::CONTROLLER, self::ACTION, self::SUBPATH), array($application, $controller, $action, $subpath), $this->_fileSpec), '/'); $spec = Tools::cleanAndInvertPath($spec); return sprintf('%s.%s', strtolower($spec), $this->_extension); }
/** * Finds a Route that matches to an Application AND current Request * * @param Next\Application\Application $application * Application to iterate Controllers * * @return array|object|boolean * * If a Route could be match against current Request URI an * array or an object will be returned (depending on Connection * Driver configuration). * * If none could, FALSE will be returned */ public function find(Application $application) { // Shortening Declarations $request = $application->getRequest(); $URI = $request->getRequestUri(); // Searching the Request in Routes Database $stmt = $this->dbh->prepare('SELECT `requestMethod`, `class`, `method`, `requiredParams`, `optionalParams` FROM `routes` WHERE `application` = ? AND ( `requestMethod` = ? AND ? REGEXP `URI` )'); $stmt->execute(array($application->getClass()->getName(), $request->getRequestMethod(), $URI)); $data = $stmt->fetch(); // Match found, let's prepare everything for a successful Dispatch if ($data !== FALSE) { /** * @internal * Setting Up Found Controller and its action to be used in View, * as part of findFilebySpec() method */ $this->controller =& $data->class; $this->action =& $data->method; // Analyzing Params $requiredParams = unserialize($data->requiredParams); // Lookup for Required Params in URL if (count($requiredParams) > 0) { $this->lookup($requiredParams, $URI, $request->getQuery()); } /** * @internal * Validating Required Params * Only Parameters with a [List|of|Possible|Values] will be validated */ $token = self::LIST_OPEN_TOKEN; $this->validate(array_filter($requiredParams, function ($item) use($token) { return strpos($item, $token) !== FALSE; }), $URI); // Process Dynamic Params, in order to register them as Request Params $params = $this->process(array_merge($requiredParams, unserialize($data->optionalParams)), $URI); // Merging manually defined GET query $data->params = array_merge($params, $request->getQuery()); // Discarding Unnecessary Information unset($data->requiredParams, $data->optionalParams); return $data; } return FALSE; }
/** * Retrieve a GET Param * * Grant access to a Request Dynamic Params using Property Notation instead Array Notation * * @param string $param * Desired Param from Dynamic Params * * @return mixed Dynamic Param Value * * @throws Next\Controller\ControllerException * Trying to access internal properties prefixed with an underscore * without use their correct accessors * * @throws Next\Controller\ControllerException * Trying to access non-existent param */ public function __get($param) { $param = trim($param); try { return $this->_application->getRequest()->getQuery($param); } catch (RequestException $e) { // Standardizing Exception throw ControllerException::paramNotFound($e); } }
/** * Get Annotations Found * * @return array * Found Annotations */ public function getAnnotations() { $data = new \ArrayIterator(); $domains = $this->matchDomainAnnotations($this->application); if (count($domains) > 0) { $data->offsetSet('Domains', $domains); } $data->offsetSet('Path', $this->matchPathAnnotation($this->application)); // Listing Controllers Methods and reducing (or amplifying) the list to its Action Methods $actions = $this->application->getControllers()->getIterator(); iterator_apply($actions, function (\Iterator $iterator) { $action = new Actions(new \ArrayIterator($iterator->current()->getClass()->getMethods())); $iterator->offsetSet($iterator->key(), $action->getAnnotations()); return TRUE; }, array($actions)); if ($actions->count() > 0) { $data->offsetSet('Routes', $actions->getArrayCopy()); } return $data; }