Автозагрузчик для функций

На прошлой неделе я узнал, что классы могут быть включены в ваш проект, написав __autoload() функция. Затем я узнал, что использование автозагрузчика - это не только техника, но и шаблон.

Сейчас я использую автозагрузчик в своем проекте и нашел его очень и очень полезным. Мне было интересно, можно ли сделать то же самое с функциями. Может быть очень полезно забыть о включении правильного файла PHP с функциями внутри него.

Итак, возможно ли создать функцию автозагрузчика?

13 ответов

Решение

Нет функции автозагрузчика для функций. У вас есть четыре реалистичных решения:

  1. Оберните все функции в классы пространства имен (соответствующий контекст). Итак, скажем, у вас есть функция с именем string_get_letters, Вы можете добавить это к классу под названием StringFunctions как статическая функция. Так что вместо звонка string_get_letters()позвонил бы StringFunctions::get_letters(), Вы бы тогда __autoload эти пространства имен пространства.

  2. Предварительно загрузите все функции. Поскольку вы используете классы, у вас не должно быть такого количества функций, поэтому просто предварительно загрузите их.

  3. Загрузите функции перед их использованием. В каждом файле require_once функциональные файлы, которые будут использоваться в этом файле.

  4. Не используйте функции в первую очередь. Если вы разрабатываете ООП-код (который, как вам кажется, в любом случае), функций вообще не должно быть. Все, для чего вам нужна функция (или несколько функций), вы можете создать ОО-способом и избежать необходимости в функциях.

Лично я бы предложил 1, 2 или 4 в зависимости от ваших потребностей, качества и размера вашей кодовой базы...

Если вы используете Composer в своем проекте, вы можете добавить директиву files в раздел автозагрузки.

Это фактически сгенерирует require_once в автозагрузчике, но это похоже на настоящую автозагрузку, потому что вам не нужно об этом заботиться.
Это не ленивая загрузка, хотя.

Пример взят из Assetic:

"autoload": {
        "psr-0": { "Assetic": "src/" },
        "files": [ "src/functions.php" ]
    }

Некоторое время назад я читал кое-что об уродливом хакере, который поймал фатальные ошибки и попытался включить и выполнить отсутствующие функции, но я определенно не пошел бы по этому пути.

Самое близкое, что у вас есть, это __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.

Ну, как обычно, для этого есть расширение PECL:

(через: http://phk.tekwire.net/joomla/support/doc/automap.htm)

Он должен автоматически загружать функции и классы. Что, однако, еще не работает с текущим интерпретатором PHP.

(Альтернативный вариант, между прочим, генерирует функции-заглушки, которые загружают и запускают аналоги пространства имен.)

Что, как говорится. Автозагрузка не всегда считается хорошей практикой. Это приводит к чрезмерно раздробленной иерархии классов и объектному счастью. И настоящая причина, по которой PHP имеет автозагрузку, заключается в том, что системы управления включением и зависимостями являются незрелыми.

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

Я использую класс и __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())

Я стараюсь использовать автозагрузку классов в свою пользу. Итак, когда класс загружается автоматически, файл класса выполняется.Поэтому я создаю класс со статическим методом boot, который ничего не делает. Когда я вызываю этот метод, класс будет загружаться автоматически, поэтому каждая функция в этом файле будет определена в глобальной области видимости. Еще интереснее то, что функции будут определены в пространстве имен класса, поэтому конфликтов не будет.

Например:

      <?PHP

namespace Functions;
// functions are defined in this file

class GlobalFunctions{
  public static function boot(){};
}

function foo(){ // code... }
?>

// the client file
<?php
  
  // VS Code automatically does this.
  use Functions\GlobalFunctions;
  use function Functions\foo; 

  // I usually put this in the bootstrap file
  GlobalFunctions::boot();


  
  // call foo() from the Functions namespace
  foo();
?>

new Functions\Debug() с автозагрузкой загрузит функции в корневое пространство имен.

Функции пространства имен
{

    Класс Debug
    {
    }
}
Пространство имен
{

    if (! function_exists('printr')) {

        /**
         *
         * @param mixed $ выражение
         */
        функция printr()
        {
            foreach (func_get_args() как $v) {
                if (is_scalar($v)) {
                    echo $v . "\ П";
                } еще {
                    print_r($v);
                }
            }
            выход();
        }
    }
}

Хотя вы не можете автоматически загружать функции и константы, вы можете использовать что-то вроде https://packagist.org/package s/jesseschalken/autoload-generator, которое автоматически определит, какие файлы содержат вещи, которые не могут быть автоматически загружены, и загружает их с нетерпением.

Решение, которое я придумал. Настолько легкий, насколько я мог придумать.

      class functions {

  public static function __callstatic($function, $arguments) {

    if (!function_exists($function)) {
      $file = strtok($function, '_') .'.php';
      include '/path/to/functions/'.$file;
    }

    return call_user_func_array($function, $arguments);
  }
}

Воспользуйтесь им, позвонивfunctions::foo_bar($anything).

Вот еще один довольно сложный пример, основанный на предложениях в этом обсуждении. Код также можно увидеть здесь: 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://stackru.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);
  }
}

Включить все файлы функций в один файл, а затем включить его

// Файл 1
db_fct.php

// Файл 2
util_fct.php

// В файл functions.php включаем все остальные файлы

<?php

require_once 'db_fct.php';
require_once 'util_fct.php';
?>

Включайте functions.php всякий раз, когда вам нужны функции.

Попробуй это

if ($handle = opendir('functions')) {
    while (false !== ($entry = readdir($handle))) {
        if (strpos($entry, '.php') !== false) {
            include("functions/$entry");
        }
    }
    closedir($handle);
}
Другие вопросы по тегам