PHPでプログラムを書く際、入力値のチェックや値の比較は非常に重要な処理です。特にフォームからの入力値やデータベースの値を扱う際には、適切な比較処理が必要になります。
例外処理を使うことで、予期せぬ入力値や不正な値に対して、プログラムを安全に動作させることができます。
カスタム例外クラスの作成
まず、基本的なカスタム例外クラスを作成してみましょう:
class ValueComparisonException extends Exception {
public function __construct($message, $code = 0) {
parent::__construct($message, $code);
}
public function errorMessage() {
return '入力値エラー: ' . $this->getMessage();
}
}
値の比較処理の実装
実際の比較処理を実装してみましょう:
function compareValues($value1, $value2) {
try {
if (!is_numeric($value1) || !is_numeric($value2)) {
throw new ValueComparisonException('数値以外の値が入力されました');
}
if ($value1 === $value2) {
return true;
}
return false;
} catch (ValueComparisonException $e) {
echo $e->errorMessage();
return false;
}
}
この関数は、実務でよく使用される基本的な比較処理の例です。
実践的な例外処理の活用方法
範囲チェックの実装
実務では、入力値が特定の範囲内にあるかどうかをチェックする必要がある場合が多くあります。例えば、年齢や金額の入力チェックなどです:
class RangeValidationException extends Exception {
private $fieldName;
private $min;
private $max;
public function __construct($fieldName, $min, $max, $message = "") {
$this->fieldName = $fieldName;
$this->min = $min;
$this->max = $max;
parent::__construct($message ?: "{$fieldName}は{$min}から{$max}の間で入力してください。");
}
}
function validateRange($value, $fieldName, $min, $max) {
try {
if (!is_numeric($value)) {
throw new ValueComparisonException("{$fieldName}には数値を入力してください");
}
$value = floatval($value);
if ($value < $min || $value > $max) {
throw new RangeValidationException($fieldName, $min, $max);
}
return true;
} catch (Exception $e) {
echo $e->getMessage();
return false;
}
}
実際の使用例
フォームからの入力値をチェックする場合の例:
// 年齢のバリデーション
$age = $_POST['age'] ?? '';
try {
if (!validateRange($age, '年齢', 0, 120)) {
// エラー処理
throw new ValueComparisonException('不正な年齢が入力されました');
}
// 正常な処理を続行
} catch (Exception $e) {
// エラーログの記録
error_log($e->getMessage());
// ユーザーへのエラー表示
echo "入力エラーが発生しました。";
}
実践的な例外処理の活用方法
範囲チェックの実装
実務では、入力値が特定の範囲内にあるかどうかをチェックする必要がある場合が多くあります。例えば、年齢や金額の入力チェックなどです:
class RangeValidationException extends Exception {
private $fieldName;
private $min;
private $max;
public function __construct($fieldName, $min, $max, $message = "") {
$this->fieldName = $fieldName;
$this->min = $min;
$this->max = $max;
parent::__construct($message ?: "{$fieldName}は{$min}から{$max}の間で入力してください。");
}
}
function validateRange($value, $fieldName, $min, $max) {
try {
if (!is_numeric($value)) {
throw new ValueComparisonException("{$fieldName}には数値を入力してください");
}
$value = floatval($value);
if ($value < $min || $value > $max) {
throw new RangeValidationException($fieldName, $min, $max);
}
return true;
} catch (Exception $e) {
echo $e->getMessage();
return false;
}
}
実際の使用例
フォームからの入力値をチェックする場合の例:
// 年齢のバリデーション
$age = $_POST['age'] ?? '';
try {
if (!validateRange($age, '年齢', 0, 120)) {
// エラー処理
throw new ValueComparisonException('不正な年齢が入力されました');
}
// 正常な処理を続行
} catch (Exception $e) {
// エラーログの記録
error_log($e->getMessage());
// ユーザーへのエラー表示
echo "入力エラーが発生しました。";
}
複数の条件を組み合わせた高度な比較
実務では、複数の条件を組み合わせて比較する必要がある場合が多くあります。以下に、より実践的な例を示します:
class DataValidationException extends Exception {
protected $errors = [];
public function __construct($message = "", array $errors = []) {
parent::__construct($message);
$this->errors = $errors;
}
public function getErrors() {
return $this->errors;
}
}
class ProductValidator {
private $product;
public function __construct(array $product) {
$this->product = $product;
}
public function validate() {
$errors = [];
try {
// 価格のチェック
if (!isset($this->product['price'])) {
throw new ValueComparisonException('価格が設定されていません');
}
if ($this->product['price'] < 0) {
throw new RangeValidationException('価格', 0, PHP_FLOAT_MAX);
}
// 在庫数のチェック
if (!isset($this->product['stock'])) {
throw new ValueComparisonException('在庫数が設定されていません');
}
if ($this->product['stock'] < 0) {
throw new RangeValidationException('在庫数', 0, PHP_INT_MAX);
}
return true;
} catch (Exception $e) {
$errors[] = $e->getMessage();
throw new DataValidationException('商品データの検証に失敗しました', $errors);
}
}
}
実際の使用例
$product = [
'name' => 'テスト商品',
'price' => -100, // 不正な価格
'stock' => 5
];
$validator = new ProductValidator($product);
try {
$validator->validate();
echo "商品データは正常です。";
} catch (DataValidationException $e) {
echo "エラーが発生しました:\n";
foreach ($e->getErrors() as $error) {
echo "- " . $error . "\n";
}
}
実務で使える例外処理のベストプラクティス
トランザクション処理との組み合わせ
データベース操作を含む処理では、例外処理とトランザクションを組み合わせることが重要です:
class DatabaseException extends Exception {}
class OrderProcessor {
private $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function processOrder(array $orderData) {
try {
$this->pdo->beginTransaction();
// 注文金額の検証
if (!isset($orderData['total_amount'])) {
throw new ValueComparisonException('注文金額が設定されていません');
}
if ($orderData['total_amount'] <= 0) {
throw new RangeValidationException('注文金額', 1, PHP_FLOAT_MAX);
}
// 在庫数との比較
$stmt = $this->pdo->prepare("SELECT stock FROM products WHERE id = ?");
$stmt->execute([$orderData['product_id']]);
$product = $stmt->fetch(PDO::FETCH_ASSOC);
if ($product['stock'] < $orderData['quantity']) {
throw new ValueComparisonException('注文数量が在庫数を超えています');
}
// 注文処理の実行
$this->executeOrder($orderData);
$this->pdo->commit();
return true;
} catch (Exception $e) {
$this->pdo->rollBack();
throw new DatabaseException('注文処理に失敗しました: ' . $e->getMessage());
}
}
private function executeOrder(array $orderData) {
// 注文処理の実装
}
}
エラーログの記録と通知
例外が発生した場合の適切なログ記録と通知も重要です:
class ErrorLogger {
public static function logException(Exception $e) {
$logMessage = sprintf(
"[%s] %s in %s:%d\nStack trace:\n%s",
date('Y-m-d H:i:s'),
$e->getMessage(),
$e->getFile(),
$e->getLine(),
$e->getTraceAsString()
);
error_log($logMessage);
// 重大なエラーの場合は管理者に通知
if ($e instanceof DatabaseException) {
self::notifyAdmin($logMessage);
}
}
private static function notifyAdmin($message) {
// メール送信やSlack通知などの実装
}
}
まとめ
例外処理を使った値の比較は、以下のような利点があります:
- エラーの詳細な情報を取得できる
- エラーの種類に応じた適切な処理が可能
- デバッグが容易になる
- コードの保守性が向上する
実務では、これらの例外処理を組み合わせて使用することで、より堅牢なアプリケーションを作ることができます。