🔄

手続き型PHPをクラスベースにリファクタリング|保守性向上の実践

に公開

手続き型PHPをクラスベースにリファクタリング|保守性向上の実践

1. はじめに

前回の記事(PHPでTODOリスト作成|CRUDとセキュリティ対策を実践)では、TODOリストのCRUD操作を実装しました。

1.1 これまでの記事で学んだこと

この記事では、手続き型コードをクラスベースに書き換え 、保守性を向上させます。PHPのクラス基本、Composerの使い方、名前空間も学びながら、実務で使えるコード構造を身につけます。

1.2 この記事で実践すること

1.3 この記事の目的

  • 手続き型コードをクラスベースに書き換え、保守性を向上させる
  • PHPのクラス基本を理解
  • Composerの使い方を習得
  • 名前空間を理解
  • 生成AIへの質問を抽象化するテクニック

1.4 このシリーズ全体の流れ

記事 タイトル
生成AIとセキュリティの基礎知識
DockerでPHP+MySQL開発環境を作る
PHP基礎文法とDB接続の安全な書き方
PHPでTODOリスト作成(CRUDとセキュリティ対策)
今ここ 手続き型PHPをクラスベースにリファクタリング
PHP実務で使うデバッグとセキュリティチェックリスト
攻撃者の視点で学ぶPHPセキュリティ

2. なぜリファクタリングするのか

2.1 手続き型の問題点

現在のコード(手続き型)

  • 関数が散在している
  • グローバル変数が多い
  • 関連する処理がバラバラ
  • 再利用しにくい

問題点

  • 変更箇所が分かりにくい
  • 影響範囲が広い
  • テストしにくい
  • 関数名の衝突が起きやすい

2.2 クラスベースのメリット

メリット

  1. 保守性の向上:変更箇所が明確で、影響範囲が限定される
  2. 再利用性:同じ機能を複数箇所で使いやすい
  3. 可読性:関連する処理がまとまって理解しやすい
  4. 関数名の衝突回避:名前空間で管理できる

デメリット

  • 小規模プロジェクトでは逆に複雑化する場合がある
  • 学習コストがかかる
  • 書く量が増える

いつクラス化すべきか

  • 同じような処理を複数箇所で使う時
  • データと処理が密接に関連している時
  • 機能が増えて管理が大変になってきた時

2.3 レガシーコードを読むために

会社の古いPHPシステムでは、手続き型コードが多く見られます。クラスベースの書き方を理解することで、レガシーコードも読める ようになります。


3. PHPクラスの基本

JavaScriptやPythonの経験がある方なら、PHPのクラスはすぐに理解できるはずです。

3.1 クラスとは何か

PHPのクラスは「プロパティ(変数)とメソッド(関数)をまとめた設計図」

基本構文

class ClassName {
    // プロパティ(変数)
    public $property;
    
    // メソッド(関数)
    public function methodName() {
        // 処理
    }
}

3.2 プロパティとメソッド

プロパティ: クラスが持つデータ(変数)
メソッド: クラスが持つ処理(関数)

使用例

class User {
    // プロパティ
    public $name;
    public $age;
    
    // メソッド
    public function greet() {
        return "こんにちは、{$this->name}さん";
    }
}

// インスタンス化
$user = new User();
$user->name = 'やまと';
$user->age = 27;
echo $user->greet(); // こんにちは、やまとさん

コンストラクタ

役割: オブジェクト生成時に自動実行される初期化メソッド

使用例

class User {
    private $name;
    
    public function __construct($name) {
        $this->name = $name;
    }
    
    public function getName() {
        return $this->name;
    }
}

$user = new User("やまと");
echo $user->getName(); // やまと

ポイント

  • newしたときに自動で呼ばれる
  • プロパティの初期化に使うのが基本

3.3 $thisの使い方

$this: クラス内で「自分自身」を指す特別な変数

使用例

class MyClass {
    private $value = 10;
    
    public function getValue() {
        return $this->value;  // 自分のプロパティにアクセス
    }
    
    public function setValue($value) {
        $this->value = $value;  // 自分のプロパティを変更
    }
}

ポイント

  • クラス内からプロパティ・メソッドにアクセスするときに使う
  • $this->プロパティ名$this->メソッド名()

3.4 アクセス修飾子

修飾子 アクセス範囲 用途
public どこからでもOK 外部に公開したいもの
private クラス内のみ 外部に隠したいもの
protected 継承先までOK 子クラスと共有したいもの

