require_onceはなぜ使用するのがとても悪いのですか?


143

優れたPHPコーディングプラクティスについて私が読んだことはすべてrequire_once、スピードのために使用しないと言い続けています。

どうしてこれなの?

と同じことをする適切な/より良い方法は何require_onceですか?問題がある場合は、PHP 5を使用しています。


9
この質問はかなり古いもので、回答は疑わしいほど関連しています。参加者からの一連の更新された回答を見るのは素晴らしいことです:)
Purefan

回答:


108

require_onceそしてinclude_once、両方のシステムがすでに含まれているもの/必要なのログを保持する必要があります。すべての*_once呼び出しは、そのログをチェックすることを意味します。では、いくつかの余分な作業が確実に行われていますが、アプリ全体の速度を損なうのに十分ですか?

...私はそれを本当に疑います...あなたが本当に古いハードウェアを使っているか、それをたくさんやっていない限り、そうではありません。

あなたがいる場合している何千ものをやって*_once、あなたは軽量な方法で作業を自分で行うことができます。単純なアプリの場合、1回だけ含めたことを確認するだけ十分ですが、それでも再定義エラーが発生する場合は、次のようにすることができます。

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

私は個人的に*_onceステートメントに固執しますが、愚かなミリオンパスのベンチマークでは、2つの間の違いを見ることができます:

                php                  hhvm
if defined      0.18587779998779     0.046600103378296
require_once    1.2219581604004      3.2908599376678

で10〜100倍遅くなりrequire_oncerequire_onceで一見遅くなるのは不思議ですhhvm。繰り返しますが、これは、*_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.

29
私はあなたのdefined()メソッドが組み込みのルックアップテーブルよりも速いのではないかと思いますが、私はあなたの全体的な点に同意します-確かに問題はありませんか?!
ボビージャック、

1
私はあなたが正しいボビーだとかなり確信していますが、私は_onceよりも定義を主張していません。それは単なるオプションです。コードを解釈するのにかかる時間によって、コードがわずかに遅くなることさえあるかもしれませんが、それでも、内部メソッドがどの程度徹底されているのかはわかりません。重複がないことを確認するために追加の作業を行う場合があります。
Oli

8
もう1つの欠点は、APCがinclude_onceおよびrequire_once呼び出しをキャッシュしないIIRC
dcousineau

3
私は2つのメソッドの非常に基本的なテストを行っただけです-定数 'testinclude'をtrueに単純に定義したファイルを含めて1,000,000回の反復を行いました。最初のテストではrequire_onceを使用し、2番目のテストではif(!defined( 'testinclude'))を使用しました。結果は興味深いものでした。
Travis Weston 2014年

realpathを使用するなどの追加のチェックを実行しないため、defineを使用した場合はより高速になります。まったく同じ2つのことを比較するのではありません。
jgmjgm 2017

150

「投稿されたソリューション」がすでにあり、すべての意図と目的のために間違っているため、このスレッドは私を不快にさせます。列挙してみましょう:

  1. PHPでは、定義は非常に高価です。自分で調べたり、テストたりできますが、PHPでグローバル定数を定義する唯一の効率的な方法は、拡張機能を使用することです。(クラス定数は実際にはかなり賢明なパフォーマンスですが、これは2のため、議論の余地があります)

  2. require_once()適切に使用している場合、つまりクラスを含める場合は、定義する必要さえありません。だけを確認してくださいclass_exists('Classname')。インクルードするファイルにコードが含まれている場合、つまり、手順に従ってファイルを使用している場合、require_once()必要な理由はまったくありません。ファイルをインクルードするたびに、サブルーチン呼び出しを行っていると想定します。

したがって、しばらくの間、多くの人々がこのclass_exists()方法をインクルージョンに使用していました。あいまいであるので、私はそれが好きではありませんがrequire_once()、PHPのより最近のバージョンの前にはかなり非効率的であったという正当な理由がありました。しかし、それは修正されており、条件付きでコンパイルする必要がある追加のバイトコード、および追加のメソッド呼び出しは、内部のハッシュテーブルのチェックをはるかに上回ってしまうというのが私の主張です。

