如何处理在 PHP 中包含所需的类

发布于 2024-07-04 11:17:04 字数 528 浏览 12 评论 0原文

我想知道处理必须在 PHP 脚本中“包含”如此多文件的问题的最佳实践是什么,以确保我的脚本可以访问我需要使用的所有类。

目前,我只是使用 include_once 来包含我访问的类直接地。 其中每一个都会 include_once 他们访问的类。

我研究过使用 __autoload 函数,但是如果您打算将类文件组织在目录树中,那么这个函数似乎不太有效。 如果您这样做,似乎您最终会遍历目录树,直到找到您正在寻找的类。 此外,我不确定这如何影响不同命名空间中具有相同名称的类。

有更简单的方法来处理这个问题吗?

或者PHP 是否不适合“企业”类型应用程序,其中许多不同的对象都位于单独的文件中,这些文件可能位于许多不同的目录中。

I'm wondering what the best practice is for handling the problem with having to "include" so many files in my PHP scripts in order to ensure that all the classes I need to use are accessible to my script.

Currently, I'm just using include_once to include the classes I access directly. Each of those would include_once the classes that they access.

I've looked into using the __autoload function, but hat doesn't seem to work well if you plan to have your class files organized in a directory tree. If you did this, it seems like you'd end up walking the directory tree until you found the class you were looking for. Also, I'm not sure how this effects classes with the same name in different namespaces.

Is there an easier way to handle this?

Or is PHP just not suited to "enterprisey" type applications with lots of different objects all located in separate files that can be in many different directories.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

花间憩 2024-07-11 11:17:04

在我的应用程序中,我通常有 setup.php 文件,其中包含所有核心类(即框架和随附的库)。 我的自定义类是使用目录布局图辅助的自动加载器加载的。

每次添加新类时,我都会运行命令行构建器脚本,该脚本扫描整个目录树以搜索模型类,然后构建关联数组,其中类名作为键,路径作为值。 然后, __autoload 函数在该数组中查找类名并获取包含路径。 代码如下:

autobuild.php

define('MAP', 'var/cache/autoload.map');
error_reporting(E_ALL);
require 'setup.php';
print(buildAutoloaderMap() . " classes mapped\n");

function buildAutoloaderMap() {
    $dirs = array('lib', 'view', 'model');
    $cache = array();
    $n = 0;
    foreach ($dirs as $dir) {
        foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir)) as $entry) {
            $fn = $entry->getFilename();
            if (!preg_match('/\.class\.php$/', $fn))
                continue;
            $c = str_replace('.class.php', '', $fn);
            if (!class_exists($c)) {
                $cache[$c] = ($pn = $entry->getPathname());
                ++$n;
            }
        }
    }
    ksort($cache);
    file_put_contents(MAP, serialize($cache));
    return $n;
}

autoload.php

define('MAP', 'var/cache/autoload.map');

function __autoload($className) {
    static $map;
    $map or ($map = unserialize(file_get_contents(MAP)));
    $fn = array_key_exists($className, $map) ? $map[$className] : null;
    if ($fn and file_exists($fn)) {
        include $fn;
        unset($map[$className]);
    }
}

请注意,文件命名约定必须是 [class_name].class.php。 更改目录,类将在 autobuild.php 中查找。 当找不到类时,您还可以从自动加载函数运行自动构建器,但这可能会使您的程序陷入无限循环。

序列化数组速度非常快。

@JasonMichael:PHP 4 已经死了。 克服它。

I my applications I usually have setup.php file that includes all core classes (i.e. framework and accompanying libraries). My custom classes are loaded using autoloader aided by directory layout map.

Each time new class is added I run command line builder script that scans whole directory tree in search for model classes then builds associative array with class names as keys and paths as values. Then, __autoload function looks up class name in that array and gets include path. Here's the code:

autobuild.php

define('MAP', 'var/cache/autoload.map');
error_reporting(E_ALL);
require 'setup.php';
print(buildAutoloaderMap() . " classes mapped\n");

