为什么 require_once 这么不好用?

发布于 2024-07-07 17:08:56 字数 159 浏览 6 评论 0原文

我读到的关于更好的 PHP 编码实践的所有内容都一直在说,因为速度问题,不要使用 require_once

为什么是这样?

做与 require_once 相同的事情的正确/更好的方法是什么? 如果重要的话,我正在使用 PHP 5。

Everything I read about better PHP coding practices keeps saying don't use require_once because of speed.

Why is this?

What is the proper/better way to do the same thing as require_once? If it matters, I'm using PHP 5.

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

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

发布评论

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

评论(14

好多鱼好多余 2024-07-14 17:08:56

这个帖子让我感到畏缩,因为已经有一个“发布的解决方案”,而且无论出于何种意图和目的,它都是错误的。 让我们列举一下:

  1. 在 PHP 中定义确实非常昂贵。 您可以查找或自己测试,但定义的唯一有效方法PHP 中的全局常量是通过扩展实现的。 (类常量实际上在性能方面相当不错,但这是一个有争议的问题,因为 2)

  2. 如果您适当地使用 require_once() ,即为了包含类,您不需要甚至不需要定义; 只需检查class_exists('Classname')是否存在。 如果您包含的文件包含代码,即您以程序方式使用它,那么绝对没有理由需要 require_once() ; 每次包含您假定要进行子例程调用的文件时。

因此有一段时间,很多人确实使用 class_exists() 方法来进行包含。 我不喜欢它,因为它很丑陋,但他们有充分的理由:在一些较新版本的 PHP 之前,require_once() 效率相当低。 但这已经得到解决,我的观点是,您必须为条件编译的额外字节码以及额外的方法调用,将远远超过任何内部哈希表检查。

现在,承认:这个东西很难测试,因为它只占执行时间的一小部分。

这是你应该考虑的问题:一般来说,包含在 PHP 中是昂贵的,因为每次解释器遇到一个它都必须切换回解析模式,生成操作码,然后跳回来。 如果您有 100 多个包含项,这肯定会对性能产生影响。 使用或不使用 require_once 是一个如此重要的问题,因为它使操作码缓存的生活变得困难。 可以在此处找到对此的解释,但这归结为是这样的:

  • 如果在解析期间,您确切地知道请求的整个生命周期中需要哪些包含文件,require()那些在最开始的文件,操作码缓存将处理所有内容

  • 如果您没有运行操作码缓存,那么您的处境就会很困难。 将所有包含内容内联到一个文件中(不要在开发期间执行此操作,仅在生产中执行此操作)当然可以帮助解析时间,但这样做很痛苦,而且您需要确切地知道在开发过程中将包含哪些内容请求。

  • 自动加载非常方便,但速度很慢,因为每次包含完成时都必须运行自动加载逻辑。 在实践中,我发现为一个请求自动加载多个专用文件不会造成太大问题,但您不应该自动加载所需的所有文件。

  • 如果您可能有 10 个包含(这是一个非常的粗略计算),那么所有这些努力都是不值得的:只需优化您的数据库查询或其他东西。

This thread makes me cringe, because there's already been a "solution posted", and it's, for all intents and purposes, wrong. Let's enumerate:

  1. Defines are really expensive in PHP. You can look it up or test it yourself, but the only efficient way of defining a global constant in PHP is via an extension. (Class constants are actually pretty decent performance wise, but this is a moot point, because of 2)

  2. If you are using require_once() appropriately, that is, for inclusion of classes, you don't even need a define; just check if class_exists('Classname'). If the file you are including contains code, i.e. you're using it in the procedural fashion, there is absolutely no reason that require_once() should be necessary for you; each time you include the file you presume to be making a subroutine call.

So for a while, a lot of people did use the class_exists() method for their inclusions. I don't like it because it's fugly, but they had good reason to: require_once() was pretty inefficient before some of the more recent versions of PHP. But that's been fixed, and it is my contention that the extra bytecode you'd have to compile for the conditional, and the extra method call, would by far overweigh any internal hashtable check.

Now, an admission: this stuff is tough to test for, because it accounts for so little of the execution time.

Here is the question you should be thinking about: includes, as a general rule, are expensive in PHP, because every time the interpreter hits one it has to switch back into parse mode, generate the opcodes, and then jump back. If you have a 100+ includes, this will definitely have a performance impact. The reason why using or not using require_once is such an important question is because it makes life difficult for opcode caches. An explanation for this can be found here, but what this boils down to is that:

  • If during parse time, you know exactly what include files you will need for the entire life of the request, require() those at the very beginning and the opcode cache will handle everything else for you.

  • If you are not running an opcode cache, you're in a hard place. Inlining all of your includes into one file (don't do this during development, only in production) can certainly help parse time, but it's a pain to do, and also, you need to know exactly what you'll be including during the request.

  • Autoload is very convenient, but slow, for the reason that the autoload logic has to be run every time an include is done. In practice, I've found that autoloading several specialized files for one request does not cause too much of a problem, but you should not be autoloading all of the files you will need.

  • If you have maybe 10 includes (this is a very back of the envelope calculation), all this wanking is not worth it: just optimize your database queries or something.