さて、入場料:実行時間のほとんどを占めていないため、これはテストするのが難しいです。

考慮すべき問題は次のとおりです。インクルーダーがヒットするたびに、インタープリターが解析モードに戻り、オペコードを生成してからジャンプする必要があるため、PHPには一般的にインクルードが高価です。100以上のインクルードがある場合、これは間違いなくパフォーマンスに影響します。require_onceを使用するか使用しないかが非常に重要である理由は、オペコードキャッシュの運用を困難にするためです。これの説明はここにありますが、要約すると次のとおりです。

  • 解析時に、リクエストの存続期間全体で必要なインクルードファイルが正確にわかっている場合は、require()最初のファイルとopcodeキャッシュが他のすべてを処理します。

  • オペコードキャッシュを実行していない場合は、難しい場所にいます。すべてのインクルードを1つのファイルにインライン化すると(開発中にこれを行わず、本番環境のみで)、時間の解析に役立ちますが、これは面倒です。また、リクエスト。

  • オートロードは非常に便利ですが、インクルードが行われるたびにオートロードロジックを実行する必要があるため、処理が遅くなります。実際には、1つの要求に対していくつかの特殊なファイルをオートロードしても問題はそれほど多くないことがわかりましたが、必要なすべてのファイルをオートロードするべきではありません。

  • おそらく10個のインクルードがある場合(これはエンベロープ計算の非常に後ろです)、データベースのクエリなどを最適化するだけで、このすばらしさには価値がありません。


14
これは、4歳ではありませんし、ほとんどの部分は、もはや、適用されdefine()require_once()そしてdefined()私のマシンで1-2マイクロそれぞれについてのすべてのテイク。
Daniel Beardsley

72
しかし、ユーザーがページを表示するまでに2マイクロ秒かかります。1年以上のページビューで、ユーザー全体を3秒節約できます。彼らはその時間にコマーシャルの1/10を見ることができました!ユーザーのことを考えてください。マイクロ秒を無駄にしないでください。
Andrew Ensley、2012年

15
皮肉を誰もが知っているように、マイクロ秒は1/1000000秒です。
アンディチェイス

1
@AndrewEnsleyあなたは単にあなたのすべての皮肉と間違われています。PHPはマイクロプロセッサでも動作するという事実については無知です。PCで1マイクロ秒はマイクロプロセッサで数ミリ秒です。では、20個のインクルードファイル、つまりより大きなプロジェクトについてはどうでしょうか。これは、導入された数ミリ秒の遅延の20倍です。そのため、人間が気付くポイントにすでに達しています。そのスクリプトが頻繁に呼び出されると、システムでパフォーマンスの問題が発生します。最適化は冗談ではなく、全世界がPCを好転させるわけではありません。使用中のCPUは1万台です。
John

2
@ジョン。それは元気な冗談でした。悪意はありません。インクルードを最適化する価値がある場合は、先に進んでください。
Andrew Ensley 2017年

66

気になったので、Adam BackstromのTech Your Universeへのリンクを調べました。この記事では、require_onceの代わりにrequireを使用する必要がある理由の1つについて説明します。しかし、彼らの主張は私の分析には及ばなかった。ソリューションを誤って分析した可能性のある場所を確認することに興味があります。比較にはPHP 5.2.0を使用しました。

require_onceを使用して別のヘッダーファイルを含める100個のヘッダーファイルを作成することから始めました。これらのファイルはそれぞれ次のようなものでした。

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

?>

私は簡単なBashハックを使用してこれらを作成しました:

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

このようにして、ヘッダーファイルを含めるときにrequire_onceとrequireの使用を簡単に切り替えることができます。次に、100個のファイルをロードする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ユーザーjiffと72.6システムjiffが使用されます。requireは、平均で39.4ユーザーjiffと72.0システムjiffを使用します。したがって、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を使用すると、実質的にヘッダーファイルごとに約2つのシステムコールが追加されます。違いの1つは、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)呼び出しです。

