Why is require_once so bad to use?

2019-01-01 14:36发布

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 PHP5.

14条回答
听够珍惜
2楼-- · 2019-01-01 14:52

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.
查看更多
浮光初槿花落
3楼-- · 2019-01-01 14:53

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.

查看更多
墨雨无痕
4楼-- · 2019-01-01 14:56

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.

查看更多
闭嘴吧你
5楼-- · 2019-01-01 14:56

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.

查看更多
弹指情弦暗扣
6楼-- · 2019-01-01 15:01

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.

查看更多
梦醉为红颜
7楼-- · 2019-01-01 15:01

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() at Tech Your Universe.

查看更多
登录 后发表回答