双手揣兜 2024-07-14 17:08:56

require_onceinclude_once 都要求系统保留已包含/需要的内容的日志。 每个 *_once 调用都意味着检查该日志。 那么肯定有一些额外的工作正在那里完成,但足以损害整个应用程序的速度?

...我真的很怀疑...除非您使用非常旧的硬件或经常使用它。

如果您正在执行数千次*_once,您可以以更轻松的方式自己完成这项工作。 对于简单的应用程序,只需确保您只包含一次应该就足够了,但如果您仍然遇到重新定义错误,您可以这样做:

if (!defined('MyIncludeName')) {
    require('MyIncludeName');
    define('MyIncludeName', 1);
}

我个人会坚持使用 * _once 语句,但在愚蠢的百万次基准测试中,您可以看到两者之间的差异:

                php                  hhvm
if defined      0.18587779998779     0.046600103378296
require_once    1.2219581604004      3.2908599376678

使用 require_once 慢 10-100 倍,并且奇怪的是 require_oncehhvm 中看起来速度较慢。 同样,只有当您运行 *_once 数千次时,这才与您的代码相关。


<?php // test.php

$LIMIT = 1000000;

$start = microtime(true);

for ($i=0; $i<$LIMIT; $i++)
    if (!defined('include.php')) {
        require('include.php');
        define('include.php', 1);
    }

$mid = microtime(true);

for ($i=0; $i<$LIMIT; $i++)
    require_once('include.php');

$end = microtime(true);

printf("if defined\t%s\nrequire_once\t%s\n", $mid-$start, $end-$mid);

<?php // include.php

// do nothing.

require_once and include_once both require that the system keeps a log of what's already been included/required. Every *_once call means checking that log. So there's definitely some extra work being done there but enough to detriment the speed of the whole app?

... I really doubt it... Not unless you're on really old hardware or doing it a lot.

If you are doing thousands of *_once, you could do the work yourself in a lighter fashion. For simple apps, just making sure you've only included it once should suffice but if you're still getting redefine errors, you could something like this:

if (!defined('MyIncludeName')) {
    require('MyIncludeName');
    define('MyIncludeName', 1);
}

I'll personally stick with the *_once statements but on silly million-pass benchmark, you can see a difference between the two:

                php                  hhvm
if defined      0.18587779998779     0.046600103378296
require_once    1.2219581604004      3.2908599376678

10-100× slower with require_once and it's curious that require_once is seemingly slower in hhvm. Again, this is only relevant to your code if you're running *_once thousands of times.


<?php // test.php

$LIMIT = 1000000;

$start = microtime(true);

for ($i=0; $i<$LIMIT; $i++)
    if (!defined('include.php')) {
        require('include.php');
        define('include.php', 1);
    }

$mid = microtime(true);

for ($i=0; $i<$LIMIT; $i++)
    require_once('include.php');

$end = microtime(true);

printf("if defined\t%s\nrequire_once\t%s\n", $mid-$start, $end-$mid);

<?php // include.php

// do nothing.
忘你却要生生世世 2024-07-14 17:08:56

我很好奇,查看了 Adam Backstrom 的链接 技术你的宇宙。 本文描述了应该使用 require 而不是 require_once 的原因之一。 然而,他们的说法并不符合我的分析。 我有兴趣看看我可能在哪里错误分析了解决方案。 我使用 PHP 5.2.0 进行比较。

我首先创建了 100 个头文件,这些文件使用 require_once 来包含另一个头文件。 这些文件中的每一个看起来都类似于:

<?php
    // /home/fbarnes/phpperf/hdr0.php
    require_once "../phpperf/common_hdr.php";