function buildAutoloaderMap() {
    $dirs = array('lib', 'view', 'model');
    $cache = array();
    $n = 0;
    foreach ($dirs as $dir) {
        foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir)) as $entry) {
            $fn = $entry->getFilename();
            if (!preg_match('/\.class\.php$/', $fn))
                continue;
            $c = str_replace('.class.php', '', $fn);
            if (!class_exists($c)) {
                $cache[$c] = ($pn = $entry->getPathname());
                ++$n;
            }
        }
    }
    ksort($cache);
    file_put_contents(MAP, serialize($cache));
    return $n;
}

autoload.php

define('MAP', 'var/cache/autoload.map');

function __autoload($className) {
    static $map;
    $map or ($map = unserialize(file_get_contents(MAP)));
    $fn = array_key_exists($className, $map) ? $map[$className] : null;
    if ($fn and file_exists($fn)) {
        include $fn;
        unset($map[$className]);
    }
}

Note that file naming convention must be [class_name].class.php. Alter the directories classes will be looked in autobuild.php. You can also run autobuilder from autoload function when class not found, but that may get your program into infinite loop.

Serialized arrays are darn fast.

@JasonMichael: PHP 4 is dead. Get over it.

蔚蓝源自深海 2024-07-11 11:17:04

您可以使用 spl_autoload_register 定义多个自动加载函数:

spl_autoload_register('load_controllers');
spl_autoload_register('load_models');

function load_models($class){
    if( !file_exists("models/$class.php") )
        return false;

    include "models/$class.php";
    return true;
}
function load_controllers($class){
    if( !file_exists("controllers/$class.php") )
        return false;

    include "controllers/$class.php";
    return true;
}

You can define multiple autoloading functions with spl_autoload_register:

spl_autoload_register('load_controllers');
spl_autoload_register('load_models');

function load_models($class){
    if( !file_exists("models/$class.php") )
        return false;

    include "models/$class.php";
    return true;
}
function load_controllers($class){
    if( !file_exists("controllers/$class.php") )
        return false;

    include "controllers/$class.php";
    return true;
}
浅语花开 2024-07-11 11:17:04

您还可以使用映射到物理目录的结构化命名约定以编程方式确定类文件的位置。 这就是 Zend 在 Zend Framework 中的做法。 因此,当您调用 Zend_Loader::loadClass("Zend_Db_Table"); 时,它通过下划线分割将类名分解为目录数组,然后 Zend_Loader 类去加载所需的文件。

与所有 Zend 模块一样,我希望您可以仅将加载器与您自己的类一起使用,但我仅将其用作使用 Zend 的 MVC 的站点的一部分。

但是,当您使用任何类型的动态类加载时,都会担心负载下的性能,例如,请参阅 这篇博文将 Zend_Loader 与类文件硬加载进行比较。

除了必须搜索 PHP 包含路径的性能损失之外,它还破坏了操作码缓存。 来自对该帖子的评论:

<块引用>

当使用任何动态类加载器时,APC 无法完全缓存这些文件,因为它不确定将在任何单个请求上加载哪些文件。 通过硬加载文件,APC 可以完整缓存它们。

You can also programmatically determine the location of the class file by using structured naming conventions that map to physical directories. This is how Zend do it in Zend Framework. So when you call Zend_Loader::loadClass("Zend_Db_Table"); it explodes the classname into an array of directories by splitting on the underscores, and then the Zend_Loader class goes to load the required file.

Like all the Zend modules, I would expect you can use just the loader on its own with your own classes but I have only used it as part of a site using Zend's MVC.

But there have been concerns about performance under load when you use any sort of dynamic class loading, for example see this blog post comparing Zend_Loader with hard loading of class files.

As well as the performance penalty of having to search the PHP include path, it defeats opcode caching. From a comment on that post:

When using ANY Dynamic class loader APC can’t cache those files fully as its not sure which files will load on any single request. By hard loading the files APC can cache them in full.

一直在等你来 2024-07-11 11:17:04

