#07 オブジェクト指向プログラミング入門

名前空間とオートローディング——大きなプロジェクトでの整理術

クラス名の衝突問題

プロジェクトが大きくなると、別々のライブラリや自分のコードで同名のクラスが生まれることがあります。

// NG: 2つの User クラスが存在したらどうなる?
require 'vendor/some-library/User.php'; // ライブラリの User
require 'src/User.php';                  // 自分の User

$user = new User(); // どっちの User? → Fatal error

これを解決するのが名前空間(namespace)です。


namespace の基本

クラスファイルの先頭に namespace を宣言することで、クラスを名前空間に属させます。

// src/Models/User.php
namespace App\Models;

class User
{
    public function __construct(
        public readonly int $id,
        public string $name,
        public string $email,
    ) {}
}
// vendor/some-library/User.php
namespace SomeLibrary;

class User
{
    // ライブラリの User
}

同名でも名前空間が違えば衝突しません。

フルパスで参照する

$myUser  = new \App\Models\User(1, '田中太郎', 'tanaka@example.com');
$libUser = new \SomeLibrary\User();

先頭の \ はグローバル名前空間からの絶対パスを意味します。


use でインポートする

フルパスを毎回書くのは面倒です。use でインポートすれば省略できます。

<?php

namespace App\Controllers;

use App\Models\User;        // User として使える
use App\Services\UserService;
use App\Exceptions\NotFoundException;

class UserController
{
    public function __construct(
        private UserService $userService
    ) {}

    public function show(int $id): void
    {
        $user = $this->userService->findById($id);

        if ($user === null) {
            throw new NotFoundException("User #{$id} が見つかりません。");
        }

        echo $user->name;
    }
}

エイリアスを付ける

同名クラスを両方使いたいときはエイリアスで区別します。

use App\Models\User;
use SomeLibrary\User as LibUser; // as でエイリアス

$myUser  = new User(1, '田中太郎', 'tanaka@example.com');
$libUser = new LibUser();

PSR-4 オートローディング規約

require でファイルを手動で読み込むのは面倒です。PSR-4は「名前空間とディレクトリ構造を対応させる」規約で、Composerと組み合わせることで自動的にファイルを読み込めます。

PSR-4の規則

名前空間プレフィックス → ベースディレクトリ
App\                  → src/
クラス(FQCN)ファイルパス
App\Models\Usersrc/Models/User.php
App\Controllers\UserControllersrc/Controllers/UserController.php
App\Services\UserServicesrc/Services/UserService.php

ルール:1ファイル1クラス、ファイル名 = クラス名


Composerでオートローディングを設定する

composer.json にPSR-4の設定を書きます。

{
    "name": "myapp/myapp",
    "require": {
        "php": "^8.2"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}

設定後に以下のコマンドを実行します。

composer dump-autoload

あとはエントリポイントで autoload.php を1回読み込むだけ。use で参照すれば自動的にファイルが読み込まれます。

// index.php(エントリポイント)
require_once __DIR__ . '/vendor/autoload.php';

use App\Controllers\UserController;
use App\Services\UserService;

$controller = new UserController(new UserService());
$controller->show(1);

実際のディレクトリ構造例

myapp/
├── composer.json
├── index.php
├── vendor/         ← Composerが管理(触らない)
└── src/
    ├── Controllers/
    │   └── UserController.php  (namespace App\Controllers)
    ├── Models/
    │   ├── User.php             (namespace App\Models)
    │   └── Post.php             (namespace App\Models)
    ├── Services/
    │   └── UserService.php      (namespace App\Services)
    ├── Repositories/
    │   └── UserRepository.php   (namespace App\Repositories)
    └── Exceptions/
        └── NotFoundException.php (namespace App\Exceptions)

テスト用の名前空間も設定する

テストコードも名前空間を分けて管理します。

{
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    }
}
// tests/Models/UserTest.php
namespace Tests\Models;

use App\Models\User;
use PHPUnit\Framework\TestCase;

class UserTest extends TestCase
{
    public function test_user_can_be_created(): void
    {
        $user = new User(1, '田中太郎', 'tanaka@example.com');
        $this->assertSame('田中太郎', $user->name);
    }
}

よくある間違い

NG: ファイル名とクラス名が違う

// NG: ファイル名は user.php だがクラス名は User
// src/user.php
namespace App\Models;

class User { ... }  // → オートローダーが見つけられない
// OK: ファイル名 = クラス名(大文字小文字も一致)
// src/Models/User.php
namespace App\Models;

class User { ... }

NG: namespace の前に空行や HTML がある

<?php
// OK
namespace App\Models;
<?php

// OK: 空行はあっても問題ない
namespace App\Models;
<?php
echo "hello"; // NG: namespace 宣言より前にコードを書いてはいけない
namespace App\Models;

まとめ

  • namespace でクラスを名前空間に属させ、名前衝突を防ぐ
  • use でクラスをインポートしてフルパスの記述を省略する
  • PSR-4は「名前空間 → ディレクトリ」の対応規約
  • Composerの autoload 設定でファイルの自動読み込みができる
  • 1ファイル1クラス・ファイル名=クラス名が大原則

次回はトレイト——継承を使わずに複数クラスで共通機能を使い回す仕組みを学びます。