PageRenderTime 51ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/Library/Kumbia/Dispatcher/Dispatcher.php

http://kumbia-enterprise.googlecode.com/
PHP | 597 lines | 296 code | 53 blank | 248 comment | 61 complexity | 9c23dfd59b70aa5257212f678b4f4a03 MD5 | raw file
  1. <?php
  2. /**
  3. * Kumbia Enterprise Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the New BSD License that is bundled
  8. * with this package in the file docs/LICENSE.txt.
  9. *
  10. * If you did not receive a copy of the license and are unable to
  11. * obtain it through the world-wide-web, please send an email
  12. * to license@loudertechnology.com so we can send you a copy immediately.
  13. *
  14. * @category Kumbia
  15. * @package Dispatcher
  16. * @copyright Copyright (c) 2008-2010 Louder Technology COL. (http://www.loudertechnology.com)
  17. * @copyright Copyright (c) 2005-2009 Andres Felipe Gutierrez (gutierrezandresfelipe at gmail.com)
  18. * @license New BSD License
  19. * @version $Id: Dispatcher.php 150 2010-09-24 16:20:10Z gutierrezandresfelipe $
  20. */
  21. /**
  22. * Dispatcher
  23. *
  24. * Clase para que administra las peticiones del Servidor de Aplicaciones
  25. *
  26. * @category Kumbia
  27. * @package Dispatcher
  28. * @copyright Copyright (c) 2008-2010 Louder Technology COL. (http://www.loudertechnology.com)
  29. * @copyright Copyright (c) 2005-2009 Andres Felipe Gutierrez (gutierrezandresfelipe at gmail.com)
  30. * @license New BSD License
  31. * @access public
  32. * @abstract
  33. */
  34. abstract class Dispatcher {
  35. /**
  36. * Contiene referencias a los controladores instanciados
  37. *
  38. * @var array
  39. * @staticvar
  40. */
  41. static private $_controllerReferences = array();
  42. /**
  43. * Estadisticas de ejecución de controladores
  44. *
  45. * @var array
  46. */
  47. static private $_controllerStatistic = array();
  48. /**
  49. * Indica si ya se han inicializado los componentes
  50. *
  51. * @var boolean
  52. * @staticvar
  53. */
  54. static private $_initializedComponents = false;
  55. /**
  56. * Indica el estado de ejecucion de la aplicacion
  57. *
  58. * @var integer
  59. * @staticvar
  60. */
  61. static private $_requestStatus = self::STATUS_UNINITIALIZED;
  62. /**
  63. * Valor devuelto por el metodo accion ejecutado
  64. *
  65. * @var string
  66. * @staticvar
  67. */
  68. static private $_valueReturned = null;
  69. /**
  70. * Objeto del controlador en ejecucion
  71. *
  72. * @var mixed
  73. * @staticvar
  74. */
  75. static private $_controller;
  76. /**
  77. * Directorio de controladores
  78. *
  79. * @var string
  80. * @staticvar
  81. */
  82. static private $_controllersDir;
  83. /**
  84. * Lista de clases que no deben ser serializadas por el Dispatcher
  85. *
  86. * @var array
  87. */
  88. static private $_notSerializableClasses = array('ActiveRecord', 'ActiveRecordResulset');
  89. /**
  90. * Codigo de error cuando no encuentra la accion
  91. */
  92. const NOT_FOUND_ACTION = 100;
  93. const NOT_FOUND_CONTROLLER = 101;
  94. const NOT_FOUND_FILE_CONTROLLER = 102;
  95. const NOT_FOUND_INIT_ACTION = 103;
  96. /**
  97. * Otros codigos de excepciones
  98. */
  99. const INVALID_METHOD_CALLBACK = 104;
  100. const INVALID_ARGUMENT_NUMBER = 105;
  101. /**
  102. * Estados de Ejecucion de la Peticion
  103. */
  104. const STATUS_UNINITIALIZED = 199;
  105. const STATUS_DISPATCHING = 200;
  106. const STATUS_RUNNING_BEFORE_FILTERS = 201;
  107. const STATUS_RUNNING_AFTER_FILTERS = 202;
  108. const STATUS_RENDER_PRESENTATION = 203;
  109. const STATUS_RUNNING_BEFORE_STORE_PERSISTENCE = 204;
  110. const STATUS_RUNNING_AFTER_STORE_PERSISTENCE = 205;
  111. const STATUS_RUNNING_CONTROLLER_ACTION = 206;
  112. /**
  113. * Ejecuta la accion init en ApplicationController
  114. *
  115. * @access public
  116. * @return boolean
  117. * @static
  118. */
  119. static public function initBase(){
  120. /**
  121. * Inicializa los componentes del Framework
  122. */
  123. self::$_requestStatus = self::STATUS_RUNNING_CONTROLLER_ACTION;
  124. self::initComponents();
  125. $applicationController = new ApplicationController();
  126. if(method_exists($applicationController, 'init')){
  127. $applicationController->init();
  128. } else {
  129. if(self::executeNotFound($applicationController)==false){
  130. //No se encontró el método init en la clase ControllerBase
  131. $message = CoreLocale::getErrorMessage(-103);
  132. self::throwException($message, self::NOT_FOUND_INIT_ACTION);
  133. } else {
  134. self::$_controller = $applicationController;
  135. }
  136. }
  137. }
  138. /**
  139. * Ejecuta la acción notFound si está definida
  140. *
  141. * @access private
  142. * @param Controller $applicationController
  143. * @static
  144. */
  145. static private function executeNotFound($applicationController=''){
  146. if($applicationController==''){
  147. $applicationController = new ApplicationController();
  148. }
  149. #if[no-plugins]
  150. PluginManager::notifyFromController('beforeNotFoundAction', $applicationController);
  151. #endif
  152. if(method_exists($applicationController, 'notFoundAction')){
  153. $notFoundStatus = call_user_func_array(
  154. array($applicationController, 'notFoundAction'),
  155. Router::getAllParameters()
  156. );
  157. if($notFoundStatus===false){
  158. return false;
  159. } else {
  160. return true;
  161. }
  162. } else {
  163. return false;
  164. }
  165. }
  166. /**
  167. * Establece el directorio de los controladores
  168. *
  169. * @access public
  170. * @param string $directory
  171. * @static
  172. */
  173. static public function setControllerDir($directory){
  174. self::$_controllersDir = $directory;
  175. }
  176. /**
  177. * Establece el controlador interno de Dispatcher
  178. *
  179. * @access public
  180. * @param Object $controller
  181. * @static
  182. */
  183. static public function setController($controller){
  184. self::$_controller = $controller;
  185. }
  186. /**
  187. * Ejecuta el filtro before presente en el controlador
  188. *
  189. * @access public
  190. * @param mixed $appController
  191. * @param string $controller
  192. * @param string $action
  193. * @param array $params
  194. * @static
  195. */
  196. static private function _runBeforeFilters($appController, $controller, $action, $params){
  197. // El método beforeFilter es llamado antes de ejecutar una acción en un
  198. // controlador, puede servir para realizar ciertas validaciones
  199. #if[dispatcher-status]
  200. self::$_requestStatus = self::STATUS_RUNNING_BEFORE_FILTERS;
  201. #endif
  202. if(method_exists($appController, 'beforeFilter')){
  203. if(call_user_func_array(array(self::$_controller, 'beforeFilter'), $params)===false){
  204. return false;
  205. }
  206. } else {
  207. if(isset(self::$_controller->beforeFilter)){
  208. if(call_user_func_array(array(self::$_controller, self::$_controller->beforeFilter), $params)===false){
  209. return false;
  210. }
  211. }
  212. }
  213. }
  214. /**
  215. * Corre los filtros after en el controlador actual
  216. *
  217. * @param string $appController
  218. * @param string $controller
  219. * @param string $action
  220. * @param array $params
  221. * @static
  222. */
  223. static private function _runAfterFilters($appController, $controller, $action, $params){
  224. // El método afterFilter es llamado despues de ejecutar una accion en un
  225. // controlador, puede servir para realizar ciertas validaciones
  226. #if[dispatcher-status]
  227. self::$_requestStatus = self::STATUS_RUNNING_BEFORE_FILTERS;
  228. #endif
  229. if(method_exists($appController, 'afterFilter')){
  230. call_user_func_array(array(self::$_controller, 'afterFilter'), $params);
  231. } else {
  232. if(isset(self::$_controller->afterFilter)){
  233. call_user_func_array(array(self::$_controller, self::$_controller->afterFilter), $params);
  234. }
  235. }
  236. }
  237. /**
  238. * Incluye los componentes para ejecutar la petición
  239. *
  240. * @access public
  241. * @static
  242. */
  243. static public function initComponents(){
  244. if(self::$_initializedComponents==false){
  245. self::$_initializedComponents = true;
  246. } else {
  247. return;
  248. }
  249. }
  250. /**
  251. * Agrega una clase que no debe ser serializada
  252. *
  253. * @access public
  254. * @param string $className
  255. * @static
  256. */
  257. static public function addNotSerializableClass($className){
  258. self::$_notSerializableClasses[] = $className;
  259. }
  260. /**
  261. * Realiza el dispatch de una ruta
  262. *
  263. * @access public
  264. * @param string $module
  265. * @param string $controller
  266. * @param string $action
  267. * @param array $parameters
  268. * @param array $allParameters
  269. * @return boolean
  270. * @static
  271. */
  272. static public function executeRoute($module, $controller, $action, $parameters, $allParameters){
  273. // Aplicacion activa
  274. $activeApp = Router::getApplication();
  275. if($module!=''){
  276. $controllersDir = self::$_controllersDir.'/'.$module;
  277. } else {
  278. $controllersDir = self::$_controllersDir;
  279. }
  280. $notFoundExecuted = false;
  281. $appController = $controller.'Controller';
  282. if(class_exists($appController, false)==false){
  283. if(Core::fileExists($controllersDir.'/'.$controller.'_controller.php')){
  284. require $controllersDir.'/'.$controller.'_controller.php';
  285. } else {
  286. $applicationController = new ApplicationController();
  287. if(self::executeNotFound($applicationController)==false){
  288. //No se encontró el controlador
  289. $message = CoreLocale::getErrorMessage(-102, $controller);
  290. self::throwException($message, self::NOT_FOUND_FILE_CONTROLLER);
  291. } else {
  292. self::$_controller = $applicationController;
  293. $notFoundExecuted = true;
  294. }
  295. }
  296. }
  297. // Incializa el nombre de la instancia
  298. Core::setInstanceName();
  299. if(class_exists($controller.'Controller', false)){
  300. //Inicializa los componentes del Framework
  301. self::initComponents();
  302. // Dispatcher mantiene referencias los controladores instanciados
  303. $instanceName = Core::getInstanceName();
  304. if(!isset(self::$_controllerReferences[$appController])){
  305. if(!isset($_SESSION['KCON'][$instanceName][$activeApp][$module][$appController])){
  306. self::$_controller = new $appController();
  307. } else {
  308. // Obtiene el objeto persistente
  309. $persistedData = $_SESSION['KCON'][$instanceName][$activeApp][$module][$appController];
  310. if($persistedData['status']=='C'){
  311. $persistedData['data'] = gzuncompress($persistedData['data']);
  312. }
  313. self::$_controller = unserialize($persistedData['data']);
  314. }
  315. self::$_controllerReferences[$appController] = self::$_controller;
  316. // Envia a la persistencia por si se genera una excepción no controlada
  317. if(self::$_controller->getPersistance()==true){
  318. $_SESSION['KCON'][$instanceName][$activeApp][$module][$appController] = array(
  319. 'data' => serialize(self::$_controller),
  320. 'time' => Core::getProximityTime(),
  321. 'status' => 'N'
  322. );
  323. }
  324. } else {
  325. self::$_controller = self::$_controllerReferences[$appController];
  326. }
  327. self::$_controller->setResponse('');
  328. self::$_controller->setControllerName($controller);
  329. self::$_controller->setActionName($action);
  330. if(isset($parameters[0])){
  331. self::$_controller->setId($parameters[0]);
  332. } else {
  333. self::$_controller->setId('');
  334. }
  335. self::$_controller->setAllParameters($allParameters);
  336. self::$_controller->setParameters($parameters);
  337. try {
  338. // Se ejecutan los filtros before
  339. if(self::_runBeforeFilters($appController, $controller, $action, $parameters)===false){
  340. return self::$_controller;
  341. }
  342. //Se ejecuta el metodo con el nombre de la accion en la clase mas el sufijo Action
  343. $actionMethod = $action.'Action';
  344. #if[dispatcher-status]
  345. self::$_requestStatus = self::STATUS_DISPATCHING;
  346. #endif
  347. if(method_exists(self::$_controller, $actionMethod)==false){
  348. if(method_exists(self::$_controller, 'notFoundAction')){
  349. call_user_func_array(array(self::$_controller, 'notFoundAction'), Router::getAllParameters());
  350. return self::$_controller;
  351. } else {
  352. //No se encontró la acción
  353. $message = CoreLocale::getErrorMessage(-100, $action, $controller, $action);
  354. self::throwException($message, Dispatcher::NOT_FOUND_ACTION);
  355. }
  356. }
  357. #if[dispatcher-status]
  358. self::$_requestStatus = self::STATUS_RUNNING_CONTROLLER_ACTION;
  359. #endif
  360. #if[compile-time]
  361. $method = new ReflectionMethod($appController, $actionMethod);
  362. if($method->isPublic()==false){
  363. $message = CoreLocale::getErrorMessage(-104, $action);
  364. self::throwException($message, self::INVALID_METHOD_CALLBACK);
  365. }
  366. $methodParameters = $method->getParameters();
  367. $paramNumber = 0;
  368. foreach($methodParameters as $methodParameter){
  369. if($methodParameter->isOptional()==false&&!isset($parameters[$paramNumber])){
  370. //Numero inválido de argumentos
  371. $message = CoreLocale::getErrorMessage(-105, $methodParameter->getName(), $action);
  372. self::throwException($message, self::INVALID_ARGUMENT_NUMBER);
  373. }
  374. ++$paramNumber;
  375. }
  376. #endif
  377. self::$_valueReturned = call_user_func_array(array(self::$_controller, $actionMethod), $parameters);
  378. //Corre los filtros after
  379. self::_runAfterFilters($appController, $controller, $action, $parameters);
  380. #if[dispatcher-status]
  381. self::$_requestStatus = self::STATUS_RENDER_PRESENTATION;
  382. #endif
  383. }
  384. catch(Exception $e){
  385. $cancelThrowException = false;
  386. // Notifica la excepción a los Plugins
  387. #if[no-plugins]
  388. $cancelThrowException = PluginManager::notifyFromApplication('onControllerException', $e);
  389. #endif
  390. if(method_exists(self::$_controller, 'onException')){
  391. self::$_controller->onException($e);
  392. } else {
  393. if($cancelThrowException==false){
  394. if(is_subclass_of($e, 'CoreException')){
  395. $fileTraced = false;
  396. foreach($e->getTrace() as $trace){
  397. if(isset($trace['file'])){
  398. if($trace['file']==$e->getFile()){
  399. $fileTraced = true;
  400. }
  401. }
  402. }
  403. if($fileTraced==false){
  404. $exceptionFile = array(array(
  405. 'file' => $e->getFile(),
  406. 'line' => $e->getLine()
  407. ));
  408. $e->setExtendedBacktrace(array_merge($exceptionFile, $e->getTrace()));
  409. } else {
  410. $e->setExtendedBacktrace($e->getTrace());
  411. }
  412. }
  413. throw $e;
  414. }
  415. }
  416. }
  417. // Se clona el controlador y se serializan las propiedades que no sean instancias de modelos
  418. if(self::$_controller->getPersistance()==true){
  419. $controller = clone self::$_controller;
  420. try {
  421. #if[dispatcher-status]
  422. self::$_requestStatus = self::STATUS_RUNNING_BEFORE_STORE_PERSISTENCE;
  423. #endif
  424. if(method_exists($controller, 'beforeStorePersistence')){
  425. $controller->beforeStorePersistence();
  426. }
  427. foreach($controller as $property => $value){
  428. if(is_object($value)){
  429. foreach(self::$_notSerializableClasses as $className){
  430. if(is_subclass_of($value, $className)){
  431. unset($controller->{$property});
  432. }
  433. unset($className);
  434. }
  435. }
  436. unset($property);
  437. unset($value);
  438. }
  439. if(isset($_SESSION['KCON'][$instanceName][$activeApp][$module][$appController])){
  440. $_SESSION['KCON'][$instanceName][$activeApp][$module][$appController] = array(
  441. 'data' => serialize($controller),
  442. 'time' => Core::getProximityTime(),
  443. 'status' => 'N'
  444. );
  445. }
  446. #if[dispatcher-status]
  447. self::$_requestStatus = self::STATUS_RUNNING_AFTER_STORE_PERSISTENCE;
  448. #endif
  449. }
  450. catch(PDOException $e){
  451. throw new CoreException($e->getMessage(), $e->getCode());
  452. }
  453. }
  454. unset($module);
  455. unset($controller);
  456. unset($action);
  457. unset($parameters);
  458. unset($allParameters);
  459. unset($instanceName);
  460. unset($appController);
  461. unset($activeApp);
  462. return self::$_controller;
  463. } else {
  464. if($notFoundExecuted==false){
  465. //No se encontró el controlador
  466. $message = CoreLocale::getErrorMessage(-101, $appController);
  467. self::throwException($message, self::NOT_FOUND_CONTROLLER);
  468. } else {
  469. return $applicationController;
  470. }
  471. }
  472. }
  473. /**
  474. * Devuelve una instancia del controlador base ControllerBase
  475. *
  476. * @return ApplicationController
  477. */
  478. static public function getControllerBase(){
  479. if(class_exists('ControllerBase', false)){
  480. return new ControllerBase();
  481. } else {
  482. return false;
  483. }
  484. }
  485. /**
  486. * Obtener el controlador en ejecución ó el último ejecutado
  487. *
  488. * @access public
  489. * @return Controller
  490. * @static
  491. */
  492. public static function getController(){
  493. return self::$_controller;
  494. }
  495. /**
  496. * Devuelve el valor devuelto por el método ejecutado en la ultima acción
  497. *
  498. * @access public
  499. * @return mixed
  500. * @static
  501. */
  502. public static function getValueReturned(){
  503. return self::$_valueReturned;
  504. }
  505. /**
  506. * Devuelve el estado de ejecucion de la peticion
  507. *
  508. * @access public
  509. * @static
  510. */
  511. public static function getDispatchStatus(){
  512. return self::$_requestStatus;
  513. }
  514. /**
  515. * Indica si el estado de ejecucion es la logica de Controlador
  516. *
  517. * @access public
  518. * @return boolean
  519. * @static
  520. */
  521. public static function isRunningController(){
  522. return self::$_requestStatus == Dispatcher::STATUS_RUNNING_CONTROLLER_ACTION;
  523. }
  524. /**
  525. * Indica si el estado de ejecucion de la aplicacion esta a nivel de usuario
  526. *
  527. * @access public
  528. * @return boolean
  529. * @static
  530. */
  531. public static function isRunningUserLevel(){
  532. return self::$_requestStatus != self::STATUS_DISPATCHING;
  533. }
  534. /**
  535. * Lanza una excepción de tipo DispatcherException
  536. *
  537. * @access public
  538. * @throws DispatcherException
  539. * @static
  540. */
  541. public static function throwException($message, $code){
  542. throw new DispatcherException($message, $code);
  543. }
  544. }