CodeIgniter 4 とは?最新バージョンの特徴と強み
軽量で高速なPHPフレームワークの代表格
CodeIgniter 4(以下、CI4)は、シンプルながらパワフルな機能を備えたPHPフレームワークとして知られています。特に以下の特徴が、多くの開発者から支持されている理由です:
- メモリ効率の高さ
- フレームワークコアが約2MB未満と軽量
- 最小限の依存パッケージで動作
- 効率的なメモリ管理システム
- 処理速度の優位性
// パフォーマンス計測例
use CodeIgniter\Debug\Timer;
$timer = new Timer();
$timer->start('benchmark');
// アプリケーションコード
$result = $model->find();
$timer->stop('benchmark');
echo $timer->getElapsedTime('benchmark'); // 一般的なフレームワークの1.5-2倍の速度
- シンプルなアーキテクチャ
- 直感的なMVCパターン
- 明確なディレクトリ構造
- 最小限の設定で開始可能
バージョン4で劇的に進化した新機能と改善点
- モダンPHPの完全採用
namespace App\Controllers;
use CodeIgniter\Controller;
use App\Models\UserModel;
class Users extends Controller
{
protected $userModel;
public function __construct()
{
$this->userModel = new UserModel();
}
public function index()
{
return view('users/index', [
'users' => $this->userModel->findAll()
]);
}
}
- 主要な機能強化
- PSR-4準拠の名前空間対応
- Composerによる依存管理
- 強化されたデータベース抽象化層
- 新しいセキュリティ機能の導入
- 開発効率を向上させる新機能
- 充実したCLIツール
- エンティティクラスのサポート
- 高度なルーティングシステム
- キャッシュシステムの刷新
他のPHPフレームワークと比較したメリット
- 開発生産性の比較
| 機能 | CodeIgniter 4 | Laravel | Symfony |
|---|---|---|---|
| 初期学習時間 | 1-2週間 | 1-2ヶ月 | 2-3ヶ月 |
| 設定の複雑さ | 低 | 中 | 高 |
| ドキュメント充実度 | ◎ | ◎ | ○ |
| 日本語資料 | ○ | ◎ | △ |
- パフォーマンス面での優位性
- 最小構成時のメモリ使用量:約4MB
- リクエスト処理時間:平均30%削減
- アプリケーション起動時間:Laravel比約50%減
- プロジェクトに最適な選択基準
- スピーディーな開発が求められるプロジェクト
- リソースに制約のある環境での開発
- APIサーバーの構築
- 中小規模のWebアプリケーション
- フレームワークの柔軟性
- 最小限の制約
- 容易なカスタマイズ
- サードパーティライブラリとの統合のしやすさ
以上の特徴から、CI4は特に「高速な開発」と「高いパフォーマンス」が求められるプロジェクトにおいて、最適な選択肢の一つとなっています。
CodeIgniter 4 の環境構築手順
必要な開発環境とシステム要件
CodeIgniter 4を快適に動作させるために、以下のシステム要件を満たす必要があります:
- PHP要件
- PHP バージョン 7.4以上(8.0以上推奨)
- 必須PHP拡張機能:
- intl
- json
- mbstring
- mysqlnd(MySQLを使用する場合)
- xml
- curl
- データベース要件(選択可能)
- MySQL (5.1+)
- PostgreSQL (7.4+)
- SQLite3
- Microsoft SQL Server (2005+)
- Webサーバー要件
- Apache 2.4+ with mod_rewrite
- Nginx 1.13+
# Nginxの基本設定例 location / { try_files $uri $uri/ /index.php$is_args$args; } - 推奨開発ツール
- Composer(パッケージ管理)
- Git(バージョン管理)
- VS Code / PHPStorm(IDE)
Composer を使用したインストール方法
- プロジェクトの新規作成
# プロジェクト作成コマンド composer create-project codeigniter4/appstarter ci4-project # プロジェクトディレクトリへ移動 cd ci4-project # 開発サーバーの起動 php spark serve
- 既存プロジェクトへの導入
# Composerを使用してCodeIgniter 4をインストール composer require codeigniter4/framework # 設定ファイルとpublicディレクトリの準備 php spark install:config
- インストール後の確認
# バージョン確認 php spark --version # 環境チェック php spark env:check # ルートの確認 php spark routes
初期設定と基本的な環境設定のポイント
- 環境設定ファイルの準備
# .envファイルの作成 cp env .env # 環境設定の編集 # CI_ENVIRONMENT = development # app.baseURL = 'http://localhost:8080/'
- データベース設定
// app/Config/Database.php
public $default = [
'DSN' => '',
'hostname' => 'localhost',
'username' => 'your_username',
'password' => 'your_password',
'database' => 'your_database',
'DBDriver' => 'MySQLi',
'DBPrefix' => '',
'pConnect' => false,
'DBDebug' => true,
'charset' => 'utf8',
'DBCollat' => 'utf8_general_ci',
'swapPre' => '',
'encrypt' => false,
'compress' => false,
'strictOn' => false,
'failover' => [],
'port' => 3306,
];
- セキュリティ設定
// app/Config/Security.php public $tokenName = 'csrf_token_name'; public $headerName = 'X-CSRF-TOKEN'; public $cookieName = 'csrf_cookie_name'; public $expires = 7200;
- アプリケーション基本設定
// app/Config/App.php public $baseURL = 'http://localhost:8080/'; public $indexPage = ''; public $defaultLocale = 'ja'; public $negotiateLocale = true; public $supportedLocales = ['en', 'ja'];
- エラー報告とロギング設定
// app/Config/Logger.php
public $threshold = 4; // 開発環境では4(全てのログを記録)
// ログハンドラの設定
public $handlers = [
'file' => [
'class' => 'CodeIgniter\Log\Handlers\FileHandler',
'baseDir' => WRITEPATH . 'logs/',
'filePermission' => 0644,
'fileExtension' => 'log'
]
];
- 開発環境特有の設定
// 開発時の推奨設定
error_reporting(-1);
ini_set('display_errors', '1');
以上の設定が完了すれば、CodeIgniter 4での開発を開始する準備が整います。環境構築時に特に注意が必要な点は以下の通りです:
- 本番環境では必ず
CI_ENVIRONMENTをproductionに設定 - セキュリティ関連の設定は本番環境で再確認
- データベース接続情報は環境ごとに適切に管理
- ログレベルは環境に応じて適切に設定
これらの設定を適切に行うことで、安全で効率的な開発環境を構築することができます。
CodeIgniter 4 の基本アーキテクチャ理解
MVCパターンの実装方法と特徴
CodeIgniter 4のMVCパターンは、シンプルながら強力な実装を提供しています。
- コントローラーの基本実装
namespace App\Controllers;
use CodeIgniter\Controller;
use App\Models\UserModel;
class Users extends Controller
{
protected $userModel;
public function __construct()
{
// モデルのインスタンス化
$this->userModel = new UserModel();
}
public function index()
{
$data = [
'users' => $this->userModel->findAll(),
'title' => 'ユーザー一覧'
];
// ビューの読み込みと表示
return view('users/index', $data);
}
public function show($id)
{
$user = $this->userModel->find($id);
if ($user === null) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
return view('users/show', ['user' => $user]);
}
}
- モデルの実装例
namespace App\Models;
use CodeIgniter\Model;
class UserModel extends Model
{
protected $table = 'users';
protected $primaryKey = 'id';
protected $returnType = 'array';
protected $allowedFields = ['name', 'email', 'password'];
// バリデーションルール
protected $validationRules = [
'name' => 'required|min_length[3]',
'email' => 'required|valid_email|is_unique[users.email]',
'password' => 'required|min_length[8]'
];
// カスタムコールバック
protected $beforeInsert = ['hashPassword'];
protected function hashPassword(array $data)
{
if (isset($data['data']['password'])) {
$data['data']['password'] = password_hash($data['data']['password'], PASSWORD_DEFAULT);
}
return $data;
}
}
- ビューの実装方法
<!-- app/Views/users/index.php -->
<?= $this->extend('layouts/main') ?>
<?= $this->section('content') ?>
<h1><?= esc($title) ?></h1>
<div class="user-list">
<?php foreach ($users as $user): ?>
<div class="user-item">
<h3><?= esc($user['name']) ?></h3>
<p><?= esc($user['email']) ?></p>
<a href="/users/<?= $user['id'] ?>">詳細を見る</a>
</div>
<?php endforeach; ?>
</div>
<?= $this->endSection() ?>
ルーティングの設定と活用テクニック
- 基本的なルーティング設定
// app/Config/Routes.php
$routes->get('/', 'Home::index');
$routes->get('users', 'Users::index');
$routes->get('users/(:num)', 'Users::show/$1');
// RESTfulリソースルート
$routes->resource('api/users');
// グループ化したルート
$routes->group('admin', ['filter' => 'auth'], function($routes) {
$routes->get('users', 'Admin\Users::index');
$routes->get('users/new', 'Admin\Users::new');
$routes->post('users', 'Admin\Users::create');
});
- カスタムルートフィルターの実装
// app/Filters/AuthFilter.php
namespace App\Filters;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Filters\FilterInterface;
class AuthFilter implements FilterInterface
{
public function before(RequestInterface $request, $arguments = null)
{
if (!session()->get('logged_in')) {
return redirect()->to('/login');
}
}
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
{
// アクション後の処理
}
}
依存性注入とサービスの活用方法
- サービスクラスの作成
namespace App\Services;
class UserService
{
protected $userModel;
protected $emailService;
public function __construct(UserModel $userModel, EmailService $emailService)
{
$this->userModel = $userModel;
$this->emailService = $emailService;
}
public function registerUser(array $userData)
{
// トランザクション処理
$db = \Config\Database::connect();
$db->transStart();
try {
// ユーザー登録
$userId = $this->userModel->insert($userData);
// ウェルカムメール送信
$this->emailService->sendWelcomeEmail($userData['email']);
$db->transComplete();
return $userId;
} catch (\Exception $e) {
$db->transRollback();
throw $e;
}
}
}
- Services.phpでのサービス登録
// app/Config/Services.php
public static function userService($getShared = true)
{
if ($getShared) {
return static::getSharedInstance('userService');
}
return new \App\Services\UserService(
model('UserModel'),
service('email')
);
}
- コントローラーでのサービス利用
class RegistrationController extends Controller
{
protected $userService;
public function __construct()
{
$this->userService = service('userService');
}
public function register()
{
try {
$userData = $this->request->getPost();
$userId = $this->userService->registerUser($userData);
return redirect()->to('/login')
->with('success', '登録が完了しました');
} catch (\Exception $e) {
return redirect()->back()
->with('error', '登録に失敗しました')
->withInput();
}
}
}
これらのアーキテクチャパターンを適切に活用することで、保守性が高く、拡張性のあるアプリケーションを構築することが可能です。特に、依存性注入を活用することで、テスタビリティの向上やコンポーネント間の結合度の低減を実現できます。
データベースオペレーションとモデルの実践的な使い方
モデルクラスを使用したCRUDオペレーションの実装
- モデルの基本設定と実装
namespace App\Models;
use CodeIgniter\Model;
class ProductModel extends Model
{
protected $table = 'products';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = true;
protected $allowedFields = [
'name', 'description', 'price',
'category_id', 'stock', 'status'
];
// 日付関連のフィールド
protected $useTimestamps = true;
protected $dateFormat = 'datetime';
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
// バリデーションルール
protected $validationRules = [
'name' => 'required|min_length[3]|max_length[255]',
'price' => 'required|numeric|greater_than[0]',
'category_id' => 'required|integer|is_not_unique[categories.id]',
'stock' => 'required|integer|greater_than_equal_to[0]'
];
protected $validationMessages = [
'name' => [
'required' => '商品名は必須です',
'min_length' => '商品名は3文字以上で入力してください'
],
'price' => [
'required' => '価格は必須です',
'numeric' => '価格は数値で入力してください',
'greater_than' => '価格は0より大きい値を入力してください'
]
];
}
- CRUD操作の実装例
class ProductController extends BaseController
{
protected $productModel;
public function __construct()
{
$this->productModel = new ProductModel();
}
// Create操作
public function create()
{
$data = [
'name' => $this->request->getPost('name'),
'price' => $this->request->getPost('price'),
'category_id' => $this->request->getPost('category_id'),
'stock' => $this->request->getPost('stock')
];
try {
$this->productModel->insert($data);
return redirect()->to('/products')
->with('success', '商品が登録されました');
} catch (\Exception $e) {
return redirect()->back()
->with('error', '商品の登録に失敗しました')
->withInput();
}
}
// Read操作(一覧取得)
public function index()
{
$data['products'] = $this->productModel
->select('products.*, categories.name as category_name')
->join('categories', 'categories.id = products.category_id')
->paginate(10);
$data['pager'] = $this->productModel->pager;
return view('products/index', $data);
}
// Update操作
public function update($id)
{
$data = $this->request->getPost();
try {
$this->productModel->update($id, $data);
return redirect()->to('/products')
->with('success', '商品が更新されました');
} catch (\Exception $e) {
return redirect()->back()
->with('error', '商品の更新に失敗しました');
}
}
// Delete操作(ソフトデリート)
public function delete($id)
{
try {
$this->productModel->delete($id);
return redirect()->to('/products')
->with('success', '商品が削除されました');
} catch (\Exception $e) {
return redirect()->back()
->with('error', '商品の削除に失敗しました');
}
}
}
クエリビルダの効果的な活用方法
- 高度な検索条件の実装
public function search()
{
$builder = $this->productModel->builder();
// 検索条件の構築
if ($category = $this->request->getGet('category')) {
$builder->where('category_id', $category);
}
if ($minPrice = $this->request->getGet('min_price')) {
$builder->where('price >=', $minPrice);
}
if ($maxPrice = $this->request->getGet('max_price')) {
$builder->where('price <=', $maxPrice);
}
if ($keyword = $this->request->getGet('keyword')) {
$builder->groupStart()
->like('name', $keyword)
->orLike('description', $keyword)
->groupEnd();
}
// 在庫状況でのフィルタリング
if ($this->request->getGet('in_stock') === 'true') {
$builder->where('stock >', 0);
}
return $builder->select('products.*, categories.name as category_name')
->join('categories', 'categories.id = products.category_id')
->orderBy('products.created_at', 'DESC')
->paginate(10);
}
- 集計クエリの実装
public function getProductStats()
{
$builder = $this->productModel->builder();
return $builder->select('
COUNT(*) as total_products,
SUM(stock) as total_stock,
AVG(price) as average_price,
MIN(price) as min_price,
MAX(price) as max_price
')->get()->getRow();
}
マイグレーションとシーディングの運用手法
- マイグレーションファイルの作成と実装
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateProductsTable extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true
],
'name' => [
'type' => 'VARCHAR',
'constraint' => 255
],
'description' => [
'type' => 'TEXT',
'null' => true
],
'price' => [
'type' => 'DECIMAL',
'constraint' => '10,2'
],
'category_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true
],
'stock' => [
'type' => 'INT',
'constraint' => 11,
'default' => 0
],
'status' => [
'type' => 'ENUM',
'constraint' => ['active', 'inactive'],
'default' => 'active'
],
'created_at' => [
'type' => 'DATETIME',
'null' => true
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true
],
'deleted_at' => [
'type' => 'DATETIME',
'null' => true
]
]);
$this->forge->addKey('id', true);
$this->forge->addForeignKey('category_id', 'categories', 'id', 'CASCADE', 'CASCADE');
$this->forge->createTable('products');
}
public function down()
{
$this->forge->dropTable('products');
}
}
- シーダーの実装
namespace App\Database\Seeds;
use CodeIgniter\Database\Seeder;
class ProductSeeder extends Seeder
{
public function run()
{
$data = [
[
'name' => 'サンプル商品1',
'description' => '商品の説明文です。',
'price' => 1000,
'category_id' => 1,
'stock' => 100,
'status' => 'active',
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s')
],
// 他のサンプルデータ...
];
$this->db->table('products')->insertBatch($data);
}
}
- マイグレーションとシーディングの実行コマンド
# マイグレーションの実行 php spark migrate # 特定のマイグレーションの実行 php spark migrate --group=products # マイグレーションのロールバック php spark migrate:rollback # シーディングの実行 php spark db:seed ProductSeeder
これらの実装パターンを活用することで、効率的なデータベース操作と保守性の高いアプリケーション開発が可能になります。特に、マイグレーションとシーディングを活用することで、開発環境とテスト環境の一貫性を保ちやすくなります。
セキュリティ対策と実務での注意点
CSRFプロテクションの実装方法
- 基本的なCSRF対策の設定
// app/Config/Security.php public $csrfProtection = 'cookie'; public $tokenName = 'csrf_token_name'; public $cookieName = 'csrf_cookie_name'; public $expires = 7200;
- フォームでのCSRFトークンの実装
<!-- ビューファイルでの実装 -->
<form method="post" action="/users/create">
<?= csrf_field() ?>
<input type="text" name="username">
<input type="password" name="password">
<button type="submit">登録</button>
</form>
- APIでのCSRF対策
// app/Filters/APICSRFFilter.php
namespace App\Filters;
use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
class APICSRFFilter implements FilterInterface
{
public function before(RequestInterface $request, $arguments = null)
{
// APIトークンの検証
$token = $request->getHeaderLine('X-CSRF-TOKEN');
if (!$token || !csrf_verify($token)) {
return service('response')
->setStatusCode(403)
->setJSON(['error' => 'Invalid CSRF token']);
}
}
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
{
}
}
XSS対策とバリデーションの設定
- 入力バリデーションの実装
// app/Models/UserModel.php
protected $validationRules = [
'username' => [
'rules' => 'required|min_length[4]|max_length[20]|alpha_numeric',
'errors' => [
'required' => 'ユーザー名は必須です',
'min_length' => 'ユーザー名は4文字以上必要です',
'alpha_numeric' => '英数字のみ使用可能です'
]
],
'email' => [
'rules' => 'required|valid_email|is_unique[users.email]',
'errors' => [
'is_unique' => 'このメールアドレスは既に登録されています'
]
],
'password' => [
'rules' => 'required|min_length[8]|containsSpecialCharacter[1]|containsNumber[1]',
'errors' => [
'containsSpecialCharacter' => '特殊文字を1文字以上含める必要があります',
'containsNumber' => '数字を1文字以上含める必要があります'
]
]
];
// カスタムバリデーションルール
public function containsSpecialCharacter(string $str, string $fields, array $data): bool
{
return preg_match('/[^a-zA-Z0-9]/', $str) > 0;
}
- XSS対策の実装
// コントローラーでの出力エスケープ
public function show($id)
{
$user = $this->userModel->find($id);
return view('users/show', [
'user' => [
'name' => esc($user['name']),
'bio' => esc($user['bio'], 'html'),
'website' => esc($user['website'], 'url')
]
]);
}
// ビューでの安全な出力
<div class="user-profile">
<h1><?= esc($user['name']) ?></h1>
<div class="bio"><?= esc($user['bio'], 'html') ?></div>
<a href="<?= esc($user['website'], 'url') ?>">ウェブサイト</a>
</div>
セッション管理とユーザー認証の実装
- セッション設定
// app/Config/Session.php public $driver = 'CodeIgniter\Session\Handlers\FileHandler'; public $cookieName = 'ci_session'; public $expiration = 7200; public $savePath = WRITEPATH . 'session'; public $matchIP = false; public $timeToUpdate = 300;
- 認証システムの実装
// app/Libraries/Auth.php
namespace App\Libraries;
class Auth
{
protected $session;
protected $userModel;
public function __construct()
{
$this->session = service('session');
$this->userModel = new \App\Models\UserModel();
}
public function attempt(string $email, string $password): bool
{
$user = $this->userModel->where('email', $email)->first();
if (!$user) {
return false;
}
if (!password_verify($password, $user['password'])) {
// 失敗回数のカウントと制限
$this->incrementLoginAttempts($email);
return false;
}
// セッションの再生成(セッション固定攻撃対策)
$this->session->regenerate();
$this->session->set([
'user_id' => $user['id'],
'user_email' => $user['email'],
'isLoggedIn' => true,
'last_activity' => time()
]);
return true;
}
protected function incrementLoginAttempts(string $email): void
{
$attempts = cache()->get('login_attempts_' . md5($email)) ?? 0;
cache()->save('login_attempts_' . md5($email), ++$attempts, 300);
if ($attempts >= 5) {
throw new \Exception('Too many login attempts. Please try again later.');
}
}
public function check(): bool
{
if (!$this->session->get('isLoggedIn')) {
return false;
}
// セッションの有効期限チェック
$lastActivity = $this->session->get('last_activity');
if (time() - $lastActivity > config('Session')->expiration) {
$this->session->destroy();
return false;
}
// セッションの更新
$this->session->set('last_activity', time());
return true;
}
public function logout(): void
{
$this->session->destroy();
}
}
- 認証フィルターの実装
// app/Filters/AuthFilter.php
namespace App\Filters;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Filters\FilterInterface;
class AuthFilter implements FilterInterface
{
public function before(RequestInterface $request, $arguments = null)
{
$auth = service('auth');
if (!$auth->check()) {
// APIリクエストの場合
if ($request->isAJAX()) {
return service('response')
->setStatusCode(401)
->setJSON(['error' => 'Unauthorized']);
}
// 通常のリクエストの場合
return redirect()
->to('/login')
->with('error', 'ログインが必要です');
}
}
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
{
}
}
これらのセキュリティ対策を実装することで、一般的な攻撃からアプリケーションを保護することができます。ただし、セキュリティは常に最新の脅威に対応する必要があるため、定期的な見直しと更新が重要です。
実践的なアプリケーション開発手法
RESTful APIの構築方法
- RESTful APIコントローラーの実装
namespace App\Controllers\API;
use CodeIgniter\RESTful\ResourceController;
use CodeIgniter\API\ResponseTrait;
class Products extends ResourceController
{
use ResponseTrait;
protected $modelName = 'App\Models\ProductModel';
protected $format = 'json';
// GET /api/products
public function index()
{
$products = $this->model->findAll();
return $this->respond($products);
}
// GET /api/products/{id}
public function show($id = null)
{
$product = $this->model->find($id);
if ($product === null) {
return $this->failNotFound('商品が見つかりません');
}
return $this->respond($product);
}
// POST /api/products
public function create()
{
$data = $this->request->getJSON();
if (!$this->validate($this->model->validationRules)) {
return $this->failValidationErrors($this->validator->getErrors());
}
$id = $this->model->insert($data);
if ($id === false) {
return $this->failServerError('商品の登録に失敗しました');
}
$product = $this->model->find($id);
return $this->respondCreated($product);
}
// PUT /api/products/{id}
public function update($id = null)
{
$data = $this->request->getJSON();
if (!$this->model->find($id)) {
return $this->failNotFound('更新対象の商品が見つかりません');
}
if (!$this->validate($this->model->validationRules)) {
return $this->failValidationErrors($this->validator->getErrors());
}
if ($this->model->update($id, $data) === false) {
return $this->failServerError('商品の更新に失敗しました');
}
return $this->respond(['message' => '商品が更新されました']);
}
// DELETE /api/products/{id}
public function delete($id = null)
{
if (!$this->model->find($id)) {
return $this->failNotFound('削除対象の商品が見つかりません');
}
if ($this->model->delete($id) === false) {
return $this->failServerError('商品の削除に失敗しました');
}
return $this->respondDeleted(['message' => '商品が削除されました']);
}
}
- APIフィルターの実装
namespace App\Filters;
use CodeIgniter\Filters\FilterInterface;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
class APIAuthFilter implements FilterInterface
{
public function before(RequestInterface $request, $arguments = null)
{
$apiKey = $request->getHeaderLine('X-API-Key');
if (!$this->validateApiKey($apiKey)) {
return service('response')
->setStatusCode(401)
->setJSON([
'status' => 401,
'error' => '無効なAPIキーです'
]);
}
}
protected function validateApiKey($apiKey)
{
// APIキーの検証ロジック
return true; // 実際の実装では適切な検証を行う
}
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
{
}
}
キャッシュ機能の効果的な活用
- キャッシュの基本設定
// app/Config/Cache.php
public $handler = 'file';
public $backupHandler = 'dummy';
public $prefix = '';
public $ttl = 60;
public $reservedCharacters = '{}()/\@:';
// Redisハンドラーの設定例
public $redis = [
'host' => '127.0.0.1',
'password' => null,
'port' => 6379,
'timeout' => 0,
'database' => 0,
];
- キャッシュの実践的な活用例
class ProductController extends BaseController
{
public function index()
{
$cache = \Config\Services::cache();
$cacheKey = 'products_list_' . md5(json_encode($this->request->getGet()));
// キャッシュの取得を試みる
if ($products = $cache->get($cacheKey)) {
return $this->response->setJSON($products);
}
// キャッシュがない場合はデータを取得
$products = $this->productModel
->select('products.*, categories.name as category_name')
->join('categories', 'categories.id = products.category_id')
->findAll();
// キャッシュに保存(5分間)
$cache->save($cacheKey, $products, 300);
return $this->response->setJSON($products);
}
// キャッシュの手動クリア
protected function clearProductCache()
{
$cache = \Config\Services::cache();
$cache->deleteMatching('products_list_*');
}
}
ユニットテストとデバッグの進め方
- テストクラスの実装
namespace App\Tests;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\DatabaseTestTrait;
use CodeIgniter\Test\FeatureTestTrait;
class ProductTest extends CIUnitTestCase
{
use DatabaseTestTrait;
use FeatureTestTrait;
protected $refresh = true;
protected $seed = 'ProductSeeder';
public function testCreate()
{
$data = [
'name' => 'テスト商品',
'price' => 1000,
'category_id' => 1,
'stock' => 100
];
$result = $this->withHeaders([
'X-API-Key' => 'your-api-key'
])->post('api/products', $data);
$result->assertStatus(201)
->assertJSONFragment(['name' => 'テスト商品']);
}
public function testInvalidCreate()
{
$data = [
'name' => '', // 空の名前(バリデーションエラー)
'price' => -100, // 負の価格
];
$result = $this->withHeaders([
'X-API-Key' => 'your-api-key'
])->post('api/products', $data);
$result->assertStatus(400)
->assertJSONFragment(['errors']);
}
public function testUpdate()
{
$data = [
'name' => '更新後の商品名',
'price' => 2000
];
$result = $this->withHeaders([
'X-API-Key' => 'your-api-key'
])->put('api/products/1', $data);
$result->assertStatus(200)
->assertJSONFragment(['message' => '商品が更新されました']);
}
}
- デバッグツールの活用
// デバッグバーの設定
// app/Config/Toolbar.php
public $collectors = [
\CodeIgniter\Debug\Toolbar\Collectors\Timers::class,
\CodeIgniter\Debug\Toolbar\Collectors\Database::class,
\CodeIgniter\Debug\Toolbar\Collectors\Logs::class,
\CodeIgniter\Debug\Toolbar\Collectors\Views::class,
\CodeIgniter\Debug\Toolbar\Collectors\Cache::class,
\CodeIgniter\Debug\Toolbar\Collectors\Files::class,
\CodeIgniter\Debug\Toolbar\Collectors\Routes::class,
\CodeIgniter\Debug\Toolbar\Collectors\Events::class,
];
// コード内でのデバッグ
public function debugExample()
{
// ログの出力
log_message('debug', 'デバッグメッセージ: {value}', ['value' => $someValue]);
// 変数の内容確認
dd($variable); // die and dump
// タイマーの使用
$benchmark = \Config\Services::timer();
$benchmark->start('my_timer');
// 処理
$benchmark->stop('my_timer');
echo $benchmark->getElapsedTime('my_timer');
}
これらの実践的な開発手法を適切に活用することで、保守性が高く、効率的なアプリケーション開発が可能になります。特に、テストとデバッグを重視することで、品質の高いアプリケーションを提供することができます。
CodeIgniter 4でのパフォーマンス最適化
キャッシュ戦略とその実装方法
- 階層的キャッシュの実装
class ProductService
{
protected $cache;
protected $productModel;
public function __construct()
{
$this->cache = \Config\Services::cache();
$this->productModel = new \App\Models\ProductModel();
}
public function getProductDetails($productId)
{
// 第1層: メモリキャッシュ
$cacheKey = "product_{$productId}";
$product = $this->cache->get($cacheKey);
if ($product === null) {
// 第2層: Redisキャッシュ
$redis = new \Redis();
$redis->connect('127.0.0.1', 6379);
$product = $redis->get($cacheKey);
if ($product === null) {
// 第3層: データベース
$product = $this->productModel->find($productId);
// キャッシュの更新
if ($product) {
$redis->setex($cacheKey, 3600, serialize($product));
$this->cache->save($cacheKey, $product, 300);
}
} else {
$product = unserialize($product);
$this->cache->save($cacheKey, $product, 300);
}
}
return $product;
}
}
- ビューキャッシュの活用
class CatalogController extends BaseController
{
public function categoryPage($categoryId)
{
$cacheKey = "category_page_{$categoryId}";
if (! $output = cache($cacheKey)) {
$data = [
'products' => $this->productModel
->where('category_id', $categoryId)
->findAll(),
'category' => $this->categoryModel->find($categoryId)
];
$output = view('catalog/category', $data);
// ビューをキャッシュに保存(1時間)
cache()->save($cacheKey, $output, 3600);
}
return $output;
}
}
データベースの最適化手法
- クエリの最適化
class OptimizedProductModel extends \CodeIgniter\Model
{
public function getProductsWithEfficiency($categoryId)
{
return $this->select('products.*,
categories.name as category_name,
COUNT(reviews.id) as review_count')
->join('categories', 'categories.id = products.category_id')
->join('reviews', 'reviews.product_id = products.id', 'left')
->where('products.category_id', $categoryId)
->groupBy('products.id')
->having('review_count >', 0)
->useIndex('idx_category') // インデックスの明示的な使用
->findAll();
}
// バルクインサートの実装
public function bulkInsertProducts($products, $batchSize = 100)
{
$chunks = array_chunk($products, $batchSize);
$this->db->transStart();
try {
foreach ($chunks as $chunk) {
$this->insertBatch($chunk);
}
$this->db->transComplete();
return $this->db->transStatus();
} catch (\Exception $e) {
$this->db->transRollback();
throw $e;
}
}
}
- インデックス最適化
-- 必要なインデックスの作成 CREATE INDEX idx_category ON products(category_id); CREATE INDEX idx_price ON products(price); CREATE INDEX idx_created_at ON products(created_at); CREATE INDEX idx_status_category ON products(status, category_id); -- 複合インデックスの作成 CREATE INDEX idx_category_price ON products(category_id, price);
- データベースコネクションの最適化
// app/Config/Database.php
public $default = [
'hostname' => 'localhost',
'username' => 'your_username',
'password' => 'your_password',
'database' => 'your_database',
'DBDriver' => 'MySQLi',
'DBPrefix' => '',
'pConnect' => false,
'DBDebug' => true,
'charset' => 'utf8mb4',
'DBCollat' => 'utf8mb4_unicode_ci',
'swapPre' => '',
'encrypt' => false,
'compress' => false,
'strictOn' => false,
'failover' => [],
'port' => 3306,
'persistent' => true,
// コネクションプーリングの設定
'pooling' => true,
'pool' => [
'max_connections' => 100,
'idle_timeout' => 60,
'wait_timeout' => 30,
],
];
本番環境でのパフォーマンスチューニング
- アプリケーション設定の最適化
// app/Config/App.php public $baseURL = 'https://your-production-domain.com/'; // キャッシュの有効化 public $cache = 'file'; // エラー表示の無効化 public $displayErrors = false; // ログレベルの調整 public $logThreshold = 3; // Error以上のみ記録 // セッション設定の最適化 public $sessionDriver = 'redis'; public $sessionExpiration = 7200;
- パフォーマンスモニタリングの実装
class PerformanceMonitor
{
protected $timer;
protected $logger;
public function __construct()
{
$this->timer = \Config\Services::timer();
$this->logger = \Config\Services::logger();
}
public function startMeasurement($point)
{
$this->timer->start($point);
}
public function endMeasurement($point)
{
$this->timer->stop($point);
$elapsed = $this->timer->getElapsedTime($point);
if ($elapsed > 1.0) { // 1秒以上かかった処理を記録
$this->logger->warning("Performance Alert: {$point} took {$elapsed} seconds");
}
return $elapsed;
}
public function getMemoryUsage()
{
$memory = memory_get_usage(true);
if ($memory > 64 * 1024 * 1024) { // 64MB以上使用している場合
$this->logger->warning("High Memory Usage: {$memory} bytes");
}
return $memory;
}
}
// 使用例
$monitor = new PerformanceMonitor();
$monitor->startMeasurement('database_query');
// データベースクエリの実行
$monitor->endMeasurement('database_query');
- 本番環境向けの.envファイル設定
# プロダクション環境設定 CI_ENVIRONMENT = production # データベース設定 database.default.hostname = your_production_db_host database.default.database = your_production_db database.default.username = your_production_user database.default.password = your_production_password database.default.DBDriver = MySQLi database.default.port = 3306 # キャッシュ設定 cache.handler = redis cache.redis.host = your_redis_host cache.redis.port = 6379 cache.redis.password = your_redis_password # セッション設定 session.handler = redis session.savePath = 'tcp://your_redis_host:6379' # メール設定 email.protocol = smtp email.SMTPHost = your_smtp_host email.SMTPUser = your_smtp_user email.SMTPPass = your_smtp_password email.SMTPPort = 587 email.SMTPCrypto = tls
これらのパフォーマンス最適化テクニックを適切に組み合わせることで、アプリケーションの応答性と安定性を大幅に向上させることができます。ただし、最適化は必ずモニタリングと計測に基づいて行い、実際のパフォーマンス改善を確認しながら進めることが重要です。