- PHP
PHP include・requireの違い|require_onceと相対パス
PHPのincludeとrequireの違いは「ファイルを読み込めなかったときに処理を続けるか、止めるか」です。includeはWarningを出して続行し、requireはFatal errorで停止します。 末尾に _once が付くものは、同じファイルを2回目以降は読み込まない、という動きが加わります。
この記事では、4つの書き方を「読み込めないときの挙動」「重複読み込み」「戻り値」の比較表にまとめたうえで、PHP 8.4.21 で実際に出力したエラーメッセージをそのまま載せています。さらに、つまずく人が多い相対パスの基準(実行元スクリプトのフォルダになる)を再現し、__DIR__ を使ったコピペ用の書き方、include_path、変数スコープ、return で値を受け取る方法、Composer のオートロードとの使い分けまで一気に確認できます。
include・requireの早見表
まずは4つの違いを1枚で確認します。「戻り値」の列は、後半で紹介する「設定ファイルを return で受け取る」書き方で役に立ちます。
| 書き方 | ファイルが無いとき | 処理の続き | 同じファイルの2回目 | 成功時の戻り値 | 読み込めないときの戻り値 |
|---|---|---|---|---|---|
include |
Warning が2行 | 続行する | もう一度読み込む | ファイル内の return の値(無ければ 1) | false |
include_once |
Warning が2行 | 続行する | 読み込まない | 初回は return の値(無ければ 1)、2回目以降は true | false |
require |
Warning のあと Fatal error | 停止する | もう一度読み込む | ファイル内の return の値(無ければ 1) | (停止するため受け取れない) |
require_once |
Warning のあと Fatal error | 停止する | 読み込まない | 初回は return の値(無ければ 1)、2回目以降は true | (停止するため受け取れない) |
次に、やりたいこと別の選び方です。
| やりたいこと | 選ぶ書き方 | コピペ用の1行 |
|---|---|---|
| 関数やクラスを定義したファイルを読み込む | require_once |
require_once __DIR__ . '/lib/functions.php'; |
| 配列を返す設定ファイルを読み込む | require |
$config = require __DIR__ . '/config/config.php'; |
| ヘッダーやフッターなどの部品を何度も差し込む | include または require |
require __DIR__ . '/parts/header.php'; |
| 無くても画面を出したい部品を読み込む | is_file で確認してから include |
if (is_file($p)) { include $p; } |
| Composer を使っているときのクラス読み込み | オートロード | require __DIR__ . '/vendor/autoload.php'; |
| 1つ上のフォルダのファイルを読み込む | __DIR__ か dirname(__DIR__) |
require dirname(__DIR__) . '/config/config.php'; |
迷ったら「パスは __DIR__ から書く」「定義ファイルは require_once」「値を返すファイルは require」の3つを覚えておけば、ほとんどの場面で用が足ります。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「PHP include・requireの違い|require_onceと相対パス」
この記事の確認環境
この記事のコードと実行結果は、次の環境で動かして確認しました。
| 項目 | 内容 |
|---|---|
| PHP | 8.4.21(CLI) |
| OS | Linux |
| Composer | 2.8.12(オートロードの確認に使用) |
| include_path の初期値 | .:/usr/share/php |
実行結果に出てくるファイルパスは長いため、作業フォルダまでの部分を /work に置き換えて掲載しています。それ以外のメッセージは出力そのままです。
また、コマンドラインで実行したため、メッセージの先頭は PHP Warning: や PHP Fatal error: になっています。ブラウザーに表示する設定(display_errors を有効にした状態)で同じスクリプトを動かすと、先頭の「PHP 」が付かない Warning: include(nai.php): Failed to open stream: ... という形で表示されることも確認しました。
includeとrequireの違いをエラーメッセージで確かめる
includeとrequireは、どちらも「別のPHPファイルをその場所に読み込んで実行する」ための書き方です。関数のように見えますが、echo と同じ言語構造なので、括弧なしで require 'file.php'; と書けます。
違いが表に出るのは、指定したファイルが見つからないときです。存在しない nai.php を読み込んで確かめます。
includeはWarningを出して続行する
<?php
// t1_include.php
echo "1: 開始\n";
$r = include 'nai.php';
var_dump($r);
echo "2: 続行できた\n";
実行結果(PHP 8.4.21):
1: 開始
PHP Warning: include(nai.php): Failed to open stream: No such file or directory in /work/t1_include.php on line 3
PHP Warning: include(): Failed opening 'nai.php' for inclusion (include_path='.:/usr/share/php') in /work/t1_include.php on line 3
bool(false)
2: 続行できた
Warning が2行出て、戻り値は false、そのあとの「2: 続行できた」まで実行されています。 終了コードも 0 でした。
2行のWarningにはそれぞれ役割があります。
| 行 | メッセージ | 読み取れること |
|---|---|---|
| 1行目 | Failed to open stream: No such file or directory |
そのパスにファイルが無い |
| 2行目 | Failed opening 'nai.php' for inclusion (include_path='...') |
どのパスを指定したか、include_path がどうなっているか |
2行目の括弧内に include_path の現在の値が出るので、パスの調査ではここが手がかりになります。
requireはFatal errorで停止する
<?php
// t2_require.php
echo "1: 開始\n";
require 'nai.php';
echo "2: ここは表示されない\n";
実行結果(PHP 8.4.21):
1: 開始
PHP Warning: require(nai.php): Failed to open stream: No such file or directory in /work/t2_require.php on line 3
PHP Fatal error: Uncaught Error: Failed opening required 'nai.php' (include_path='.:/usr/share/php') in /work/t2_require.php:3
Stack trace:
#0 {main}
thrown in /work/t2_require.php on line 3
Warning のあとに Fatal error が出て、「2: ここは表示されない」は実行されませんでした。 終了コードは 255 です。
メッセージに Uncaught Error とあるとおり、PHP 8.4 では require で読み込めなかったときに Error が投げられます。そのため try-catch で受け取ることもできます。
<?php
// t2b.php
try {
require 'nai.php';
} catch (Throwable $e) {
echo get_class($e), "\n";
}
echo "到達\n";
実行結果:
PHP Warning: require(nai.php): Failed to open stream: No such file or directory in /work/t2b.php on line 3
Error
到達
受け取れることは確認できましたが、requireを選ぶのは「このファイルが無いなら先へ進めても意味がない」という場面です。catch して続行したいなら、最初から後述の「is_file で確認してから include」の形にするほうが意図が伝わります。
どちらを選ぶかの判断表
| 読み込むファイル | 無かったときに起きてほしいこと | 選ぶもの |
|---|---|---|
| 関数・クラス・定数の定義 | その先で未定義エラーが連鎖するので、すぐ止まってほしい | require_once |
| 設定ファイル(接続先など) | 設定なしで動き続けると原因が見えにくいので、止まってほしい | require |
| ヘッダー・フッターなど画面の骨組み | 骨組みが欠けた画面を出すより、止まって気づけるほうがよい | require |
| お知らせ欄など、無くても成り立つ部品 | その部品だけ省いて画面を出したい | is_file + include |
「無くても成り立つかどうか」で決めると迷いません。成り立たないなら require、成り立つなら include です。
無くてもよい部品は確認してから読み込む
includeは続行できますが、Warningはログや画面に出ます。無いことが想定内の部品なら、先に is_file で確かめると出力がすっきりします。
<?php
// guard.php
$path = __DIR__ . '/parts/sidebar.php';
if (is_file($path)) {
include $path;
} else {
echo "(サイドバーは省略)\n";
}
echo "本文は表示される\n";
実行結果:
(サイドバーは省略)
本文は表示される
@include のようにエラー制御演算子で Warning を抑える書き方もありますが、読み込んだファイルの中で起きた別の Warning まで見えなくなります。is_file で分岐する形のほうが、原因を追いやすくなります。
require_onceとinclude_onceで重複読み込みを防ぐ
_once が付く書き方は、「このファイルは読み込み済みか」をPHPが覚えていて、2回目以降は読み込まないという動きをします。
同じ関数定義を2回読み込むとどうなるか
<?php
// funcs.php
function greet(string $name): string
{
return "こんにちは、{$name}さん";
}
<?php
// t3_twice.php
require 'funcs.php';
require 'funcs.php';
echo greet('田中'), "\n";
実行結果(PHP 8.4.21):
PHP Fatal error: Cannot redeclare function greet() (previously declared in /work/funcs.php:2) in /work/funcs.php on line 2
同じ名前の関数を2回定義することになり、Cannot redeclare function greet() で停止しました。クラスを定義したファイルでも同じで、次のメッセージになります。
PHP Fatal error: Cannot redeclare class Item (previously declared in /work/cls.php:2) in /work/cls.php on line 2
直接2回書くことは少なくても、「A.php と B.php がどちらも funcs.php を読み込み、index.php が A と B の両方を読み込む」という形で起きます。 ファイルが増えるほど気づきにくくなります。
require_onceに変えると1回だけ読み込まれる
<?php
// t4_once.php
var_dump(require_once 'funcs.php');
var_dump(require_once 'funcs.php');
var_dump(include_once 'funcs.php');
echo greet('田中'), "\n";
print_r(array_map('basename', get_included_files()));
実行結果:
int(1)
bool(true)
bool(true)
こんにちは、田中さん
Array
(
[0] => t4_once.php
[1] => funcs.php
)
ここから3つのことがわかります。
- 1回目は 1(return の無いファイルを読み込めたときの値)、2回目以降は true が返る
- require_once と include_once は読み込み済みの記録を共有している(require_once のあとの include_once も読み込まない)
get_included_files()で、読み込み済みファイルの一覧を確認できる
なお、同じファイルを 'funcs.php'、'./funcs.php'、__DIR__ . '/funcs.php' と3通りの書き方で require_once しても、再定義エラーにならず1回だけ読み込まれることも確認しました。パスの書き方が違っても、同じファイルとして扱われます。
includeと_onceを混ぜたときの動き
_once が付かない include / require は、読み込み済みかどうかを見ずに毎回読み込みます。混ぜたときの動きを確かめます。
<?php
// counter.php
echo "counter.php を読み込みました\n";
<?php
// t5.php
include 'counter.php';
include 'counter.php';
include_once 'counter.php';
echo "---\n";
実行結果:
counter.php を読み込みました
counter.php を読み込みました
---
include は2回とも読み込み、3行目の include_once は「読み込み済み」と判断して読み込みませんでした。 順番を逆にして、include_once を2回書いたあとに include を書いた場合は、次のように2回表示されました(1回目の include_once と、最後の include の分です)。
counter.php を読み込みました
counter.php を読み込みました
| 1つのファイルに対する書き方 | 読み込まれる回数 |
|---|---|
| include を3回 | 3回 |
| include_once を3回 | 1回 |
| include 2回 → include_once 1回 | 2回(最後は読み込まれない) |
| include_once 2回 → include 1回 | 2回(最後の include は読み込む) |
_onceを付けるかどうかの決め方
| ファイルの中身 | _once | 理由 |
|---|---|---|
| 関数・クラス・定数の定義 | 付ける | 2回読み込むと再定義エラーになる |
| 初期化処理(セッション開始、接続の用意など) | 付ける | 1回だけ実行したい |
| 配列を return する設定ファイル | 付けない | 2回目は true が返り、配列を受け取れない |
| 繰り返し差し込むHTML部品(一覧の1行など) | 付けない | 毎回出力したい |
「定義は _once、値を返すファイルと繰り返す部品は _once なし」と覚えると整理しやすくなります。
相対パスで読み込めない原因と__DIR__を使った書き方
include / require でいちばん時間を取られやすいのが相対パスです。相対パスは「そのコードが書かれているファイルの場所」ではなく、「実行を始めたときのカレントディレクトリ」を基準に解決されることがあるためです。
パスの解決ルール
PHPマニュアルの include のページ(今回開いて確認しました)では、パスの書き方によって探し方が次のように分かれると説明されています。
| パスの書き方 | 例 | 探す場所 |
|---|---|---|
| 絶対パス | /var/www/app/lib/a.php |
その場所だけ |
./ や ../ で始まる相対パス |
'../config/config.php' |
カレントディレクトリ基準だけ(include_path は使われない) |
| 何も付けない相対パス | 'lib/a.php'、'a.php' |
include_path の各フォルダ → 呼び出し元スクリプトのフォルダ → カレントディレクトリ |
カレントディレクトリは、コマンドラインなら php を実行したときにいたフォルダ、Webサーバー経由なら通常は最初に呼ばれたスクリプトのフォルダです。「読み込む側のファイルがどこにあるか」とは別のものという点がポイントです。
相対パスが実行元基準になる例を再現する
次のフォルダ構成で確かめます。
/work/app
├── config
│ └── config.php
├── lib
│ └── helper.php
└── public
└── index.php
<?php
// app/config/config.php
return [
'app_name' => '備品管理',
'per_page' => 20,
];
<?php
// app/lib/helper.php
// helper.php から見た「隣のフォルダ」のつもりで相対パスを書いている
$config = require '../config/config.php';
echo "helper: ", $config['app_name'], "\n";
<?php
// app/public/index.php
echo "cwd = ", getcwd(), "\n";
require __DIR__ . '/../lib/helper.php';
helper.php は lib フォルダにあるので、../config/config.php は正しそうに見えます。これを、いる場所を変えて実行します。
app/public フォルダで php index.php を実行した結果:
cwd = /work/app/public
helper: 備品管理
app フォルダで php public/index.php を実行した結果:
cwd = /work/app
PHP Warning: require(../config/config.php): Failed to open stream: No such file or directory in /work/app/lib/helper.php on line 3
PHP Fatal error: Uncaught Error: Failed opening required '../config/config.php' (include_path='.:/usr/share/php') in /work/app/lib/helper.php:3
Stack trace:
#0 /work/app/public/index.php(3): require()
#1 {main}
thrown in /work/app/lib/helper.php on line 3
同じコードなのに、実行したフォルダが変わっただけで読み込めなくなりました。 ../config/config.php は helper.php の場所からではなく、カレントディレクトリ(cwd)から数えられているからです。
| 実行したフォルダ(cwd) | ../config/config.php が指す場所 |
結果 |
|---|---|---|
| /work/app/public | /work/app/config/config.php | 読み込める |
| /work/app/lib | /work/app/config/config.php | 読み込める |
| /work/app | /work/config/config.php | Fatal error |
| /work | /config/config.php | Fatal error |
ブラウザーからのアクセスでは動いていたのに、cron やコマンドラインのバッチから同じファイルを使うと読み込めない、というときは、このカレントディレクトリの違いが原因になっていることが多いです。
__DIR__を使うとどこから実行しても読み込める
__DIR__ は、そのコードが書かれているファイルのフォルダを絶対パスで返すマジック定数です。これを先頭に付けると絶対パスになり、カレントディレクトリの影響を受けなくなります。
直す前:
$config = require '../config/config.php';
直した後:
<?php
// app/lib/helper_ok.php
$config = require __DIR__ . '/../config/config.php';
echo "helper_ok: ", $config['app_name'], "\n";
/work から実行した結果と、ルートディレクトリ(/)から実行した結果:
helper_ok: 備品管理
helper_ok: 備品管理
どこから実行しても同じ結果になりました。 書き方のポイントは次の2つです。
__DIR__の末尾には/が付かないので、つなぐ文字列は/から始める(__DIR__ . '/lib/a.php')- 文字列の連結なので、
.(ドット)でつなぐ
dirname(DIR)で上のフォルダを指す
/../ を重ねる代わりに、dirname() で上のフォルダを取り出す書き方もあります。第2引数に、いくつ上がるかを数値で渡せます。
<?php
// app/lib/dn.php(表示用に、作業フォルダまでを /work に置き換えて出力)
echo str_replace(dirname(__DIR__, 2), '/work', __DIR__), "\n";
echo str_replace(dirname(__DIR__, 2), '/work', dirname(__DIR__)), "\n";
echo str_replace(dirname(__DIR__, 2), '/work', dirname(__DIR__, 2)), "\n";
echo str_replace(dirname(__DIR__, 2), '/work', __FILE__), "\n";
$config = require dirname(__DIR__) . '/config/config.php';
echo $config['app_name'], "\n";
実行結果:
/work/app/lib
/work/app
/work
/work/app/lib/dn.php
備品管理
| 書き方 | 返る値(dn.php が /work/app/lib にある場合) |
|---|---|
__FILE__ |
/work/app/lib/dn.php |
__DIR__ |
/work/app/lib |
dirname(__DIR__) |
/work/app |
dirname(__DIR__, 2) |
/work |
__DIR__ と dirname(__FILE__) が同じ値になることも確認しました。以前からあるコードで dirname(__FILE__) を見かけたら、__DIR__ と同じ意味だと読み替えられます。
コピペ用:基準フォルダを定数にまとめる形
ファイルが増えてきたら、入口のファイルで基準フォルダを定数にしておくと、どのファイルからでも同じ書き方で読み込めます。
<?php
// app/public/index.php(入口のファイル)
define('APP_ROOT', dirname(__DIR__)); // /work/app
require_once APP_ROOT . '/lib/functions.php'; // 定義ファイル
$config = require APP_ROOT . '/config/config.php'; // 値を返すファイル
require APP_ROOT . '/parts/header.php'; // 画面の部品
このコードはフォルダ構成に合わせて使う形の例で、個々の行(dirname(__DIR__) の値、__DIR__ を使った require)は上で確認した動きの組み合わせです。
./を付けたときと付けないときの差
「同じフォルダのファイルだから ./ を付けておこう」と書くと、かえって読み込めなくなる場合があります。lib フォルダに sibling.php を置き、同じフォルダの2つのファイルから読み込んでみます。
<?php
// app/lib/a_plain.php
require 'sibling.php'; // ./ も ../ も付けない
<?php
// app/lib/a_dot.php
require './sibling.php'; // ./ を付ける
<?php
// app/public/idx2.php
require __DIR__ . '/../lib/a_plain.php';
require __DIR__ . '/../lib/a_dot.php';
/work から php app/public/idx2.php を実行した結果:
sibling.php を読み込みました
PHP Warning: require(./sibling.php): Failed to open stream: No such file or directory in /work/app/lib/a_dot.php on line 2
PHP Fatal error: Uncaught Error: Failed opening required './sibling.php' (include_path='.:/usr/share/php') in /work/app/lib/a_dot.php:2
Stack trace:
#0 /work/app/public/idx2.php(3): require()
#1 {main}
thrown in /work/app/lib/a_dot.php on line 2
何も付けない 'sibling.php' は「呼び出し元スクリプトのフォルダ」も探してくれるので読み込めましたが、'./sibling.php' はカレントディレクトリだけを見るので読み込めませんでした。
ただし、何も付けない書き方は include_path を先に探すため、同じ名前のファイルが別の場所にあるとそちらが読み込まれます。確実なのはやはり __DIR__ . '/sibling.php' です。
| 書き方 | 探す場所 | 実行場所が変わっても動くか |
|---|---|---|
require 'sibling.php'; |
include_path → 自分のフォルダ → cwd | 動くが、同名ファイルがあると別のものを読む可能性 |
require './sibling.php'; |
cwd だけ | cwd しだい |
require __DIR__ . '/sibling.php'; |
そのファイルのフォルダだけ | 常に動く |
include_pathの確認と設定
include_path は、何も付けない相対パスを探すフォルダの一覧です。php.ini で設定でき、実行中に変更することもできます。区切り文字は Linux や macOS では :、Windows では ; で、定数 PATH_SEPARATOR を使うとどちらでも動く書き方になります。
<?php
// ip.php(vendorlib フォルダに mail.php を置いてある)
echo get_include_path(), "\n";
set_include_path(__DIR__ . '/vendorlib' . PATH_SEPARATOR . get_include_path());
echo str_replace(__DIR__, '/app', get_include_path()), "\n";
require 'mail.php';
var_dump(stream_resolve_include_path('mail.php') !== false);
var_dump(stream_resolve_include_path('nai.php'));
実行結果:
.:/usr/share/php
/app/vendorlib:.:/usr/share/php
vendorlib/mail.php を読み込みました
bool(true)
bool(false)
| 関数・設定 | できること |
|---|---|
get_include_path() |
現在の include_path を文字列で取得 |
set_include_path($path) |
実行中に include_path を変更 |
stream_resolve_include_path($file) |
include_path を使って探し、見つかれば絶対パス、無ければ false |
php.ini の include_path |
既定値を設定 |
初期値の先頭にある . はカレントディレクトリを表します。include_path は「共通ライブラリのフォルダを1か所登録しておく」用途で使われてきましたが、どのファイルが読み込まれるかが設定に左右されるため、新しく書くコードでは __DIR__ からの絶対パスか、後述のオートロードを使う形が読みやすくなります。エラーメッセージの (include_path='...') を読めるようにしておく、という位置づけで十分です。
読み込んだファイルの変数スコープ
読み込まれたファイルのコードは、include / require を書いたその行に貼り付けたかのように動きます。 変数のスコープ(見える範囲)も、書いた場所のものを引き継ぎます。
<?php
// scope_part.php
echo "part: title = {$title}\n";
$fromPart = 'part で作った変数';
<?php
// scope.php
$title = '在庫一覧';
include __DIR__ . '/scope_part.php';
echo "main: {$fromPart}\n";
function render(): void
{
$title = '関数の中のタイトル';
include __DIR__ . '/scope_part.php';
echo "render: {$fromPart}\n";
}
render();
function render2(): void
{
include __DIR__ . '/scope_part.php';
}
render2();
実行結果(PHP 8.4.21):
part: title = 在庫一覧
main: part で作った変数
part: title = 関数の中のタイトル
render: part で作った変数
PHP Warning: Undefined variable $title in /work/scope_part.php on line 2
part: title =
| include を書いた場所 | 読み込まれた側から見える変数 | 読み込まれた側で作った変数 |
|---|---|---|
| ファイルの直下(グローバル) | グローバル変数 | グローバル変数になる |
| 関数の中 | その関数のローカル変数だけ | その関数のローカル変数になる |
render2() では、関数の中に $title が無いので Undefined variable $title の Warning が出ました。グローバルの $title は、関数の中で include したファイルからは見えません。
一方、読み込んだファイルの中で定義した関数やクラスは、どこで include しても全体から使えます(PHPマニュアルの include のページでも、関数とクラスはグローバルスコープになると説明されています)。スコープを引き継ぐのは変数だけです。
この性質を使ったテンプレートの書き方
「関数の中で include すると、その関数のローカル変数だけが見える」性質を使うと、渡した変数だけが見える小さなテンプレート関数を作れます。
<?php
// tpl.php
function render(string $file, array $vars): string
{
extract($vars, EXTR_SKIP);
ob_start();
include $file;
return ob_get_clean();
}
// card.php の中身: <li><?= htmlspecialchars($name) ?>(<?= $qty ?>個)</li>
echo render(__DIR__ . '/card.php', ['name' => 'ノートPC', 'qty' => 3]);
echo render(__DIR__ . '/card.php', ['name' => 'モニター', 'qty' => 5]);
実行結果:
<li>ノートPC(3個)</li>
<li>モニター(5個)</li>
同じ部品を2回差し込みたいので、ここは _once を付けない include を使っています。extract は配列のキーを変数名として展開する関数で、EXTR_SKIP を付けると既にある変数($file など)を上書きしません。
returnで値を受け取る書き方
読み込まれるファイルの中で return を書くと、include / require の式の値として受け取れます。 設定を配列で返すファイルでよく使われる形です。
<?php
// ret.php
$config = require __DIR__ . '/app/config/config.php';
var_dump($config['per_page']);
$r1 = include __DIR__ . '/counter.php'; // return が無いファイル
var_dump($r1);
$r2 = @include __DIR__ . '/nai.php'; // 無いファイル
var_dump($r2);
// 括弧の付け方で結果が変わる例
if ((include __DIR__ . '/counter.php') === 1) {
echo "OK\n";
}
実行結果:
int(20)
counter.php を読み込みました
int(1)
bool(false)
counter.php を読み込みました
OK
| ファイルの状態 | include / require の値 |
|---|---|
return [...] がある |
その配列 |
| return が無い | 1 |
| ファイルが無い(include) | false |
| _once の2回目以降 | true |
設定ファイルを require_once で読み込むと、2回目は配列ではなく true が返るため、$config['per_page'] のようなアクセスが期待どおりに動かなくなります。値を返すファイルには _once を付けない、という先ほどの表はこの動きが理由です。
戻り値を比較するときは全体を括弧で囲む
include は関数ではないので、include('counter.php') === 1 と書くと、('counter.php') === 1 が先に評価されてしまいます。
<?php
// paren.php
if (include('counter.php') === 1) {
echo "OK\n";
} else {
echo "NG\n";
}
実行結果(PHP 8.4.21):
PHP Fatal error: Uncaught ValueError: Path must not be empty in /work/paren.php:2
Stack trace:
#0 {main}
thrown in /work/paren.php on line 2
('counter.php') === 1 は false になり、include false、つまり空のパスを読み込もうとして Path must not be empty になりました。比較するときは (include 'counter.php') === 1 のように、include 全体を括弧で囲みます。 普段から include 'file.php'; と括弧なしで書いておくと、この取り違えに気づきやすくなります。
Composerのオートロードとの使い分け
クラスの数が増えると、ファイルごとに require_once を並べるのが大変になります。そこで使われるのがオートロードです。まだ読み込まれていないクラスが使われた瞬間に、PHPが登録済みの関数を呼び出し、その中で対応するファイルを require します。
仕組みをspl_autoload_registerで確かめる
<?php
// src/Model/Item.php
namespace App\Model;
class Item
{
public function __construct(public string $name) {}
}
<?php
// auto.php
spl_autoload_register(function (string $class): void {
$prefix = 'App\\';
if (!str_starts_with($class, $prefix)) {
return;
}
$file = __DIR__ . '/src/' . str_replace('\\', '/', substr($class, strlen($prefix))) . '.php';
echo "autoload: {$class}\n";
if (is_file($file)) {
require $file;
}
});
$item = new App\Model\Item('ノートPC');
echo $item->name, "\n";
$item2 = new App\Model\Item('モニター'); // 2回目は読み込み済み
echo $item2->name, "\n";
実行結果:
autoload: App\Model\Item
ノートPC
モニター
「autoload:」の行は1回だけです。クラスが定義済みになれば、2回目以降は呼ばれません。オートロードの中でも結局は require が動いている、という関係がわかります。
Composerのautoload.phpを使う
Composer を使うと、この登録処理を自動で作ってくれます。composer.json に名前空間とフォルダの対応(PSR-4)を書きます。
{
"autoload": {
"psr-4": { "App\\": "src/" },
"files": ["funcs.php"]
}
}
composer dump-autoload
<?php
// main.php
require __DIR__ . '/vendor/autoload.php';
use App\Model\Item;
$item = new Item('ノートPC');
echo $item->name, "\n";
echo greet('田中'), "\n";
実行結果(Composer 2.8.12 / PHP 8.4.21):
ノートPC
こんにちは、田中さん
自分で書く require は vendor/autoload.php の1行だけになりました。files に書いたファイル(ここでは関数を定義した funcs.php)は、autoload.php を読み込んだ時点で一緒に読み込まれます。関数はクラスと違ってオートロードの対象にならないため、この files に登録しておく形が定番です。
なお、今回確認したのは自分のフォルダ内のクラスと関数のオートロードです。外部パッケージを composer require で取得して読み込む流れは未実行です(autoload.php を1行読み込めば使える、という点は同じ仕組みです)。
手書きのrequireとオートロードの使い分け表
| 読み込むもの | おすすめの方法 | 補足 |
|---|---|---|
| クラス(名前空間あり) | Composer の PSR-4 オートロード | 使われたときだけ読み込まれる |
| 関数だけのファイル | composer.json の files、または require_once |
関数はオートロードされない |
| 配列を返す設定ファイル | require(_once なし) |
return の値を受け取る |
| HTMLの部品・テンプレート | require / include |
差し込む位置に書く |
| Composer を使わない小さなスクリプト | require_once __DIR__ . '/...' |
ファイル数が少ないうちは十分 |
| WordPress のテーマやプラグインの分割ファイル | require_once(テーマでは get_template_directory() などと組み合わせる書き方が一般的) |
この記事では未実行 |
オートロードを使っていても、設定ファイルやテンプレートの読み込みには require / include が残ります。 どちらか一方に寄せるのではなく、「クラスはオートロード、それ以外は __DIR__ 付きの require」と役割で分けるとすっきりします。
読み込み方を選ぶ判断フロー
ここまでの内容を1つの流れにまとめます。
flowchart TD
A[別のPHPファイルを読み込みたい] --> B{中身はクラス?}
B -- はい --> C{Composerを使っている?}
C -- はい --> D[オートロードに任せる]
C -- いいえ --> E[require_once]
B -- いいえ --> F{関数や定数の定義?}
F -- はい --> E
F -- いいえ --> G{returnで値を返す?}
G -- はい --> H[require で受け取る]
G -- いいえ --> I{無くても画面は成り立つ?}
I -- はい --> J[is_file で確認して include]
I -- いいえ --> K[require]
どの枝に進んでも、パスは __DIR__ から書くことは共通です。
変数でパスを組み立てるときの注意
読み込むファイル名を変数で切り替えるときは、画面から受け取った値をそのままパスに入れず、あらかじめ用意した一覧から選ぶ形にします。受け取った文字列に ../ が含まれていると、想定していないファイルを指してしまうためです。
<?php
// router.php
$pages = [
'home' => __DIR__ . '/pages/home.php',
'items' => __DIR__ . '/pages/items.php',
];
function show(array $pages, string $key): void
{
if (isset($pages[$key])) {
require $pages[$key];
} else {
echo "404: ページが見つかりません\n";
}
}
show($pages, 'items');
show($pages, '../../etc/passwd');
実行結果:
備品一覧ページ
404: ページが見つかりません
一覧にあるキーだけが読み込まれ、それ以外は案内のメッセージになりました。「キーで選び、パスは自分で書いたものだけを使う」形にしておくと安心です。
よくあるつまずきと直し方
| 出たメッセージ・症状 | 原因 | 直し方 |
|---|---|---|
Failed to open stream: No such file or directory |
パスが指す場所にファイルが無い。相対パスが cwd 基準になっている | __DIR__ . '/...' で書く。メッセージ中のパスと getcwd() を見比べる |
Failed opening required '...' (include_path='...') |
require で読み込めず停止した | 上と同じ。括弧内の include_path も確認 |
Cannot redeclare function ...() |
関数を定義したファイルを2回読み込んだ | require_once に変える |
Cannot redeclare class ... |
クラスを定義したファイルを2回読み込んだ | require_once かオートロードに変える |
| 設定を読み込んだ変数が true になる | 値を返すファイルを _once で2回読み込んだ | require(_once なし)にする |
Undefined variable $...(読み込んだファイルの中) |
関数の中で include していて、外の変数が見えない | 引数で渡す、または配列にまとめて渡す |
Path must not be empty |
include('a.php') === 1 のように括弧の位置で式が変わった |
(include 'a.php') === 1 と全体を囲む |
| ブラウザーでは動くがバッチでは読み込めない | 実行時のカレントディレクトリが違う | __DIR__ を基準にする |
読み込みを書くときのチェックリスト
| 確認すること | チェック |
|---|---|
パスは __DIR__ または基準フォルダの定数から書いている |
□ |
__DIR__ の直後の文字列が / から始まっている |
□ |
| 定義ファイル(関数・クラス・定数)は _once 付き | □ |
| 値を返すファイルは _once なしで受け取っている | □ |
無くてもよい部品は is_file で確認している |
□ |
| 変数でファイルを切り替えるときは、用意した一覧から選んでいる | □ |
| クラスが増えてきたらオートロードを検討した | □ |
よくある質問
Q. requireとrequire_onceはどちらを使えばよいですか? A. 関数・クラス・定数を定義したファイルは require_once、配列などを return で返すファイルや、繰り返し差し込む部品は require が向いています。require_once は2回目以降 true を返して読み込まないため、値を受け取りたいファイルには付けない、という使い分けになります。
Q. include 'file.php' と include('file.php') は違いますか?
A. 読み込むだけなら同じ動きです。include は関数ではなく言語構造なので、括弧は省略できます。戻り値を比較するときだけ (include 'file.php') === 1 のように全体を囲む必要があり、括弧なしで書く習慣にしておくと取り違えにくくなります。
Q. DIR と $_SERVER['DOCUMENT_ROOT'] はどちらを使うとよいですか?
A. ファイルの読み込みには __DIR__ が向いています。__DIR__ はそのファイル自身の場所なので、コマンドラインから実行しても同じ値になります。$_SERVER['DOCUMENT_ROOT'] はWebサーバーの設定に由来する値で、コマンドラインでの実行時には期待した値が入らないことが一般に知られています(この記事では未実行)。
Q. require_onceをたくさん書くと遅くなりますか? A. 読み込み済みかどうかの確認が入るぶん、_once なしよりわずかに処理が増えますが、この記事では速度の計測はしていません(未実行)。ファイル数が増えて管理が大変になってきたら、速度よりも保守のしやすさの面で、Composer のオートロードに移すのがおすすめです。
まとめ
- includeは読み込めなくても Warning を出して続行、requireは Fatal error で停止する。「無くても成り立つか」で選ぶ
- _once は同じファイルを1回だけ読み込む。関数・クラスの定義ファイルに付けると
Cannot redeclareを防げる - 戻り値は、return があればその値、無ければ 1、include で読み込めないと false、_once の2回目以降は true
./や../で始まる相対パスはカレントディレクトリ基準になり、実行場所が変わると読み込めなくなる__DIR__ . '/...'と書けば、どこから実行しても同じファイルを指す。上のフォルダはdirname(__DIR__)- 変数のスコープは include を書いた場所を引き継ぐ。関数の中で読み込むと、その関数のローカル変数だけが見える
- クラスは Composer のオートロード、設定ファイルやテンプレートは
__DIR__付きの requireと役割で分ける
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「PHP include・requireの違い|require_onceと相対パス」
株式会社RJCでは、PHPのファイル読み込みやパスの考え方を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。