使用例

class SampleClass {
    public $publicVar;      // どこからでもアクセス可
    private $privateVar;    // クラス内のみ
    protected $protectedVar; // 継承先まで
}

protectedの使用例(継承時)

class Animal {
    protected $name;  // 子クラスからアクセス可能
    
    public function __construct($name) {
        $this->name = $name;
    }
}

class Dog extends Animal {
    public function bark() {
        return "{$this->name}がワンワン";  // protectedプロパティにアクセス可能
    }
}

$dog = new Dog('ポチ');
echo $dog->bark();  // ポチがワンワン
// echo $dog->name;  // ❌ エラー(外部からはアクセス不可)

ポイント

  • protectedは継承関係で使うことが多い
  • 子クラスからはアクセス可能だが、外部からはアクセス不可
  • 実務では、privateprotectedを適切に使い分ける

3.5 他言語との比較

JavaScript(ES6以降)

class User {
    constructor(name) {
        this.name = name;
    }
    
    greet() {
        return `こんにちは、${this.name}さん`;
    }
}

PHP

class User {
    public function __construct($name) {
        $this->name = $name;
    }
    
    public function greet() {
        return "こんにちは、{$this->name}さん";
    }
}

主な違い

  • PHPは変数に$が必要
  • メソッド呼び出しは->を使う
  • コンストラクタは__construct()

4. TodoModelクラスを作る

実際にクラスを作成しましょう。手続き型コードをクラスベースに書き換えます。

4.1 Modelクラスの設計

Modelクラスの役割

  • データベース接続を管理
  • CRUD操作をメソッドとして提供
  • データと処理を一箇所に集約

4.2 Before(手続き型)

www/index.php(手続き型)

<?php
// グローバル変数
$pdo = null;

// 接続関数
function connect_db() {
    global $pdo;
    $pdo = new PDO(
        'mysql:host=mysql;dbname=mydb;charset=utf8mb4',
        'dbuser',
        'dbpass',
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
        ]
    );
}

// TODO全件取得関数
function get_todos() {
    global $pdo;
    $stmt = $pdo->query('SELECT * FROM todos ORDER BY created_at DESC');
    return $stmt->fetchAll();
}

// TODO 1件取得関数
function get_todo_by_id($id) {
    global $pdo;
    $stmt = $pdo->prepare('SELECT * FROM todos WHERE id = :id');
    $stmt->bindValue(':id', $id, PDO::PARAM_INT);
    $stmt->execute();
    return $stmt->fetch();
}

// TODO登録関数
function create_todo($title) {
    global $pdo;
    $stmt = $pdo->prepare('INSERT INTO todos (title, status) VALUES (:title, 0)');
    $stmt->bindValue(':title', $title, PDO::PARAM_STR);
    $stmt->execute();
    return $pdo->lastInsertId();
}

// TODO更新関数
function update_todo($id, $title, $status) {
    global $pdo;
    $stmt = $pdo->prepare('UPDATE todos SET title = :title, status = :status WHERE id = :id');
    $stmt->bindValue(':title', $title, PDO::PARAM_STR);
    $stmt->bindValue(':status', $status, PDO::PARAM_INT);
    $stmt->bindValue(':id', $id, PDO::PARAM_INT);
    $stmt->execute();
}

// TODO削除関数
function delete_todo($id) {
    global $pdo;
    $stmt = $pdo->prepare('DELETE FROM todos WHERE id = :id');
    $stmt->bindValue(':id', $id, PDO::PARAM_INT);
    $stmt->execute();
}

// 使い方
connect_db();
$todos = get_todos();
$todo = get_todo_by_id(1);
$new_id = create_todo('新しいTODO');
update_todo(1, '更新されたTODO', 1);
delete_todo(1);
?>

