PHP - Automatización de include, require, include_once y require_once

PHP - Automatización de include, require, include_once y require_once

¿Cómo incluir archivos en nuestros proyectos sin necesidad de indicar toda la ruta hacia los mismos?

Para los que empiezan con php y necesitan una aclaración extra sobre estas funciones y como tener disponibles nuestros archivos para ser incluidos en nuestro proyecto sin necesidad de escribir toda la ruta, sea esta, absoluta o relativa explico el concepto y como siempre lo refuerzo con ejemplos. Más adelante te encontrarás funciones para tratar con directorios que pueden ser útiles por separado. Si ejecutamos la funcion phpinfo(); se nos mostrará la configuración almacenada en nuestro "php.ini" en este listado si buscamos la directiva include_path veremos la ruta por defecto de donde apache intentará hacer las inclusiónes de los archivos que instanciemos mediante alguna de las siguientes funciones:
  • include
  • require
  • include_once
  • require_once
en mi caso cuento con la siguiente ruta por defecto: /Applications/MAMP/bin/php5.3/lib/php esto quiere decir que si en algún archivo dentro de mi proyecto hago lo siguiente: include("algun_archivo_a_incluir.php"); include_once("otro_archivo_a_incluir.inc"); require("un_archivo_mas.html"); Apache automaticamente irá a buscarlos dentro del directorio php en la ruta anterior. Si no existierán entonces lanzaria un error.

Ejemplo aclaratorio:

Supongamos que contamos con la siguiente estructura de archivos /my_project /classes /models model_user.inc /views /users view_user_new.inc view_user_detail.inc view_list.inc /clients view_client_new.inc view_client_detail.inc view_list.inc /controllers controller_user.inc /plugins /vendors Si en nuestro controlador controller_user.inc desearamos utilizar el modelo model_user.inc y la vista view_client_detail deberíamos hacer lo siguiente: <?php include_once("../models/model_user.inc"); include_once("../views/users/view_user_detail.inc"); //Archivo controller_user.inc class ControllerUser extends MainController { public function __construct(){} public function detail() { //atributo en link del listado de usuarios que forma una url con formato //controller=controller_user&method=detail&user_id=usr1234 $id_usuario = $_GET["user_id"]; //Creo el usuario desde el Modelo $oUsuario = new ModelUser($id_usuario); //carga el usuario en el objeto Usuario. Ejecuta la //sql "SELECT * FROM tabla_usuario WHERE id='$id_usuario'; $oUsuario->read(); //Despues de read() podriamos mostrar los datos del select directamente //con los getters del objeto: $oUsuario->get_name(); $oUsuario->get_email()... //Creo la vista $oUserDetail = new ViewUserDetail($oUsuario); //Esto muestra todo el html $oUserDetail->render(); } } //Creo el objeto controlador y ejecuto el metodo indicado, que a su vez creará //la vista detalle, un formulario con campos readonly, es lo que se indica en la url. $oControllerUser = new ControllerUser(); $sMethodName = $_GET["method"]; $oControllerUser->$sMethodName(); ?> Podriamos mejorar la inclusión de los archivos sin necesidad de indicar la ruta relativa o absoluta a los mismos. ¿Cómo? con la función set_include_path($path); Para el ejemplo anterior deberiamos hacer lo siguiente en las primeras lineas de ejecución de nuestro proyecto: <?php //recuperamos la ruta que tiene almacenada por defecto. $arPaths[] = get_include_path(); //guardamos las rutas personalizadas. Para este ejemplo solo utilizaré //los modelos controladores y vistas $arPaths[] = "/Applications/MAMP/htdocs/my_project/classes/models"; $arPaths[] = "/Applications/MAMP/htdocs/my_project/classes/views"; $arPaths[] = "/Applications/MAMP/htdocs/my_project/classes/controllers"; $sMergedPaths = implode(PATH_SEPARATOR,$arPaths); set_include_path($sMergedPaths); //El nuevo included path será algo como esto: //.:/Applications/MAMP/bin/php5.3/lib/php:/Applications/MAMP/htdocs/my_project/classes/models: ///Applications/MAMP/htdocs/my_project/classes/views:/Applications/MAMP/htdocs/my_project/classes/controllers ?> Así nuestro archivo controllador quedaría modificado a : <?php include_once("model_user.inc"); include_once("view_user_detail.inc"); //Archivo controller_user.inc class ControllerUser extends MainController { ... ?>

Para automatizar la carga de rutas he creado unas funciones que trabajan recorriendo un arbol de directorios

En otro post explicaré cómo hacer una clase con ellas. Estas funciones son las siguientes.

¿Qué hacen y Cómo?

