62

上周我了解到可以通过编写__autoload()函数将类包含在您的项目中。然后我了解到使用自动加载器不仅是一种技术,而且是一种模式。

现在我在我的项目中使用自动加载器,我发现它非常有用。我想知道是否可以用函数做同样的事情。忘记包含正确的 PHP 文件和其中的函数可能非常有用。

那么,是否可以创建一个函数自动加载器?

4

11 回答 11

62

函数没有函数自动加载器。你有四个现实的解决方案:

  1. 将所有函数包装到命名空间类中(适当的上下文)。因此,假设您有一个名为string_get_letters. 您可以将其添加到称为StringFunctions静态函数的类中。所以不要打电话string_get_letters(),你会打电话StringFunctions::get_letters()。然后,您将使用__autoload那些命名空间的类。

  2. 预加载所有功能。由于您使用的是类,因此您不应该拥有那么多功能,因此只需预加载它们即可。

  3. 在使用函数之前加载函数。在每个文件中,require_once将在该文件中使用的函数文件。

  4. 首先不要使用函数。如果您正在开发 OOP 代码(无论如何您似乎就是这样),那么根本不需要函数。您需要一个(或多个)函数的所有内容,您都可以以 OO 方式构建并避免对函数的需求。

就个人而言,我会建议 1、2 或 4,具体取决于您的确切需求以及代码库的质量和大小......

于 2011-01-19T15:43:01.567 回答
40

如果您在项目中使用 Composer,则可以将files指令添加到 autoload 部分。

这将比实际在自动加载器中生成一个 require_once,但感觉就像真正的自动加载,因为您不必照顾它。
虽然它不是延迟加载。

取自Assetic的示例:

"autoload": {
        "psr-0": { "Assetic": "src/" },
        "files": [ "src/functions.php" ]
    }
于 2013-07-29T13:31:33.513 回答
16

不久前,我读到了一些关于一个丑陋的 hack,它捕获了致命错误并试图包含并执行丢失的函数,但我绝对不会走那条路。

您拥有的最接近的东西是__call() 魔术方法,它是一种__autoload()for 方法,而不是函数。它可能足以满足您的需求;如果您有能力调用一个类并分别需要每个不同的功能。自 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 中定义的函数。

于 2011-01-19T16:07:12.453 回答
8

好吧,像往常一样,有一个 PECL 扩展:

(通过: http: //phk.tekwire.net/joomla/support/doc/automap.htm

它应该自动加载函数和类。然而,这不适用于当前的 PHP 解释器。

(顺便说一句,另一种选择是生成加载和运行命名空间对应项的存根函数。)

话虽如此。自动加载并不是普遍认为的好习惯。它导致过度分裂的阶级等级和对象幸福感。PHP 具有自动加载功能的真正原因是包含和依赖管理系统不成熟。

于 2011-01-19T16:15:27.290 回答
3
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

于 2014-03-15T03:03:04.173 回答
2

我使用 Class 和__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())
于 2018-09-24T22:39:17.437 回答
1

new Functions\Debug() 会将函数加载到根命名空间。

命名空间函数
{

    类调试
    {
    }
}
命名空间
{

    if (!function_exists('printr')) {

        /**
         *
         * @param 混合 $ 表达式
         */
        函数 printr()
        {
            foreach (func_get_args() as $v) {
                if (is_scalar($v)) {
                    回声 $v 。"\n";
                } 别的 {
                    print_r($v);
                }
            }
            出口();
        }
    }
}
于 2017-11-28T13:02:11.437 回答
0

这是另一个相当复杂的示例,基于此讨论中的建议。代码也可以在这里看到: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);
  }
}
于 2014-09-02T20:13:27.680 回答
0

虽然您不能自动加载函数和常量,但您可以使用jesseschalken/autoload-generator 之类的东西,它会自动检测哪些文件包含无法自动加载的内容并立即加载它们。

于 2015-07-29T12:37:05.267 回答
0

将所有函数文件包含在一个文件中,然后包含它

//文件1
db_fct.php

//文件2
util_fct.php

//在functions.php中包含所有其他文件

<?php

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

每当你需要函数时,包括functions.php ..

于 2016-10-21T09:25:45.437 回答
-2

试试这个

if ($handle = opendir('functions')) {
    while (false !== ($entry = readdir($handle))) {
        if (strpos($entry, '.php') !== false) {
            include("functions/$entry");
        }
    }
    closedir($handle);
}
于 2015-05-20T19:26:29.383 回答