もう1つの注意点は、APC最適化パッケージに「apc.include_once_override」と呼ばれるオプションがあり、require_onceおよびinclude_once呼び出しによって行われるシステムコールの数が減ると主張していることです(PHPのドキュメントを参照)。


6
そして、そのようなごくわずかな違いを確認するために10,000回実行しなければならない「最適化」は、心配する価値すらありません。プロファイラーを使用して、実際のボトルネックがアプリケーションのどこにあるかを調べます。この質問がボトルネックであるとは思えません。
DGM

1
これが意味することは、それはまったく問題ではないということです。論理的にあなたのためにうまくいくものは何でも使ってください。
バトルButkus

ワットはjiffies
OverCoder 2016

21

それを避けるように言うこれらのコーディング慣行へのリンクを教えていただけますか?私に関する限り、これは完全な問題ではありません。私は、ソースコードを自分自身を見ていないが、私は間の唯一の違いを想像するincludeと、include_onceつまりinclude_once、配列の各時間の経過アレイと小切手にそのファイル名を追加します。その配列を並べ替えておくのは簡単です。そのため、その配列を検索するとO(log n)になるはずです。


1つは、chazzuka.com / blog /?p = 163ですが、実際には「行わない」ことはできませんでしたが、「高価な」ものが多すぎます。そして実際に、すべての付属/必要なファイルが内部の配列(theresのそれを返す関数)に追加され、私は追加するにstrcmpの、アレイと行うことをループに_onceさんが持っていると思います
Uberfuzzy

7

物事を行うためのより良い方法は、オブジェクト指向のアプローチを使用して__autoload()を使用することです。


3
しかし、リンクしたオートロードオブジェクトページの最初の例ではrequire_onceを使用しています
Shabbyrobe

これは買わない。OOが他のパラダイムほど適切に適合しない状況が数多くあります。そのため、__ autoload()を使用して小さな利点を得るように強制するべきではありません。
ボビージャック、

1
オートロードは実際には* _onceよりも時間がかかると思います(必要なものだけを必要とする場合)。
nickf 2008年

その現実にはPHPは、実際には/含める必要とするために適用されるすべての場所を通じてpotentionally不要なチェックを行う-それは、少なくともではない確かに、ではありませんいいえ、自動ロードはまだ何とか含まれており、エラーが失敗する前に、それはPHPのための最後の手段であることが必要であるAFTER THAT(いずれかが定義されている場合)、それは自動ロードを呼ぶだろう... PSを:__autoload()推奨され、それが将来的に廃止されることがあり、あなたが使用する必要がありますspl_autoload_register(...)これらの日... PS2:誤解しないでください、私は時々使用の自動ロード機能を実行します。 )
jave.web 2016

6

悪い機能を使っていません。全体的なコードベースで、それをいつどのように使用するかについての誤った理解です。誤解されている可能性がある概念にもう少しコンテキストを追加します。

人々は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クラスは他の3つのクラスすべてを使用するように設計されています。けっこうだ!

しかし、今度は、訪問者がサイトを閲覧していて、ログインさえしておらず、フレームワークがロードされている場合はどうでしょうかrequire_once("includes/user.php");

これには、その特定のリクエスト中に使用しない1 + 3の不要なクラスが含まれています。これにより、肥大化したフレームワークは、5 MB以下ではなく、リクエストごとに40 MBを使用することになります。


クラスが誤用される他の方法は、クラスが他の多くの人によって再利用されるときです!helper関数を使用するクラスが約50あるとします。helpers読み込まれたときにこれらのクラスで確実に使用できるようにするには、次のようにします。

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

ここ自体は何も問題はありません。ただし、1つのページ要求に15の同様のクラスが含まれている場合。あなたはrequire_once15回走っています、または素晴らしいビジュアルのために:

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関数は必要なときに便利ですが、すべてのクラスをロードするためにどこでも使用する単一のソリューションと見なすべきではありません。


5

PEAR2 wiki(存在する場合)は、少なくともライブラリコードについて、オートロードを優先してすべてのrequire / includeディレクティブを破棄する正当な理由をリストするために使用されていました。これらは、pharのような代替のパッケージ化モデルが地平線上にあるときに、堅固なディレクトリ構造に縛られます。