如果您的类有一致的命名约定(告诉函数它们在目录树中的位置),则 __autoload 效果很好。 MVC 特别适合这种事情,因为您可以轻松地将类拆分为模型、视图和控制器。

或者,将名称的关联数组保留到类的文件位置,并让 __autoload 查询该数组。

__autoload works well if you have a consistent naming convention for your classes that tell the function where they're found inside the directory tree. MVC lends itself particularly well for this kind of thing because you can easily split the classes into models, views and controllers.

Alternatively, keep an associative array of names to file locations for your class and let __autoload query this array.

静水深流 2024-07-11 11:17:04

到目前为止的建议中,我偏向凯文的,但不一定是绝对的。 我看到有几个与 __autoload 一起使用的不同选项。

  1. 将所有类文件放入一个目录中。 在类之后命名文件,即 classes/User.phpclasses/User.class.php
  2. Kevin 的想法是将模型放入一个目录,将控制器放入另一个目录,等等。如果所有类都很好地适合 MVC 框架,那么效果很好,但有时,事情会变得混乱。
  3. 在类名中包含目录。 例如,名为 Model_User 的类实际上位于 classes/Model/User.php 中。 您的 __autoload 函数会知道将下划线转换为目录分隔符以查找文件。
  4. 只需解析一次整个目录结构即可。 无论是在 __autoload 函数中,还是在定义它的同一个 PHP 文件中,循环遍历 classes 目录的内容并缓存文件所在的位置。 因此,如果您尝试加载 User 类,无论它是在 classes/User.php 还是 classes/Models/User.php< /code> 或 classes/Utility/User.php。 一旦它在 classes 目录中的某个位置找到 User.php,它就会知道当 User 类需要自动加载时要包含哪个文件。

Of the suggestions so far, I'm partial to Kevin's, but it doesn't need to be absolute. I see a couple different options to use with __autoload.

  1. Put all class files into a single directory. Name the file after the class, ie, classes/User.php or classes/User.class.php.
  2. Kevin's idea of putting models into one directory, controllers into another, etc. Works well if all of your classes fit nicely into the MVC framework, but sometimes, things get messy.
  3. Include the directory in the classname. For example, a class called Model_User would actually be located at classes/Model/User.php. Your __autoload function would know to translate an underscore into a directory separator to find the file.
  4. Just parse the whole directory structure once. Either in the __autoload function, or even just in the same PHP file where it's defined, loop over the contents of the classes directory and cache what files are where. So, if you try to load the User class, it doesn't matter if it's in classes/User.php or classes/Models/User.php or classes/Utility/User.php. Once it finds User.php somewhere in the classes directory, it will know what file to include when the User class needs to be autoloaded.
饮湿 2024-07-11 11:17:04

@凯文:

我只是想指出 spl_autoload_register 是 __autoload 的更好替代品,因为您可以定义多个加载器,并且它们不会相互冲突。 如果您还必须包含定义 __autoload 函数的库,这会很方便。

你确定吗? 文档的说法不同:

如果您的代码具有现有的 __autoload 函数,则必须在 __autoload 堆栈上显式注册该函数。 这是因为 spl_autoload_register() 将通过 spl_autoload() 或 spl_autoload_call() 有效地替换 __autoload 函数的引擎缓存。

=> 您还必须显式注册任何库的 __autoload 。 但除此之外,你当然是对的,这个函数是更好的选择。

@Kevin:

I was just trying to point out that spl_autoload_register is a better alternative to __autoload since you can define multiple loaders, and they won't conflict with each other. Handy if you have to include libraries that define an __autoload function as well.

Are you sure? The documentation says differently:

If your code has an existing __autoload function then this function must be explicitly registered on the __autoload stack. This is because spl_autoload_register() will effectively replace the engine cache for the __autoload function by either spl_autoload() or spl_autoload_call().

=> you have to explicitly register any library's __autoload as well. But apart from that you're of course right, this function is the better alternative.

画中仙 2024-07-11 11:17:04

__autoload 可以工作,但仅在 PHP 5 中有效。

__autoload will work, but only in PHP 5.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文