public static function getInstance() { if (self::$instance === NULL) { self::$instance = new YuppConfig(); } return self::$instance; }
/** * @param String appName * @param Array datasource puede servir para usar distintos datasources para distintas pruebas mas alla de los 3 modos de ejecucion. */ public function __construct($appName, $datasource = NULL) { Logger::getInstance()->log("DAL::construct"); // =============================================== $cfg = YuppConfig::getInstance(); // TODO: pasarle el nombre de la app actual. // FIXME: esto no funciona si la app es "core", // y trabajo con distintas apps, por ejemplo // al generar todas las tablas en las dbs. // Necesito pasarle como parametro al constructor // de DAL el nombre de la app para la cual quiero // el datasource, y que sea PM el que obtenga el // appName correcto, sea del contexto o porque genere // las tablas para una app particular. //$ctx = YuppContext::getInstance(); //$appName = $ctx->getApp(); $this->appName = $appName; // Logger::getInstance()->on(); // Logger::getInstance()->log("DAL __construct appName: $appName"); // Logger::getInstance()->off(); if ($datasource == NULL) { $datasource = $cfg->getDatasource($appName); } // FIXME: Esto es solo para mysql y postgres ===== $this->url = $datasource['url']; $this->user = $datasource['user']; $this->pass = $datasource['pass']; $this->database = $datasource['database']; // =============================================== // Constructor por configuracion del dbms // OBS: cada vez que agregue un soporte nuevo tengo que agregar la opcion al switch. // FIXME: que la configuracion use directamente los nombres de las clases de DB para ahorrar el switch. // TODO: deberia tener una fabrica con esto adentro, y la fabrica tal vez deberia cargar // las clases automaticamente en lugar de ir agregando cada tipo de conector en el switch. //switch( $cfg->getDatabaseType() ) switch ($datasource['type']) { case YuppConfig::DB_MYSQL: YuppLoader::load("core.db", "DatabaseMySQL"); $this->db = new DatabaseMySQL(); break; case YuppConfig::DB_SQLITE: YuppLoader::load("core.db", "DatabaseSQLite"); $this->db = new DatabaseSQLite(); break; case YuppConfig::DB_POSTGRES: YuppLoader::load("core.db", "DatabasePostgreSQL"); $this->db = new DatabasePostgreSQL(); break; case YuppConfig::DB_SQLSRV: YuppLoader::load("core.db", "DatabaseSQLServer"); $this->db = new DatabaseSQLServer(); break; default: throw new Exception('datasource type no soportado: ' . $datasource['type']); } // TODO: que dmbs desde config, perfecto para factory pattern. $this->db->connect($this->url, $this->user, $this->pass, $this->database); // TODO: POR AHORA LOS DATOS PARA ACCEDER A LA BD SE CONFIGURAR AQUI... }
/** * Debe retornar true si pasa o un ViewCommand si no pasa, o sea redireccionar o ejecutar una accion de un cotroller o hacer render de un string... * FIXME: $app ya no es necesario xq el filtro es por app. */ public function apply($app, $controller, $action) { $mode = YuppConfig::getInstance()->getCurrentMode(); // En prod no deberia poder ejecutar las acciones de gestion de CoreController if ($mode == YuppConfig::MODE_PROD) { return ViewCommand::display('403', new ArrayObject(array('message' => 'No puede ejecutar tareas de gestión en modo PROD')), new ArrayObject()); } // En otro caso, lo dejo pasar tranquilo return true; }
/** * @param app nombre de la aplicacion para la que crear la DB. */ public function createDbAction() { // http://code.google.com/p/yupp/issues/detail?id=123 $app = $this->params['app']; try { $dal = new DAL($app); // Falla sino existe la base para la app $appName } catch (Exception $e) { if ($e->getCode() == 666) { // Verifica que la DB no existe // Veo el nombre de la base para esta app $cfg = YuppConfig::getInstance(); $datasource = $cfg->getDatasource($app); $dbName = $datasource['database']; // Tuve que crear una instancia de DAL y pasarle 'core' como app // porque no puedo declarar createDatabase como estatico por // usar internamente el this->db para hacer la query CREATE DATABASE. $dal = new DAL('core'); $dal->createDatabase($dbName); $this->flash['message'] = "La base de datos {$dbName} se ha creado con exito!"; } } return $this->redirect(array('action' => 'dbStatus')); }
*/ $_base_dir = substr($_SERVER['SCRIPT_NAME'], 0, strrpos($_SERVER['SCRIPT_NAME'], '/')); $lg = Logger::getInstance(); $lg->setFile('log.log'); $lg->on(); // TODO: considerar el language de la app $ctx = YuppContext::getInstance(); $locale = $ctx->setLocale('en'); // Hace el request y catchea por posibles errores. try { RequestManager::doRequest(); } catch (Exception $e) { // FIXME: mostrar la vista de error 500 // FIXME: en modo PROD NUNCA deberia mostrar paths ni el stacktrace. // http://code.google.com/p/yupp/issues/detail?id=147 if (YuppConfig::getInstance()->getCurrentMode() === YuppConfig::MODE_PROD) { echo 'Disculpe las molestias, verificaremos el error en breve.'; // FIXME: i18n // Redirect a pagina de error por defecto? if (file_exists('ylogs/500')) { FileSystem::write('ylogs/500/err_' . date("Ymd.his") . '.log', print_r($ctx, true)); } } else { echo '<html><body>'; echo '<h1>Ha ocurrido un error!</h1>'; // TODO: i18n echo '<div style="border:1px solid #333; padding:10px; width:800px;">'; echo '<div style="border:1px solid #333; background-color:#ffffaa; overflow:auto; padding:5px; margin-bottom:2px;">'; echo 'Mensaje:'; // TODO: i18n echo '</div>';
public static function doRequest() { global $_base_dir; // TODO: que el mostrar el tiempo de proceso sea configurable. //$tiempo_inicio = microtime(true); $timer_process = new Timer(); $timer_process->start(); // Establezco la url base, independiente del directorio donde este situado el script. // Si la uri es: http://localhost:8081/Persistent/test/usermanager/person/create?name=pepe&age=23&height=180 // y este script esta en http://localhost:8081/Persistent/test/ // Url sera: usermanager/person/create?name=pepe&age=23&height=180 // ==================================================== // ROUTING: el objetivo es devolver un $command $router = new Router($_SERVER['REQUEST_URI']); $lr = $router->getLogicalRoute(); $ctx = YuppContext::getInstance(); //Logger::struct( $lr, "LOGICAR ROUTE 1 " .__FILE__.' '.__LINE__ ); // ==================================================================== // FIXME: esto lo deberia hacer el router // ==================================================================== // Verifica salida del router y setea valores que no vienen seteados. // TODO: OJO, luego debe pasar el verificador de si el controller // y action existen, y si no, ejecutar contra core. // Esto dice a donde ir cuando se accede a la aplicacion YUPP, // esta bien que se haga aca, no es cosa del router. if (empty($lr['app'])) { $config = YuppConfig::getInstance(); $modeDefaultMapping = $config->getModeDefaultMapping(); $lr['app'] = $modeDefaultMapping['app']; $lr['controller'] = $modeDefaultMapping['controller']; $lr['action'] = $modeDefaultMapping['action']; $router->addCustomParams($modeDefaultMapping['params']); } // FIXME: esto lo deberia hacer el router // Si la ruta en la URL llega hasta la app, // se muestran los controladores de la app. if (empty($lr['controller'])) { if (!Yupp::appExists($lr['app'])) { // Tira 404: Not Found $command = ViewCommand::display('404', new ArrayObject(array('message' => 'La aplicación <b>' . $lr['app'] . '</b> no existe')), new ArrayObject()); } else { //Logger::getInstance()->po_log("RM: ".__FILE__ .' '.__LINE__); $router->addCustomParams(array('app' => $lr['app'])); $lr['app'] = "core"; // Le dice a core/core que muestre los controllers de la app $lr['app'] $lr['controller'] = "core"; $lr['action'] = "appControllers"; } } else { // Prefiero el parametro por url "_action_nombreAccion", a la accion que viene en la URL (app/controlador/accion). // Esto es porque los formularios creados con YuppForm generan acciones distintas para botones de // submit distintos y la accion es pasada codificada en un parametros _action_nombreAcction. // Por si la accion viene codificada en una key de un param como '_action_laAccion', por ejemplo: esto pasa en un submit de un YuppForm. $actionParam = $router->getActionParam(); if (empty($actionParam)) { if (!isset($lr['action']) || $lr['action'] === "") { $lr['action'] = 'index'; } } else { // FIXME: hay un problema con actionParam cuando se manda desde un form. // La accion que aparece en la URL es la de la action del form, pero la // vista que se muestra es la que renderea la acction actionParam. La URL // deberia ser tambien la que diga actionParam. Por eso haria un redirect // en lugar de un render. $lr['action'] = $actionParam; } } // Si en logicalRoute se ponen parametros ej. en el AppMapping, // se deben agregar al router como customParams: if (isset($lr['params'])) { $router->addCustomParams($lr['params']); // Deberia ser un array... } //Logger::struct( $lr, "LOGICAR ROUTE 2 " .__FILE__.' '.__LINE__ ); // ******************************************************************************* // FIXME: puedo tener app, controlador y accion, pero pueden ser nombres // errados, es decir, que no existen, por ejemplo si en la url le paso /x/y/z. // Aqui hay que verificar si existe antes de seguir, y si la app no existe, // o si existe pero el controlador no existe, o si ambos existen, si la accion // en el controlador no existe, deberia devolver un error y mostrarlo lindo (largar una exept). // Estaria bueno definir codigos estandar de errores de yupp, para poder tener una // lista ed todos los errores que pueden ocurrir. // ******************************************************************************* // FIXME: no armar esto a mano, pedirselo a alguna clase de convensiones o la nueva clase App. $appPath = "apps/" . $lr['app']; $controllerClassName = String::firstToUpper($lr['controller']) . "Controller"; $controllerFileName = "apps." . $lr['app'] . ".controllers." . $controllerClassName . ".class.php"; $controllerPath = "apps/" . $lr['app'] . "/controllers/" . $controllerFileName; /// ACTUALIZAR CONTEXTO /// $ctx->setApp($lr['app']); $ctx->setController($lr['controller']); $ctx->setAction($lr['action']); /// ACTUALIZAR CONTEXTO /// //Logger::struct( $lr, "LOGICAR ROUTE 3 " .__FILE__.' '.__LINE__ ); //echo "<hr/>PATH: $controllerPath<br/>"; // Verifico que lo que pide existe... if (!file_exists($appPath)) { Logger::getInstance()->log("Path1 '{$appPath}' no existe"); // Tira 404: Not Found $command = ViewCommand::display('404', new ArrayObject(array('message' => 'La aplicación <b>' . $lr['app'] . '</b> no existe')), new ArrayObject()); } else { if (!file_exists($controllerPath)) { Logger::getInstance()->log("Path2 '{$controllerPath}' no existe"); // Tira 404: Not Found $command = ViewCommand::display('404', new ArrayObject(array('message' => 'El controlador <b>' . $lr['controller'] . '</b> no existe')), new ArrayObject()); } else { // Aca deberia chekear si la clase $lr['controller'] . "Controller" tiene le metodo $lr['action'] . "Action". // Esto igual salta en el executer cuando intenta llamar al metodo, y salta si no existe. // Logger::struct($lr, "LOGICAL ROUTE 2"); // FIXME: esto es una regla re ruteo. // TODO: Si accede a la app sin poner el controller, se intenta buscar un controller con el mismo nombre de la app. // Si no existe, se redirige al core controller como se hace aqui. // Verificacion de controller filters (v0.1.6.3) $controllerFiltersPath = 'apps/' . $lr['app'] . '/AppControllerFilters.php'; // Nombre y ubicacion por defecto. $controllerFiltersInstance = NULL; if (file_exists($controllerFiltersPath)) { // FIXME: con la carga bajo demanda de PHP esto se haria automaticamente! include_once $controllerFiltersPath; // FIXME: no usa YuppLoader (nombre de archivo no sigue estandares!). $controllerFiltersInstance = new AppControllerFilters(); // Esta clase esta definida en el archivo incluido (es una convension de Yupp). } //Logger::struct( $router->getParams(), "ROUTER PARAMS " .__FILE__.' '.__LINE__ ); //Logger::struct( $_POST, "POST " .__FILE__.' '.__LINE__ ); //Logger::struct( $_GET, "GET " .__FILE__.' '.__LINE__ ); $executer = new Executer($router->getParams()); $command = $executer->execute($controllerFiltersInstance); // $controllerFiltersInstance puede ser null! } } // /ROUTING // ==================================================== // Aun mejor, si devuelvo un array, lo tomo como modelo y tomo la accion y controller para encontrar el view, si el view existe o no, lo trato luego con paginas logicas o views escaffoldeados... // Si no devuelve nada, hago lo mismo, y tomo como modelo un array vacio, lo que podria hacer, es si el controller tiene atributos, es usar esos atributos (los valores) como modelo (y los nombres los uso como key en el model). // View/Redirect // TODO:.... // Si no vienen las cosas seteadas puedo adivinar por ejemplo que view mostrar en funcion de la accion y contorller, como en grails. // TODO: Verificar si no es null, si tiene todos los atributos necesarios para hacer o que dice el comando, etc. // FIXME: SI EL COMANDO ES NULL QUIERO HACER ACCIONES POR DEFECTO! como mostrar la view correspondiente al controller, y la action ejecutadas. if ($command === NULL || empty($command)) { // O le falta el command o es que la accion es de pedir un recurso estatico el que se devuelve como stream. // Error 500: Internal Server Error $command = ViewCommand::display('500', new ArrayObject(array('message' => 'Hubo un error al crear el comando')), new ArrayObject()); } // ============== // TEST: ver si guarda el estado en la sesion //$test = CurrentFlows::getInstance()->getFlow( 'createUser' ); //Logger::show( "Flow en sesion antes de hacer render: " . print_r($test->getCurrentState(), true) . ", " . __FILE__ . " " . __LINE__ ); // ================ // Siempre llega algun comando if ($command->isDisplayCommand()) { // Aqui llegan tambien los errores ej 500 o 404 para mostrar una vista linda. // FIXME: mostrar o no el tiempo de procesamiento deberia ser configurable. //$tiempo_final = microtime(true); //$tiempo_proc = $tiempo_final - $tiempo_inicio; $timer_process->stop(); $tiempo_proc = $timer_process->getElapsedTime(); //$tiempo_inicio = microtime(true); $timer_render = new Timer(); $timer_render->start(); // FIXME: en router esta toda la info, porque pasar todo? self::render($lr, $command, $ctx, $router); //$tiempo_final = microtime(true); //$tiempo_render = $tiempo_final - $tiempo_inicio; $timer_render->stop(); $tiempo_render = $timer_render->getElapsedTime(); // TODO: configurar si se quiere o no ver el tiempo de proceso. //echo "<br/><br/>Tiempo de proceso: " . $tiempo_proc . " s<br/>"; //echo "Tiempo de render: " . $tiempo_render . " s<br/>"; return; } else { if ($command->isStringDisplayCommand()) { echo $command->getString(); return; } else { if ($command->isDisplayTemplateCommand()) { $params = array(); // TODO: poder pasarle path al helper, asi puedo poner el template en cualquier lado. $params['name'] = $command->viewName(); // Nombre del template $params['args'] = $command->params(); Helpers::template($params); return; } else { // TODO: me gustaria poner todo esto en una clase "Redirect". // echo "DICE QUE NO ES DISPLAY!!!!"; // La idea es que cmo es excecute, redirija a unc compo/controller/action/params que diga el command. // Entonces es reentrante a este modulo, el problema es que no tengo el // request hecho de forma que pueda llamarlo de afuera, deberia hacerlo aparte // llamar a ese para la primer entrada y las posibles redirecciones que se puedan hacer. // // -> excecuteControllerAction // Que hago con el command que tira este? tengo que revisar las llamadas recursivas... //$command = self::excecuteControllerAction( $app->app(), $app->controller(), $app->action(), $urlproc->params() ) // FIXME: no hace nada con el model, deberia pasar lo que puede como params de GET. // TODO: habria que ver como hacer un request por POST asi puedo mandar info sin que se vea en el request. $model = Model::getInstance(); $model->addFlash($command->flash()); // Uso el helper para armar la url. Obs> hay funciones estandar de php que arman urls, como // - http://www.php.net/manual/es/function.http-build-url.php // - http://www.php.net/manual/es/function.http-build-str.php // // $url_params = array(); $url_params['app'] = $command->app(); $url_params['controller'] = $command->controller(); $url_params['action'] = $command->action(); $url_params['params'] = $command->params(); // T#63 solo pasar los params del modelo no los del request. // Agrega params a la url (fix a perdida del flash en redirect) foreach ($command->flash() as $key => $value) { // FIXME: si en flash se ponen arrays y se hace redirect, urlencode va a fallar porque espera un string... $url_params['flash_' . $key] = urlencode($value); // Por ejemplo flash.message='un mensaje', url encode por si tiene simbolos. } //print_r( $url_params ); $url = Helpers::url($url_params); // http://www.php.net/manual/es/function.http-redirect.php // retorna false si no puede, exit si si puede. //http_redirect( $url ); // [, array $params [, bool $session = FALSE [, int $status ]]]] ) if (!headers_sent()) { // No funciona si hay algun output antes, como el log prendido. // http://www.php.net/header // header('Location: http://' . $_SERVER['HTTP_HOST'] . $url); exit; } else { // TODO: esto deberia ser un template de redireccion automatica fallida. // TODO: los mensajes deberian ser i18n. $url = 'http://' . $_SERVER['HTTP_HOST'] . $url; echo "<html>" . "<head></head><body>" . "Ya se han enviado los headers por lo que no se puede redirigir de forma automatica.<br/>" . "Intenta redirigir a: <a href=\"{$url}\">{$url}</a>" . "</body></html>"; } // TODO: Puede redirigir a una pagina logica (como en el CMS) o a una pagina de scaffolding // (no existe fisicamente pero se genera mediante un template y muestra la info que se le // pasa de forma estandar, considerando si es un list, show, create o edit). //return; // TODO } } } // NO DEBERIA LLEGAR ACA, DEBE HACERSE UN RENDER O UN REDIRECT ANTES... }
/** * Select para modificar el modo de ejecucion. */ public static function mode_chooser($params) { $ctx = YuppContext::getInstance(); $config = YuppConfig::getInstance(); $url = self::url(array('app' => 'core', 'controller' => 'core', 'action' => 'changeMode')); $res = '<form action="' . $url . '" style="width:270px; margin:0px; padding:0px;">'; $res .= '<select name="mode">'; foreach ($config->getAvailableModes() as $mode) { $res .= '<option value="' . $mode . '" ' . ($mode === $ctx->getMode() ? 'selected="true"' : '') . '>' . $mode . '</option>'; } $res .= '<input type="hidden" name="back_app" value="' . $ctx->getApp() . '" />'; $res .= '<input type="hidden" name="back_controller" value="' . $ctx->getController() . '" />'; $res .= '<input type="hidden" name="back_action" value="' . $ctx->getAction() . '" />'; $res .= '</select>'; $res .= '<input type="submit" name="Cambiar" />'; $res .= '</form>'; return $res; }
<?php $m = Model::getInstance(); $createDatabaseForApps = $m->get('createDatabaseForApps'); $cfg = YuppConfig::getInstance(); global $_base_dir; ?> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <style> body { font-family: arial, verdana, tahoma; font-size: 12px; background-color: #efefef; margin: 0; } h1 { margin: 0px; padding-top: 35px; display: inline-block; } table, tr, td { margin: 0px; padding: 0px; border: 0px; } #actions { background: #fff url(<?php echo $_base_dir; ?>