| この記事の要点 |
|
基本
class User
{
public string $name;
public int $age;
public function __construct(string $name, int $age = 0)
{
$this->name = $name;
$this->age = $age;
}
}
$u = new User('田中', 30);
echo $u->name; // 田中
$u2 = new User('鈴木'); // 既定値を使う
$u3 = new User(age: 25, name: '佐藤'); // 名前付き引数(PHP 8 以降)
PHP 4 時代の「クラス名と同じ名前のメソッド」は PHP 8 で削除されました。__construct 以外はただのメソッドとして扱われます。
コンストラクタプロパティ昇格(PHP 8)
// 従来: 同じ名前を 3 回書く
class User
{
private string $name;
public function __construct(string $name) { $this->name = $name; }
}
// PHP 8: 引数に可視性を書くだけでプロパティになる
class User
{
public function __construct(
private string $name,
private int $age = 0,
) {}
}
// readonly を付ければ生成後に変更できない(PHP 8.1 以降)
class Point
{
public function __construct(
public readonly int $x,
public readonly int $y,
) {}
}
$p = new Point(1, 2);
echo $p->x; // 1
// $p->x = 5; // Error: Cannot modify readonly property Point::$x
値を保持するだけのクラスは、この書き方で数行に収まります。値オブジェクトには readonly を付けて、あとから壊されないようにしておくと安全です。
オーバーロードはできない
class Point
{
public function __construct(int $x, int $y) {}
public function __construct(string $text) {} // Fatal error: Cannot redeclare Point::__construct()
}
方法 1: 既定値と可変長引数
class Point
{
public function __construct(
public readonly int $x = 0,
public readonly int $y = 0,
) {}
}
new Point(); // (0, 0)
new Point(1); // (1, 0)
new Point(y: 5); // (0, 5)
方法 2: static のファクトリメソッド(推奨)
class Point
{
private function __construct(
public readonly int $x,
public readonly int $y,
) {}
public static function of(int $x, int $y): static
{
return new static($x, $y);
}
public static function fromText(string $text): static
{
[$x, $y] = array_map('intval', explode(',', $text));
return new static($x, $y);
}
public static function origin(): static
{
return new static(0, 0);
}
}
$a = Point::of(1, 2);
$b = Point::fromText('3,4');
$c = Point::origin();
作り方ごとに名前が付くので、呼び出し側を読むだけで意図が分かります。__construct を private にすれば、他の作り方を封じられます。
継承したときは parent を呼ぶ
class Base
{
public function __construct(protected string $name) {}
}
class Admin extends Base
{
public function __construct(string $name, private int $level)
{
parent::__construct($name); // ← これを書かないと $name が設定されない
}
}
class Broken extends Base
{
public function __construct(private int $level) {} // parent を呼び忘れ
}
$b = new Broken(3);
// $b->name にアクセスすると
// Error: Typed property Base::$name must not be accessed before initialization
子クラスで __construct を定義しなければ親のものがそのまま使われます。定義した瞬間に親の初期化は行われなくなるため、呼び忘れが最頻出のバグです。
デストラクタ
class FileHandle
{
private $fp;
public function __construct(string $path)
{
$this->fp = fopen($path, 'r');
}
public function __destruct()
{
if (is_resource($this->fp)) {
fclose($this->fp);
}
}
}
$h = new FileHandle('data.txt');
unset($h); // 参照が 0 になった時点で __destruct が呼ばれる
__destruct は参照がすべて無くなったときか、スクリプト終了時に呼ばれます。ただし呼ばれる順序は保証されず、循環参照があると遅れます。確実に閉じたい資源は、明示的な close() メソッドを用意してください。
コンストラクタに書かないほうがよいこと
// 悪い例: 生成しただけで通信・DB 接続が走る
class ReportService
{
private array $rates;
public function __construct()
{
$this->rates = (new TaxApi())->fetchAll(); // テストで差し替えられない
}
}
// 良い例: 必要なものを受け取るだけ
class ReportService
{
public function __construct(private TaxRateProvider $rates) {}
public function build(): string
{
return $this->rates->get('JP'); // 実際に使うときに呼ぶ
}
}
| コンストラクタで | やること |
|---|---|
| やってよい | 引数の代入、値の検証、既定値の設定 |
| 避ける | DB 接続・HTTP 通信・ファイル読み込み・重い計算 |
| 避ける | $this を外部に渡す(初期化途中の状態が漏れる) |
値の検証を入れる
class Age
{
public function __construct(public readonly int $value)
{
if ($value < 0 || $value > 150) {
throw new InvalidArgumentException("年齢が範囲外です: {$value}");
}
}
}
new Age(30); // OK
new Age(-1); // InvalidArgumentException
コンストラクタで検証すると、そのクラスのインスタンスは常に正しい値を持つと保証できます。以降の処理で毎回チェックする必要がなくなります。
複製したときの挙動(__clone)
class Order
{
public function __construct(
public string $code,
public Address $address,
) {}
}
$a = new Order('A-1', new Address('東京'));
$b = clone $a;
$b->address->city = '大阪';
echo $a->address->city; // 大阪 ← 中のオブジェクトは共有されたまま
clone は浅いコピーです。プロパティに入っているオブジェクトは同じ実体を指し続けます。中身まで複製したいときは __clone() を書きます。
class Order
{
public function __construct(
public string $code,
public Address $address,
) {}
public function __clone(): void
{
$this->address = clone $this->address; // 中のオブジェクトも複製する
}
}
$b = clone $a;
$b->address->city = '大阪';
echo $a->address->city; // 東京 影響しない
readonly プロパティは PHP 8.3 から __clone() の中でだけ再代入できるようになりました。値オブジェクトの「一部だけ変えた新しいインスタンス」を作るときに使えます。
初期化されていないプロパティ
class User
{
public string $name; // 型付きで既定値なし → 未初期化状態がある
public function __construct() {} // $name に入れていない
}
$u = new User();
echo $u->name;
// Error: Typed property User::$name must not be accessed before initialization
var_dump(isset($u->name)); // false 未初期化は isset で false になる
型付きプロパティは null ではなく「未初期化」という状態を持ちます。null を許すなら ?string $name = null; と書き、そうでなければコンストラクタで必ず値を入れてください。
作れないときは例外を投げる
class Config
{
private array $values;
public function __construct(string $path)
{
if (!is_file($path)) {
throw new RuntimeException("設定ファイルがありません: {$path}");
}
$this->values = require $path;
}
}
try {
$c = new Config('/no/such/file.php');
} catch (RuntimeException $e) {
echo $e->getMessage();
}
「作れたのに中身が正しくない」インスタンスを返さないのが原則です。失敗したら例外を投げて、生成そのものを失敗させます。null を返したくなったら、それは static のファクトリメソッド(tryFrom() のような名前)で表現してください。
関連
- 文法 — 親カテゴリ
- クラスとインスタンス
- static関数 — ファクトリメソッド
- 継承の構文
- カプセル化の具体例
- 抽象クラス・メソッドの構文と具体例
子ページはありません
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 3Dグラフィックスとは|モデリング/レンダリング/主要ソフトウェア (Blender / Maya)
- 7 Spring Frameworkのアノテーション一覧
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- プロジェクトをTomcatプロジェクトとして認識させる方法 2026-10-07 22:32:50
- MySQLの1366 Incorrect string value|Laravelの文字コード・絵文字エラー 2026-10-07 21:54:03
- curlの証明書ホスト名不一致|旧エラー51・現行60の確認と対処 2026-10-07 21:54:03
- LaravelのMassAssignmentException|fillableの原因と安全な対処 2026-10-07 21:54:03
- Eclipse で Tomcat の起動ログがコンソールに出ない時の確認手順 2026-10-07 21:54:02
- MySQLにおける中央値(Median)の導き方(バージョン8未満) 2026-10-07 13:49:45
- getInputForward 2026-10-07 13:41:15
- JSONから配列に変換 2026-10-07 13:41:15
- ビューから値をモデルに格納しコントローラーで受け取る方法 2026-10-07 13:23:41
- Laravelのテーブル作成と定義変更|マイグレーション・up/down・注意点 2026-10-07 13:23:41
- NumPy 配列に要素を追加する方法 (append / concatenate) 2026-10-07 13:23:41
- MariaDB・MySQLで現在日時を取得する方法|NOW・タイムゾーン・保存型 2026-10-07 13:13:36
- 【django】テンプレートで定数を使用する方法 2026-10-07 13:10:15
- Spring BootにおけるApplication.propertiesの環境依存設定の分割方法 2026-10-07 12:09:35
- Not supported for DML operations【Springエラー】 2026-10-07 11:09:38