あっぽログ
← 記事一覧に戻る

PHPのマジックメソッドを使いこなす:__construct・__toString・__getなど主要メソッドを徹底解説

マジックメソッドとは?

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

まとめ

メソッドトリガーとなるタイミング
__constructnew でオブジェクト生成時
__destructオブジェクト破棄時
__toString文字列として使われたとき
__get / __set未定義プロパティへのアクセス・代入
__isset / __unsetisset() / unset() 使用時
__call / __callStatic未定義メソッド呼び出し時
__invokeオブジェクトを関数として呼んだとき
__cloneclone でオブジェクトを複製したとき

マジックメソッドは便利な反面、使いすぎると可読性が下がることもあります。「なぜそのマジックメソッドが必要なのか」を意識しながら、適切な場面で活用しましょう。LaravelやSymfonyなどの著名なフレームワークのソースコードにも多く登場するので、読み解く際にも役立つ知識です。

← 記事一覧に戻る