?>

我使用快速 Bash hack 创建了这些文件:

for i in /home/fbarnes/phpperf/hdr{00..99}.php; do
    echo "<?php
    // $i" > $i
    cat helper.php >> $i;
done

这样,在包含头文件时,我可以轻松地在使用 require_once 和 require 之间进行切换。 然后我创建了一个 app.php 来加载一百个文件。 这看起来像:

<?php
    // Load all of the php hdrs that were created previously
    for($i=0; $i < 100; $i++)
    {
        require_once "/home/fbarnes/phpperf/hdr$i.php";
    }

    // Read the /proc file system to get some simple stats
    $pid = getmypid();
    $fp = fopen("/proc/$pid/stat", "r");
    $line = fread($fp, 2048);
    $array = split(" ", $line);

    // Write out the statistics; on RedHat 4.5 with kernel 2.6.9
    // 14 is user jiffies; 15 is system jiffies
    $cntr = 0;
    foreach($array as $elem)
    {
        $cntr++;
        echo "stat[$cntr]: $elem\n";
    }
    fclose($fp);
?>

我将 require_once 标头与使用如下标头文件的 require 标头进行了对比:

<?php
    // /home/fbarnes/phpperf/h/hdr0.php
    if(!defined('CommonHdr'))
    {
        require "../phpperf/common_hdr.php";
        define('CommonHdr', 1);
    }
?>

使用 require 与 require_once 运行此标头时,我没有发现太大差异。 事实上,我最初的测试似乎暗示 require_once 稍快一些,但我不一定相信这一点。 我用 10000 个输入文件重复了这个实验。 在这里我确实看到了一致的差异。 我多次运行测试,结果很接近,但使用 require_once 平均使用 30.8 个用户 jiffies 和 72.6 个系统 jiffies; 使用 require 平均使用 39.4 个用户 jiffies 和 72.0 个系统 jiffies。 因此,看起来使用 require_once 的负载会稍微低一些。 然而,挂钟时间略有增加。 10,000 个 require_once 调用平均需要 10.15 秒才能完成,10,000 个 require 调用平均需要 9.84 秒才能完成。

下一步是研究这些差异。 我使用 strace 来分析正在进行的系统调用。

在从 require_once 打开文件之前,会进行以下系统调用:

time(NULL)                              = 1223772434
lstat64("/home", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes/phpperf", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes/phpperf/h", {st_mode=S_IFDIR|0755, st_size=270336, ...}) = 0
lstat64("/home/fbarnes/phpperf/h/hdr0.php", {st_mode=S_IFREG|0644, st_size=88, ...}) = 0
time(NULL)                              = 1223772434
open("/home/fbarnes/phpperf/h/hdr0.php", O_RDONLY) = 3

这与 require:

time(NULL)                              = 1223772905
lstat64("/home", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes/phpperf", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes/phpperf/h", {st_mode=S_IFDIR|0755, st_size=270336, ...}) = 0
lstat64("/home/fbarnes/phpperf/h/hdr0.php", {st_mode=S_IFREG|0644, st_size=146, ...}) = 0
time(NULL)                              = 1223772905
open("/home/fbarnes/phpperf/h/hdr0.php", O_RDONLY) = 3

Tech Your Universe 形成对比,意味着 require_once 应该进行更多的 lstat64 调用。 但是,它们都进行相同数量的 lstat64 调用。 可能的区别是我没有运行 APC 来优化上面的代码。 然而,接下来我比较了整个运行的 strace 输出:

[fbarnes@myhost phpperf]$ wc -l strace_1000r.out strace_1000ro.out
  190709 strace_1000r.out
  210707 strace_1000ro.out
  401416 total

实际上,使用 require_once 时,每个头文件大约有两个以上的系统调用。 一个区别是 require_once 对 time() 函数进行了额外的调用:

[fbarnes@myhost phpperf]$ grep -c time strace_1000r.out strace_1000ro.out
strace_1000r.out:20009
strace_1000ro.out:30008

另一个系统调用是 getcwd():

[fbarnes@myhost phpperf]$ grep -c getcwd strace_1000r.out strace_1000ro.out
strace_1000r.out:5
strace_1000ro.out:10004

调用它是因为我决定在 hdrXXX 文件中引用相对路径。 如果我将其设为绝对引用,那么唯一的区别是代码中进行的额外 time(NULL) 调用:

