PHPのfinalキーワードは、クラスやメソッドの継承・オーバーライドを禁止するための修飾子です。メソッドに final を付けると子クラスでの再定義が不可能になり、クラスに付けるとそのクラス自体を継承できなくなります。
セキュリティ上重要な処理や、変更されると不具合が生じるメソッドに final を付けることで、意図しないオーバーライドを防ぎ、クラスの安全性を確保できます。
基本的な使い方
メソッド定義の前に final を付けると、子クラスでオーバーライドできなくなります。
PHP
<?php
class PaymentProcessor {
public int $amount;
public function __construct(int $amount) {
$this->amount = $amount;
}
// finalメソッド:子クラスでオーバーライドできない
final public function processPayment(): string {
$validated = $this->validate();
if (!$validated) {
return "バリデーションエラー";
}
return "決済完了: {$this->amount}円";
}
// こちらはオーバーライド可能
protected function validate(): bool {
return $this->amount > 0;
}
}
class CreditCardPayment extends PaymentProcessor {
// validate()はオーバーライドOK
protected function validate(): bool {
return parent::validate() && $this->amount <= 1000000;
}
// processPayment()はオーバーライドNG
// final public function processPayment(): string { ... } // Fatal Error!
}
$payment = new CreditCardPayment(5000);
echo $payment->processPayment() . "\n";
$large = new CreditCardPayment(2000000);
echo $large->processPayment() . "\n";
実行結果
決済完了: 5000円
バリデーションエラー
processPayment() は final なのでオーバーライドできませんが、その中で呼ばれる validate() はオーバーライド可能です。これにより、処理の流れは固定しつつ、部分的なカスタマイズを許可する設計ができます。
finalクラス
クラス自体に final を付けると、そのクラスを継承すること自体が禁止されます。
PHP
<?php
final class Singleton {
private static ?self $instance = null;
private function __construct(
private string $name
) {}
public static function getInstance(string $name = "default"): self {
if (self::$instance === null) {
self::$instance = new self($name);
}
return self::$instance;
}
public function getName(): string {
return $this->name;
}
}
// class ExtendedSingleton extends Singleton {} // Fatal Error!
$s1 = Singleton::getInstance("アプリ");
$s2 = Singleton::getInstance("別名");
echo $s1->getName() . "\n";
echo $s2->getName() . "\n";
echo ($s1 === $s2 ? "同一インスタンス" : "別インスタンス") . "\n";
実行結果
アプリ
アプリ
同一インスタンス
テンプレートメソッドパターン
final はテンプレートメソッドパターンで特に活躍します。全体の流れを固定し、個別の処理だけを子クラスに委譲します。
PHP
<?php
abstract class ReportGenerator {
// テンプレートメソッド(処理の流れを固定)
final public function generate(): string {
$header = $this->createHeader();
$body = $this->createBody();
$footer = $this->createFooter();
return $header . "\n" . $body . "\n" . $footer;
}
abstract protected function createHeader(): string;
abstract protected function createBody(): string;
protected function createFooter(): string {
return "--- レポート終了 ---";
}
}
class SalesReport extends ReportGenerator {
protected function createHeader(): string {
return "=== 売上レポート ===";
}
protected function createBody(): string {
return "月間売上: 1,500,000円\n前月比: +15%";
}
}
class InventoryReport extends ReportGenerator {
protected function createHeader(): string {
return "=== 在庫レポート ===";
}
protected function createBody(): string {
return "在庫数: 2,450個\n要発注: 3品目";
}
protected function createFooter(): string {
return "※ 在庫は毎日更新されます";
}
}
echo (new SalesReport())->generate() . "\n\n";
echo (new InventoryReport())->generate() . "\n";
実行結果
=== 売上レポート ===
月間売上: 1,500,000円
前月比: +15%
--- レポート終了 ---
=== 在庫レポート ===
在庫数: 2,450個
要発注: 3品目
※ 在庫は毎日更新されます
finalを使うべき場面
セキュリティに関わる処理(認証・決済など)、テンプレートメソッドの骨格、シングルトンパターンのクラスなど、意図しない変更が深刻な問題を引き起こす場面で final を使います。フレームワークの内部クラスでも頻繁に使われています。
注意
final は継承によるオーバーライドのみを禁止します。リフレクション等による直接操作は防げないため、完全なセキュリティ対策にはなりません。また、final を付けすぎるとクラスの拡張性が失われるため、本当に必要な場面に限定しましょう。
まとめ
finalをメソッドに付けると子クラスでのオーバーライドを禁止できるfinalをクラスに付けるとそのクラスの継承自体を禁止できる- テンプレートメソッドパターンで処理の流れを固定するのに最適
- セキュリティに関わる処理やシングルトンに適している
- 過度な使用は拡張性を損なうため、必要な場面に限定する