PHPでオブジェクト指向プログラミングを学ぶ際、多くの人が「継承」とセットで出会うのが「オーバーライド」という概念です。
親クラスの便利な機能を引き継ぎつつ、子クラス独自の振る舞いを加えられる強力な仕組みとなります。
しかし、「オーバーロードと名前が似ていて混乱する」「具体的な書き方やルールがわからない」といった疑問を持つ方も少なくないでしょう。
この記事では、PHPのオーバーライドについて、その基本的な定義から具体的な実装方法、parent:: の使い方、そして最も混同しやすいオーバーロードとの違いまで、豊富なサンプルコードと共に徹底的に解説していきます。
受講生から評判の良いプログラミングスクール
スクール |
特徴 |
受講料金 |
| 大手比較サイトで4年連続人気NO.1!受講生からの評判も非常に高く、Web系のエンジニアを目指すならRUNTEQ一択。 | 657,000円 (最大約53万円の給付金が適用される) |
|
| 月単価80万円以上の現役エンジニア講師による指導!一度入会すればサポートは半永久的。 | 498,000円 |
|
| 格安で質の高いWeb制作スキルを習得したい人におすすめ!業界最安級の料金でありながら、コミュニティやサポートが充実。 | 129,800円~ |
|
| 完全無料でプログラミングが学べる貴重なスクール!最短1ヶ月で卒業可能。ゼロスク運営会社への就職もできる。 | 完全無料 |
|
| 長期間に渡って学習し、希少人材を目指す人に最適なスクール!受講料は高いものの、高収入を得られる人材を目指せる。 | 96~132万円 |
PHPのオーバーライドとは?
まず、オーバーライドがどのような概念なのか、なぜ必要なのかについて解説します。
オーバーライドを理解するには、オブジェクト指向の「継承」が前提知識となります。
継承を前提とした「上書き」機能
オーバーライド(Override)とは、日本語で「上書きする」「覆い隠す」といった意味を持つ言葉です。
PHPのオブジェクト指向において、オーバーライドは「継承」関係にあるクラス間で発生します。
具体的には、親クラス(スーパークラス)で定義されているメソッドを、子クラスで改めて定義し直すことを指します。
例えば、「動物」という親クラスに「鳴く」というメソッドがあるとします。
この「動物」クラスを継承した「犬」クラスや「猫」クラスが、それぞれ「ワン!」や「ニャー!」といった独自の鳴き声で「鳴く」メソッドを再定義するイメージです。
オーバーライドの目的とメリット
オーバーライドの主な目的は、親クラスの基本的な機能を引き継ぎつつ、子クラス固有の要件に合わせて動作をカスタマイズすることにあります。
-
機能の具体化・カスタマイズ
親クラスでは汎用的に定義されていた処理を、子クラスでより具体的に、あるいは全く異なる処理内容に変更できます。 -
コードの再利用性と保守性の向上
親クラスの共通処理はそのまま利用し、異なる部分だけを子クラスで上書きします。これにより、同じようなコードを何度も書く必要がなくなり、修正が必要な場合も対象クラスのメソッド修正だけで完結させられます。 -
ポリモーフィズム(多態性)の実現
同じ「鳴く」メソッドを呼び出しても、インスタンスが「犬」クラスなら「ワン!」、「猫」クラスなら「ニャー!」と、実行されるインスタンスの種類によって実際の動作が変わります。このような性質をポリモーフィズム(多態性)と呼び、柔軟性の高いコード設計の基盤となります。
PHPでオーバーライドを実装する方法
オーバーライドの概念を理解したところで、次はPHPで実際にどのようにコードを記述するのかを見ていきましょう。
extends キーワードを使ったクラスの継承が基本となります。
基本的なサンプルコードと実行結果
ここでは、「乗り物(Vehicle)」クラスを親として、「車(Car)」クラスが、その走行メソッド(run)をオーバーライドする簡単な例を示します。
<?php
// 親クラス(スーパークラス)
class Vehicle {
public function run() {
echo "乗り物が移動します。" . PHP_EOL;
}
}
// 子クラス(サブクラス)
// extends キーワードで Vehicle クラスを継承
class Car extends Vehicle {
// 親クラスの run() メソッドをオーバーライド
public function run() {
echo "車が道路を走ります。" . PHP_EOL;
}
}
// 親クラスのインスタンスを生成
$vehicle = new Vehicle();
$vehicle->run();
// 子クラスのインスタンスを生成
$car = new Car();
$car->run(); // こちらはオーバーライドされたメソッドが呼ばれる
実行結果
乗り物が移動します。
車が道路を走ります。
ソースコードの解説
class Vehicleで親クラスを定義し、run()メソッドを持たせています。class Car extends Vehicleの部分で、CarクラスがVehicleクラスを継承することを示しています。Carクラスの内部で、親クラスと全く同じ名前(public function run())でメソッドを再度定義しました。これがオーバーライドです。- 親クラスの
run()は「乗り物が移動します。」と表示しますが、子クラスのrun()は「車が道路を走ります。」と表示するように内容を上書きしています。 $vehicle->run()を呼び出すと、当然ながらVehicleクラスのrun()が実行されます。$car->run()を呼び出すと、Vehicleクラスにもrun()は存在しますが、Carクラス自身が持つオーバーライドされたrun()メソッドが優先的に呼び出されるのです。
親の処理を活かす「parent::」キーワード
オーバーライドはメソッドを完全に上書きしますが、場合によっては「親クラスの処理も実行しつつ、子クラス独自の処理も追加したい」というケースが頻繁に発生します。
その際に活躍するのが parent:: というキーワードです。
parent:: を使ったサンプルコード
親クラスのコンストラクタ(初期化処理)を呼び出しつつ、子クラス独自の処理を追加する例を見てみましょう。
<?php
// 親クラス
class User {
protected $name;
// コンストラクタ
public function __construct($name) {
$this->name = $name;
echo "User: " . $this->name . " が初期化されました。" . PHP_EOL;
}
public function profile() {
echo "名前: " . $this->name . PHP_EOL;
}
}
// 子クラス
class AdminUser extends User {
private $role;
// コンストラクタをオーバーライド
public function __construct($name, $role) {
// parent:: で親クラスのコンストラクタを呼び出す
parent::__construct($name);
// 子クラス独自の処理
$this->role = $role;
echo "AdminUser: 権限 '" . $this->role . "' が設定されました。" . PHP_EOL;
}
// profile() メソッドをオーバーライド
public function profile() {
// parent:: で親クラスの profile() を呼び出す
parent::profile();
// 子クラス独自の処理を追加
echo "権限: " . $this->role . PHP_EOL;
}
}
// 子クラスのインスタンスを生成
$admin = new AdminUser("Suzuki", "管理者");
echo "---" . PHP_EOL;
$admin->profile();
実行結果
User: Suzuki が初期化されました。
AdminUser: 権限 '管理者' が設定されました。
---
名前: Suzuki
権限: 管理者
ソースコードの解説
AdminUserクラスのコンストラクタ__constructでは、引数を2つ($name,$role)受け取ります。parent::__construct($name);の部分で、親クラス(User)のコンストラクタを明示的に呼び出しています。これにより、親クラスの$this->name = $name;の処理が実行され、「User: Suzuki が初期化されました。」と表示されます。- その後、子クラス独自の処理である
$this->role = $role;などが実行されます。 profile()メソッドでも同様です。parent::profile();によって親クラスのprofile()が呼び出され、「名前: Suzuki」が表示されます。- その後に、子クラス独自の処理として「権限: 管理者」が表示されます。
このように parent:: を使用することで、親クラスの機能を再利用しつつ、機能の拡張(上書き)を安全に行うことが可能になります。
オーバーライドとオーバーロードの決定的な違い
PHPの学習において、おそらく最も混同しやすいのが「オーバーライド」と「オーバーロード」でしょう。
この2つは名前こそ似ていますが、機能や目的は全く異なります。
2つの概念を比較表で整理
オーバーライドとオーバーロードの違いを、以下の表で明確に区別しましょう。
| オーバーライド (Override) | オーバーロード (Overload) | |
|---|---|---|
| 日本語の意味 | 上書き、優先する | 過積載、多重定義 |
| 関係する場所 | 親クラスと子クラスの間(継承が前提) | 同一クラスの内部 |
| 主な目的 | 親クラスのメソッドを、子クラスで再定義する | 同じメソッド名で、引数の数や型が異なる処理を実装する(ように見せかける) |
| PHPでの実装 | extends を使い、子クラスで同名メソッドを定義する |
マジックメソッド (__call, __callStatic) を使って実現する(※) |
【(※)補足】
JavaやC#といった他の言語では、オーバーロードは言語機能として標準サポートされています。(例: add(int a, int b) と add(int a, int b, int c) を両方定義できる)
しかし、PHPでは厳密な意味でのオーバーロードはサポートされていません。
PHPでは、未定義のメソッドが呼び出された時に動作する「マジックメソッド」を利用して、擬似的にオーバーロードのような振る舞いを実装します。
PHPにおけるオーバーロードの実装方法(参考)
オーバーライドとの違いを明確にするため、参考までにPHPでのオーバーロード(擬似的)の実装例を紹介します。
<?php
class MathUtility {
// 未定義のメソッドが呼び出された時に実行されるマジックメソッド
public function __call($methodName, $arguments) {
if ($methodName == 'add') {
// 引数の個数に応じて処理を分岐
if (count($arguments) == 2) {
echo "2つの合計: " . ($arguments[0] + $arguments[1]) . PHP_EOL;
} elseif (count($arguments) == 3) {
echo "3つの合計: " . ($arguments[0] + $arguments[1] + $arguments[2]) . PHP_EOL;
} else {
echo "引数は2個または3個にしてください。" . PHP_EOL;
}
}
}
}
$math = new MathUtility();
$math->add(10, 20); // 引数が2個
$math->add(10, 20, 30); // 引数が3個
$math->add(10); // 想定外
実行結果
2つの合計: 30
3つの合計: 60
引数は2個または3個にしてください。
ソースコードの解説
MathUtilityクラスには、add()という名前のメソッドは直接定義されていません。$math->add(...)のように未定義のメソッドが呼び出されると、PHPは自動的にマジックメソッド__call()を探し出し、実行します。__call()は、第1引数に呼び出されたメソッド名('add')、第2引数に渡された引数の配列([10, 20]など)を受け取ります。__call()の内部で、引数の個数count($arguments)をチェックし、処理を分岐させています。
このように、PHPのオーバーロードは「継承」とは全く関係なく、同一クラス内で引数の違いによって動的に処理を振り分けるテクニックを指します。
オーバーライド実装時の重要ルールと制約
オーバーライドは強力な機能ですが、PHPのルールに従って正しく実装する必要があります。
特に注意すべき「アクセス修飾子」と「final キーワード」に関する制約を解説します。
アクセス修飾子(可視性)のルール
メソッドをオーバーライドする際、子クラスのメソッドのアクセス修飾子(public, protected, private)は、親クラスのメソッドよりも厳しく(アクセス範囲を狭く)することはできません。
- OK: 親が
protected→ 子がpublic(緩くするのはOK) - NG: 親が
public→ 子がprotected(厳しくするのはNG) - NG: 親が
public→ 子がprivate(厳しくするのはNG)
このルールは「親クラスで許可されていた範囲のアクセスを、子クラスが勝手に狭めてはいけない」という原則に基づいています。
<?php
class ParentClass {
// 親は protected
protected function testMethod() {
echo "親クラスの protected メソッド" . PHP_EOL;
}
}
class ChildClass extends ParentClass {
// 子は public (親より緩い) → OK
public function testMethod() {
echo "子クラスで public にオーバーライド" . PHP_EOL;
}
}
$child = new ChildClass();
$child->testMethod(); // 実行可能
もし親が public なのに子を protected にしてオーバーライドしようとすると、Fatal error: Access level to ChildClass::testMethod() must be public (as in class ParentClass) のような致命的なエラーが発生します。
なお、親クラスで private に設定されたメソッドは、そもそも子クラスからは見えない(引き継がれない)ため、オーバーライドの対象になりません。
子クラスで同名のメソッドを定義しても、それは単に子クラス固有の新しいメソッドとして扱われます。
final キーワードによるオーバーライドの禁止
親クラスのメソッド定義時に final キーワードを付けると、そのメソッドは子クラスでのオーバーライドが一切禁止されます。
「このメソッドは、どのクラスが継承しても、絶対にこの処理内容を変えてはならない」という強い制約を加えたい場合に使用します。
<?php
class AppConfig {
// このメソッドはオーバーライドを禁止する
final public function getVersion() {
return "1.0.0";
}
}
class MyCustomConfig extends AppConfig {
// final メソッドをオーバーライドしようとする
public function getVersion() {
// この定義自体がエラーとなる
return "2.0.0";
}
}
上記のコードは、MyCustomConfig クラスを定義した時点で Fatal error: Cannot override final method AppConfig::getVersion() という致命的なエラーを引き起こし、実行できません。
ちなみに、メソッドだけでなく final class AppConfig のようにクラス自体に final を付けることも可能です。その場合、そのクラス自体が extends による継承を一切禁止します。
まとめ
この記事では、PHPのオーバーライドについて、その基本概念から具体的な実装コード、混同しやすいオーバーロードとの違い、そして実装時のルールまでを網羅的に解説しました。
なお、PHPを体系的に学んだり、PHPのスキルを高めたりするためには、プログラミングスクールを利用するのも有効です。
細かな疑問がすぐに解決するだけでなく、現役エンジニアが「質の高いポートフォリオ」を作成するための手助けをしてくれたり、エンジニア就職・転職のコツを教えてくれたりするなど、様々なメリットがありますので、独学に疲れた方は検討してみてはいかがでしょうか。




