PHP のコードを書いていると、Logger や Client のような「ありがちな名前」のクラスがぶつかってしまうことがあります。この問題を解決するのが namespace(名前空間) と use です。この記事では、名前空間が必要になる理由から、宣言の書き方、完全修飾名と非修飾名の違い、use によるインポートと別名の付け方、そして Composer の PSR-4 オートロードとの関係までを順番に解説します。名前空間を使い始めたときにつまずきやすい「Class not found」の原因もあわせて整理します。
目次
namespace は名前の衝突を防ぐための仕組み
PHP では、同じ名前のクラスを2回定義することはできません。自分のプロジェクトに Logger クラスを作ったあと、外部ライブラリを読み込んだらそちらにも Logger があった、というだけで処理が止まってしまいます。
<?php
class Logger
{
public function write(string $message): void {}
}
// 別のライブラリにも同じ名前のクラスがあった
class Logger
{
public function log(string $message): void {}
}
// Fatal error: Cannot declare class Logger, because the name is already in use
名前空間は、この衝突を避けるためにクラス名の前に付ける「住所」のようなものです。App\Service\Logger と Vendor\Log\Logger のように所属が違えば、末尾の名前が同じでも別のクラスとして共存できます。ファイルシステムのディレクトリと同じ発想で、区切り文字にはバックスラッシュ(\)を使います。
名前空間で区別できるのはクラス(インターフェース・トレイト・enum を含む)と関数、そして定数の3種類です。変数には名前空間の概念がないので、$logger のような変数名は今までどおりです。
namespace の宣言はファイルの先頭に書く
名前空間は namespace キーワードで宣言します。書ける場所は決まっていて、<?php の直後、他のどのコードよりも先でなければなりません。宣言以降、そのファイルで定義したクラス・関数・定数はすべてその名前空間に所属します。
<?php
// このファイルで定義するものは App\Service に所属する
namespace App\Service;
class Logger
{
public function write(string $message): void
{
echo $message . PHP_EOL;
}
}
このクラスの正式な名前は App\Service\Logger になります。App の部分をベンダー名やプロジェクト名にし、その下を機能ごとに区切っていくのが一般的な付け方です。
namespace より前に書けるのは declare 文だけです。declare(strict_types=1); を使う場合はこの順番になります。
<?php
declare(strict_types=1); // namespace より前に書けるのはこれだけ
namespace App\Service;
class Mailer
{
public function send(string $to, string $body): void {}
}
1ファイルに1つの名前空間が基本
文法上は、波括弧を使って1つのファイルに複数の名前空間を書くこともできます。ただしその場合はファイル内のすべての名前空間を波括弧で囲む必要があり、波括弧の外にコードを書くこともできません。
<?php
namespace App\Service {
class Logger {}
}
namespace App\Http {
class Client {}
}
とはいえ、この書き方が役に立つ場面はほとんどありません。次に説明するオートロードは「1ファイル1クラス」を前提にしているため、実務では1ファイルに1つの名前空間・1つのクラスと決めてしまうのが安全です。PSR-12 でも波括弧構文は使わない方針が示されています。
名前の書き方は3種類ある
名前空間を導入すると、クラス名の書き方が「どこから見た名前か」によって3通りに分かれます。ここを押さえておくと、あとで出てくるエラーの原因がすぐ分かるようになります。現在の名前空間が App\Http だとしたときの解釈は次のとおりです。
| 書き方 | 呼び名 | 解決される名前 |
|---|---|---|
Client | 非修飾名 | App\Http\Client(現在の名前空間から探す) |
Auth\Token | 修飾名 | App\Http\Auth\Token(現在の名前空間に連結する) |
\App\Service\Logger | 完全修飾名 | App\Service\Logger(先頭の \ から絶対指定) |
namespace\Client | namespace 演算子 | App\Http\Client(現在の名前空間を明示的に指す) |
ポイントは先頭のバックスラッシュの有無です。付いていれば「ルートからの絶対パス」なので現在の名前空間に一切影響されず、付いていなければ「現在地からの相対パス」として扱われます。ファイルパスの /etc/hosts と etc/hosts の違いと同じだと考えると覚えやすいはずです。
use で別の名前空間のクラスをインポートする
毎回 \App\Service\Logger と完全修飾名で書くのは大変です。そこで use を使って、そのファイルの中だけで使える短い名前を登録します。use は namespace 宣言のすぐあと、クラス定義より前に並べるのが決まりです。
<?php
namespace App\Http;
// App\Service\Logger を、このファイル内では Logger と書けるようにする
use App\Service\Logger;
use App\Service\Mailer;
class Client
{
public function __construct(private Logger $logger) {}
public function request(string $url): void
{
// 完全修飾名で書かずに済む
$this->logger->write('GET ' . $url);
}
}
use に書く名前は常に完全修飾名として扱われるので、先頭にバックスラッシュを付ける必要はありません。use \App\Service\Logger; と書いても動きますが、余計な記号なので付けないのが標準的なスタイルです。
また use の効果はそのファイルの中だけです。読み込んだ別のファイルには引き継がれないので、クラスを使うファイルごとに書く必要があります。
as で別名を付けて衝突を回避する
別々の名前空間から同じ末尾の名前をインポートすると、今度はファイル内で名前がぶつかります。このときは as で好きな別名を付けます。
<?php
namespace App\Http;
use App\Service\Logger;
use Monolog\Logger as MonologLogger; // 別名を付けて区別する
$mine = new Logger(); // App\Service\Logger
$vendor = new MonologLogger(); // Monolog\Logger
別名は「元の名前が長すぎて読みにくいとき」にも使えますが、多用するとどのクラスなのか追いづらくなります。衝突したときの回避策と考えて、必要な場面に限って使うのがおすすめです。
関数と定数は use function / use const
use だけを書いた場合はクラス(インターフェース・トレイト・enum)のインポートになります。名前空間に属する関数や定数をインポートしたいときは、use function と use const を使います。
<?php
namespace App\Support;
const VERSION = '1.0.0';
function format_date(string $date): string
{
return date('Y年n月j日', strtotime($date));
}
<?php
namespace App;
use function App\Support\format_date;
use const App\Support\VERSION;
echo format_date('2026-08-13'); // 2026年8月13日
echo VERSION; // 1.0.0
ここで use App\Support\format_date; と書いてしまうと「format_date というクラスのインポート」と解釈され、関数呼び出しには効きません。エラーにもならず静かに無視されるので、関数のときは function を忘れないよう注意してください。
インポートの書き方を整理すると次のようになります。
| 書き方 | インポートされるもの |
|---|---|
use App\Service\Logger; | クラス・インターフェース・トレイト・enum |
use App\Service\Logger as AppLogger; | 同上(AppLogger という別名で使う) |
use function App\Support\format_date; | 名前空間に属する関数 |
use const App\Support\VERSION; | 名前空間に属する定数 |
use App\Service\{Logger, Mailer}; | 同じ階層のものをまとめて(PHP 7.0 以降) |
最後の波括弧を使う書き方はグループ use 宣言と呼ばれ、PHP 7.0 で追加されました。同じ名前空間から多数のクラスを取り込むときに行数を減らせますが、use を1行ずつ書いたほうが差分が見やすいという理由で、あえて使わないプロジェクトもあります。
名前空間の中から組み込みのクラス・関数を呼ぶとき
名前空間を導入して最初に戸惑うのが、DateTime や Exception といったPHP 組み込みのクラスが急に見つからなくなる現象です。これは仕様どおりの動きで、クラス名はグローバルへのフォールバックをしないためです。
<?php
namespace App\Service;
class Logger
{
public function now(): string
{
// これは App\Service\DateTime を探しに行くので失敗する
$date = new DateTime();
// Fatal error: Uncaught Error: Class "App\Service\DateTime" not found
return $date->format('Y-m-d');
}
}
解決方法は2つあります。先頭にバックスラッシュを付けて完全修飾名にするか、ファイルの先頭で use しておくかです。組み込みクラスはグローバル名前空間(ルート直下)にあるので、use DateTime; と書くだけでインポートできます。
<?php
namespace App\Service;
use DateTime; // グローバルのクラスをインポートしておく
use InvalidArgumentException;
class Logger
{
public function now(): string
{
$date = new DateTime(); // use しているのでこれで動く
$utc = new \DateTimeZone('UTC'); // 完全修飾名で直接書いてもよい
return $date->setTimezone($utc)->format('Y-m-d H:i');
}
public function level(string $name): void
{
if ($name === '') {
// 例外クラスも同じ。use するか \ を付ける
throw new InvalidArgumentException('レベル名が空です');
}
}
}
一方、関数と定数は挙動が違います。名前空間の中で strlen() を呼ぶと、PHP はまず App\Service\strlen() を探し、見つからなければグローバルの strlen() にフォールバックします。そのため、組み込み関数は今までどおり何も付けずに呼べます。定数も同じくフォールバックするので PHP_EOL はそのまま使えます。
<?php
namespace App\Service;
// 名前空間内に同名の関数を定義すると、そちらが優先される
function count(array $items): int
{
return 999;
}
echo count([1, 2, 3]); // 999(App\Service\count が呼ばれる)
echo \count([1, 2, 3]); // 3(先頭の \ でグローバルの count を明示)
echo strlen('abc'); // 3(同名がないのでグローバルにフォールバック)
フォールバックがあるおかげで書きやすい反面、同じ名前の関数を名前空間内に定義すると意図せず上書きされるという落とし穴があります。組み込み関数を確実に呼びたいときは \strlen() のように明示するとよく、ライブラリのコードではこの書き方をよく見かけます。
ディレクトリ構成と PSR-4 オートロード
ここまでで名前の付け方は分かりましたが、まだ「クラスの定義されたファイルをどうやって読み込むか」という問題が残っています。use は名前を短くするだけでファイルを読み込まないため、そのままでは Class not found になります。
これを自動化するのが オートロードで、現在の標準は Composer が実装している PSR-4 です。PSR-4 は「名前空間の階層とディレクトリの階層を一致させ、クラス名とファイル名も一致させる」という取り決めで、この規則に従っていれば必要なファイルが自動的に読み込まれます。
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
JSON の文字列ではバックスラッシュがエスケープ文字なので、App\\ のように2つ重ねて書く点に注意してください。この設定は「名前空間 App\ で始まるものは src/ ディレクトリの下にある」という意味になり、次のような対応になります。
| クラスの完全修飾名 | 読み込まれるファイル |
|---|---|
App\Service\Logger | src/Service/Logger.php |
App\Http\Client | src/Http/Client.php |
App\Model\User\Profile | src/Model/User/Profile.php |
先頭の App\ が src/ に置き換わり、残りの区切りがそのままディレクトリの区切りになる、と読めば分かりやすいはずです。設定を書いたら composer dump-autoload を実行してオートローダーを生成し、エントリーポイントで vendor/autoload.php を読み込みます。
<?php
// これ1行で、必要なクラスのファイルが自動的に読み込まれるようになる
require __DIR__ . '/vendor/autoload.php';
use App\Http\Client;
use App\Service\Logger;
$client = new Client(new Logger()); // require を書かなくても動く
$client->request('https://example.com');
クラスが初めて必要になった瞬間にオートローダーが呼ばれ、上の対応表に従ってファイルを探して読み込みます。require をクラスごとに書く必要がなくなるのが、名前空間と PSR-4 を組み合わせる最大の利点です。
なお、関数を定義したファイルはクラスと違って「呼ばれた瞬間に探す」仕組みがありません。先ほどの helpers.php のようなファイルは files で指定し、毎回読み込ませます。
{
"autoload": {
"psr-4": {
"App\\": "src/"
},
"files": [
"src/Support/helpers.php"
]
}
}
Class not found が出るときに確認すること
名前空間まわりのトラブルは、ほとんどが Class "..." not found という形で現れます。エラーメッセージに出ているクラス名をまず読むことが解決の近道です。PHP が探しに行った完全修飾名がそのまま表示されているので、自分の意図とどこがずれているかが分かります。
use はファイルを読み込まない
もっとも多い勘違いがこれです。use は他言語の import と見た目が似ていますが、やっているのは「長い名前に短い別名を割り当てる」ことだけで、ファイルの読み込みは一切行いません。オートロードを設定していないプロジェクトでは、use を書いたうえで require も必要です。
<?php
// オートロードを使わない場合はファイルの読み込みが別途必要
require __DIR__ . '/src/Service/Logger.php';
use App\Service\Logger;
$logger = new Logger();
Composer を使っているのに読み込まれない場合は、vendor/autoload.php を require し忘れていないか、composer.json の autoload を追加・変更したあとに composer dump-autoload を実行したかを確認してください。オートローダーの対応表は生成時点の設定で作られるため、設定を書き換えただけでは反映されません。
名前空間とディレクトリ・ファイル名がずれている
PSR-4 は名前とパスの一致が前提なので、少しでもずれていると見つかりません。src/Service/Logger.php に置いたファイルの中で namespace App\Services;(複数形)と書いてしまう、といったタイプミスが典型例です。
さらに厄介なのが大文字と小文字の違いです。PHP のクラス名は大文字小文字を区別しませんが、ファイルシステムは Linux では区別します。src/service/Logger.php のようにディレクトリ名を小文字で作ってしまうと、macOS や Windows の開発環境では動くのに、本番の Linux サーバーにデプロイした途端 Class not found になります。名前空間の階層と実際のディレクトリ名・ファイル名を、1文字ずつ見比べて一致させてください。
クラス名の前に \ が必要な場所で省略している
先ほど説明したとおり、名前空間の中で new Exception() や new PDO() と書くと、現在の名前空間の下を探しに行きます。エラーメッセージが Class "App\Service\PDO" not found のように自分の名前空間から始まっているなら、これが原因です。use PDO; を追加するか、new \PDO(...) と書けば解決します。
クラス名を文字列で扱うときも同じ注意が必要です。'App\Service\Logger' のような文字列は、書かれたとおりの完全修飾名として解釈され、use の別名は効きません。文字列でクラス名を渡すときは、Logger::class を使うと use が解決した完全修飾名を得られるので安全です。
<?php
namespace App;
use App\Service\Logger;
echo Logger::class; // App\Service\Logger(完全修飾名が得られる)
// ダブルクォートの中では \n などがエスケープとして解釈されてしまう
$name = 'App\Service\Logger'; // シングルクォートなら安全
クラス名を文字列で書く場合はシングルクォートを使ってください。ダブルクォートだと \n や \t がエスケープシーケンスとして解釈され、名前が壊れることがあります。
namespace 宣言より前に出力やコードがある
クラスが見つからない以前に、そもそもファイルが読めていないケースもあります。namespace は declare を除くすべてのコードより前に書く必要があるため、位置を間違えると次のエラーで止まります。
<?php
require __DIR__ . '/vendor/autoload.php'; // ← namespace より前に書いている
namespace App;
// Fatal error: Namespace declaration statement has to be the very first
// statement or after any declare call in the script
見落としやすいのが、<?php の前に紛れ込んだ空行や BOM(バイトオーダーマーク)です。1文字でも出力が発生すると同じエラーになるので、エディタの文字コード設定を「BOM なしの UTF-8」にしておきましょう。クラス定義だけのファイルでは、末尾の ?> を書かないのが慣例です。閉じタグの後ろに改行が入るだけで意図しない出力になるためで、PSR-12 でも省略が求められています。
まとめ
namespace は、クラス・関数・定数に「住所」を付けて名前の衝突を防ぐ仕組みです。宣言は declare を除いてファイルの先頭に置き、1ファイル1名前空間・1クラスにしておくとオートロードと相性よく扱えます。名前の書き方は、現在の名前空間から探す非修飾名、連結される修飾名、先頭の \ で絶対指定する完全修飾名の3種類があり、この違いが分かっていればエラーの原因もすぐ特定できます。use は長い名前に短い別名を割り当てるもので、必要なら as で名前を変えられ、関数は use function、定数は use const を使います。名前空間の中ではクラスはグローバルへフォールバックしないので、DateTime や Exception は use するか \ を付けて呼び出してください。そして use はファイルを読み込まないため、実際の読み込みは composer.json の PSR-4 設定によるオートロードに任せるのが定石です。