[PHP]__construct()とは?クラスのコンストラクタを正しく使いこなす方法を徹底解説

PHP

はじめに

オブジェクト指向プログラミングにおいて、「オブジェクトが生成されるタイミングで、初期化のための処理を自動的に実行したい」という場面は非常によくあります。PHPでこの役割を担うのが __construct() という特別なメソッド(マジックメソッド)です。

__construct() は、new ClassName() によってオブジェクトが生成される際に自動的に呼び出される、PHPのオブジェクト指向機能の中でも最も基本的かつ重要な仕組みの一つです。プロパティの初期化、依存オブジェクトの注入、必須パラメータの検証など、オブジェクトが正しい状態で誕生することを保証するために欠かせません。本記事では、その基本的な使い方から、コンストラクタプロパティプロモーションのようなモダンな書き方、継承時の注意点まで、実践的なコード例とともに詳しく解説します。


メソッド概要

項目内容
メソッド名__construct()
種別マジックメソッド(PHPが特別扱いする命名規則のメソッド)
シグネチャpublic function __construct(...$args)
呼び出しタイミングnew ClassName(...) によるオブジェクト生成時に自動的に呼び出される
戻り値なし(値を返すことができない)
対応バージョンPHP 4以降(PHP 5で現在の統一された命名規則になった)
対になるメソッド__destruct()(オブジェクト破棄時に呼ばれる)
特徴的な機能コンストラクタプロパティプロモーション(PHP 8.0以降)

オブジェクト生成の流れ(イメージ図)

  new User('太郎', 30)
              │
              ▼
   ┌───────────────────────┐
   │ 1. メモリ上に空のオブジェクトを確保  │
   │ 2. __construct('太郎', 30) を自動呼び出し │
   │    ここで渡した引数がそのまま         │
   │    コンストラクタの引数になる          │
   └───────────┬───────────┘
              ▼
  class User {
      public function __construct(
          public string $name,
          public int $age
      ) {
          // ここでプロパティの初期化や検証を行う
      }
  }
              │
              ▼
  完全に初期化された$userオブジェクトが返る

ポイントは、__construct() が**new キーワードによって暗黙的に呼び出される**という点です。開発者が明示的に $obj->__construct() と呼び出すことは通常なく(呼び出すこと自体は可能ですが、意図しない再初期化を招くため避けるべきです)、オブジェクト生成の瞬間に必ず1回だけ実行される、いわば「誕生の儀式」のような役割を担っています。


実践サンプル7選

例1:基本的な使い方

<?php

class BasicUser
{
    public string $name;
    public int $age;

    public function __construct(string $name, int $age)
    {
        // コンストラクタ内でプロパティを初期化する
        $this->name = $name;
        $this->age = $age;
    }
}

$user = new BasicUser('太郎', 30);
echo "{$user->name}さん({$user->age}歳)" . PHP_EOL;

例2:コンストラクタプロパティプロモーションによる簡潔な記述(PHP 8.0以降)

<?php

class PromotedUser
{
    /**
     * PHP 8.0以降では、引数にアクセス修飾子を付けるだけで
     * プロパティの宣言と代入が自動的に行われる
     */
    public function __construct(
        public string $name,
        public int $age,
        private string $email = ''
    ) {
    }

    public function getEmail(): string
    {
        return $this->email;
    }
}

$user = new PromotedUser('花子', 25, 'hanako@example.com');
echo "{$user->name}: {$user->getEmail()}" . PHP_EOL;

例3:必須パラメータの検証を行うクラス

<?php

class ValidatedProduct
{
    public function __construct(
        public readonly string $name,
        public readonly float $price
    ) {
        // コンストラクタ内で不正な値を検出し、早期に例外をスローする
        if ($price < 0) {
            throw new InvalidArgumentException('価格は0以上である必要があります');
        }
        if (trim($name) === '') {
            throw new InvalidArgumentException('商品名は必須です');
        }
    }
}

try {
    $product = new ValidatedProduct('ノート', -100);
} catch (InvalidArgumentException $e) {
    echo 'エラー: ' . $e->getMessage() . PHP_EOL;
}

例4:依存オブジェクトを注入するクラス(依存性注入)

<?php

interface LoggerInterface
{
    public function log(string $message): void;
}

class FileLogger implements LoggerInterface
{
    public function log(string $message): void
    {
        echo "[LOG] {$message}" . PHP_EOL;
    }
}

class OrderProcessor
{
    /**
     * コンストラクタで依存オブジェクトを受け取ることで、
     * クラスが必要とする協力オブジェクトを明示的に保証する
     */
    public function __construct(private LoggerInterface $logger)
    {
    }

    public function process(string $orderId): void
    {
        $this->logger->log("注文 {$orderId} を処理しました");
    }
}

$processor = new OrderProcessor(new FileLogger());
$processor->process('ORDER-001');

例5:継承関係における親クラスのコンストラクタ呼び出し

<?php

class Animal
{
    public function __construct(protected string $name)
    {
    }
}

