PageRenderTime 35ms CodeModel.GetById 16ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 0ms

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