[fbarnes@myhost phpperf]$ wc -l strace_1000r.out strace_1000ro.out
  190705 strace_1000r.out
  200705 strace_1000ro.out
  391410 total
[fbarnes@myhost phpperf]$ grep -c time strace_1000r.out strace_1000ro.out
strace_1000r.out:20008
strace_1000ro.out:30008

这似乎意味着您可以通过使用绝对路径而不是相对路径来减少系统调用的数量。 除此之外的唯一区别是 time(NULL) 调用,它似乎用于检测代码以比较更快的代码。

另一个注意事项是 APC 优化包有一个名为“apc.include_once_override”的选项,该选项声称它减少了 require_once 和 include_once 调用所进行的系统调用数量(请参阅 PHP 文档)。

I got curious and checked out Adam Backstrom's link to Tech Your Universe. This article describes one of the reasons that require should be used instead of require_once. However, their claims didn't hold up to my analysis. I'd be interested in seeing where I may have misanalysed the solution. I used PHP 5.2.0 for comparisons.

I started out by creating 100 header files that used require_once to include another header file. Each of these files looked something like:

<?php
    // /home/fbarnes/phpperf/hdr0.php
    require_once "../phpperf/common_hdr.php";

?>

I created these using a quick Bash hack:

for i in /home/fbarnes/phpperf/hdr{00..99}.php; do
    echo "<?php
    // $i" > $i
    cat helper.php >> $i;
done

This way I could easily swap between using require_once and require when including the header files. I then created an app.php to load the one hundred files. This looked like:

<?php
    // Load all of the php hdrs that were created previously
    for($i=0; $i < 100; $i++)
    {
        require_once "/home/fbarnes/phpperf/hdr$i.php";
    }

    // Read the /proc file system to get some simple stats
    $pid = getmypid();
    $fp = fopen("/proc/$pid/stat", "r");
    $line = fread($fp, 2048);
    $array = split(" ", $line);

    // Write out the statistics; on RedHat 4.5 with kernel 2.6.9
    // 14 is user jiffies; 15 is system jiffies
    $cntr = 0;
    foreach($array as $elem)
    {
        $cntr++;
        echo "stat[$cntr]: $elem\n";
    }
    fclose($fp);
?>

I contrasted the require_once headers with require headers that used a header file looking like:

<?php
    // /home/fbarnes/phpperf/h/hdr0.php
    if(!defined('CommonHdr'))
    {
        require "../phpperf/common_hdr.php";
        define('CommonHdr', 1);
    }
?>

I didn't find much difference when running this with require vs. require_once. In fact, my initial tests seemed to imply that require_once was slightly faster, but I don't necessarily believe that. I repeated the experiment with 10000 input files. Here I did see a consistent difference. I ran the test multiple times, the results are close but using require_once uses on average 30.8 user jiffies and 72.6 system jiffies; using require uses on average 39.4 user jiffies and 72.0 system jiffies. Therefore, it appears that the load is slightly lower using require_once. However, the wall clock time is slightly increased. The 10,000 require_once calls use 10.15 seconds to complete on average and 10,000 require calls use 9.84 seconds on average.

The next step is to look into these differences. I used strace to analyse the system calls that are being made.

Before opening a file from require_once the following system calls are made:

time(NULL)                              = 1223772434
lstat64("/home", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes/phpperf", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes/phpperf/h", {st_mode=S_IFDIR|0755, st_size=270336, ...}) = 0
lstat64("/home/fbarnes/phpperf/h/hdr0.php", {st_mode=S_IFREG|0644, st_size=88, ...}) = 0
time(NULL)                              = 1223772434
open("/home/fbarnes/phpperf/h/hdr0.php", O_RDONLY) = 3

This contrasts with require:

