Main.class.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743
  1. <?PHP
  2. /**
  3. * The main class for the STAPLE Framwork, Staple_Main creates the application and
  4. * coordinates all the other modules. Using this object will require the entire
  5. * framework code to be available. Many other modules can stand apart with varying
  6. * levels of autonomy.
  7. *
  8. * @author Ironpilot
  9. * @copyright Copywrite (c) 2011, STAPLE CODE
  10. *
  11. * This file is part of the STAPLE Framework.
  12. *
  13. * The STAPLE Framework is free software: you can redistribute it and/or modify
  14. * it under the terms of the GNU Lesser General Public License as published by the
  15. * Free Software Foundation, either version 3 of the License, or (at your option)
  16. * any later version.
  17. *
  18. * The STAPLE Framework is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  20. * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
  21. * more details.
  22. *
  23. * You should have received a copy of the GNU Lesser General Public License
  24. * along with the STAPLE Framework. If not, see <http://www.gnu.org/licenses/>.
  25. */
  26. class Staple_Main
  27. {
  28. const DONT_THROW_LOADER_ERRORS = 504;
  29. const DEFAULT_AUTOLOAD_CLASS = 'Staple_Autoload';
  30. /**
  31. *
  32. * The instance property holds the singleton instance for Staple_Main
  33. * @var Staple_Main
  34. */
  35. protected static $instance;
  36. /**
  37. *
  38. * Settings array
  39. * @var array
  40. */
  41. protected $settings = array();
  42. /**
  43. *
  44. * Holds a reference to the database object
  45. * @var Staple_DB
  46. */
  47. protected $db;
  48. /**
  49. *
  50. * Holds the current route
  51. * @var string
  52. */
  53. protected static $route;
  54. /**
  55. * Holds the route executed on the last page call.
  56. * @var string
  57. */
  58. protected static $referrer;
  59. /**
  60. * Holds references to the current instantiated controllers
  61. * @var array of Staple_Controller
  62. */
  63. protected static $controllers = array();
  64. /**
  65. * Holds a reference to the Staple_Auth class
  66. * @var Staple_Auth
  67. */
  68. private $auth;
  69. /**
  70. * The autoloader class instance
  71. * @var Staple_Autoload
  72. */
  73. protected $loader;
  74. /**
  75. * Instance of the error handler object
  76. * @var Staple_Error
  77. */
  78. protected $errorHander;
  79. /**
  80. *
  81. * Private constructor insures that the application is instantiated as a Singleton
  82. */
  83. private $headersent = false;
  84. private $footersent = false;
  85. /**
  86. *
  87. * Application constructor. This function creates a new Staple application. It defines the constants: CONFIG_ROOT, LAYOUT_ROOT,
  88. * FORMS_ROOT, MODEL_ROOT, CONTROLLER_ROOT, VIEW_ROOT, and SCRIPT_ROOT. All of these constants exist as folders inside of the
  89. * PROGRAM_ROOT directory. The constructor loads and checks configuration, sets up the autoloader, sets custom error handlers,
  90. * starts output buffering and begins a session.
  91. */
  92. private function __construct()
  93. {
  94. //Setup STAPLE Constants
  95. defined('CONFIG_ROOT')
  96. || define('CONFIG_ROOT', PROGRAM_ROOT . 'config/');
  97. defined('LAYOUT_ROOT')
  98. || define('LAYOUT_ROOT', PROGRAM_ROOT . 'layouts/');
  99. defined('FORMS_ROOT')
  100. || define('FORMS_ROOT', PROGRAM_ROOT . 'forms/');
  101. defined('MODEL_ROOT')
  102. || define('MODEL_ROOT', PROGRAM_ROOT . 'models/');
  103. defined('CONTROLLER_ROOT')
  104. || define('CONTROLLER_ROOT', PROGRAM_ROOT . 'controllers/');
  105. defined('VIEW_ROOT')
  106. || define('VIEW_ROOT', PROGRAM_ROOT . 'views/');
  107. defined('SCRIPT_ROOT')
  108. || define('SCRIPT_ROOT',PROGRAM_ROOT . 'scripts/');
  109. defined('STAPLE_ROOT')
  110. || define('STAPLE_ROOT',LIBRARY_ROOT . 'Staple/');
  111. //Parse the settings file
  112. $this->settings = parse_ini_file(CONFIG_ROOT.'application.ini',true);
  113. $this->checkSettings();
  114. //Include, create and set the autoloader
  115. //Include the Staple Autoload class always
  116. require_once STAPLE_ROOT.'Autoload.class.php';
  117. //Check for a custom loader
  118. if(array_key_exists('loader', $this->settings['application']))
  119. {
  120. if(class_exists($this->settings['application']['loader']))
  121. {
  122. $itGotRidOfTheRedSquigglyLineThingInPHPStorm = $this->settings['application']['loader'];
  123. $loader = new $itGotRidOfTheRedSquigglyLineThingInPHPStorm();
  124. if($loader instanceof Staple_Autoload)
  125. {
  126. $this->loader = $loader;
  127. spl_autoload_register(array($this->loader, 'load'));
  128. }
  129. }
  130. }
  131. //If no other loader is found or set, use the Staple_Autoload class
  132. if(!($this->loader instanceof Staple_Autoload))
  133. {
  134. $this->loader = new Staple_Autoload();
  135. spl_autoload_register(array($this->loader, 'load'));
  136. }
  137. // Setup Error Handlers
  138. $this->setErrorHander(new Staple_Error());
  139. //Start Output buffering
  140. ob_start();
  141. //Create a session
  142. session_start();
  143. if($this->settings['errors']['devmode'] == 1)
  144. {
  145. Staple_Dev::StartTimer();
  146. }
  147. }
  148. /**
  149. *
  150. * @return Staple_Error $errorHander
  151. */
  152. public function getErrorHander()
  153. {
  154. return $this->errorHander;
  155. }
  156. /**
  157. *
  158. * @param Staple_Error $errorHander
  159. */
  160. public function setErrorHander(Staple_Error $errorHander)
  161. {
  162. $this->errorHander = $errorHander;
  163. set_error_handler(array(
  164. $this->errorHander,
  165. 'handleError'
  166. ), E_USER_ERROR | E_USER_WARNING | E_WARNING);
  167. set_exception_handler(array(
  168. $this->errorHander,
  169. 'handleException'
  170. ));
  171. return $this;
  172. }
  173. public function inDevMode()
  174. {
  175. return (bool)Staple_Config::getValue('errors', 'devmode');
  176. }
  177. /**
  178. * The application destructor renders the footer of the website and flushes the
  179. * output buffer.
  180. */
  181. public function __destruct()
  182. {
  183. $this->processFooter();
  184. $_SESSION['Staple']['Controllers'] = self::$controllers; //Store the controllers in the session
  185. $_SESSION['Staple']['Main']['Referrer'] = self::$route; //Store the last executed route
  186. ob_end_flush();
  187. /*if(Staple_Config::getValue('errors','devmode') == 1) //@todo use "register_shutdown_function" to accomplish this.
  188. {
  189. echo '<!-- Execution Time: '.Staple_Dev::StopTimer()." -->\n";
  190. echo '<!-- Memory Usage: '.number_format(memory_get_peak_usage(true)).' bytes -->';
  191. }*/
  192. }
  193. /**
  194. *
  195. * Instantiates the application as a singleton, and/or returns the current instance.
  196. */
  197. public static function get()
  198. {
  199. if (!(self::$instance instanceof Staple_Main)) {
  200. $c = __CLASS__;
  201. self::$instance = new $c();
  202. }
  203. return self::$instance;
  204. }
  205. /**
  206. * Returns the current route.
  207. * @return $route
  208. */
  209. public static function getRoute()
  210. {
  211. return self::$route;
  212. }
  213. public static function getRouteAction()
  214. {
  215. $route = explode('/', self::$route);
  216. if(count($route) == 0)
  217. {
  218. return 'index/index';
  219. }
  220. elseif(count($route) == 1)
  221. {
  222. return $action = $route[0].'/index';
  223. }
  224. else
  225. {
  226. $action = $route[0].'/'.$route[1];
  227. return $action;
  228. }
  229. }
  230. public static function getReferrer()
  231. {
  232. return self::$referrer;
  233. }
  234. /**
  235. * Returns a reference to a controller object
  236. *
  237. * @param string $class
  238. * @return Staple_Controller | NULL
  239. */
  240. public static function getController($class)
  241. {
  242. if(array_key_exists($class, self::$controllers))
  243. {
  244. return self::$controllers[$class];
  245. }
  246. else
  247. {
  248. /*$controllerClass = $class.'Controller';
  249. if(class_exists($controllerClass))
  250. {
  251. self::$controllers[$class] = new $controllerClass();
  252. self::$controllers[$class]->_start();
  253. return self::$controllers[$class];
  254. }
  255. else
  256. {*/
  257. return NULL;
  258. //}
  259. }
  260. }
  261. /**
  262. *
  263. * Executes the application process.
  264. */
  265. public function run($directive = NULL)
  266. {
  267. //Create and enable site-wide authorization.
  268. if(array_key_exists('enabled', $this->settings['auth']))
  269. if($this->settings['auth']['enabled'] == 1)
  270. $this->auth = Staple_Auth::get();
  271. //Create and connect to the database.
  272. if(array_key_exists('autoconnect', $this->settings['db']))
  273. if($this->settings['db']['autoconnect'] == 1)
  274. $this->db = Staple_DB::get();
  275. //Load the controllers from the session.
  276. if(array_key_exists('Staple', $_SESSION))
  277. if(array_key_exists('Controllers', $_SESSION['Staple']))
  278. if(is_array($_SESSION['Staple']['Controllers']))
  279. self::$controllers = $_SESSION['Staple']['Controllers'];
  280. //Load the referring route from the session.
  281. if(array_key_exists('Staple', $_SESSION))
  282. if(array_key_exists('Main', $_SESSION['Staple']))
  283. if(array_key_exists('Referrer',$_SESSION['Staple']['Main']))
  284. self::$referrer = $_SESSION['Staple']['Main']['Referrer'];
  285. //Processes Initialization Directives
  286. if(isset($directive))
  287. {
  288. switch($directive)
  289. {
  290. case self::DONT_THROW_LOADER_ERRORS:
  291. $this->loader->setThrowOnFailure(false);
  292. break;
  293. }
  294. }
  295. //Run the route through the router.
  296. $this->route();
  297. }
  298. /**
  299. * Draws the header of the site, if found.
  300. * @deprecated
  301. */
  302. public function processHeader($force = false)
  303. {
  304. //Create the site header if used.
  305. if(array_key_exists('header', $this->settings['page']))
  306. {
  307. if($this->settings['page']['header'] != '')
  308. {
  309. $headerFile = $this->settings['page']['header'];
  310. if(file_exists($headerFile))
  311. {
  312. if(!($this->headersent) || $force === true)
  313. {
  314. include $headerFile;
  315. $this->headersent = true;
  316. }
  317. }
  318. else
  319. {
  320. throw new Exception('Invalid Header Location', Staple_Error::APPLICATION_ERROR);
  321. }
  322. }
  323. }
  324. }
  325. /**
  326. * Draws the footer of the site, if found.
  327. * @deprecated
  328. */
  329. public function processFooter()
  330. {
  331. if(array_key_exists('footer', $this->settings['page']))
  332. {
  333. if($this->settings['page']['footer'] != '')
  334. {
  335. $footerFile = $this->settings['page']['footer'];
  336. if(file_exists($footerFile))
  337. {
  338. if(!($this->footersent))
  339. {
  340. include $footerFile;
  341. $this->footersent = true;
  342. }
  343. }
  344. else
  345. {
  346. throw new Exception('Invalid Footer Location', Staple_Error::APPLICATION_ERROR);
  347. }
  348. }
  349. }
  350. }
  351. /**
  352. *
  353. * Main routing function for the application
  354. * @todo Delegate to Staple_Route
  355. * @param string $route
  356. * @throws Exception
  357. */
  358. public function route($route = NULL)
  359. {
  360. //First determine which routing information to use
  361. if(!is_null($route)) //Use the supplied Route
  362. {
  363. self::$route = $route;
  364. }
  365. elseif(array_key_exists('PATH_INFO', $_SERVER)) //Use the URI route
  366. {
  367. self::$route = $_SERVER['PATH_INFO'];
  368. self::$route = urldecode(self::$route); //URL decode any special characters
  369. if(strpos(self::$route, '?') !== false)
  370. {
  371. self::$route = substr(self::$route, 0, strpos(self::$route, '?'));
  372. }
  373. if(strlen(self::$route) == 0 || self::$route == '/')
  374. {
  375. self::$route = 'index/index';
  376. }
  377. }
  378. elseif(array_key_exists('REQUEST_URI', $_SERVER)) //Use the URL route
  379. {
  380. self::$route = $_SERVER['REQUEST_URI'];
  381. self::$route = urldecode(self::$route); //URL decode any special characters
  382. if(strpos(self::$route, '?') !== false)
  383. {
  384. self::$route = substr(self::$route, 0, strpos(self::$route, '?'));
  385. }
  386. if(strlen(self::$route) == 0 || self::$route == '/')
  387. {
  388. self::$route = 'index/index';
  389. }
  390. }
  391. else //Use the default route
  392. {
  393. self::$route = 'index/index';
  394. }
  395. //Run some route cleaning operations.
  396. self::$route = str_replace('\\','/',self::$route); //Convert backslashes to forward slashes
  397. if(substr(self::$route, 0, 1) == '/') //Remove a starting forward slash
  398. {
  399. self::$route = substr(self::$route, 1, strlen(self::$route)-1);
  400. }
  401. if(($end = strpos(self::$route,'.')) !== false) //End routing information on the first "." occurance
  402. {
  403. self::$route = substr(self::$route, 0, $end);
  404. }
  405. //echo '<p>Current Route: '.self::$route.'</p>';
  406. //Check to see if a script exists with that route.
  407. $scriptRoute = SCRIPT_ROOT.self::$route.'.php';
  408. if(file_exists($scriptRoute))
  409. {
  410. //Check for valid path information
  411. if(ctype_alnum(str_replace(array('/','_','-'),'',self::$route)))
  412. {
  413. //Authentication Check
  414. if(Staple_Config::getValue('auth', 'enabled') != 0)
  415. {
  416. //Check for an excluded script route
  417. $allowedScripts = (array)Staple_Config::getValue('auth', 'allowedRoute');
  418. if(in_array(self::$route, $allowedScripts) === true)
  419. {
  420. //Script does not require auth, Dispatch Script
  421. $this->dispatchScript($scriptRoute);
  422. }
  423. else
  424. {
  425. //Check for a login
  426. if($this->auth->isAuthed() === true)
  427. {
  428. //Valid login found, Dispatch Script
  429. $this->dispatchScript($scriptRoute);
  430. }
  431. else
  432. {
  433. //No valid login, no Auth
  434. $this->auth->noAuth();
  435. }
  436. }
  437. }
  438. else
  439. {
  440. //Auth Disabled, Dispatch Script
  441. $this->dispatchScript($scriptRoute);
  442. }
  443. return true;
  444. }
  445. }
  446. else
  447. {
  448. //No Script found, routing to controller/action
  449. //Split the route into it's component elements.
  450. $splitRoute = explode('/',self::$route);
  451. //If the route only contains a controller add the index action
  452. if(count($splitRoute) == 1)
  453. {
  454. array_push($splitRoute, 'index');
  455. }
  456. elseif(count($splitRoute) >= 2)
  457. {
  458. //Correct for extra ending slash.
  459. if(strlen($splitRoute[1]) < 1)
  460. {
  461. $splitRoute[1] = 'index';
  462. }
  463. //If the action is numeric, it is not the action. Insert the index action into the route.
  464. if(is_numeric($splitRoute[1]))
  465. {
  466. $shift = array_shift($splitRoute);
  467. array_unshift($splitRoute, $shift, 'index');
  468. }
  469. }
  470. $class = Staple_Link::methodCase(array_shift($splitRoute));
  471. $method = Staple_Link::methodCase(array_shift($splitRoute));
  472. if(ctype_alnum($class) && ctype_alnum($method))
  473. {
  474. $dispatchClass = $class.'Controller';
  475. $started = false;
  476. //Check for the controller existence
  477. if(class_exists($dispatchClass))
  478. {
  479. //Check for the action existence
  480. if(method_exists($dispatchClass, $method))
  481. {
  482. //If the controller has not been created yet, create an instance and store it in the front controller
  483. if(!array_key_exists($class, self::$controllers))
  484. {
  485. self::$controllers[$class] = new $dispatchClass();
  486. self::$controllers[$class]->_start();
  487. $started = true;
  488. }
  489. //Verify that an instance of the controller class exists and is of the right type
  490. if(self::$controllers[$class] instanceof Staple_Controller)
  491. {
  492. //Check if global Auth is enabled.
  493. if(Staple_Config::getValue('auth', 'enabled') != 0)
  494. {
  495. //Check the sub-controller for access to the method
  496. if(self::$controllers[$class]->_auth($method) === true)
  497. {
  498. $this->dispatchController($class, $method, $splitRoute, $started);
  499. }
  500. else
  501. {
  502. $this->auth->noAuth();
  503. }
  504. }
  505. else
  506. {
  507. $this->dispatchController($class, $method, $splitRoute, $started);
  508. }
  509. return true;
  510. }
  511. }
  512. }
  513. }
  514. }
  515. //If a valid page cannot be found, throw page not found exception
  516. throw new Exception('Page Not Found',Staple_Error::PAGE_NOT_FOUND);
  517. }
  518. /**
  519. *
  520. * Function executes a controller action passing parameters using call_user_func_array().
  521. * It also builds the view for the route.
  522. *
  523. * @param string $class
  524. * @param string $method
  525. * @param array $params
  526. */
  527. protected function dispatchController($controller,$action, array $params, $started = false)
  528. {
  529. if($started !== true)
  530. {
  531. //Start up the controller
  532. call_user_func(array(self::$controllers[$controller],'_start'));
  533. }
  534. //Set the view's controller to match the route
  535. self::$controllers[$controller]->view->setController($controller);
  536. //Set the view's action to match the route
  537. self::$controllers[$controller]->view->setView($action);
  538. //Call the controller action
  539. $actionMethod = new ReflectionMethod(self::$controllers[$controller],$action);
  540. $actionMethod->invokeArgs(self::$controllers[$controller], $params);
  541. //call_user_func_array(array(self::$controllers[$controller],$action), $params);
  542. //Grab the buffer contents from the controller and post it after the header.
  543. $buffer = ob_get_contents();
  544. ob_clean();
  545. //Process the header
  546. $this->processHeader();
  547. if(self::$controllers[$controller]->layout instanceof Staple_Layout)
  548. {
  549. self::$controllers[$controller]->layout->build($buffer);
  550. }
  551. else
  552. {
  553. echo $buffer;
  554. self::$controllers[$controller]->view->build();
  555. }
  556. }
  557. /**
  558. * This function runs the dispatch for a given script route.
  559. *
  560. * @param string $route
  561. */
  562. protected function dispatchScript($route)
  563. {
  564. //Create a blank layout
  565. $layout = new Staple_Layout();
  566. //Find the default Layout
  567. $defaultLayout = Staple_Config::getValue('layout', 'default');
  568. //Setup the default layout
  569. if($defaultLayout != '') $layout->setName($defaultLayout);
  570. //run the script
  571. require $route;
  572. //Grab the buffer contents from the controller and post it after the header.
  573. $buffer = ob_get_contents();
  574. ob_clean();
  575. //Process the Header
  576. $this->processHeader();
  577. if($layout->getName() != '')
  578. {
  579. //Build the Layout
  580. $layout->build($buffer);
  581. }
  582. else
  583. {
  584. //Echo the Buffer
  585. echo $buffer;
  586. }
  587. }
  588. /**
  589. * The purpose of this function is to dispatch an action/view and return the results in a string.
  590. * Any errors that occur will return a boolean false.
  591. * @return string | boolean
  592. */
  593. public function pocketDispatch(Staple_Route $route)
  594. {
  595. //@todo complete the function
  596. }
  597. /**
  598. * @todo This function not implemented yet
  599. */
  600. protected function checkSettings()
  601. {
  602. //Check the settings array
  603. }
  604. /**
  605. * This function has become a helper of the Staple_Link object. Returns a link from
  606. * the specified link parameters.
  607. *
  608. * @see Staple_Link::get()
  609. */
  610. public function link($route, array $get = array())
  611. {
  612. return Staple_Link::get($route,$get);
  613. }
  614. /**
  615. * Removed - no more baselinks....
  616. * Returns a urlencoded link relative to the public base of the website.
  617. * @param string $link
  618. */
  619. /*public function baseLink($link,$get = NULL)
  620. {
  621. return htmlentities($this->settings['application']['public_location'].$link.$get);
  622. }*/
  623. /**
  624. *
  625. * This function creates an internal redirection within the script itself. It accepts the
  626. * redirect as a routing string. This can be generated using the Staple_Link::get() function.
  627. *
  628. * @link Staple_Link::get()
  629. *
  630. * @param mixed $newRoute
  631. */
  632. public function redirect($newRoute)
  633. {
  634. ob_clean();
  635. $this->route(Staple_Link::get($newRoute));
  636. exit(0);
  637. }
  638. /**
  639. *
  640. * Registers a controller that was instantiated outside of the Staple_Main class.
  641. * @param Staple_Controller $controller
  642. */
  643. public function registerController(Staple_Controller $controller)
  644. {
  645. $class_name = substr(get_class($controller),strlen(get_class($controller))-10,10);
  646. if(!array_key_exists($class_name, self::$controllers))
  647. {
  648. self::$controllers[$class_name] = $controller;
  649. }
  650. }
  651. public function excludeHeaderFooter()
  652. {
  653. $this->headersent = true;
  654. $this->footersent = true;
  655. }
  656. /**
  657. * @return the $loader
  658. */
  659. public function getLoader()
  660. {
  661. return $this->loader;
  662. }
  663. /**
  664. * @param Staple_Autoload $loader
  665. */
  666. public function setLoader(Staple_Autoload $loader)
  667. {
  668. $this->loader = $loader;
  669. }
  670. }