更新:WikiのWebアーカイブバージョンは非常に醜いので、以下の最も説得力のある理由をコピーしました。

  • (PEAR)パッケージを使用するには、include_pathが必要です。このため、PEARパッケージを別のアプリケーション内に独自のinclude_pathでバンドルしたり、必要なクラスを含む単一のファイルを作成したり、ソースコードを大幅に変更せずにPEARパッケージをpharアーカイブに移動することが難しくなります。
  • トップレベルのrequire_onceが条件付きrequire_onceと混在している場合、これにより、APCなどのオペコードキャッシュ(PHP 6にバンドルされる)でキャッシュできないコードが生成される可能性があります。
  • 相対require_onceには、include_pathがすでに正しい値に設定されている必要があるため、適切なinclude_pathがないとパッケージを使用できません

5

*_once()関数STATあなたが含めているファイルがすでに含まれていたものと同じではないことを確認するために、すべての親ディレクトリを。それが減速の理由の一部です。

ベンチマークにはSiegeのようなツールを使用することをお勧めします。提案されたすべての方法を試し、応答時間を比較できます。

詳しくrequire_once()Tech Your Universeをご覧ください


記事へのポインタをありがとう。require_once()は、ダブルインクルードファイルに対する優れた安全ベルトであり、引き続き使用しますが、クリーンにできるのは素晴らしいことです。
アンディレスター

2

require_onceand include_once require and include(または代替手段が存在する可能性がある場合)より遅い場合でも、ここではマイクロ最適化の最小レベルについて説明します。あなたの時間は、そのような何かを心配するよりも、不十分に書かれたループまたはデータベースクエリを最適化することに費やされた方がはるかに優れていますrequire_once

さて、require_onceインクルードをクリーンで整理された状態に保つことに注意を払う必要はないが、それ自体は関数自体、特に速度とは関係がないため、不適切なコーディングプラクティスを許容すると言って議論することができます。

明らかに、コードのクリーンさとメンテナンスの容易さのためにはオートローディングの方が優れていますが、これは速度とは関係がないことを明確にしたいと思います。



0

はい、それは普通のrequire()よりも少し高価です。インクルードが重複しないようにコードを整理しておくことができれば、* _ once()関数を使用しないでください。これにより、サイクルが短縮されます。

ただし、_once()関数を使用しても、アプリケーションが終了することはありません。基本的に、インクルードを整理する必要がない言い訳として使用しないでください。場合によっては、それを使用することが依然として避けられず、大したことではありません。


-2

PEARのドキュメントには、require、require_once、include、include_onceに関する推奨事項があると思います。私はそのガイドラインに従います。アプリケーションはより明確になります。


いくつかのリファレンスは正しいでしょう。
Peter Mortensen

-3

スピードとは関係ありません。それは優雅に失敗することです。

require_once()が失敗した場合、スクリプトは完了です。他には何も処理されません。include_once()を使用する場合、スクリプトの残りの部分はレンダリングを続行しようとするため、ユーザーはスクリプトで失敗したものに対して、それ以外の場合には賢くないでしょう。


1
必ずしも。エラーハンドラーまたはシャットダウンハンドラーを実際にフックして、ユーザーに素敵なエラーページを提供することができます(ただし、ほとんどの人がそうすることはありません)。開発者として、私はむしろむしろ物事をすぐにエラーにしたいと思います。
エドワードZ.ヤン

1
または、場合によっては、正常に失敗しないこともあります。重要なファイルがrequire()で適切に処理されていない場合は、あきらめて停止することをお勧めします。しかし、それはrequireとincludeですが、質問はrequireとrequire_onceに重点を置いていると思います。
HoboBen 2008年

-4

require_once(またはinclude_once)を使用すると、ファイルが既にインクルードされているかどうかをrequire_onceがチェックし、二重インクルードファイルのエラーを抑制して致命的なエラー(関数/クラスなどの重複宣言など)を抑制するため、 。

ファイルを含める必要があるかどうかを知っておく必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.