Ayudan en la creación de rutas a partir de una carpeta (directorio) raíz, recorriendo recursivamente sus subdirectorios. Los parámetros son los siguientes: $sPathDir Ruta del directorio raiz. A partir de este se generarán todas las rutas. $arNotToCountOn Lista de carpetas ó directorios que no se tomarán en cuenta. Pe: las carpetas de subversion ".svn" $sDS Directory Separator. Permite que se pueda configurar el separa dor para windows. Por defecto esta para sistemas unix. <?php //NOTA: La función "bug()" comentada es un var_dump customizado. function get_folders($sPathDir,$arNotToCountOn=array(),$sDS="/") { $arFolders = array(); if ($oDirHandler = opendir($sPathDir)) { while(($dirElement = readdir($oDirHandler))!==false) { $sTmPath = $sPathDir . $sDS . $dirElement; //bug("dirElement=$dirElement");bug($arNotToCountOn); //bug("inarray=".in_array($dirElement, $arNotToCountOn)); if(is_dir($sTmPath) && $dirElement!="." && $dirElement!=".." && !in_array($dirElement, $arNotToCountOn)) { $arFolders[] = $dirElement; } } closedir($oDirHandler); } //bug($arFolders); return $arFolders; } function has_dir($sPathDir,$arNotToCountOn=array(),$sDS="/") { if($oDirHandler = opendir($sPathDir)) { while(($dirElement = readdir($oDirHandler))!==false) { $sTmPath = $sPathDir . $sDS . $dirElement; //bug(is_dir($sTmPath),$sTmPath); //bug($arNotToCountOn); if(is_dir($sTmPath) && $dirElement!="." && $dirElement!=".." && !in_array($dirElement, $arNotToCountOn)) { closedir($oDirHandler); return true; } } closedir($oDirHandler); } return false; } function dir_tree($sPathDir,$arNotToCountOn=array(),$sDS="/") { $arTree = array(); if(is_dir($sPathDir)) { $arTree[] = $sPathDir; //bug($sPathDir,"is dir"); //bug($arNotToCountOn); if(has_dir($sPathDir,$arNotToCountOn,$sDS)) { //bug($sPathDir,"has folders"); $arSubDir = get_folders($sPathDir,$arNotToCountOn,$sDS); //bug($arSubDir,"subdir"); foreach($arSubDir as $sFolderName) { $sPath = $sPathDir .$sDS. $sFolderName; //$arTree[] = dir_tree($sPath); foreach(dir_tree($sPath,$arNotToCountOn,$sDS) as $sPaths) { $arTree[] = $sPaths; } } } } return $arTree; } //FUNCION PARA AUTOCARGA DE LA RUTA DE INCLUSIÓN: function load_recursive_include_path($sPathDir, $arNotToCountOn=array(), $sDS="/") { //bug($arNotToCountOn);die; $arDirPaths = dir_tree($sPathDir,$arNotToCountOn, $sDS); $sDirPaths = implode(PATH_SEPARATOR,$arDirPaths); if(!empty($sDirPaths)) { $sDirPaths = get_include_path().PATH_SEPARATOR.$sDirPaths; set_include_path($sDirPaths); } } ?>

Su utilización:

Aprovecho para poner un ejemplo de un prototipo de un archivo bootstrap para un framewor MVC. <?php //Archivo index.php. //================================ // CARGA DE RUTAS //================================ //Defino la ruta absoluta de mi proyecto $sProjectDir = "/Applications/MAMP/htdocs/my_project"; //Aplico set_include_path de forma recursiva saltando todas las carpetas de //subversion. load_recursive_include_path($sProjectDir,array(".svn")); //================================ // DEFINE CONTROLADOR DESDE URL //================================ $arPathToController=array("usuarios"=>array("controller_user.inc","ControllerUser") ,...); $sControllerName = $_GET["controller"]; $sMethod = $_GET["method"]; $sControllerFile = $arPathToController[$sControllerName][0]; $sControllerClass = $arPathToController[$sControllerName][1]; include_once($sControllerFile); $oControllerName = new $sControllerClass(); $oControllerName->$sMethod(); ?> Antes de terminar quisiera comentar sobre esta forma de autocarga en el set_include_path(), como todo esto tiene sus ventajas y desventajas. Si nos fijamos en parte de la estructura, concretamente en las vistas de listado de "users" y "clients", ambas tienen el mismo nombre view_list.inc. ... /views /users view_user_new.inc view_user_detail.inc view_list.inc /clients view_client_new.inc view_client_detail.inc view_list.inc ... Si en nuestro controlador hacemos include_once("view_list.inc"); primero se buscaria la existencia en la carpeta "clients" al ser encontrado el archivo view_list.inc se incluiria y no se buscaría ningún otro para ser añadido. Al no ser este el .inc que necesitamos ya que deseamos mostrar el listado de usuarios, es decir, el view_list.inc de la carpeta "users" no obtendriamos el comportamiento deseado. Por lo tanto, a mi parecer es una pequeña desventaja y esta radica en que deberiamos o bién no tomar en cuenta estas carpetas (users, clients) en $arNotToCountOn y usar el include_once("users/view_list.inc"); ó renombrar los archivos. Espero que la entrada les sirva de ayuda. Un saludo a tod@s!

Autor: Eduardo A. F.
Publicado: 26-04-2012 21:28
Actualizado: 27-04-2012 18:33