Esto es una implementacion, mas que nada de Controller/Routing, que hackie.. en un dia, para usar mod_rewrite, clean urls, mvc en el laburo.
Si alguien ve algun tipo de vulnerabilidad. porfavor avise, puteeme, riansee. viene bien.
Asumamos una estructura del root de un vhost:
Código:
.
..
index.php
controller/
model/
view/
mastropiero/
includes/
Junto a un .htacces:
Código:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?url=$1 [L,QSA]
En cristiano: todo request que no mapee a un archivo o directorio existente. se pasa lo que viene despues del host, como parametro url a index.php.
index.php
Código:
<?
/**
* LICENSE
* GPL, blah blah. google it.
*
* @category MastropieroVC
* @package ControllerRuteo
* @copyright Copyright (c) 2006-2008 Allkom S.A. http://www.all-kom.com.ar
* @license http://www.gnu.org/licenses/gpl.txt
*/
include("includes/configurations.php");
/**
* Registro Controladores permitidos. fuera de estos, ninguno va a poder ser ejecutado
*/
$controladores = array("Soluciones", "Servicios", "Casos", "Rrhh", "Admin", "Usuario", "Home", "Hardware");
/**
* Defino controlador y accion por defecto.
*
*/
define('CONTROLADOR_DEFECTO', "Home");
define('ACCION_DEFECTO', "index");
/**
* Agarro la url que me pasa el modrewrite
*/
if(isset($_GET["url"])){
$parametros = explode("/",$_GET["url"]);
unset($_GET["url"]);
}
/**
* creo el Meta-Controlador
* Registro los controladores que defini con el meta-controller
*/
$controller = new ControllerRuteo($controladores);
$controller->Render($parametros);
?>
Basicamente, uno agrega una capa por sobre $_GET. Reemplazando el uso de $_GET, por parametros, que ahora van a ver como se van pasando de lado a lado.
ControllerRuteo.php
Código:
<?
/**
* LICENSE
* GPL, blah blah. google it.
*
* @category MastropieroVC
* @package ControllerRuteo
* @copyright Copyright (c) 2006-2008 Allkom S.A. http://www.all-kom.com.ar
* @license http://www.gnu.org/licenses/gpl.txt
*/
class ControllerRuteo{
/**
* Controller que se llama el request.
* @var string $controller
*/
public $controller;
/**
* Perfil/Role del usuario. Para implementar ACL sobre Controllers.
*
* @var string $_perfil;
*/
protected $_perfil;
/**
* array de con controler autorizados.
*
* @var array(string) $_clasesRegistradas
*/
protected $_clasesRegistradas;
/**
* Inicio con controlador por defecto, luego cambia, si es necesario.
*/
public function __construct($registrados = array()){
$this->controller = CONTROLADOR_DEFECTO;
$this->_perfil = $_SESSION["usuario"]["perfil"];
$this->_clasesRegistradas = $registrados;
}
/**
* Dice si tiene acceso a ese control en particular
* Rutina de control de acceso al controller
* TODO: Podria llamar un objeto que implemente una interface, si quisiera extenderlo en tal medida.
* @return Boolean
*/
protected function _tienePermiso($controller){
$bool = true;
return $bool;
}
/**
* Set/Get de clases registradas
*
* @param array $reg
* @return array
*/
public function Registrados($reg = array()){
if(count($reg) == 0)
return $this->_clasesRegistradas;
else
$this->_clasesRegistradas = $reg;
}
/**
* Aca esta la magia.
* crea una instancia de la subclase de Controller y llama render de ese controller.
* hace un define del Controller, para tener el "enviroment" del request en todo momento.
*/
public function Render($params = array()){
//Primer param es siempre el controller.
if(count($params) > 0){
$this->controller = array_shift($params);
define("CONTROLLER", $this->controller);
}
//chequeo que este registrado el controller en la lista.
if(in_array($this->controller, $this->_clasesRegistradas)){
//si el usuario tiene permiso sobre el controller.
if($this->_tienePermiso($this->controller)){
//hace un "dispatch" (basicamente es un factory pattern) del controller solicitado.
$var = $this->controller."_Controller";
$class = new $var;
$class->Render($params);
}else{
//TODO: Llamar una excepcion custom que redirija/loggee, etc...
throw new Exception("No tiene acceso sobre el controlador");
}
}else{
//Idem todo arriba.
throw new Exception("El controlador: $this->controller no esta registrado");
}
}
}
?>
Esta re bien comentado, aveces me sorprendo.
Cuando menciono patter, noten como utilizo convencion para llamar a una clase con nombre Controlador + "_controller" ( que es un toque verboso, pero cuando tenes controller y model de una misma entidad, ayuda a no confundir. Entidad_Controller y Entidad_Model)
Controller.php
Código:
<?
/**
* LICENSE
* GPL, blah blah. google it.
*
* @category MastropieroVC
* @package Controller
* @copyright Copyright (c) 2006-2008 Allkom S.A. http://www.all-kom.com.ar
* @license http://www.gnu.org/licenses/gpl.txt
*/
class Controller{
/**
* Accion del controller. 2 parametro de la url. 1ero que me llega.
* @var string $_action
*/
protected $_action;
/**
* Again, es al pedo realmente.
* @var $_SESSION["usuario"]["perfil"] o similar
*/
protected $_perfil;
public function __construct(){
$this->_action = ACCION_DEFECTO;
$this->_perfil = $_SESSION["usuario"]["perfil"];
}
/**
* Dice si tiene acceso a esa Accion en particular
* Rutina de control de acceso al controller
* @return Boolean
*/
protected function _tienePermiso($accion){
$bool = true;
return $bool;
}
/**
* Dispatcher de acciones.
*/
public function Render($params = array()){
if(count($params) > 0){
foreach ($params as $parametro){
if(!ereg("^[a-zA-Z0-9_\ \-]+$", $parametro)) //valido que sea un string "valido" como accion.
throw new Exception("Naughty you! Invalid chars para llamar accion", 7);
}
$this->_action = array_shift($params);
define("ACTION", $this->_action); // defino la accion en el enviroment del request.
}
//Aparte de no poder llamar funciones que empiezen con _, Render. Los metodos deben ser publicos para que se puedan ejecutar.
if($this->_tienePermiso($this->_action) && (substr($this->_action, 0, 1) != "_") && $this->_action != "Render"){
if(count($params) <= 0){
$var = $this->_action;
$this->$var();
}else{
/**
* Ejecuta la accion y pasa lo que queda de los parametros a la accion.
* al mandar el stack de parametros, no necesito ningun eval.
*/
$this->{$this->_action}($params);
}
}else
//un redirect a un 404 seria mas conviniente/prolijo
throw new Exception("No tiene acceso sobre la accion");
}
}
?>