class Dog extends Animal
{
    /**
     * 子クラスで__construct()をオーバーライドする場合、
     * 親クラスの初期化処理を引き継ぐにはparent::__construct()を
     * 明示的に呼び出す必要がある
     */
    public function __construct(string $name, private string $breed)
    {
        parent::__construct($name);
    }

    public function describe(): string
    {
        return "{$this->name}({$this->breed})";
    }
}

$dog = new Dog('ポチ', '柴犬');
echo $dog->describe() . PHP_EOL;

例6:デフォルト値とnull許容型を組み合わせた柔軟な初期化

<?php

class Configuration
{
    private array $options;

    /**
     * デフォルト値を持つ引数を活用し、
     * 一部のパラメータだけを指定した生成を可能にする
     */
    public function __construct(
        private string $environment = 'production',
        ?array $options = null
    ) {
        $this->options = $options ?? ['debug' => false, 'cache' => true];
    }

    public function describe(): string
    {
        return "{$this->environment}: " . json_encode($this->options);
    }
}

$default = new Configuration();
$custom = new Configuration('development', ['debug' => true]);
echo $default->describe() . PHP_EOL;
echo $custom->describe() . PHP_EOL;

例7:staticファクトリーメソッドと組み合わせて生成方法を制限する

<?php

class Money
{
    /**
     * コンストラクタをprivateにすることで、
     * new Money(...)による直接生成を禁止し、
     * 用途に応じたファクトリーメソッド経由の生成を強制する
     */
    private function __construct(
        public readonly int $amountInCents,
        public readonly string $currency
    ) {
    }

    public static function fromYen(int $yen): self
    {
        return new self($yen * 100, 'JPY');
    }

    public static function fromDollars(float $dollars): self
    {
        return new self((int) round($dollars * 100), 'USD');
    }
}

$yenAmount = Money::fromYen(1500);
echo "{$yenAmount->amountInCents} {$yenAmount->currency}" . PHP_EOL;
// new Money(...) は直接呼び出せない(privateのため)

関連機能との比較

機能役割__construct()との違い
__construct()オブジェクト生成時に自動実行される初期化処理本記事の対象。newによって暗黙的に呼ばれる
__destruct()オブジェクト破棄時に自動実行される終了処理__construct()とは逆に、オブジェクトのライフサイクルの終わりを扱う
__invoke()オブジェクトを関数のように呼び出した際に実行されるオブジェクト生成後の呼び出しに関するマジックメソッドで、目的が異なる
staticファクトリーメソッド名前付きの生成方法を提供する__construct()自体を隠蔽・制限しつつ、複数の生成パターンを提供できる
コンストラクタプロパティプロモーション引数宣言とプロパティ代入を統合する記法__construct()の中身を簡潔に書くための、PHP 8.0以降の構文的な仕組み

よくある落とし穴(注意点)

  1. 戻り値を返すことができない __construct() はいかなる値も return することができません(return; のように値なしで早期リターンすることは可能です)。初期化の結果を呼び出し元に伝えたい場合は、プロパティやメソッドを通じてアクセスする設計にする必要があります。
  2. 継承時にparent::__construct()の呼び出しを忘れる 子クラスで __construct() をオーバーライドすると、親クラスの __construct() は自動的には呼び出されません。親クラスの初期化処理も引き継ぎたい場合は、明示的に parent::__construct() を呼び出す必要があります(例5を参照)。
  3. コンストラクタ内で重い処理や失敗しうる処理を行いすぎる データベース接続やファイルI/Oのような、失敗する可能性のある重い処理をコンストラクタ内に詰め込みすぎると、オブジェクト生成そのものが不安定になりがちです。可能であれば、こうした処理は専用のメソッドや初期化フェーズに分離することを検討しましょう。
  4. プロパティプロモーションとデフォルト値のあるプロパティ宣言を重複させる コンストラクタプロパティプロモーション(例2)を使う場合、クラス本体で同名のプロパティを別途宣言する必要はありません。両方を書いてしまうと構文エラーになります。
  5. __construct()を明示的に呼び出してオブジェクトを再初期化しようとする $obj->__construct(...) のように直接呼び出すこと自体は文法上可能ですが、これは通常のオブジェクトのライフサイクルを想定していない使い方であり、意図しない状態の変化を招く可能性があります。再初期化が必要な設計は、専用のメソッドを用意する方が安全です。

まとめ

観点まとめ
何をするメソッドかnewによるオブジェクト生成時に自動的に呼び出される、初期化用のマジックメソッド
主な用途プロパティの初期化、必須パラメータの検証、依存オブジェクトの注入
モダンな書き方PHP 8.0以降のコンストラクタプロパティプロモーションで記述を簡潔化できる
継承時の注意オーバーライド時はparent::__construct()の呼び出しが必要な場合がある
注意点戻り値を返せないこと、重い処理を詰め込みすぎないこと、再初期化目的での直接呼び出しを避けること

__construct() は、PHPのオブジェクト指向プログラミングにおいて、オブジェクトが常に正しい状態で生成されることを保証するための中心的な仕組みです。コンストラクタプロパティプロモーションのようなモダンな構文と組み合わせることで、簡潔かつ堅牢なクラス設計を実現できます。

タイトルとURLをコピーしました