notify($this->_event); $this->_intercepted = TRUE; } function notify(&$event) { $this->_event = &$event; $this->execute($event->getRegistry(), $event->getRequest()); return $this->_intercepted; } } class FAController { var $_commands; var $_default; var $_interceptors; function FAController() { $this->_commands = &new FANamedDispatcher; $this->_filters = &new FADependencyDispatcher; $this->_interceptors = &new FAMixedDispatcher; } function addCommand(&$command) { assert(is_a($command, 'FAWebCommand')); if (!$this->_default) { $this->_default = $command->getObserverName(); } $this->_commands->addObserver($command); } function addFilter(&$filter) { assert(is_a($filter, 'FAWebFilter')); $this->_filters->addObserver($filter); } function addInterceptor(&$interceptor) { assert(is_a($interceptor, 'FAWebInterceptor')); $this->_interceptors->addObserver($interceptor); } function run() { $name = (isset($_REQUEST[FA_EVENT_VAR])) ? $_REQUEST[FA_EVENT_VAR] : $this->_default; $event = &new FAWebEvent($name); $this->_filters->notifyAll($event); if (!$this->_interceptors->notifyAll($event)) { $this->_commands->notifyAll($event); } } } class FAApplicationController { var $_registry; var $_request; var $_response; var $_dba; var $_model; var $_tpl; var $_page; var $_action; var $_item; var $_controller; var $_pre_filters; var $_post_filters; //-------------------------------------------- // Constructor, bring in the request, registry and // response. //-------------------------------------------- function FAApplicationController() { $this->_registry = &$this->createRegistry(); $this->_request = &$this->createRequest(); $this->_response = &$this->createResponse(); $this->_event = &new FARequestEvent($this->_registry, $this->_request, $this->_response); $this->_pre_filters = &new FADispatcher; $this->_post_filters = &new FADispatcher; } //-------------------------------------------- // Connect to a database. //-------------------------------------------- function connectToDb() { $filename = FA_CONFIG_DIR . '/databases.php'; if (!is_file($filename) || !is_readable($filename)) { trigger_error("Missing database configuration file", E_USER_ERROR); } $dbs = parse_ini_file($filename, TRUE); if (!isset($dbs['release'])) { trigger_error("Missing database for: release", E_USER_ERROR); } $this->_dba = &FADatabase::connect($dbs['release']); $this->_model = &new FAModel($this->_dba); $this->_registry->set('dba', $this->_dba); $this->_registry->set('model', $this->_model); } function &createRegistry() { $registry = &new FARegistry; return $registry; } function &createRequest() { $request = &new FARequest; return $request; } function &createResponse() { $response = &new FAResponse; return $response; } function initRequest() { $this->_request->setArray($this->_vars); } //-------------------------------------------- // Load the current page controller. //-------------------------------------------- function loadPageController() { $filename = $this->_dir . $this->_page . '_controller.php'; if (!is_file($filename) || !is_readable($filename)) { trigger_error("No controller for the page: {$this->_page}", E_USER_ERROR); } require_once $filename; $class = ucfirst($this->_page) . 'Controller'; if (!class_exists($class)) { trigger_error("No appropriate controller defined for the page: {$this->_page}", E_USER_ERROR); } $controller = &new $class($this->_registry, $this->_request, $this->_response); if (!is_a($controller, 'FAPageController')) { trigger_error("No appropriate controller defined for the page: {$this->_page}", E_USER_ERROR); } $this->_controller = &$controller; } //-------------------------------------------- // Load filters that are executed after actions. //-------------------------------------------- function loadPostFilters() { $filters = $this->_controller->getPostFilters(); foreach ($filters as $filter) { $filename = FA_FILTER_DIR . "/{$filter}_filter.php"; if (!is_file($filename) || !is_readable($filename)) { trigger_error("No such filter: $filter", E_USER_ERROR); } require $filename; $class = camelize($filter) . 'Filter'; if (!class_exists($class)) { trigger_error("No appropriate filter for: $filter", E_USER_ERROR); } $obj = &new $class($this->_model); if (!is_a($obj, 'FAPostFilter')) { trigger_error("No appropriate filter for: $filter", E_USER_ERROR); } $this->_post_filters->addObserver($obj); } } //-------------------------------------------- // Load filters that get executed before actions // do. //-------------------------------------------- function loadPreFilters() { $filters = $this->_controller->getPreFilters(); foreach ($filters as $filter) { $filename = FA_FILTER_DIR . "/{$filter}_filter.php"; if (!is_file($filename) || !is_readable($filename)) { trigger_error("No such filter: $filter", E_USER_ERROR); } require_once $filename; $class = camelize($filter) . 'Filter'; if (!class_exists($class)) { trigger_error("No appropriate filter for: $filter", E_USER_ERROR); } $obj = &new $class($this->_model); if (!is_a($obj, 'FAPreFilter')) { trigger_error("No appropriate filter for: $filter", E_USER_ERROR); } $this->_pre_filters->addObserver($obj); } } //-------------------------------------------- // Parse the path request variable and derive the // page controller from it, then return an array // of all of the elements in the path (without the // leading controller name). //-------------------------------------------- function parsePath() { //-------------------------------------------- // Get the path and then split it up by the /'s // P.S. explode() does not work in this scenario // because explode() will get empty elements. //-------------------------------------------- $path = $this->_request->get('path'); $path = preg_split('~/~', $path, -1, PREG_SPLIT_NO_EMPTY); //-------------------------------------------- // Try to find the controller. //-------------------------------------------- $this->_dir = FA_CONTROLLER_DIR . '/'; //-------------------------------------------- // Shift off elements of the array if directories // in the controller dir exist for this path. //-------------------------------------------- while (!empty($path) && is_dir($this->_dir . $path[0])) { $this->_dir .= array_shift($path) . '/'; } //-------------------------------------------- // Figure out what controller function should be // used. //-------------------------------------------- $page = (empty($path)) ? 'index' : array_shift($path); //-------------------------------------------- // Set the info. //-------------------------------------------- $this->_page = $page; $this->_path = $path; } //-------------------------------------------- // Take a route variable, check if it has a forced // type and return the variable name along with the // type. //-------------------------------------------- function checkRoutePartVarInfo($rpart, $ppart) { $type_cast = substr_utf($rpart, 1, 5); $ret = array( 'var' => substr_utf($rpart, 1), 'val' => $ppart, 'clear' => FALSE, ); //-------------------------------------------- // Separate the variable name, type cast, return // the type cast and check if the path part is // properly formatted. //-------------------------------------------- if($type_cast == '(int)') { //-------------------------------------------- // Make sure that the path variable passed in is // an integer. //-------------------------------------------- if(ctype_digit($ppart)) { $ret['clear'] = TRUE; } $ret['var'] = substr_utf($rpart, 6); $ret['val'] = intval($ppart); } else if ($type_cast == '(str)') { //-------------------------------------------- // Make sure that the path variable passed in is // a string. //-------------------------------------------- if(is_string($ppart) || is_numeric($ppart)) { $ret['clear'] = TRUE; } $ret['var'] = substr_utf($rpart, 6); $ret['val'] = strval($ppart); } else { $ret['clear'] = TRUE; } return $ret; } //-------------------------------------------- // Parse the routes and figure out which one is the // one we're on. // Note to geoffrey: I remade a lot of this due to problems // with conflicts with custom index actions and normal custom // actions with variables. - Peter. //-------------------------------------------- function parseRoutes() { //-------------------------------------------- // Get the routes defined in the controllers and // setup some other variables while at it. //-------------------------------------------- $routes = $this->_controller->getRoutes(); $path_count = count($this->_path); $this->_action = 'index'; $this->_vars = array(); $matched = FALSE; //-------------------------------------------- // If there are parts to the path... //-------------------------------------------- if ($path_count > 0) { //-------------------------------------------- // Loop through the routes from the controllers. //-------------------------------------------- foreach ($routes as $action => $route) { //-------------------------------------------- // Split the routes from the controllers into $rparts. // Take the current split up path as $pparts. //-------------------------------------------- $rparts = preg_split('~/~', $route, -1, PREG_SPLIT_NO_EMPTY); $pparts = $this->_path; $rparts_count = count($rparts); $pparts_count = count($pparts); $vars = array(); //-------------------------------------------- // Loop through the route and path parts where // each index, $i, exists for them both. //-------------------------------------------- for ($i = 0; $i < $pparts_count; $i++) { //-------------------------------------------- // If there are more parts in the path than in the // route than it can't possibly be this route. //-------------------------------------------- if (!isset($rparts[$i])) { continue 2; } $rpart = $rparts[$i]; $ppart = $pparts[$i]; //-------------------------------------------- // Parse out the variables or just the elements of // the route. //-------------------------------------------- if ($rpart{0} == ':') { //-------------------------------------------- // deal with typecasting. //-------------------------------------------- $ret = $this->checkRoutePartVarInfo($rpart, $ppart); //-------------------------------------------- // Do some checking if this is the first part of // the route. Note, $ppart is passed directly into // the method_exists() because you don't want the // typecasting to overwrite it. //-------------------------------------------- if($i == 0 && !$ret['clear'] && method_exists($this->_controller, $ppart)) { continue 2; } //-------------------------------------------- // Add the casted variable to the $vars array. //-------------------------------------------- $vars[$ret['var']] = $ret['val']; } elseif ($rpart != $ppart) { continue 2; } } //-------------------------------------------- // Change what our action should be //-------------------------------------------- $this->_action = $action; $this->_vars = $vars; $matched = TRUE; break; } } else { //-------------------------------------------- // If we have a custom index controller and we // have gotten to this point, then see if the // page variable, the first element on the // path REQUEST variable, is an applicable action // in the controller. //-------------------------------------------- if (!method_exists($this->_controller, $this->_action)) { if (method_exists($this->_controller, $this->_page)) { $this->_action = $this->_page; } } } //-------------------------------------------- // If we didn't match any of the routes and the // path isn't empty, let's go look for our normal, // nothing special (no request variables) actions. //-------------------------------------------- if (!$matched && !empty($this->_path)) { if (method_exists($this->_controller, $this->_path[0])) { $this->_action = array_shift($this->_path); } } //-------------------------------------------- // Make sure we're not trying to access a private // method of the controller. //-------------------------------------------- if ($this->_action{0} == '_' || $this->_action == 'getRoutes' || $this->_action == 'getPreFilters' || $this->_action == 'getPostFilters') { trigger_error("[ERROR] You cannot access a private method of this controller."); } } function renderPage() { $layout = FA_VIEW_DIR . '/' . $this->_controller->getLayout(); if (is_file($layout)) { $this->_response->render($layout); } } function run() { $this->parsePath(); $this->connectToDb(); $this->loadPageController(); $this->loadPreFilters(); $this->loadPostFilters(); $this->parseRoutes(); $this->initRequest(); $this->runPreFilters(); $this->runPageController(); $this->renderPage(); $this->runPostFilters(); } function runPageController() { if (!method_exists($this->_controller, $this->_action)) { trigger_error("No controller for the action: {$this->_action}", E_USER_ERROR); } //$this->_request->setItem($this->_item); call_user_func_array( array(&$this->_controller, $this->_action), array(&$this->_registry, &$this->_request, &$this->_response) ); } function runPreFilters() { $this->_pre_filters->notifyAll($this->_event); } function runPostFilters() { $this->_post_filters->notifyAll($this->_event); } } class FAPageController { var $_layout; var $_model; var $_registry; function FAPageController(&$registry, &$request, &$response) { $this->_model = &$registry->get('model'); $this->_registry = &$registry; $this->_request = &$request; $this->_response = &$response; } function getLayout() { return $this->_layout; } function &get($key) { return $this->_registry->get($key); } function &getFinder($table) { $ret = &$this->_model->getFinder($table); return $ret; } function &getRegistry() { return $this->_registry; } function &getRequest() { return $this->_request; } function &getResponse() { return $this->_response; } function getPostFilters() { return array(); } function getPreFilters() { return array(); } function getRoutes() { return array(); } function setLayout($filename) { $this->_layout = $filename; } function &getExtension($name) { $file_name = K4_BASE_DIR .'/extensions/'. $name .'.php'; $ext = NULL; if(file_exists($file_name)) { require_once $file_name; if(class_exists($name)) { $ext = &new $name(); if(!is_a($ext, 'FAExtension')) { $ext = NULL; } } } return $ext; } } // TODO:{[Change to eliminate need[[FARequest} class FARequest extends FAWebRequest { } ?>