Автозагрузчик для функций
На прошлой неделе я узнал, что классы могут быть включены в ваш проект, написав функцию __autoload()
. Затем я узнал, что использование автозагрузчика - это не только техника, но и шаблон.
Теперь я использую автозагрузчик в своем проекте, и я нашел его очень полезным. Мне было интересно, можно ли делать то же самое с функциями. Было бы очень полезно забыть о включении правого PHP файла с функциями внутри него.
Итак, можно ли создать функцию автозагрузчика?
Ответы
Ответ 1
Для функций нет функции автозагрузчика. У вас есть четыре реалистичных решения:
-
Оберните все функции в классы имен (подходящий контекст). Итак, скажем, у вас есть функция под названием string_get_letters
. Вы можете добавить это к классу с именем StringFunctions
как статическую функцию. Поэтому вместо вызова string_get_letters()
вы вызываете StringFunctions::get_letters()
. Тогда вы должны были бы __autoload
те классы с именами.
-
Предварительно загрузите все функции. Поскольку вы используете классы, у вас не должно быть таких функций, поэтому просто загружайте их.
-
Загрузите функции перед их использованием. В каждом файле require_once
файлы функций, которые будут использоваться в этом файле.
-
Не используйте функции в первую очередь. Если вы разрабатываете OOP-код (который, как вам кажется, в любом случае), он вообще не нуждается в каких-либо функциях. Все, для чего вам понадобится функция (или несколько), вы могли бы создать способ OO и избежать необходимости в функциях.
Лично я предлагаю вам 1, 2 или 4 в зависимости от вашей конкретной потребности и качества и размера вашей кодовой базы...
Ответ 2
Если вы используете Composer в своем проекте, вы можете добавить директиву files в раздел автозагрузки.
Это не приведет к созданию require_once в автозагрузчике, но это похоже на реальную автозагрузку, потому что вам не нужно это делать.
Его не ленивая загрузка, хотя.
Пример из Assetic:
"autoload": {
"psr-0": { "Assetic": "src/" },
"files": [ "src/functions.php" ]
}
Ответ 3
Я кое-что прочитал об уродливом хаке, который вызвал фатальные ошибки и попытался включить и выполнить недостающие функции, но я определенно не пошел бы по этой дороге.
Самое близкое, что у вас есть, - это магический метод __call()
, который является своего рода __autoload()
для методов, а не для функций. Это может быть достаточно для ваших нужд; если вы можете позволить себе позвонить классу и потребовать каждую отдельную функцию отдельно. Начиная с PHP 5.3.0, у вас также есть __callStatic()
.
Пример с использованием __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);
Это вызовет функцию functionToLoad()
, определенную в./path/to/functions/functionToLoad.php.
Ответ 4
Ну, как обычно, для этого есть расширение PECL:
(через: http://phk.tekwire.net/joomla/support/doc/automap.htm)
Он должен выполнять функции автозагрузки, а также классы. Что еще не работает с текущим интерпретатором PHP.
(Альтернативный вариант btw, генерирует функции-заглушки, которые загружают и запускают сопоставленные экземпляры экземпляров.)
Это сказано. Автозагрузка не всегда считается хорошей практикой. Это приводит к чрезмерно раздробленным иерархиям классов и объекту счастья. И настоящая причина, по которой PHP имеет автозагрузку, заключается в том, что системы управления включением и зависимостями являются inmature.
Ответ 5
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);
}
}
И используя пространства имен, мы можем сделать: Fn::myFunc()
и spl_autoload_register()
. Я использовал этот код с примерами: https://goo.gl/8dMIMj
Ответ 6
new Functions\Debug() загрузит функции в корневое пространство имен.
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();
}
}
}
Ответ 7
Я использую класс и __invoke. Метод __invoke
вызывается, когда скрипт вызывает класс как функцию. Я часто делаю что-то вроде этого:
<?php
namespace API\Config;
class Slim {
function __invoke() {
return [
'settings' => [
'displayErrorDetails' => true,
'logger' => [
'name' => 'api',
'level' => Monolog\Logger\Logger::DEBUG,
'path' => __DIR__ . '/../../logs/api.log',
],
]
];
}
}
Затем я могу вызвать как функцию:
$config = API\Config\Slim;
$app = Slim\App($config())
Ответ 8
Вот еще один довольно сложный пример, основанный на предложениях в этом обсуждении.
Код также можно увидеть здесь: 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://stackoverflow.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);
}
}
Ответ 9
Пока вы не можете выполнять автонагрузку функций и констант, вы можете использовать что-то вроде jesseschalken/autoload-generator, которое будет автоматически определять, какие файлы содержат вещи, которые могут "Автозагрузка и загрузка с нетерпением".
Ответ 10
Включить все файлы функций в один файл, а затем включить его
//Файл 1
db_fct.php
//Файл 2
util_fct.php
//В functions.php включены все остальные файлы
<?php
require_once 'db_fct.php';
require_once 'util_fct.php';
?>
Включите functions.php, когда вам нужны функции.
Ответ 11
попробуйте это
if ($handle = opendir('functions')) {
while (false !== ($entry = readdir($handle))) {
if (strpos($entry, '.php') !== false) {
include("functions/$entry");
}
}
closedir($handle);
}