time(NULL)                              = 1223772905
lstat64("/home", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes/phpperf", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat64("/home/fbarnes/phpperf/h", {st_mode=S_IFDIR|0755, st_size=270336, ...}) = 0
lstat64("/home/fbarnes/phpperf/h/hdr0.php", {st_mode=S_IFREG|0644, st_size=146, ...}) = 0
time(NULL)                              = 1223772905
open("/home/fbarnes/phpperf/h/hdr0.php", O_RDONLY) = 3

Tech Your Universe implies that require_once should make more lstat64 calls. However, they both make the same number of lstat64 calls. Possibly, the difference is that I am not running APC to optimize the code above. However, next I compared the output of strace for the entire runs:

[fbarnes@myhost phpperf]$ wc -l strace_1000r.out strace_1000ro.out
  190709 strace_1000r.out
  210707 strace_1000ro.out
  401416 total

Effectively there are approximately two more system calls per header file when using require_once. One difference is that require_once has an additional call to the time() function:

[fbarnes@myhost phpperf]$ grep -c time strace_1000r.out strace_1000ro.out
strace_1000r.out:20009
strace_1000ro.out:30008

The other system call is getcwd():

[fbarnes@myhost phpperf]$ grep -c getcwd strace_1000r.out strace_1000ro.out
strace_1000r.out:5
strace_1000ro.out:10004

This is called because I decided to relative path referenced in the hdrXXX files. If I make this an absolute reference, then the only difference is the additional time(NULL) call made in the code:

[fbarnes@myhost phpperf]$ wc -l strace_1000r.out strace_1000ro.out
  190705 strace_1000r.out
  200705 strace_1000ro.out
  391410 total
[fbarnes@myhost phpperf]$ grep -c time strace_1000r.out strace_1000ro.out
strace_1000r.out:20008
strace_1000ro.out:30008

This seems to imply that you could reduce the number of system calls by using absolute paths rather than relative paths. The only difference outside of that is the time(NULL) calls which appear to be used for instrumenting the code to compare what is faster.

One other note is that the APC optimization package has an option called "apc.include_once_override" that claims that it reduces the number of system calls made by the require_once and include_once calls (see the PHP documentation).

三人与歌 2024-07-14 17:08:56

您能给我们提供这些编码实践的链接,说明要避免这种情况吗? 就我而言,这完全不是问题。 我自己没有看过源代码,但我想 includeinclude_once 之间的唯一区别是 include_once 添加了filename 到一个数组并每次检查该数组。 保持该数组排序很容易,因此搜索它应该是 O(log n),甚至中大型应用程序也只有几十个包含。

Can you give us any links to these coding practices which say to avoid it? As far as I'm concerned, it's a complete non-issue. I haven't looked at the source code myself, but I'd imagine that the only difference between include and include_once is that include_once adds that filename to an array and checks over the array each time. It'd be easy to keep that array sorted, so searching over it should be O(log n), and even a medium-largish application would only have a couple of dozen includes.

神回复 2024-07-14 17:08:56

更好的方法是使用面向对象的方法并使用 __autoload()

A better way to do things is to use an object-oriented approach and use __autoload().

溺ぐ爱和你が 2024-07-14 17:08:56

PEAR2 wiki(当它存在时)用于列出 放弃所有 require/include 指令以支持自动加载的充分理由,至少对于库代码来说是如此。 当使用 phar 等替代打包模型时,这些会将您束缚在严格的目录结构中即将到来。

更新:由于维基的网络存档版本丑陋得令人眼花缭乱,我复制了以下最令人信服的原因:

  • 为了使用 (PEAR) 包,需要 include_path。 这使得将 PEAR 包捆绑到另一个应用程序中变得困难。
    自己的 include_path,创建包含所需类的单个文件,
    将 PEAR 包移动到 phar 存档,无需大量源代码
    修改。
  • 当顶级 require_once 与条件 require_once 混合时,这可能会导致操作码缓存无法缓存代码,例如
    APC,将与 PHP 6 捆绑在一起。
  • 相对 require_once 要求 include_path 已设置为正确的值,因此无法在没有
    正确的 include_path

The PEAR2 wiki (when it existed) used to list good reasons for abandoning all the require/include directives in favor of autoloading, at least for library code. These tie you down to rigid directory structures when alternative packaging models like phar are on the horizon.

Update: As the web archived version of the wiki is eye-gougingly ugly, I've copied the most compelling reasons below:

  • include_path is required in order to use a (PEAR) package. This makes it difficult to bundle a PEAR package within another application with its
    own include_path, to create a single file containing needed classes,
    to move a PEAR package to a phar archive without extensive source code
    modification.
  • when top-level require_once is mixed with conditional require_once, this can result in code that is uncacheable by opcode caches such as
    APC, which will be bundled with PHP 6.
  • relative require_once requires that include_path already be set up to the correct value, making it impossible to use a package without
    proper include_path
上课铃就是安魂曲 2024-07-14 17:08:56

并不是使用这个函数是不好的。 在整个代码库中,对如何以及何时使用它的理解是不正确的。 我只是为这个可能被误解的概念添加更多背景信息:

人们不应该认为 require_once 是一个缓慢的函数。 您必须以一种或另一种方式包含您的代码。 require_once()require() 的速度不是问题。 这是关于盲目使用它可能导致的性能阻碍警告。 如果在不考虑上下文的​​情况下广泛使用,可能会导致巨大的内存浪费或浪费代码。

我所看到的非常糟糕的是,大型整体框架以所有错误的方式使用 require_once() ,尤其是在复杂的面向对象 (OO) 环境中。

以在许多库中看到的在每个类的顶部使用 require_once() 为例:

require_once("includes/usergroups.php");
require_once("includes/permissions.php");
require_once("includes/revisions.php");
class User{
  // User functions
}

因此 User 类被设计为使用所有其他三个类。 很公平!

但是现在,如果访问者正在浏览网站,甚至没有登录,并且框架会针对每个请求加载: require_once("includes/user.php"); ,该怎么办?

它包括 1+3 个在该特定请求期间不会使用的不必要的类。 这就是臃肿的框架最终每个请求使用 40 MB 而不是 5 MB 或更少的原因。


其他可能被滥用的方式是当一个类被许多其他人重复使用时!
假设您有大约 50 个使用 helper 函数的类。 为了确保加载这些类时 helpers 可用,您会得到:

require_once("includes/helpers.php");
class MyClass{
  // Helper::functions(); // etc..
}

这里本身没有任何问题。 但是,如果一个页面请求恰好包含 15 个类似的类。 您正在运行 require_once 15 次,或者为了获得良好的视觉效果:

require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");

从技术上讲,使用 require_once() 会影响运行该函数 14 次的性能,此外还必须解析那些不必要的行。 仅用 10 个其他频繁使用的类来解决类似的问题,就可以解释 100 多行此类毫无意义的重复代码。

因此,可能值得在应用程序或框架的引导程序中使用 require("includes/helpers.php"); 。 但由于一切都是相对的,因此一切都取决于 helpers 类的权重与使用频率是否值得节省 15-100 行 require_once()。 但是,如果在任何给定请求上不使用 helpers 文件的可能性为零,那么 require 绝对应该位于您的主类中。 在每个类中单独使用 require_once 会造成资源浪费。


require_once 函数在必要时很有用,但不应将其视为在任何地方使用来加载所有类的单一解决方案。

It's not using the function that is bad. It's an incorrect understanding of how and when to use it, in an overall code base. I'll just add a bit more context to that possibly misunderstood notion:

People shouldn't think that require_once is a slow function. You have to include your code one way or another. require_once() vs. require()'s speed isn't the issue. It's about the performance hindering caveats that may results for using it blindly. If used broadly without consideration for context, it can lead to huge memory waste or wasteful code.

What I have seen that's really bad, is when huge monolithic frameworks use require_once() in all the wrong ways, especially in a complex object-oriented (OO) environment.

Take the example of using require_once() at the top of every class as seen in many libraries:

require_once("includes/usergroups.php");
require_once("includes/permissions.php");
require_once("includes/revisions.php");
class User{
  // User functions
}

So the User class is designed to use all three other classes. Fair enough!

But now what if a visitor is browsing the site and not even logged in and the framework loads: require_once("includes/user.php"); for every single request.

It's including 1+3 unnecessary classes it won't ever use during that particular request. This is how bloated frameworks end up using 40 MB per request as opposed to 5 MB or less.


The other ways it can be misused, is when a class is re-used by many others!
Say you have about 50 classes that use helper functions. To make sure helpers are available for those classes when they are loaded, you get:

require_once("includes/helpers.php");
class MyClass{
  // Helper::functions(); // etc..
}

There is nothing wrong here per se. However if one page request happens to include 15 similar classes. You are running require_once 15 times, or for a nice visual:

require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");
require_once("includes/helpers.php");

The use of require_once() technically affects performance for running that function 14 times, on top of having to parse those unnecessary lines. With just 10 other highly used classes with that similar problem, it could account for 100+ lines of such rather pointless repetitive code.

With that, it's probably worth using require("includes/helpers.php"); at the bootstrap of your application or framework, instead. But since everything is relative, it all depends if the weight versus usage frequency of the helpers class is worth saving 15-100 lines of require_once(). But if the probability of not using the helpers file on any given request is none, then require should definitely be in your main class instead. Having require_once in each class separately becomes a waste of resources.


The require_once function is useful when necessary, but it shouldn't be regarded as a monolithic solution to use everywhere for loading all classes.

幸福丶如此 2024-07-14 17:08:56

*_once() 函数 stat 每个父目录以确保您要包含的文件与已包含的文件不同。 这是经济放缓的部分原因。

我建议使用 Siege 等工具进行基准测试。 您可以尝试所有建议的方法并比较响应时间。

有关 require_once() 的更多信息,请访问 技术你的宇宙 url

The *_once() functions stat every parent directory to ensure the file you're including isn't the same as one that's already been included. That's part of the reason for the slowdown.

I recommend using a tool like Siege for benchmarking. You can try all the suggested methodologies and compare response times.

More on require_once() is at Tech Your Universe url.

一身软味 2024-07-14 17:08:56

即使 require_onceinclude_once requireinclude (或任何替代方案)慢可能存在),我们在这里讨论的是最小级别的微优化。 您的时间最好花在优化编写得不好的循环或数据库查询上,而不是担心像 require_once 这样的事情。

