こんにちは、かつコーチです。
久しぶりにPHPの話題を取り上げます。
前回は名前空間(namespace)とautoloadの基本を扱いました。
今回からいよいよ「オブジェクト指向」に入っていきます。
PHPを勉強していると、必ずどこかで「クラス」「インスタンス」という言葉にぶつかりますよね。
初心者のうちは配列と関数だけでも十分プログラムが書けるので、「わざわざクラスを使う意味って何?」と感じる人も多いと思います。
私自身も最初はそうでした。
今回は、そんな疑問を解消しながら、クラスとインスタンスの基本をゼロから解説していきます。
クラスとインスタンスとは?
クラスは「設計図」
クラスとは、データとその操作方法をひとまとめにした「設計図」のことです。
たとえば「ユーザー」という概念を扱うプログラムなら、名前・メールアドレスといったデータと、あいさつする・退会するといった操作を1つの単位でまとめて定義できます。
インスタンスは「設計図から作った実体」
インスタンスとは、クラスという設計図をもとに実際に作られた「実体」のことです。
設計図(クラス)が1つあれば、そこから何個でも実体(インスタンス)を作れます。
「ユーザー」というクラスから、田中さんのインスタンス、佐藤さんのインスタンスをそれぞれ別々に作れる、とイメージすると分かりやすいです。
配列で管理する場合との違い
これまでの記事では、ユーザー情報を連想配列で扱うこともできました。
<?php
$user = [
'name' => '田中太郎',
'email' => 'tanaka@example.com',
];
echo $user['name'] . 'さん、こんにちは';
?>
これでも動きますが、配列には「このデータをどう扱うべきか」というルールを持たせられません。
たとえば name に空文字を入れることも、age に文字列を入れることも配列は止めてくれません。
クラスを使うと、データと「そのデータをどう扱うべきか」というルール(メソッド)を1つにまとめられるので、意図しない使われ方を防ぎやすくなります。
クラスの基本の書き方
プロパティとメソッド
クラスの中に定義する変数をプロパティ、クラスの中に定義する関数をメソッドと呼びます。
<?php
class User
{
// プロパティ(データ)
public string $name;
public string $email;
// メソッド(操作)
public function greet(): string
{
return $this->name . 'さん、こんにちは';
}
}
?>
public は「どこからでもアクセスできる」という意味のアクセス修飾子です(詳しくは次回の記事で扱います)。
$this の役割
メソッドの中に出てくる $this は、「そのインスタンス自身」を指す特別な変数です。
$this->name と書くことで、「今扱っているインスタンスの name プロパティ」にアクセスできます。
インスタンスの作り方
クラスからインスタンスを作るには new キーワードを使います。
<?php
$user1 = new User();
$user1->name = '田中太郎';
$user1->email = 'tanaka@example.com';
$user2 = new User();
$user2->name = '佐藤花子';
$user2->email = 'sato@example.com';
echo $user1->greet(); // 田中太郎さん、こんにちは
echo $user2->greet(); // 佐藤花子さん、こんにちは
?>
同じ User クラスから作った $user1 と $user2 は、それぞれ独立したデータを持ちます。
$user1->name を変更しても $user2->name には影響しません。
コンストラクタで初期化を確実にする
コンストラクタとは
先ほどの例のように、インスタンスを作った後で1つずつプロパティに値を入れる書き方だと、入れ忘れが起きやすくなります。
そこで使うのがコンストラクタ(__construct メソッド)です。
コンストラクタは、new でインスタンスを作った瞬間に自動的に実行される特別なメソッドです。
❌ Before:コンストラクタを使わず、後から値を入れる
<?php
class User
{
public string $name;
public string $email;
}
$user = new User();
// emailを入れ忘れても、エラーにならずに実行できてしまう
$user->name = '田中太郎';
echo $user->email; // 警告:初期化されていないプロパティにアクセス
?>
このコードは、email を入れ忘れたまま greet() のようなメソッドを呼んでも、実行するまでミスに気づけません。
✅ After:コンストラクタで必須項目を強制する
<?php
class User
{
public string $name;
public string $email;
public function __construct(string $name, string $email)
{
$this->name = $name;
$this->email = $email;
}
}
// nameとemailを渡さないとインスタンスを作れない
$user = new User('田中太郎', 'tanaka@example.com');
echo $user->name; // 田中太郎
?>
コンストラクタに引数を必須で定義しておけば、「値を入れ忘れたままインスタンスができてしまう」という事故をPHP自身が防いでくれます。
プロモートされたプロパティ(PHP 8以降)
PHP 8以降では、コンストラクタの引数にアクセス修飾子を付けることで、プロパティの宣言とコンストラクタの代入をまとめて書けます。
<?php
class User
{
public function __construct(
public string $name,
public string $email,
) {
}
}
$user = new User('田中太郎', 'tanaka@example.com');
echo $user->name; // 田中太郎
?>
コードがぐっとシンプルになるので、新しいプロジェクトではこの書き方をよく見かけます。
私がクラスの必要性を実感したつまずき
正直に言うと、私も最初は「配列でよくない?」とずっと思っていました。
考えが変わったのは、あるフォーム処理の機能を作っていたときです。
ユーザー登録処理の中で、「名前」「メール」「登録日時」を持つ連想配列を、あちこちの関数に受け渡していました。
<?php
function formatUserForDisplay(array $user): string
{
return $user['name'] . '(' . $user['emial'] . ')'; // "email" のタイプミス
}
?>
$user['emial'] とタイプミスしていても、PHPはエラーを出さず、Undefined array key "emial" という警告を実行時に出すだけでした。
配列のキー名は文字列なので、PHP自身は「そのキーが存在するはずかどうか」を事前にチェックしてくれません。
同じ処理をクラスのプロパティで書いていれば、$user->emial とタイプミスした時点でエディタが赤い波線で教えてくれますし、実行前に気づけたはずです。
このとき初めて、「クラスを使う理由は、コードを守ってくれる仕組みを増やすことなんだ」と腹落ちしました。
Laravelとの接点:なぜLaravelではクラスを多用するのか
Laravelを触り始めると、コントローラもモデルもすべてクラスとして定義されていることに気づきます。
これは決して「かっこいいから」ではなく、今回説明したように、データと処理をひとまとめにして安全に扱うためです。
Laravelの User モデルクラスも、今回作った User クラスと考え方の根っこは同じです。
「1件のユーザーというデータに、ユーザーに関する処理をまとめて持たせる」という発想が、Laravel全体の設計の土台になっています。
今の時点では細かい仕組みを覚える必要はありませんが、「クラス=データと処理をまとめる箱」という感覚だけ持っておくと、Laravel学習がぐっと楽になります。
まとめ
この記事のポイント
- クラスは「設計図」、インスタンスは「設計図から作った実体」
- クラスの中の変数を「プロパティ」、関数を「メソッド」と呼ぶ
$thisはそのインスタンス自身を指す特別な変数- コンストラクタ(
__construct)を使うと、必須項目の入れ忘れを防げる - 配列と違い、クラスはタイプミスや不正な使い方をエディタや実行時に検知しやすい
次に読むべき記事
→ 次の記事:PHPの継承・インターフェースをわかりやすく解説