問題点

  • グローバル変数を使っている(global $pdo
  • 関数が散在している(別ファイルに書かれている可能性)
  • 再利用しにくい(関数を呼ぶ前にconnect_db()が必要)
  • テストしにくい(グローバル変数に依存)

4.3 After(クラスベース)

src/Models/TodoModel.phpを作成

注意: 実際の実装では、Databaseクラス(シングルトンパターン)を使用してPDO接続を管理しています。

<?php
namespace App\Models;

use App\Utils\Database;
use PDO;
use PDOException;

class TodoModel {
    private PDO $pdo;
    
    // コンストラクタで初期化
    public function __construct() {
        $this->pdo = Database::getInstance();
    }
    
    // 全件取得
    public function getAll(): array {
        try {
            $stmt = $this->pdo->query('SELECT * FROM todos ORDER BY created_at DESC');
            return $stmt->fetchAll();
        } catch (PDOException $e) {
            error_log("TODO取得エラー: " . $e->getMessage());
            return [];
        }
    }
    
    // 1件取得
    public function getById(int $id): ?array {
        try {
            $stmt = $this->pdo->prepare('SELECT * FROM todos WHERE id = :id');
            $stmt->bindValue(':id', $id, PDO::PARAM_INT);
            $stmt->execute();
            
            $result = $stmt->fetch();
            return $result ?: null;
        } catch (PDOException $e) {
            error_log("TODO取得エラー: " . $e->getMessage());
            return null;
        }
    }
    
    // 登録
    public function create(string $title): int {
        try {
            $stmt = $this->pdo->prepare('INSERT INTO todos (title, status) VALUES (:title, 0)');
            $stmt->bindValue(':title', $title, PDO::PARAM_STR);
            $stmt->execute();
            
            return (int)$this->pdo->lastInsertId();
        } catch (PDOException $e) {
            error_log("TODO登録エラー: " . $e->getMessage());
            throw new PDOException("TODO登録に失敗しました");
        }
    }
    
    // 更新
    public function update(int $id, string $title, int $status): bool {
        try {
            $stmt = $this->pdo->prepare('UPDATE todos SET title = :title, status = :status WHERE id = :id');
            $stmt->bindValue(':title', $title, PDO::PARAM_STR);
            $stmt->bindValue(':status', $status, PDO::PARAM_INT);
            $stmt->bindValue(':id', $id, PDO::PARAM_INT);
            $stmt->execute();
            
            return $stmt->rowCount() > 0;
        } catch (PDOException $e) {
            error_log("TODO更新エラー: " . $e->getMessage());
            throw new PDOException("TODO更新に失敗しました");
        }
    }
    
    // 削除
    public function delete(int $id): bool {
        try {
            $stmt = $this->pdo->prepare('DELETE FROM todos WHERE id = :id');
            $stmt->bindValue(':id', $id, PDO::PARAM_INT);
            $stmt->execute();
            
            return $stmt->rowCount() > 0;
        } catch (PDOException $e) {
            error_log("TODO削除エラー: " . $e->getMessage());
            throw new PDOException("TODO削除に失敗しました");
        }
    }
}

ポイント:

  • Database::getInstance()でPDO接続を取得(シングルトンパターン)
  • 型宣言を使用(PHP 7.4以降)
  • エラーハンドリングを実装
  • rowCount()で更新・削除の成功を判定

使い方

実際の実装では、TodoControllerクラスを通じて使用します:

<?php
require __DIR__ . '/../project/vendor/autoload.php';

use App\Controllers\TodoController;

$controller = new TodoController();
$todos = $controller->index();
?>

TodoController内でTodoModelを使用しています:

// TodoController.php内
public function index(): array {
    return $this->model->getAll();
}

変更ポイント

  • グローバル変数 → プロパティ($this->pdo
  • 関数 → メソッド
  • 接続処理 → コンストラクタ(__construct
  • 名前空間内でグローバルクラスを使う方法use PDO;でインポート(\PDOの代わり)

名前空間内でグローバルクラスを使う方法

名前空間内でPDOなどのグローバルクラスを使う場合、3つの方法があります。推奨順位が違うだけです。

❌ 方法1:何もつけない(NG)

namespace App\Models;

class TodoModel {
    public function __construct() {
        $this->pdo = new PDO(...);  // エラー!
    }
}

結果: App\Models\PDOというクラスを探しに行く → 存在しないのでエラー


⚠️ 方法2:バックスラッシュをつける(OK だけど...)

namespace App\Models;

class TodoModel {
    public function __construct() {
        $this->pdo = new \PDO(...);  // 動く!
    }
}

結果: ちゃんとグローバルのPDOクラスが使える

メリット:

  • 動作する
  • その場で完結する

デメリット:

  • コード内にバックスラッシュが散在して読みにくい
  • 使っているクラスがファイル見た瞬間に分からない

✅ 方法3:use文でインポート(最推奨!)

namespace App\Models;

use PDO;  // ← ファイルの先頭で宣言

class TodoModel {
    public function __construct() {
        $this->pdo = new PDO(...);  // スッキリ!
    }
}

結果: 一番読みやすい

メリット:

  • ファイル見た瞬間に何を使っているか分かる
  • コード内がスッキリ(バックスラッシュ不要)
  • 変更が楽(use文だけ変更すればOK)
  • 一般的な書き方

まとめ表

方法 書き方 動く? 推奨度
方法1 new PDO() ❌ エラー ×
方法2 new \PDO() ✅ 動く △(悪くはない)
方法3 use PDO;
new PDO()
✅ 動く ◎(最推奨)

実務でどうする?

答え:use文使っとけばOK!

namespace App\Models;

use PDO;  // ← これ書く!

class TodoModel {
    private $pdo;
    
    public function __construct() {
        $this->pdo = new PDO(...);  // ← スッキリ!
    }
}

なぜuse文が一番いいの?

理由1:ファイル見た瞬間に何使ってるか分かる

use PDO;
use Exception;
use DateTime;
// ↑ 「あ、このファイルではこの3つ使ってるんだな」って分かる

理由2:コード内がスッキリ

// ❌ 方法2だと毎回バックスラッシュ
$pdo = new \PDO(...);
$date = new \DateTime();
$exception = new \Exception();

// ✅ 方法3だとスッキリ
$pdo = new PDO(...);
$date = new DateTime();
$exception = new Exception();

理由3:変更が楽

// もし別のクラスに変えたくなったら
use PDO;  // ← ここだけ変えればOK
// ↓
use MyCustomPDO as PDO;  // こんな感じで

4.4 Before/After比較

4.2と4.3で実装例を見ましたが、ここでは手続き型とクラスベースの違いを整理します。

Before(手続き型)の問題点

  • グローバル変数で状態管理global $pdo

    • どこからでも変更できてしまう
    • バグの原因を特定しにくい
  • 関数が散在

    • 別ファイルに書かれている可能性
    • 関連する処理がバラバラ
  • 再利用しにくい

    • 関数を呼ぶ前にconnect_db()が必要
    • 毎回接続処理を呼ばないといけない
  • テストしにくい

    • グローバル変数に依存
    • モックオブジェクトを注入できない

After(クラスベース)の改善点

  • プロパティで状態管理$this->pdo

    • クラス内で完結
    • 外部から直接変更できない(private
  • メソッドがまとまっている

    • 1つのクラスに集約
    • 関連する処理が一箇所に
  • 再利用しやすい

    • new TodoModel()するだけで使える
    • 接続処理は自動で実行される
  • テストしやすい

    • モックオブジェクトを注入可能
    • 依存関係が明確

比較表

項目 Before(手続き型) After(クラスベース)
状態管理 グローバル変数(global $pdo プロパティ($this->pdo
コードの構造 関数が散在 メソッドがまとまっている
再利用性 関数を呼ぶ前に接続が必要 newするだけで使える
テスト グローバル変数に依存 モックオブジェクトを注入可能
可読性 関数名から関連性が分かりにくい クラス名から関連性が明確

5. 【実践コラム】生成AIに質問するための抽象化テクニック

生成AIとセキュリティの基礎知識で学んだ匿名化テクニックを、クラス設計の質問で実践しましょう。

5.1 構造だけを伝える質問法

実際の業務ロジックを伏せて、構造だけを伝える ことで、セキュリティを守りながら効果的に学べます。

5.2 Before(NG例):具体的すぎる質問

「うちの会社のcustomer_masterテーブルから
顧客のクレジットカード番号を取得するコードがエラーになります。
APIキー: sk_live_abc123xyz を使って payment_gateway に接続しています。
サーバーは /var/www/company-internal/production にあります。」

なぜNGか

  • 実際のテーブル名(customer_master)を晒している
  • APIキーを晒している
  • ファイルパスから社内構造が分かる

5.3 After(OK例):抽象化済み

「データベースから情報を取得する際にエラーが発生しています。
以下のような構成です:

// データ取得部分
$stmt = $pdo->prepare("SELECT item_data FROM items WHERE id = ?");
$stmt->execute([$id]);

// 外部API接続部分
$api_key = getenv('API_KEY');  // 環境変数から取得
$response = callApi($api_key, $data);

エラーメッセージは「Connection refused」です。
Docker環境で、MySQLコンテナのサービス名はmysqlです。」

抽象化のポイント

  • customer_masteritems(汎用的なテーブル名)
  • クレジットカード番号item_data(機密情報を隠す)
  • sk_live_abc123xyzgetenv('API_KEY')(環境変数に置き換え)
  • /var/www/company-internal/production → 削除

5.4 質問の具体例(NG例とOK例)

❌ NG例:具体的すぎる質問

「うちの会社のusersテーブル(user_id, email, password, created_at)に
接続するコードを書いて」

→ 実際のテーブル構造を晒している

✅ OK例:抽象化した質問

「TODOリストのtodosテーブル(id, title, status, created_at)に
接続するクラスを作りたいです。
PDOを使ったクラスベースの実装例を教えてください。」

→ 完全にダミーのデータ構造

5.5 実践的な質問テクニック

効果的な質問の5つのコツ

  1. 具体的かつ明確に指示する

    • ❌ 悪い例:「エラーが出ました」
    • ✅ 良い例:「PHPで500エラーが出ました。エラーログには○○と表示されています」
  2. 背景や文脈を共有する

    • 使用している技術スタック(PHP 8.2、MySQL 8.0など)
    • 何を実現しようとしているか
    • どこまで試したか
  3. 段階的に質問する

    • 複雑な問題は小さく分割
    • 1回で完璧を求めない
    • 最初の回答を元に質問を改善
  4. コーディングの参考例を含める

    • 自分が書いた類似コード(匿名化済み)
    • 期待する動作の例
  5. 役割を指定する

    • 「あなたは10年のPHP経験があるエンジニアです」
    • 「セキュリティ専門家として回答してください」

6. Composerの導入

Composerは、PHPのパッケージ依存管理ツールです。npmやpipと似たようなツールです。

6.1 Composerとは

Composerの役割

  • 外部ライブラリ(パッケージ)を管理
  • 依存関係を自動解決
  • オートロード機能でrequire文不要

メリット

  • 依存関係を自動解決
  • バージョン管理が楽
  • チーム開発で環境を統一できる
  • オートロード機能でrequire文不要

6.2 インストール方法

Docker環境の場合

Dockerfileに以下を追加します。

FROM php:8-apache

# Composerをインストール
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# PDO MySQL・mysqli拡張機能をインストール
RUN docker-php-ext-install pdo pdo_mysql mysqli

# Apacheのmod_rewriteを有効化
RUN a2enmod rewrite

6.3 composer.jsonの作成

プロジェクトのルートディレクトリにcomposer.jsonを作成します。

{
    "name": "php-todo-project/todo-app",
    "description": "TODOリストアプリケーション",
    "type": "project",
    "require": {
        "php": ">=8.0"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}

6.4 autoloadの設定

PSR-4とは

  • PHP-FIGが策定した、クラスの自動読み込み規約
  • 名前空間とディレクトリ構造を対応させる

設定例

{
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}

意味

  • App\名前空間 → src/ディレクトリ
  • App\Models\TodoModelsrc/Models/TodoModel.php

6.5 composer install実行

composer install

実行結果

  • vendor/ディレクトリが作成される
  • vendor/autoload.phpが生成される

6.6 autoloadの確認

www/index.phpで使用

<?php
require __DIR__ . '/../vendor/autoload.php';

use App\Models\TodoModel;

$model = new TodoModel();
$todos = $model->getAll();
?>

ポイント

  • vendor/autoload.phpを1回読み込むだけで、全てのクラスが使える
  • use文で名前空間をインポート

7. 名前空間(namespace)の基礎

名前空間は、クラスや関数の名前が重複しないように「区分け」する仕組みです。

7.1 名前空間とは

役割

  • クラス名・関数名の衝突を防ぐ
  • ファイルシステムのディレクトリのように階層的に管理
  • コードの整理と可読性向上
  • PSR-4と組み合わせてオートロード

よくある例

<?php
// ファイル1: src/App/User.php
class User {
    public function getName() {
        return "App User";
    }
}

// ファイル2: src/Admin/User.php
class User {
    public function getName() {
        return "Admin User";
    }
}

// エラー! Cannot redeclare class User

名前空間を使えば、この問題が解決します。

7.2 namespaceの宣言方法

基本構文

<?php
namespace App\Models;

class User {
    public function getName() {
        return "User";
    }
}

重要なポイント

  • ファイルの先頭(<?phpの直後)に記述
  • HTMLや他のコードより前に書く(declareを除く)
  • 階層は\(バックスラッシュ)で区切る
  • サブ名前空間は何階層でもOK

7.3 use文でインポート

完全修飾名で呼び出す

<?php
$user = new \App\Models\User();

use文でインポート

<?php
use App\Models\User;

$user = new User();

複数のクラスをインポート

<?php
use App\Models\User;
use App\Models\TodoModel;

7.4 autoloadとの連携

PSR-4規約に従う

  • App\Models\Userクラス → src/Models/User.php
  • App\Controllers\TodoControllersrc/Controllers/TodoController.php

composer.jsonの設定

{
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}

結果

  • use App\Models\User;と書くだけで、自動的にsrc/Models/User.phpが読み込まれる
  • require文が不要

8. リファクタリング後の動作確認

実際に動かして確認しましょう。

8.1 composer install実行

composer install

確認

  • vendor/ディレクトリが作成されているか
  • vendor/autoload.phpが存在するか

8.2 autoload確認

www/index.phpを更新

実際の実装では、TodoControllerクラスを使用しています:

<?php
require __DIR__ . '/../project/vendor/autoload.php';

use App\Controllers\TodoController;

// セッション開始
session_start();

$controller = new TodoController();
$todos = $controller->index();

// CSRFトークン生成(フォーム用)
if (!isset($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>TODO一覧</title>
</head>
<body>
    <h1>TODO一覧</h1>
    <?php if (empty($todos)): ?>
        <p>TODOがありません</p>
    <?php else: ?>
        <ul>
            <?php foreach ($todos as $todo): ?>
                <li><?php echo htmlspecialchars($todo['title'], ENT_QUOTES, 'UTF-8'); ?></li>
            <?php endforeach; ?>
        </ul>
    <?php endif; ?>
</body>
</html>

ポイント:

  • require __DIR__ . '/../project/vendor/autoload.php';でautoloadを読み込み(Docker環境のパス構造に対応)
  • TodoControllerクラスを使用
  • セッション管理を実装

8.3 よくあるエラー

エラー1:クラスが見つからない

症状

Fatal error: Uncaught Error: Class 'App\Models\TodoModel' not found

原因

  • composer installを実行していない
  • vendor/autoload.phpを読み込んでいない

対処法

composer install

エラー2:名前空間のパスが間違っている

症状

Fatal error: Uncaught Error: Class 'App\Models\TodoModel' not found

原因

  • ファイルの配置場所が間違っている
  • composer.jsonの設定が間違っている

対処法

  • src/Models/TodoModel.phpに配置されているか確認
  • composer.jsonautoload設定を確認

9. まとめ|次のステップ

おめでとうございます!手続き型コードをクラスベースに書き換え、保守性を向上させました。

この記事で学んだこと

  • PHPのクラス基本(プロパティ、メソッド、コンストラクタ)
  • 手続き型からクラスベースへの変換
  • Composerの使い方(autoload、PSR-4)
  • 名前空間の基礎
  • 生成AIへの質問を抽象化するテクニック

保守性を意識した書き方

  • ✅ クラスでデータと処理をまとめる
  • ✅ Composerのautoloadでrequire文を減らす
  • ✅ 名前空間でクラス名の衝突を防ぐ
  • ✅ プロパティはprivateで隠蔽

次のステップ

次回は、実務で必要なデバッグ手法とセキュリティチェック を学びます。デバッグツールの使い方、セッション管理、CSRF対策、レガシーコードのアンチパターンまで実践的に学びます。

困った時は

  • エラーが出たら、まずcomposer dump-autoloadでautoloadを再生成
  • 詰まったら、生成AIに質問してみましょう(ただし、セキュリティは守って)
  • 生成AIとセキュリティの基礎知識の匿名化チェックリストを確認

10. 参考資料

10.1 PHP公式ドキュメント

10.2 信頼できる解説記事


11. 📚 シリーズ記事一覧

この記事: 手続き型PHPをクラスベースにリファクタリング

前の記事: PHPでTODOリスト作成(CRUDとセキュリティ対策)
→ TODOリストのCRUD操作を実装しながら、XSS対策やバリデーションも学びます。

次の記事: PHP実務で使うデバッグとセキュリティチェックリスト
→ 実務で必要なデバッグ手法とセキュリティチェックを実践的に学びます。

シリーズ全体

  1. 生成AIとセキュリティの基礎知識
  2. DockerでPHP+MySQL開発環境を作る
  3. PHP基礎文法とDB接続の安全な書き方
  4. PHPでTODOリスト作成(CRUDとセキュリティ対策)
  5. 手続き型PHPをクラスベースにリファクタリング ← 今ここ
  6. PHP実務で使うデバッグとセキュリティチェックリスト
  7. 攻撃者の視点で学ぶPHPセキュリティ

Discussion