现在,有人可能会提出一个论点,说 require_once 允许不良的编码实践,因为您不需要注意保持包含的干净和有组织,但这与函数 它本身,尤其不是它的速度。

显然,出于代码整洁和易于维护的考虑,自动加载更好,但我想澄清的是,这与速度无关。

Even if require_once and include_once are slower than require and include (or whatever alternatives might exist), we're talking about the smallest level of micro-optimization here. Your time is much better spent optimizing that poorly written loop or database query than worrying about something like require_once.

Now, one could make an argument saying that require_once allows for poor coding practices because you don't need to pay attention to keeping your includes clean and organized, but that has nothing to do with the function itself and especially not its speed.

Obviously, autoloading is better for the sake of code cleanliness and ease of maintenance, but I want to make it clear that this has nothing to do with speed.

中性美 2024-07-14 17:08:56

您使用 include、oli 的替代方案和 __autoload() 进行测试; 并使用安装类似APC的东西进行测试。

我怀疑使用常量会加快速度。

You test, using include, oli's alternative and __autoload(); and test it with something like APC installed.

I doubt using constant will speed things up.

纵性 2024-07-14 17:08:56

是的,它比普通的 require() 稍微贵一些。 我认为重点是,如果您可以使代码组织得足够好,不重复包含,则不要使用 *_once() 函数,因为它会节省您一些周期。

