php - register - Autocargador para funciones
spl_autoload_register multiple directories (10)
La semana pasada aprendí que las clases se pueden incluir en su proyecto al escribir una __autoload()
. Luego aprendí que usar un autocargador no es solo una técnica sino también un patrón.
Ahora estoy usando el autocargador en mi proyecto y lo he encontrado muy útil. Me preguntaba si sería posible hacer lo mismo con las funciones. Podría ser muy útil olvidarse de incluir el archivo PHP correcto con funciones dentro de él.
Entonces, ¿es posible crear un autocargador de funciones?
Aquí hay otro ejemplo bastante complejo, basado en las sugerencias en esta discusión. El código también se puede ver aquí: lib/btr.php
<?php
/**
* A class that is used to autoload library functions.
*
* If the function btr::some_function_name() is called, this class
* will convert it into a call to the function
* ''BTranslator/some_function_name()''. If such a function is not
* declared then it will try to load these files (in this order):
* - fn/some_function_name.php
* - fn/some_function.php
* - fn/some.php
* - fn/some/function_name.php
* - fn/some/function.php
* - fn/some/function/name.php
* The first file that is found will be loaded (with require_once()).
*
* For the big functions it makes more sense to declare each one of them in a
* separate file, and for the small functions it makes more sense to declare
* several of them in the same file (which is named as the common prefix of
* these files). If there is a big number of functions, it can be more
* suitable to organize them in subdirectories.
*
* See: http://.com/questions/4737199/autoloader-for-functions
*/
class btr {
/**
* Make it TRUE to output debug info on ''/tmp/btr.log''.
*/
const DEBUG = FALSE;
/**
* The namespace of the functions.
*/
const NS = ''BTranslator'';
/**
* Relative directory where the functions are located.
*/
const FN = ''fn'';
private function __construct() {}
private function __wakeup() {}
private function __clone() {}
/**
* Return the full name (with namespace) of the function to be called.
*/
protected static function function_name($function) {
return self::NS . ''//' . $function;
}
/**
* Return the full path of the file to be loaded (with require_once).
*/
protected static function file($fname) {
return dirname(__FILE__) . ''/'' . self::FN . ''/'' . $fname . ''.php'';
}
/**
* If a function does not exist, try to load it from the proper file.
*/
public static function __callStatic($function, $args) {
$btr_function = self::function_name($function);
if (!function_exists($btr_function)) {
// Try to load the file that contains the function.
if (!self::load_search_dirs($function) or !function_exists($btr_function)) {
$dir = dirname(self::file($fname));
$dir = str_replace(DRUPAL_ROOT, '''', $dir);
throw new Exception("Function $btr_function could not be found on $dir");
}
}
return call_user_func_array($btr_function, $args);
}
/**
* Try to load files from subdirectories
* (by replacing ''_'' with ''/'' in the function name).
*/
protected static function load_search_dirs($fname) {
do {
self::debug($fname);
if (file_exists(self::file($fname))) {
require_once(self::file($fname));
return TRUE;
}
if (self::load_search_files($fname)) {
return TRUE;
}
$fname1 = $fname;
$fname = preg_replace(''#_#'', ''/'', $fname, 1);
} while ($fname != $fname1);
return FALSE;
}
/**
* Try to load files from different file names
* (by removing the part after the last undescore in the functin name).
*/
protected static function load_search_files($fname) {
$fname1 = $fname;
$fname = preg_replace(''/_[^_]*$/'', '''', $fname);
while ($fname != $fname1) {
self::debug($fname);
if (file_exists(self::file($fname))) {
require_once(self::file($fname));
return TRUE;
}
$fname1 = $fname;
$fname = preg_replace(''/_[^_]*$/'', '''', $fname);
}
return FALSE;
}
/**
* Debug the order in which the files are tried to be loaded.
*/
public static function debug($fname) {
if (!self::DEBUG) {
return;
}
$file = self::file($fname);
$file = str_replace(DRUPAL_ROOT, '''', $file);
self::log($file, ''Autoload'');
}
/**
* Output the given parameter to a log file (useful for debugging).
*/
public static function log($var, $comment ='''') {
$file = ''/tmp/btr.log'';
$content = "/n==> $comment: " . print_r($var, true);
file_put_contents($file, $content, FILE_APPEND);
}
}
Bueno, como siempre hay una extensión PECL para eso:
(a través de: http://phk.tekwire.net/joomla/support/doc/automap.htm )
Se supone que debe cargar automáticamente las funciones y las clases. Sin embargo, esto no funciona con el intérprete actual de PHP.
(Una opción alternativa, por cierto, es generar funciones stub que carguen y ejecuten contrapartidas espaciadas).
Habiendo dicho eso. La carga automática no se considera una buena práctica universalmente. Conduce a jerarquías de clase excesivamente fracturadas y felicidad del objeto. Y la verdadera razón por la que PHP tiene carga automática es porque los sistemas de administración de inclusión y dependencia son inmaduros.
Hace un tiempo leí algo sobre un feo truco que captó errores fatales y traté de incluir y ejecutar la (s) función (es) que faltaban, pero definitivamente no seguiría ese camino.
Lo más parecido que tienes es el método mágico __call()
, que es una especie de __autoload()
para métodos, no para funciones. Puede ser lo suficientemente bueno para sus necesidades; si puede permitirse llamar a una clase y requerir cada función diferente por separado. Desde PHP 5.3.0, también tiene __callStatic()
.
Un ejemplo usando __callStatic()
:
class Test
{
public function __callStatic($m, $args)
{
if (function_exists($m) !== true)
{
if (is_file(''./path/to/functions/'' . $m . ''.php'') !== true)
{
return false;
}
require(''./path/to/functions/'' . $m . ''.php'');
}
return call_user_func_array($m, $args);
}
}
Test::functionToLoad(1, 2, 3);
Esto llamaría a la functionToLoad()
definida en ./path/to/functions/functionToLoad.php.
Incluya todo el archivo de funciones en un archivo y luego inclúyalo
// Archivo 1
db_fct.php
// Archivo 2
util_fct.php
// En a functions.php incluye todos los demás archivos
<?php
require_once ''db_fct.php'';
require_once ''util_fct.php'';
?>
Incluye functions.php siempre que necesites funciones.
No hay un cargador automático de funciones para funciones. Tienes cuatro soluciones realistas:
Ajustar todas las funciones en las clases de espaciado de nombres (contexto apropiado). Entonces digamos que tienes una función llamada
string_get_letters
. Puede agregar eso a una clase llamadaStringFunctions
como una función estática. Entonces, en lugar de llamar astring_get_letters()
, llamarías aStringFunctions::get_letters()
. A continuación,__autoload
esas clases de espacio de nombres.Precarga todas las funciones. Ya que estás usando clases, no deberías tener tantas funciones, así que solo pre-cargalas.
Cargue las funciones antes de usarlas. En cada archivo,
require_once
los archivos de función que se usarán en ese archivo.No use funciones en primer lugar. Si está desarrollando un código OOP (que parece ser de todos modos), debería haber poca o ninguna necesidad de funciones. Todo lo que necesitaría para una función (o varias), podría construir de manera OO y evitar la necesidad de funciones.
Personalmente, sugeriría 1, 2 o 4 dependiendo de su necesidad exacta y la calidad y tamaño de su base de código ...
Si bien no puede cargar funciones y constantes automáticas, puede usar algo como jesseschalken/autoload-generator que detectará automáticamente qué archivos contienen elementos que no se pueden cargar automáticamente y los cargará con entusiasmo.
Si está utilizando Composer en su proyecto, puede agregar una directiva de files a la sección de autocarga.
Esto realmente generará un requerimiento en el autocargador, pero se siente como carga automática real, porque no tiene que preocuparse por eso.
Aunque no es una carga floja.
Ejemplo tomado de Assetic :
"autoload": {
"psr-0": { "Assetic": "src/" },
"files": [ "src/functions.php" ]
}
las nuevas funciones / Debug () con carga automática cargarán las funciones al espacio de nombres raíz.
namespace Functions { class Debug { } } namespace { if (! function_exists(''printr'')) { /** * * @param mixed $expression */ function printr() { foreach (func_get_args() as $v) { if (is_scalar($v)) { echo $v . "/n"; } else { print_r($v); } } exit(); } } }
prueba esto
if ($handle = opendir(''functions'')) {
while (false !== ($entry = readdir($handle))) {
if (strpos($entry, ''.php'') !== false) {
include("functions/$entry");
}
}
closedir($handle);
}
namespace MyNamespace;
class Fn {
private function __construct() {}
private function __wakeup() {}
private function __clone() {}
public static function __callStatic($fn, $args) {
if (!function_exists($fn)) {
$fn = "YOUR_FUNCTIONS_NAMESPACE//$fn";
require str_replace(''//', ''/'', $fn) . ''.php'';
}
return call_user_func_array($fn, $args);
}
}
Y al utilizar espacios de nombres, podemos hacer lo siguiente: Fn::myFunc()
y spl_autoload_register()
. He usado este código con ejemplos en: https://goo.gl/8dMIMj