. */ class Staple_Main { const DONT_THROW_LOADER_ERRORS = 504; const DEFAULT_AUTOLOAD_CLASS = 'Staple_Autoload'; /** * * The instance property holds the singleton instance for Staple_Main * @var Staple_Main */ protected static $instance; /** * * Settings array * @var array */ protected $settings = array(); /** * * Holds a reference to the database object * @var Staple_DB */ protected $db; /** * * Holds the current route * @var string */ protected static $route; /** * Holds the route executed on the last page call. * @var string */ protected static $referrer; /** * Holds references to the current instantiated controllers * @var array of Staple_Controller */ protected static $controllers = array(); /** * Holds a reference to the Staple_Auth class * @var Staple_Auth */ private $auth; /** * The autoloader class instance * @var Staple_Autoload */ protected $loader; /** * Instance of the error handler object * @var Staple_Error */ protected $errorHander; /** * * Private constructor insures that the application is instantiated as a Singleton */ private $headersent = false; private $footersent = false; /** * * Application constructor. This function creates a new Staple application. It defines the constants: CONFIG_ROOT, LAYOUT_ROOT, * FORMS_ROOT, MODEL_ROOT, CONTROLLER_ROOT, VIEW_ROOT, and SCRIPT_ROOT. All of these constants exist as folders inside of the * PROGRAM_ROOT directory. The constructor loads and checks configuration, sets up the autoloader, sets custom error handlers, * starts output buffering and begins a session. */ private function __construct() { //Setup STAPLE Constants defined('CONFIG_ROOT') || define('CONFIG_ROOT', PROGRAM_ROOT . 'config/'); defined('LAYOUT_ROOT') || define('LAYOUT_ROOT', PROGRAM_ROOT . 'layouts/'); defined('FORMS_ROOT') || define('FORMS_ROOT', PROGRAM_ROOT . 'forms/'); defined('MODEL_ROOT') || define('MODEL_ROOT', PROGRAM_ROOT . 'models/'); defined('CONTROLLER_ROOT') || define('CONTROLLER_ROOT', PROGRAM_ROOT . 'controllers/'); defined('VIEW_ROOT') || define('VIEW_ROOT', PROGRAM_ROOT . 'views/'); defined('SCRIPT_ROOT') || define('SCRIPT_ROOT',PROGRAM_ROOT . 'scripts/'); defined('STAPLE_ROOT') || define('STAPLE_ROOT',LIBRARY_ROOT . 'Staple/'); //Parse the settings file $this->settings = parse_ini_file(CONFIG_ROOT.'application.ini',true); $this->checkSettings(); //Include, create and set the autoloader //Include the Staple Autoload class always require_once STAPLE_ROOT.'Autoload.class.php'; //Check for a custom loader if(array_key_exists('loader', $this->settings['application'])) { if(class_exists($this->settings['application']['loader'])) { $itGotRidOfTheRedSquigglyLineThingInPHPStorm = $this->settings['application']['loader']; $loader = new $itGotRidOfTheRedSquigglyLineThingInPHPStorm(); if($loader instanceof Staple_Autoload) { $this->loader = $loader; spl_autoload_register(array($this->loader, 'load')); } } } //If no other loader is found or set, use the Staple_Autoload class if(!($this->loader instanceof Staple_Autoload)) { $this->loader = new Staple_Autoload(); spl_autoload_register(array($this->loader, 'load')); } // Setup Error Handlers $this->setErrorHander(new Staple_Error()); //Start Output buffering ob_start(); //Create a session session_start(); if($this->settings['errors']['devmode'] == 1) { Staple_Dev::StartTimer(); } } /** * * @return Staple_Error $errorHander */ public function getErrorHander() { return $this->errorHander; } /** * * @param Staple_Error $errorHander */ public function setErrorHander(Staple_Error $errorHander) { $this->errorHander = $errorHander; set_error_handler(array( $this->errorHander, 'handleError' ), E_USER_ERROR | E_USER_WARNING | E_WARNING); set_exception_handler(array( $this->errorHander, 'handleException' )); return $this; } public function inDevMode() { return (bool)Staple_Config::getValue('errors', 'devmode'); } /** * The application destructor renders the footer of the website and flushes the * output buffer. */ public function __destruct() { $this->processFooter(); $_SESSION['Staple']['Controllers'] = self::$controllers; //Store the controllers in the session $_SESSION['Staple']['Main']['Referrer'] = self::$route; //Store the last executed route ob_end_flush(); /*if(Staple_Config::getValue('errors','devmode') == 1) //@todo use "register_shutdown_function" to accomplish this. { echo '\n"; echo ''; }*/ } /** * * Instantiates the application as a singleton, and/or returns the current instance. */ public static function get() { if (!(self::$instance instanceof Staple_Main)) { $c = __CLASS__; self::$instance = new $c(); } return self::$instance; } /** * Returns the current route. * @return $route */ public static function getRoute() { return self::$route; } public static function getRouteAction() { $route = explode('/', self::$route); if(count($route) == 0) { return 'index/index'; } elseif(count($route) == 1) { return $action = $route[0].'/index'; } else { $action = $route[0].'/'.$route[1]; return $action; } } public static function getReferrer() { return self::$referrer; } /** * Returns a reference to a controller object * * @param string $class * @return Staple_Controller | NULL */ public static function getController($class) { if(array_key_exists($class, self::$controllers)) { return self::$controllers[$class]; } else { /*$controllerClass = $class.'Controller'; if(class_exists($controllerClass)) { self::$controllers[$class] = new $controllerClass(); self::$controllers[$class]->_start(); return self::$controllers[$class]; } else {*/ return NULL; //} } } /** * * Executes the application process. */ public function run($directive = NULL) { //Create and enable site-wide authorization. if(array_key_exists('enabled', $this->settings['auth'])) if($this->settings['auth']['enabled'] == 1) $this->auth = Staple_Auth::get(); //Create and connect to the database. if(array_key_exists('autoconnect', $this->settings['db'])) if($this->settings['db']['autoconnect'] == 1) $this->db = Staple_DB::get(); //Load the controllers from the session. if(array_key_exists('Staple', $_SESSION)) if(array_key_exists('Controllers', $_SESSION['Staple'])) if(is_array($_SESSION['Staple']['Controllers'])) self::$controllers = $_SESSION['Staple']['Controllers']; //Load the referring route from the session. if(array_key_exists('Staple', $_SESSION)) if(array_key_exists('Main', $_SESSION['Staple'])) if(array_key_exists('Referrer',$_SESSION['Staple']['Main'])) self::$referrer = $_SESSION['Staple']['Main']['Referrer']; //Processes Initialization Directives if(isset($directive)) { switch($directive) { case self::DONT_THROW_LOADER_ERRORS: $this->loader->setThrowOnFailure(false); break; } } //Run the route through the router. $this->route(); } /** * Draws the header of the site, if found. * @deprecated */ public function processHeader($force = false) { //Create the site header if used. if(array_key_exists('header', $this->settings['page'])) { if($this->settings['page']['header'] != '') { $headerFile = $this->settings['page']['header']; if(file_exists($headerFile)) { if(!($this->headersent) || $force === true) { include $headerFile; $this->headersent = true; } } else { throw new Exception('Invalid Header Location', Staple_Error::APPLICATION_ERROR); } } } } /** * Draws the footer of the site, if found. * @deprecated */ public function processFooter() { if(array_key_exists('footer', $this->settings['page'])) { if($this->settings['page']['footer'] != '') { $footerFile = $this->settings['page']['footer']; if(file_exists($footerFile)) { if(!($this->footersent)) { include $footerFile; $this->footersent = true; } } else { throw new Exception('Invalid Footer Location', Staple_Error::APPLICATION_ERROR); } } } } /** * * Main routing function for the application * @todo Delegate to Staple_Route * @param string $route * @throws Exception */ public function route($route = NULL) { //First determine which routing information to use if(!is_null($route)) //Use the supplied Route { self::$route = $route; } elseif(array_key_exists('PATH_INFO', $_SERVER)) //Use the URI route { self::$route = $_SERVER['PATH_INFO']; self::$route = urldecode(self::$route); //URL decode any special characters if(strpos(self::$route, '?') !== false) { self::$route = substr(self::$route, 0, strpos(self::$route, '?')); } if(strlen(self::$route) == 0 || self::$route == '/') { self::$route = 'index/index'; } } elseif(array_key_exists('REQUEST_URI', $_SERVER)) //Use the URL route { self::$route = $_SERVER['REQUEST_URI']; self::$route = urldecode(self::$route); //URL decode any special characters if(strpos(self::$route, '?') !== false) { self::$route = substr(self::$route, 0, strpos(self::$route, '?')); } if(strlen(self::$route) == 0 || self::$route == '/') { self::$route = 'index/index'; } } else //Use the default route { self::$route = 'index/index'; } //Run some route cleaning operations. self::$route = str_replace('\\','/',self::$route); //Convert backslashes to forward slashes if(substr(self::$route, 0, 1) == '/') //Remove a starting forward slash { self::$route = substr(self::$route, 1, strlen(self::$route)-1); } if(($end = strpos(self::$route,'.')) !== false) //End routing information on the first "." occurance { self::$route = substr(self::$route, 0, $end); } //echo '

Current Route: '.self::$route.'

'; //Check to see if a script exists with that route. $scriptRoute = SCRIPT_ROOT.self::$route.'.php'; if(file_exists($scriptRoute)) { //Check for valid path information if(ctype_alnum(str_replace(array('/','_','-'),'',self::$route))) { //Authentication Check if(Staple_Config::getValue('auth', 'enabled') != 0) { //Check for an excluded script route $allowedScripts = (array)Staple_Config::getValue('auth', 'allowedRoute'); if(in_array(self::$route, $allowedScripts) === true) { //Script does not require auth, Dispatch Script $this->dispatchScript($scriptRoute); } else { //Check for a login if($this->auth->isAuthed() === true) { //Valid login found, Dispatch Script $this->dispatchScript($scriptRoute); } else { //No valid login, no Auth $this->auth->noAuth(); } } } else { //Auth Disabled, Dispatch Script $this->dispatchScript($scriptRoute); } return true; } } else { //No Script found, routing to controller/action //Split the route into it's component elements. $splitRoute = explode('/',self::$route); //If the route only contains a controller add the index action if(count($splitRoute) == 1) { array_push($splitRoute, 'index'); } elseif(count($splitRoute) >= 2) { //Correct for extra ending slash. if(strlen($splitRoute[1]) < 1) { $splitRoute[1] = 'index'; } //If the action is numeric, it is not the action. Insert the index action into the route. if(is_numeric($splitRoute[1])) { $shift = array_shift($splitRoute); array_unshift($splitRoute, $shift, 'index'); } } $class = Staple_Link::methodCase(array_shift($splitRoute)); $method = Staple_Link::methodCase(array_shift($splitRoute)); if(ctype_alnum($class) && ctype_alnum($method)) { $dispatchClass = $class.'Controller'; $started = false; //Check for the controller existence if(class_exists($dispatchClass)) { //Check for the action existence if(method_exists($dispatchClass, $method)) { //If the controller has not been created yet, create an instance and store it in the front controller if(!array_key_exists($class, self::$controllers)) { self::$controllers[$class] = new $dispatchClass(); self::$controllers[$class]->_start(); $started = true; } //Verify that an instance of the controller class exists and is of the right type if(self::$controllers[$class] instanceof Staple_Controller) { //Check if global Auth is enabled. if(Staple_Config::getValue('auth', 'enabled') != 0) { //Check the sub-controller for access to the method if(self::$controllers[$class]->_auth($method) === true) { $this->dispatchController($class, $method, $splitRoute, $started); } else { $this->auth->noAuth(); } } else { $this->dispatchController($class, $method, $splitRoute, $started); } return true; } } } } } //If a valid page cannot be found, throw page not found exception throw new Exception('Page Not Found',Staple_Error::PAGE_NOT_FOUND); } /** * * Function executes a controller action passing parameters using call_user_func_array(). * It also builds the view for the route. * * @param string $class * @param string $method * @param array $params */ protected function dispatchController($controller,$action, array $params, $started = false) { if($started !== true) { //Start up the controller call_user_func(array(self::$controllers[$controller],'_start')); } //Set the view's controller to match the route self::$controllers[$controller]->view->setController($controller); //Set the view's action to match the route self::$controllers[$controller]->view->setView($action); //Call the controller action $actionMethod = new ReflectionMethod(self::$controllers[$controller],$action); $actionMethod->invokeArgs(self::$controllers[$controller], $params); //call_user_func_array(array(self::$controllers[$controller],$action), $params); //Grab the buffer contents from the controller and post it after the header. $buffer = ob_get_contents(); ob_clean(); //Process the header $this->processHeader(); if(self::$controllers[$controller]->layout instanceof Staple_Layout) { self::$controllers[$controller]->layout->build($buffer); } else { echo $buffer; self::$controllers[$controller]->view->build(); } } /** * This function runs the dispatch for a given script route. * * @param string $route */ protected function dispatchScript($route) { //Create a blank layout $layout = new Staple_Layout(); //Find the default Layout $defaultLayout = Staple_Config::getValue('layout', 'default'); //Setup the default layout if($defaultLayout != '') $layout->setName($defaultLayout); //run the script require $route; //Grab the buffer contents from the controller and post it after the header. $buffer = ob_get_contents(); ob_clean(); //Process the Header $this->processHeader(); if($layout->getName() != '') { //Build the Layout $layout->build($buffer); } else { //Echo the Buffer echo $buffer; } } /** * The purpose of this function is to dispatch an action/view and return the results in a string. * Any errors that occur will return a boolean false. * @return string | boolean */ public function pocketDispatch(Staple_Route $route) { //@todo complete the function } /** * @todo This function not implemented yet */ protected function checkSettings() { //Check the settings array } /** * This function has become a helper of the Staple_Link object. Returns a link from * the specified link parameters. * * @see Staple_Link::get() */ public function link($route, array $get = array()) { return Staple_Link::get($route,$get); } /** * Removed - no more baselinks.... * Returns a urlencoded link relative to the public base of the website. * @param string $link */ /*public function baseLink($link,$get = NULL) { return htmlentities($this->settings['application']['public_location'].$link.$get); }*/ /** * * This function creates an internal redirection within the script itself. It accepts the * redirect as a routing string. This can be generated using the Staple_Link::get() function. * * @link Staple_Link::get() * * @param mixed $newRoute */ public function redirect($newRoute) { ob_clean(); $this->route(Staple_Link::get($newRoute)); exit(0); } /** * * Registers a controller that was instantiated outside of the Staple_Main class. * @param Staple_Controller $controller */ public function registerController(Staple_Controller $controller) { $class_name = substr(get_class($controller),strlen(get_class($controller))-10,10); if(!array_key_exists($class_name, self::$controllers)) { self::$controllers[$class_name] = $controller; } } public function excludeHeaderFooter() { $this->headersent = true; $this->footersent = true; } /** * @return the $loader */ public function getLoader() { return $this->loader; } /** * @param Staple_Autoload $loader */ public function setLoader(Staple_Autoload $loader) { $this->loader = $loader; } }