はじめに
PHPのデフォルトオートローダー spl_autoload() はクラス名からファイルパスを自動生成しますが、その際にどの拡張子のファイルを検索するかを制御しているのが spl_autoload_extensions() です。
デフォルトでは .inc,.php の2種類が対象ですが、この設定を変更することで .php のみに絞ったり、プロジェクト固有の拡張子を追加したりできます。
関数としてはシンプルですが、spl_autoload() や spl_autoload_register() と密接に連携しているため、オートロード全体の挙動に影響を与える重要な設定ポイントです。
関数の基本情報
| 項目 | 内容 |
|---|---|
| 関数名 | spl_autoload_extensions() |
| 利用可能バージョン | PHP 5.1以降 |
| 所属 | SPL(Standard PHP Library) |
| 戻り値 | string(現在の拡張子設定) |
| 拡張機能 | SPL(デフォルトで有効) |
シグネチャ
spl_autoload_extensions(?string $file_extensions = null): string
パラメータ
| パラメータ | 型 | 説明 |
|---|---|---|
$file_extensions | string|null | 設定する拡張子(カンマ区切り)。nullまたは省略で現在値を返すだけ |
戻り値
変更前後を問わず、現在設定されている拡張子文字列を返します。
// 引数なし → 現在値を取得
$current = spl_autoload_extensions(); // ".inc,.php"
// 引数あり → 設定して現在値(設定後の値)を返す
$new = spl_autoload_extensions('.php'); // ".php"
デフォルト値と設定の影響範囲
| 項目 | 内容 |
|---|---|
| デフォルト値 | .inc,.php |
| 影響する関数 | spl_autoload() のみ |
| 影響しない関数 | spl_autoload_register() で登録したカスタムハンドラ |
| スコープ | プロセス全体(グローバル設定) |
重要:
spl_autoload_extensions()の設定はspl_autoload()というデフォルトハンドラのみに影響します。spl_autoload_register()で登録したカスタムクロージャやクラスメソッドは、この設定を参照しません。独自ハンドラを使う場合は拡張子の扱いを自分で実装します。
spl_autoload() のファイル検索フロー
spl_autoload('App\Model\User') が呼ばれる
↓ クラス名を変換
└─ 小文字化 + \ → / 変換
→ "app/model/user"
↓ include_path の各ディレクトリを走査
→ "/var/www/src", ".", "/usr/share/php"
↓ spl_autoload_extensions() の各拡張子を試行
→ "app/model/user.inc" (.inc を先に試す)
→ "app/model/user.php" (.php を次に試す)
↓ 最初にヒットしたファイルを require
実践サンプル集(PHP 8.x対応)
サンプル1:基本的な取得と設定
<?php
declare(strict_types=1);
// デフォルト値を確認
$default = spl_autoload_extensions();
echo "デフォルト: {$default}\n"; // .inc,.php
// .php のみに絞る(レガシーな .inc を除外)
spl_autoload_extensions('.php');
echo "変更後: " . spl_autoload_extensions() . "\n"; // .php
// 複数の拡張子を設定
spl_autoload_extensions('.php,.class.php,.inc.php');
echo "複数設定: " . spl_autoload_extensions() . "\n"; // .php,.class.php,.inc.php
// 元に戻す
spl_autoload_extensions($default);
echo "復元: " . spl_autoload_extensions() . "\n"; // .inc,.php
実行結果:
デフォルト: .inc,.php
変更後: .php
複数設定: .php,.class.php,.inc.php
復元: .inc,.php
解説: 設定はプロセス全体に影響するグローバル値です。変更前に現在値を退避しておくと、後で確実に元に戻せます。
サンプル2:推奨構成 — .php のみに絞る
現代のPHPプロジェクトでの標準的な設定です。
<?php
declare(strict_types=1);
// .inc はレガシーな拡張子。現代のプロジェクトでは .php のみを推奨
spl_autoload_extensions('.php');
// include_path にソースディレクトリを追加
set_include_path(
get_include_path()
. PATH_SEPARATOR . __DIR__ . '/src'
. PATH_SEPARATOR . __DIR__ . '/lib'
);
// spl_autoload 自体をハンドラとして登録
spl_autoload_register('spl_autoload');
// 検索順序の確認
echo "拡張子設定: " . spl_autoload_extensions() . "\n";
echo "include_path:\n";
foreach (explode(PATH_SEPARATOR, get_include_path()) as $path) {
echo " {$path}\n";
}
/*
* 以降のクラス参照で自動ロードされるファイルパスの例:
*
* クラス名: App\Model\User
* → src/app/model/user.php
* → lib/app/model/user.php
*
* クラス名: Util\StringHelper
* → src/util/stringhelper.php
* → lib/util/stringhelper.php
*/
実行結果:
拡張子設定: .php
include_path:
.
/var/www/project/src
/var/www/project/lib
解説: .inc は古いPHPプロジェクトで使われた拡張子です。ウェブサーバーで直接アクセスされると内部コードが露出するリスクがあるため、現代のプロジェクトでは .php のみを使うことが推奨されます。
サンプル3:設定を安全に管理するラッパークラス
グローバル設定を安全に変更・復元する管理クラスです。
<?php
declare(strict_types=1);
/**
* spl_autoload_extensions のグローバル設定を安全に管理するクラス
*/
class AutoloadExtensionManager
{
private string $current;
private string $original;
public function __construct()
{
$this->original = spl_autoload_extensions();
$this->current = $this->original;
}
/**
* 拡張子一覧を配列で取得
*
* @return list<string>
*/
public function getExtensions(): array
{
return array_map(
'trim',
explode(',', spl_autoload_extensions())
);
}
/**
* 拡張子を追加(重複は無視)
*/
public function add(string $extension): self
{
$extension = '.' . ltrim($extension, '.');
$current = $this->getExtensions();
if (!in_array($extension, $current, true)) {
$current[] = $extension;
$this->apply($current);
}
return $this;
}
/**
* 拡張子を削除
*/
public function remove(string $extension): self
{
$extension = '.' . ltrim($extension, '.');
$filtered = array_filter(
$this->getExtensions(),
fn($e) => $e !== $extension
);
$this->apply(array_values($filtered));
return $this;
}
/**
* 指定した拡張子のみに設定(上書き)
*
* @param list<string> $extensions
*/
public function set(array $extensions): self
{
$normalized = array_map(
fn($e) => '.' . ltrim($e, '.'),
$extensions
);
$this->apply($normalized);
return $this;
}
/**
* 初期値に戻す
*/
public function restore(): self
{
spl_autoload_extensions($this->original);
$this->current = $this->original;
return $this;
}
public function has(string $extension): bool
{
$extension = '.' . ltrim($extension, '.');
return in_array($extension, $this->getExtensions(), true);
}
public function dump(): void
{
echo "現在の拡張子: " . spl_autoload_extensions() . "\n";
echo "初期値: {$this->original}\n";
}
private function apply(array $extensions): void
{
$str = implode(',', $extensions);
$this->current = spl_autoload_extensions($str);
}
}
// --- 使用例 ---
$manager = new AutoloadExtensionManager();
$manager->dump();
echo "\n--- 追加 ---\n";
$manager->add('.class.php')->add('.inc.php')->add('.php'); // .phpは既存のため無視
$manager->dump();
echo "\n--- 削除 ---\n";
$manager->remove('.inc'); // レガシー拡張子を削除
$manager->dump();
echo "\n--- 存在確認 ---\n";
echo ".php: " . ($manager->has('.php') ? 'yes' : 'no') . "\n";
echo ".inc: " . ($manager->has('.inc') ? 'yes' : 'no') . "\n";
echo ".class.php: " . ($manager->has('.class.php') ? 'yes' : 'no') . "\n";
echo "\n--- 上書き設定 ---\n";
$manager->set(['.php']);
$manager->dump();
echo "\n--- 復元 ---\n";
$manager->restore();
$manager->dump();
実行結果:
現在の拡張子: .inc,.php
初期値: .inc,.php
--- 追加 ---
現在の拡張子: .inc,.php,.class.php,.inc.php
初期値: .inc,.php
--- 削除 ---
現在の拡張子: .php,.class.php,.inc.php
初期値: .inc,.php
--- 存在確認 ---
.php: yes
.inc: no
.class.php: yes
--- 上書き設定 ---
現在の拡張子: .php
初期値: .inc,.php
--- 復元 ---
現在の拡張子: .inc,.php
初期値: .inc,.php
解説: 初期値を退避し、restore() で確実に戻せる設計です。テストや一時的な設定変更時に安全に利用できます。
サンプル4:テスト前後の設定保護
テスト実行中だけ設定を変更し、終了後に確実に元に戻すパターンです。
<?php
declare(strict_types=1);
/**
* テスト用:拡張子設定を一時変更するスコープクラス
*/
class ExtensionScope
{
private string $previous;
public function __construct(string $extensions)
{
$this->previous = spl_autoload_extensions();
spl_autoload_extensions($extensions);
echo "[Scope] 設定変更: {$this->previous} → {$extensions}\n";
}
public function restore(): void
{
spl_autoload_extensions($this->previous);
echo "[Scope] 設定復元: " . spl_autoload_extensions() . "\n";
}
public function __destruct()
{
$this->restore();
}
/**
* クロージャのスコープ内だけ設定を変更する
*/
public static function run(string $extensions, callable $callback): mixed
{
$scope = new self($extensions);
try {
return $callback();
} finally {
$scope->restore();
}
}
}
// --- 使用例 ---
echo "テスト前: " . spl_autoload_extensions() . "\n\n";
// スコープ内だけ .php に限定
$result = ExtensionScope::run('.php', function () {
echo "スコープ内: " . spl_autoload_extensions() . "\n";
// この範囲内でのオートロードは .php のみ検索
spl_autoload_register('spl_autoload');
// ... テスト処理 ...
return '処理完了';
});
echo "\nテスト後: " . spl_autoload_extensions() . "\n";
echo "結果: {$result}\n";
実行結果:
テスト前: .inc,.php
[Scope] 設定変更: .inc,.php → .php
スコープ内: .php
[Scope] 設定復元: .inc,.php
[Scope] 設定復元: .inc,.php
テスト後: .inc,.php
結果: 処理完了
解説: try-finally とデストラクタを組み合わせることで、例外が発生しても確実に設定が元に戻ります。グローバル設定を一時的に変える際のベストプラクティスです。
サンプル5:拡張子設定とカスタムハンドラの組み合わせ
spl_autoload_extensions() を参照するカスタムハンドラを実装するパターンです。
<?php
declare(strict_types=1);
/**
* spl_autoload_extensions() の設定を尊重するカスタムローダー
*
* 注意: spl_autoload() 自体はこの設定を参照するが、
* カスタムハンドラは自分で参照ロジックを実装する必要がある
*/
class ExtensionAwareLoader
{
public function __construct(
private readonly string $baseDir
) {}
public function register(): void
{
spl_autoload_register([$this, 'load']);
}
public function load(string $class): bool
{
// spl_autoload_extensions() の設定を動的に参照
$extensions = array_map(
'trim',
explode(',', spl_autoload_extensions())
);
$relativePath = str_replace('\\', DIRECTORY_SEPARATOR, $class);
foreach ($extensions as $ext) {
$file = $this->baseDir . DIRECTORY_SEPARATOR . $relativePath . $ext;
echo "[Loader] 試行: {$file}\n";
if (file_exists($file)) {
require $file;
echo "[Loader] ✅ ロード成功: {$file}\n";
return true;
}
}
return false;
}
}
// --- 設定と登録 ---
spl_autoload_extensions('.php,.class.php');
echo "拡張子: " . spl_autoload_extensions() . "\n\n";
$loader = new ExtensionAwareLoader(__DIR__ . '/src');
$loader->register();
// クラスを参照するとオートロードが発火
// new App\Service\CacheService();
// 拡張子を変更するとローダーの試行順が変わる
spl_autoload_extensions('.class.php,.php');
echo "変更後の拡張子: " . spl_autoload_extensions() . "\n";
// new App\Service\CacheService(); // 今度は .class.php を先に試す
実行結果(ファイルが存在する場合):
拡張子: .php,.class.php
変更後の拡張子: .class.php,.php
解説: カスタムハンドラは spl_autoload_extensions() を自動参照しないため、参照したい場合は explode(',', spl_autoload_extensions()) で自分で読み取ります。こうすることで、拡張子設定の変更がカスタムハンドラにも反映されます。
サンプル6:プロジェクト初期化時の一元設定
アプリケーション起動時に拡張子設定を含むオートロード全体を初期化するパターンです。
<?php
declare(strict_types=1);
/**
* アプリケーションのオートロード設定を一元管理するブートストラップクラス
*/
final class AutoloadBootstrap
{
private static bool $initialized = false;
/**
* オートロードを初期化する(2回目以降は無視)
*
* @param array{
* extensions: list<string>,
* src_dirs: list<string>,
* namespace_map: array<string, string>
* } $config
*/
public static function init(array $config): void
{
if (self::$initialized) {
return;
}
// 1. 拡張子を設定
$extensions = implode(',', array_map(
fn($e) => '.' . ltrim($e, '.'),
$config['extensions']
));
spl_autoload_extensions($extensions);
echo "[Bootstrap] 拡張子: {$extensions}\n";
// 2. include_path を設定
$paths = array_filter($config['src_dirs'], 'is_dir');
if (!empty($paths)) {
set_include_path(
implode(PATH_SEPARATOR, array_merge(['.'], $paths))
);
echo "[Bootstrap] include_path: " . implode(', ', $paths) . "\n";
}
// 3. 名前空間マップベースのカスタムハンドラを登録
$namespaceMap = $config['namespace_map'];
spl_autoload_register(function (string $class) use ($namespaceMap): void {
foreach ($namespaceMap as $prefix => $dir) {
$prefix = rtrim($prefix, '\\') . '\\';
if (!str_starts_with($class, $prefix)) {
continue;
}
$relative = str_replace(
['\\', $prefix],
[DIRECTORY_SEPARATOR, ''],
substr($class, strlen($prefix))
);
foreach (array_map('trim', explode(',', spl_autoload_extensions())) as $ext) {
$file = rtrim($dir, '/') . '/' . $relative . $ext;
if (file_exists($file)) {
require $file;
return;
}
}
}
});
// 4. フォールバックとして spl_autoload を登録
spl_autoload_register('spl_autoload');
self::$initialized = true;
echo "[Bootstrap] 登録ハンドラ数: " . count(spl_autoload_functions()) . "\n";
echo "[Bootstrap] 初期化完了\n";
}
public static function status(): void
{
echo "\n=== Autoload Status ===\n";
echo "拡張子: " . spl_autoload_extensions() . "\n";
echo "include_path: " . get_include_path() . "\n";
echo "ハンドラ数: " . count(spl_autoload_functions()) . "\n";
}
}
// --- アプリケーション起動時に1回だけ呼ぶ ---
AutoloadBootstrap::init([
'extensions' => ['php'], // .php のみ
'src_dirs' => [
__DIR__ . '/src',
__DIR__ . '/lib',
],
'namespace_map' => [
'App\\' => __DIR__ . '/src',
'Tests\\' => __DIR__ . '/tests',
'Vendor\\' => __DIR__ . '/vendor/src',
],
]);
AutoloadBootstrap::status();
// 2回目は無視される
AutoloadBootstrap::init(['extensions' => ['inc'], 'src_dirs' => [], 'namespace_map' => []]);
echo "\n(2回目の init は無視される)\n";
echo "拡張子変わらず: " . spl_autoload_extensions() . "\n";
実行結果:
[Bootstrap] 拡張子: .php
[Bootstrap] include_path: /var/www/project/src, /var/www/project/lib
[Bootstrap] 登録ハンドラ数: 2
[Bootstrap] 初期化完了
=== Autoload Status ===
拡張子: .php
include_path: .:/var/www/project/src:/var/www/project/lib
ハンドラ数: 2
(2回目の init は無視される)
拡張子変わらず: .php
解説: 拡張子・include_path・名前空間マップ・ハンドラ登録を一括で行うブートストラップクラスです。$initialized フラグで二重初期化を防いでいます。実際のフレームワークの起動処理に近いパターンです。
よくある落とし穴
① カスタムハンドラには自動で適用されない
spl_autoload_extensions('.myext');
// ❌ このカスタムハンドラは .myext を知らない
spl_autoload_register(function (string $class): void {
$file = __DIR__ . '/src/' . $class . '.php'; // ← 拡張子をハードコード
if (file_exists($file)) {
require $file;
}
});
// ✅ 動的に参照する
spl_autoload_register(function (string $class): void {
foreach (explode(',', spl_autoload_extensions()) as $ext) {
$file = __DIR__ . '/src/' . $class . trim($ext);
if (file_exists($file)) {
require $file;
return;
}
}
});
② 設定はグローバルで上書きされる
spl_autoload_extensions('.php');
// サードパーティライブラリやプラグインがこれを上書きするかもしれない
some_library_init(); // 内部で spl_autoload_extensions('.php,.inc') を呼ぶかも
// ✅ 自分の設定を最後に適用する、またはカスタムハンドラで拡張子を管理する
③ 拡張子の前のドットを忘れない
// ❌ ドットなし
spl_autoload_extensions('php,inc');
// → ".php" や ".inc" では検索されない
// ✅ ドットあり
spl_autoload_extensions('.php,.inc');
④ 設定はスレッドセーフではない(CLI並列処理)
// CLIでpcntl_fork()やFiberを使う場合、
// グローバル設定の変更が他の処理に影響することがある
// → 可能な限りカスタムハンドラで拡張子をハードコードする
まとめ
| ポイント | 内容 |
|---|---|
| 主な役割 | spl_autoload() が検索するファイル拡張子の設定・取得 |
| デフォルト | .inc,.php |
| 影響範囲 | spl_autoload() のみ(カスタムハンドラには自動適用されない) |
| 現代の推奨 | .php のみに絞る(.inc はレガシー) |
| 安全な変更 | 変更前に退避し try-finally または restore() で元に戻す |
| 複数設定 | カンマ区切りで指定。順序が検索優先順になる |
spl_autoload_extensions() 単体はシンプルな設定関数ですが、spl_autoload() と組み合わせることでオートロードの挙動を細かく制御できます。カスタムハンドラを使う場合は、この設定を動的に参照するかどうかを意識的に設計することが、保守性の高いオートロード実装につながります。
PHP 8.x / 執筆時点の最新安定版にて動作確認済み
