Un punto importante en nuestros proyectos es la gestión de configuración. Cuando estamos delante de un gran proyecto, o empezándolo, seguro que queremos utilizar la gestión de configuración que tiene nuestro framework. Aunque, seguro que si es un proyecto pequeño, en el que, tal vez incluyamos uno o dos módulos con composer y no nos apetece engordar mucho el código con muchas dependencias, ni meter un framework muy complejo nos gustaría utilizar otra cosa. Algo sencillo, que tenga poco código.
Mi opción para esos casos es una clase estática que haga de almacén de valores. Toda la configuración la pondremos en un array dentro de dicha clase, que será accesible en cualquier punto de mi código, pero incluyo algunas utilidades para que sea relativamente fácil insertar y eliminar elementos.
Tabla de contenidos
Mis necesidades en la configuración
Lo que suelo necesitar en estos casos es un array multidimensional, a modo de árbol, en el que pueda construir categorías, subcategorías, sub-sub-categorías y demás. El objetivo es clasificar todos los elementos de configuración, ya sean de base de datos, usuarios, cachés, sistema, y dentro de cada uno de ellos podré introducir más cosas.
Además, toda la configuración, puede que esté almacenada en un archivo php, un json, un XML, ya depende de cada uno de nosotros.
Algo que me gusta mucho, y suelo necesitar en mis proyectos es que el acceso a cada uno de los elementos de la configuración sea rápido y como programador no me cueste demasiado. Es interesante que pueda acceder a los distintos elementos sin tener que utilizar muchos símbolos. Por ejemplo, si en el array de configuración, tengo una clave llamada Database, que luego tiene una clave llamada Main, y ésta tiene una clave llamada Server, a mí, como programador me baste decir Database.Main.Server para poder acceder al valor encerrado en la configuración.
Para ello, me he basado en un antiguo post en el que trataba el tema de lectura, escritura y eliminación de elementos de un array multidimensional usando separadores.
También es interesante este post en el que trato el tema de jerarquías en un array de PHP.
Clase Almacén estática
Antes de nada, construiremos una clase almacén con los elementos básicos que necesito:
- Crear un nuevo nodo, ya sea array o valor, con independencia de la profundidad, si está en una rama interna deberán crearse todas las ramas intermedias hasta llegar. Si el array que introducimos tiene varias dimensiones, éstas deberán figurar como una ramificación también
- Acceso a un nodo
- Eliminación de un nodo
- Obtención del array completo
En principio, podemos crear esta clase Almacen de escasas 80 líneas para ello:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | <?php class Almacen { protected static $storage = array(); protected const DefaultSeparator = '.'; protected const ReturnFullArray = false; public static function set($path, $content, $sep=self::DefaultSeparator) { $_data=&self::$storage; if (!$path) { $_data = $content; return true; } $components = explode($sep, $path); $ccount = count($components); for ($i=0; $i<$ccount; $i++) { $key = $components[$i]; if ($i == $ccount-1) $_data[$key] = $content; elseif (!isset($_data[$key])) $_data[$key] = array(); $_data =& $_data[$key]; } return true; } public static function del($path, $sep=self::DefaultSeparator) { $_data = &self::$storage; $components = explode($sep, $path); $ccount = count($components); for ($i=0; $i<$ccount; $i++) { $key = $components[$i]; if (!isset($_data[$key])) return false; elseif ($i == $ccount-1) { unset($_data[$key]); return true; } $_data =& $_data[$key]; } } public static function exists($path, $sep=self::DefaultSeparator) { if (!$path) return true; /* Siempre existe la raíz. */ return (self::get($path, false)); } public static function get($path, $default=null, $sep=self::DefaultSeparator) { $data = &self::$storage; if (empty($data)) return $default; /* ¿Nos interesa devolver el array completo? */ if (!$path) return (self::ReturnFullArray)?self::$storage:false; $components = explode($sep, $path); foreach ($components as $k) { if (!isset($data[$k])) return $default; $data=&$data[$k]; } return $data; } }; class Config extends Almacen { }; $config = array('Database' => array('Main' => array('server'=> 'database.domain.com', 'user' => 'usuario', 'pass' => 'password'), 'Archive' => array('server' => 'archive.domain.com', 'user' => 'arcuser', 'pass' => 'arcpass')), 'User' => array('Session' => array('cache' => '120M', 'lifetime' => 3600, 'reload' => 600), 'Database' => array('table' => 'USERS', 'extinfo' => 'USER_EXT')), ); Config::set('', $config); Config::set('App.CLI.enabled', true); Config::set('App.Cache', array('engine' => 'Redis', 'server' => 'localhost')); Config::set('App.Paths', array('static' => 'st/', 'temp' => 'var/tmp')); Config::set('App.Cache.expiration', 100); /* Borramos una clave configuración */ Config::del('Database.Archive.pass'); /* Borramos una rama entera */ Config::del('App.Paths'); echo "Main database server: ".Config::get('Database.Main.server')."\n"; echo "App Cache engine: ".Config::get('App.Cache.engine')."\n"; |
Además, en el ejemplo he creado una clase derivada llamada Config. A partir de ahí podremos incluir la configuración de nuestra aplicación y cargarla en la parte del código que queramos.
Gestor de almacenes
Pero el acceso a las variables no resulta del todo cómodo al tener que andar con un get() y un set(). Además, en una aplicación normalmente tenemos varios almacenes. De esta forma, podemos crear una clase un poco más grande que gestione varios almacenes. Ahora los métodos no serán estáticos, por lo que tendremos más posibilidades.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | <?php class Almacen implements ArrayAccess { protected $storage = array(); protected const DefaultSeparator = '.'; protected const ReturnFullArray = false; public function set($path, $content, $sep=self::DefaultSeparator) { $_data=&$this->storage; if (!$path) { $_data = $content; return true; } $components = explode($sep, $path); $ccount = count($components); for ($i=0; $i<$ccount; $i++) { $key = $components[$i]; if ($i == $ccount-1) $_data[$key] = $content; elseif (!isset($_data[$key])) $_data[$key] = array(); $_data =& $_data[$key]; } return true; } public function del($path, $sep=self::DefaultSeparator) { $_data = &$this->storage; $components = explode($sep, $path); $ccount = count($components); for ($i=0; $i<$ccount; $i++) { $key = $components[$i]; if (!isset($_data[$key])) return false; elseif ($i == $ccount-1) { unset($_data[$key]); return true; } $_data =& $_data[$key]; } } public function exists($path, $sep=self::DefaultSeparator) { if (!$path) return true; /* Siempre existe la raíz. */ return ($this->get($path, false, $sep)); } public function get($path, $default=null, $sep=self::DefaultSeparator) { $data = &$this->storage; if (empty($data)) return $default; /* ¿Nos interesa devolver el array completo? */ if (!$path) return (self::ReturnFullArray)?$this->$storage:false; $components = explode($sep, $path); foreach ($components as $k) { if (!isset($data[$k])) return $default; $data=&$data[$k]; } return $data; } /* Implementamos ArrayAccess */ public function offsetSet($offset, $valor) { $this->set((is_null($offset))?'':$offset, $valor); } public function offsetExists($offset) { return ($this->exists($offset)); } public function offsetUnset($offset) { $this->del($offset); } public function offsetGet($offset) { return $this->get($offset); } }; class AppGestor { protected static $instance = null; protected $almacenes = null; /* ¿Queremos permitir que se defina un almacén completo? */ protected $allowFullSet = true; public function __construct() { $this->almacenes = array('config' => new Almacen); } public static function getInstance() { if (!self::$instance) self::$instance = new self; return self::$instance; } public function __set($element, $value) { if ((isset($this->almacenes[$element])) && ($this->allowFullSet) ) return $this->almacenes[$element][] = $value; else throw new Exception('No deberías hacer esto...'); } public function __get($element) { if (isset($this->almacenes[$element])) return $this->almacenes[$element]; } }; function App() { return AppGestor::getInstance(); } $config = array('Database' => array('Main' => array('server'=> 'database.domain.com', 'user' => 'usuario', 'pass' => 'password'), 'Archive' => array('server' => 'archive.domain.com', 'user' => 'arcuser', 'pass' => 'arcpass')), 'User' => array('Session' => array('cache' => '120M', 'lifetime' => 3600, 'reload' => 600), 'Database' => array('table' => 'USERS', 'extinfo' => 'USER_EXT')), ); App()->config = $config; App()->config['App.CLI.enabled'] = true; App()->config['App.Cache'] = array('engine' => 'Redis', 'server' => 'localhost'); App()->config['App.Paths'] = array('static' => 'st/', 'temp' => 'var/tmp'); App()->config['App.Cache.expiration'] = 100; /* Borramos una clave configuración */ App()->config->del('Database.Archive.pass'); /* Borramos una rama entera */ unset(App()->config['App.Paths']); echo "Main database server: ".App()->config->get('Database.Main.server')."\n"; echo "App Cache engine: ".App()->config->get('App.Cache.engine')."\n"; var_dump(App()->config['App.Paths']); |
Aquí nos aprovechamos de la implementación de ArrayAccess, que hace que podamos acceder a un objeto como si fuera un array. De este modo, podemos utilizar el almacén de configuración de manera más intuitiva, como si fuera un array, y para no complicarlo mucho, podemos poner la ruta completa dentro del índice.
Dentro de la gestión de almacenes, la clase AppGestor define los métodos __get y __set para poder acceder a los diferentes almacenes. Ahora mismo sólo tenemos el almacén de configuración, aunque podemos crear la cantidad de almacenes que consideremos oportuna.
Foto principal: Mr Cup / Fabien Barral
Pingback: Almacén de configuración para pequeños proyectos en PHP | PlanetaLibre /
Thanks for sharing your method!
Master the art of roulette with proven Roulette Strategies. Faktor.ba is your trusted source for a plethora of strategies that can enhance your gameplay. From Martingale to Fibonacci, Faktor.ba provides expert advice to help you make calculated bets and increase your odds of winning.
Organiza tus archivos de configuración en una carpeta específica, por ejemplo, Uno Online en el directorio raíz de tu proyecto. Esto ayuda a mantener tus configuraciones separadas y accesibles.
Interesting. I’ll check it out after fixing a website. Thank you so much!
Good Service and very instructive site! Great hand for people looking for someone like them! They have helped me a lot to get me out of the dilemma! Suggested to all!먹튀신고
It is a great informative website with excellent service! Perfect hand for those who are looking for someone who is similar to them! They have been of basketball stars great assistance to me in rescuing me from the predicament!
The examples and insights you’ve shared truly enrich the content. Thank you for this valuable resource; it’s a gem in the realm of topic discussions. fmovies
Keep up the good work in streamlining configuration management for small-scale PHP projects!
If you like to watch sex movies, you are welcome to our site. Twitter amateur porn
This is excellent article, thank you for the share! This is what I am looking for, hope in future you will continue sharing such an superb work.
Hello, Neat post. There is an issue with your website in internet explorer, might check this?
IE nonetheless is the marketplace chief and a good portion of people will omit your wonderful writing due to this problem.
Understanding the significance of configuration management in projects is crucial, whether large or small. While larger projects may benefit from the robust features of existing frameworks, smaller projects require a more lightweight approach. It’s essential to balance dependencies and complexity accordingly. Similarly, tailored support is invaluable in navigating complex legal landscapes, such as company law. Seeking assistance from reputable company law assignment help services can provide clarity and guidance, ensuring compliance and understanding of legal intricacies. Just as projects require meticulous management, legal matters demand attention to detail and expertise. Trusting project management and company law experts ensures success and adherence to best practices.