マジックメソッドとは?
PHPには「マジックメソッド」と呼ばれる、特定のタイミングで自動的に呼び出される特殊なメソッド群があります。これらはすべて __(アンダースコア2つ)で始まる名前が付いており、直接呼び出すのではなく、PHPが内部的に特定の状況でトリガーします。
マジックメソッドを使いこなすと、より表現力豊かで柔軟なクラス設計が可能になります。本記事では、よく使われる主要なマジックメソッドを厳選し、実用的なコード例とともに解説します。
__construct と __destruct
最もよく使われるマジックメソッドです。
<?php
class DatabaseConnection
{
private PDO $pdo;
// オブジェクト生成時に自動で呼ばれる
public function __construct(string $dsn, string $user, string $pass)
{
$this->pdo = new PDO($dsn, $user, $pass);
echo "データベースに接続しました\n";
}
// オブジェクトが破棄されるときに自動で呼ばれる
public function __destruct()
{
echo "接続を閉じました\n";
}
}
$db = new DatabaseConnection('sqlite::memory:', '', '');
// スクリプト終了時に自動で __destruct が呼ばれる
__construct はコンストラクタとして初期化処理に使い、__destruct はファイルハンドルやネットワーク接続のクリーンアップに活用できます。
__toString
オブジェクトを文字列として扱いたいときに実装するメソッドです。
<?php
class Money
{
public function __construct(
private int $amount,
private string $currency = 'JPY'
) {}
// echo や文字列結合時に自動で呼ばれる
public function __toString(): string
{
return number_format($this->amount) . ' ' . $this->currency;
}
}
$price = new Money(1980);
echo $price; // 1,980 JPY
echo "価格: {$price}"; // 価格: 1,980 JPY
__toString を実装しておくと、デバッグ時のログ出力やビューへの埋め込みが自然に書けるようになります。
__get と __set
存在しないプロパティや、アクセス不可なプロパティへのアクセス時に呼ばれます。
<?php
class DynamicModel
{
private array $attributes = [];
// 未定義・アクセス不可プロパティの読み取り時
public function __get(string $name): mixed
{
return $this->attributes[$name] ?? null;
}
// 未定義・アクセス不可プロパティへの書き込み時
public function __set(string $name, mixed $value): void
{
$this->attributes[$name] = $value;
}
// isset() や empty() 使用時
public function __isset(string $name): bool
{
return isset($this->attributes[$name]);
}
// unset() 使用時
public function __unset(string $name): void
{
unset($this->attributes[$name]);
}
}
$model = new DynamicModel();
$model->name = 'Alice'; // __set が呼ばれる
$model->email = 'alice@example.com';
echo $model->name; // __get が呼ばれる → Alice
var_dump(isset($model->email)); // __isset が呼ばれる → true
LaravelのEloquentモデルがプロパティのように列にアクセスできるのも、内部でこの仕組みを活用しているためです。
__call と __callStatic
存在しないメソッドが呼ばれたときに動作します。
<?php
class QueryBuilder
{
private array $conditions = [];
// 未定義インスタンスメソッド呼び出し時
public function __call(string $name, array $args): static
{
// whereXxx() 形式を自動処理
if (str_starts_with($name, 'where')) {
$column = lcfirst(substr($name, 5)); // whereEmail → email
$this->conditions[] = "{$column} = ?";
echo "条件追加: {$column} = {$args[0]}\n";
}
return $this;
}
// 未定義静的メソッド呼び出し時
public static function __callStatic(string $name, array $args): string
{
return "静的メソッド [{$name}] が呼ばれました";
}
}
$qb = new QueryBuilder();
$qb->whereEmail('alice@example.com') // 条件追加: email = alice@example.com
->whereName('Alice'); // 条件追加: name = Alice
echo QueryBuilder::someStaticMethod(); // 静的メソッド [someStaticMethod] が呼ばれました
__invoke
オブジェクトを関数のように呼び出したときに実行されます。
<?php
class Multiplier
{
public function __construct(private int $factor) {}
// $obj() のように呼んだとき
public function __invoke(int $value): int
{
return $value * $this->factor;
}
}
$double = new Multiplier(2);
$triple = new Multiplier(3);
echo $double(5); // 10
echo $triple(5); // 15
// callable として配列の map にも渡せる
$numbers = [1, 2, 3, 4, 5];
$result = array_map($double, $numbers);
print_r($result); // [2, 4, 6, 8, 10]
__invoke を実装したオブジェクトは callable として扱えるため、コールバックを求める関数にそのまま渡せます。
__clone
clone キーワードでオブジェクトを複製したときに呼ばれます。
<?php
class UserProfile
{
public array $tags;
public function __construct(
public string $name,
array $tags = []
) {
$this->tags = $tags;
}
// clone 実行後に自動で呼ばれる
public function __clone(): void
{
// 配列は参照ではないのでそのままでも良いが、
// 内部にオブジェクトを持つ場合はここで深いコピーを行う
$this->tags = $this->tags; // 明示的なコピー
echo "{$this->name} のクローンを作成しました\n";
}
}
$original = new UserProfile('Alice', ['admin', 'editor']);
$copy = clone $original; // __clone が呼ばれる
$copy->name = 'Bob';
echo $original->name; // Alice(元は変わらない)
echo $copy->name; // Bob
まとめ
| メソッド | トリガーとなるタイミング |
|---|---|
__construct | new でオブジェクト生成時 |
__destruct | オブジェクト破棄時 |
__toString | 文字列として使われたとき |
__get / __set | 未定義プロパティへのアクセス・代入 |
__isset / __unset | isset() / unset() 使用時 |
__call / __callStatic | 未定義メソッド呼び出し時 |
__invoke | オブジェクトを関数として呼んだとき |
__clone | clone でオブジェクトを複製したとき |
マジックメソッドは便利な反面、使いすぎると可読性が下がることもあります。「なぜそのマジックメソッドが必要なのか」を意識しながら、適切な場面で活用しましょう。LaravelやSymfonyなどの著名なフレームワークのソースコードにも多く登場するので、読み解く際にも役立つ知識です。