但使用 _once() 函数不会终止您的应用程序。 基本上,只是不要用它作为不必组织包含内容的借口。 在某些情况下,使用它仍然是不可避免的,而且没什么大不了的。

Yes, it is slightly more expensive than plain ol' require(). I think the point is if you can keep your code organized enough to not duplicate includes, don't use the *_once() functions, as it will save you some cycles.

But using the _once() functions isn't going to kill your application. Basically, just don't use it as an excuse to not have to organize your includes. In some cases, using it is still unavoidable, and it's not a big deal.

九命猫 2024-07-14 17:08:56

我认为在 PEAR 文档中,建议使用 require、require_once、include 和 include_once。 我确实遵循该准则。 你的申请会更加清晰。

I think in PEAR documentation, there is a recommendation for require, require_once, include and include_once. I do follow that guideline. Your application would be more clear.

反差帅 2024-07-14 17:08:56

这与速度无关。 这是关于优雅地失败。

如果 require_once() 失败,您的脚本就完成了。 没有其他任何处理。 如果您使用 include_once() ,脚本的其余部分将尝试继续渲染,因此您的用户可能对脚本中失败的内容一无所知。

It has nothing to do with speed. It's about failing gracefully.

If require_once() fails, your script is done. Nothing else is processed. If you use include_once() the rest of your script will try to continue to render, so your users potentially would be none-the-wiser to something that has failed in your script.

国粹 2024-07-14 17:08:56

我个人的观点是,使用 require_once (或 include_once)是不好的做法,因为 require_once 会检查您是否已经包含该文件并抑制双重包含文件的错误,从而导致致命错误(例如函数/类/等的重复声明) 。

您应该知道是否需要包含文件。

My personal opinion is that the usage of require_once (or include_once) is bad practice because require_once checks for you if you already included that file and suppress errors of double included files resulting in fatal errors (like duplicate declaration of functions/classes/etc.).

You should know if you need to include a file.

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