codeigniter について
CI3 または CI4 を扱う CodeIgniter 開発者向けの開発ガイドです。コーディング前にプロジェクトのバージョンを特定し、フレームワークのパターン、安全対策、基本機能、CI3 から CI4 への移行情報を扱います。
主な機能
- 01CI3 と CI4 のプロジェクトバージョン特定
- 02コントローラー、モデル、ルーティングのガイド
- 03クエリ、フォーム、セッションのパターン
- 04両バージョンのセキュリティチェックリスト
- 05CI3 から CI4 への移行対応表
活用シーン
- 01既存の CodeIgniter プロジェクトへの機能実装
- 02適用するバージョン別パターンの確認
- 03入力処理と出力エスケープの確認
- 04CI3 から CI4 への移行計画
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ yasserstudio/ codeigniter-skills --skill codeigniterインストール後、Claude Code で/codeigniterを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/ci3.mdci4.mddeployment.md詳細
CodeIgniter 開発
あなたは CodeIgniter のエキスパートです。CI3(3.1.x)と CI4(4.x、最新の安定版 4.7.x)の両方に対応し、それぞれの基本的なアーキテクチャの違いを理解しています。コードを書く前に、必ずプロジェクトで使用されているバージョンを特定してください。
バージョン固有の機能について詳しくは、次を参照してください。
- CI3 の詳細解説 — ファイルアップロード、メール、キャッシュ、ページネーション、ライブラリ、HMVC、フック、ヘルパー、ログ
- CI4 の詳細解説 — REST コントローラー、エンティティ、フィルター、マイグレーション、シーダー、アップロード、メール、キャッシュ、サービス、イベント、ビュー、セル、Spark CLI、テスト、Shield 認証、コンテンツネゴシエーション
- デプロイと移行 — サーバー設定、本番環境のチェックリスト、CI3 から CI4 への完全な移行対応表
バージョンの検出
| 手がかり | バージョン |
|---|---|
system/core/CodeIgniter.php が存在する | CI3 |
$this->load->model() / $this->load->view() | CI3 |
application/config/config.php | CI3 |
app/Config/App.php が存在する | CI4 |
コントローラー内に namespace App\Controllers; がある | CI4 |
プロジェクトのルートに spark CLI がある | CI4 |
composer.json が codeigniter4/framework を要求している | CI4 |
CI3 — 基本パターン
プロジェクト構成
project/
├── application/
│ ├── config/ # autoload.php, config.php, database.php, routes.php, hooks.php
│ ├── controllers/
│ ├── models/
│ ├── views/
│ ├── libraries/
│ ├── helpers/
│ ├── hooks/
│ ├── core/ # MY_Controller, MY_Model overrides
│ └── third_party/
├── system/ # framework core — never edit
└── index.php # front controller
コントローラー
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Products extends CI_Controller
{
public function __construct()
{
parent::__construct();
$this->load->model('product_model');
$this->load->helper('url');
}
public function index()
{
$data['products'] = $this->product_model->get_all();
$this->load->view('products/index', $data);
}
public function view($id)
{
$data['product'] = $this->product_model->get($id);
if (empty($data['product'])) {
show_404();
}
$this->load->view('products/view', $data);
}
}
ルール: クラス名はファイル名と一致させる必要があります(先頭は大文字)。必ず parent::__construct() を呼び出してください。Linux ではファイル名の大文字と小文字が区別されます。
モデル
<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Product_model extends CI_Model
{
protected $table = 'products';
public function get_all()
{
return $this->db->get($this->table)->result();
}
public function get($id)
{
return $this->db->get_where($this->table, ['id' => $id])->row();
}
public function insert($data)
{
$this->db->insert($this->table, $data);
return $this->db->insert_id();
}
public function update($id, $data)
{
$this->db->where('id', $id);
return $this->db->update($this->table, $data);
}
public function delete($id)
{
return $this->db->delete($this->table, ['id' => $id]);
}
}
クエリビルダー
// SELECT with chaining
$results = $this->db
->select('p.*, c.name as category_name')
->from('products p')
->join('categories c', 'c.id = p.category_id', 'left')
->where('p.active', 1)
->where('p.price >', 10)
->order_by('p.name', 'ASC')
->limit(20, $offset)
->get()
->result();
// INSERT / UPDATE / DELETE
$this->db->insert('products', ['name' => $name, 'price' => $price]);
$this->db->where('id', $id)->update('products', ['price' => $new_price]);
$this->db->where('id', $id)->delete('products');
// Transactions
$this->db->trans_start();
$this->db->insert('orders', $order_data);
$this->db->insert('order_items', $item_data);
$this->db->trans_complete();
if ($this->db->trans_status() === FALSE) { /* failed */ }
// Raw query (always bind params)
$this->db->query('SELECT * FROM products WHERE slug = ?', [$slug]);
ルーティング
$route['default_controller'] = 'home';
$route['404_override'] = 'errors/page_missing';
$route['products'] = 'catalog/index';
$route['products/(:num)'] = 'catalog/view/$1';
$route['api/products/(:any)'] = 'api/products/$1';
入力とセキュリティ
// ALWAYS use $this->input — never raw $_POST/$_GET
$name = $this->input->post('name', TRUE); // TRUE = XSS filter
$id = $this->input->get('id');
// CSRF — enable in config.php
$config['csrf_protection'] = TRUE;
// Output escaping in views
<?php echo html_escape($user_input); ?>
フォーム検証
$this->load->library('form_validation');
$this->form_validation->set_rules('name', 'Name', 'required|min_length[3]|max_length[255]');
$this->form_validation->set_rules('email', 'Email', 'required|valid_email|is_unique[users.email]');
if ($this->form_validation->run() === FALSE) {
$this->load->view('form', $data);
} else {
$this->product_model->insert($this->input->post());
redirect('products');
}
セッション
$this->session->set_userdata('user_id', $id);
$user_id = $this->session->userdata('user_id');
$this->session->set_flashdata('success', 'Product created.');
CI4 — 基本パターン
プロジェクト構成
project/
├── app/
│ ├── Config/ # App.php, Database.php, Routes.php, Filters.php, Services.php, Events.php
│ ├── Controllers/ # BaseController.php
│ ├── Models/
│ ├── Views/
│ ├── Filters/
│ ├── Entities/
│ ├── Cells/
│ ├── Commands/
│ ├── Database/ # Migrations/, Seeds/
│ └── Language/
├── public/ # web root — point server here
│ └── index.php
├── writable/ # cache, logs, session — must be writable by web server
├── tests/
├── .env
├── spark # CLI tool
└── composer.json
コントローラー
<?php
namespace App\Controllers;
class Products extends BaseController
{
public function index()
{
$model = model('ProductModel');
return view('products/index', ['products' => $model->findAll()]);
}
public function show($id = null)
{
$product = model('ProductModel')->find($id);
if ($product === null) {
throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
}
return view('products/show', ['product' => $product]);
}
public function create()
{
if (! $this->validate([
'name' => 'required|min_length[3]|max_length[255]',
'price' => 'required|numeric|greater_than[0]',
])) {
return view('products/create', ['validation' => $this->validator]);
}
model('ProductModel')->insert($this->request->getPost());
return redirect()->to('/products')->with('success', 'Product created.');
}
}
モデル
<?php
namespace App\Models;
use CodeIgniter\Model;
class ProductModel extends Model
{
protected $table = 'products';
protected $primaryKey = 'id';
protected $returnType = 'array';
protected $useSoftDeletes = true;
protected $useTimestamps = true;
protected $allowedFields = ['name', 'price', 'category_id', 'active'];
protected $validationRules = [
'name' => 'required|min_length[3]|max_length[255]',
'price' => 'required|numeric|greater_than[0]',
];
}
重要: $allowedFields は必須です。リストにないフィールドは、挿入/更新時に通知なく破棄されます。これにより一括代入を防止できます。
クエリビルダー
$db = db_connect();
// SELECT with joins + pagination
$model = model('ProductModel');
$data = [
'products' => $model->where('active', 1)->paginate(20),
'pager' => $model->pager,
];
// Raw builder
$results = $db->table('products')
->select('products.*, categories.name as category')
->join('categories', 'categories.id = products.category_id', 'left')
->where('products.active', 1)
->orderBy('products.name', 'ASC')
->get()
->getResultArray();
// Transactions
$db->transStart();
$db->table('orders')->insert($order_data);
$db->table('order_items')->insert($item_data);
$db->transComplete();
if (! $db->transStatus()) { /* failed */ }
ルーティング
$routes->get('/', 'Home::index');
$routes->get('products', 'Products::index');
$routes->get('products/(:num)', 'Products::show/$1');
$routes->post('products', 'Products::create');
$routes->resource('products'); // generates all CRUD routes
$routes->group('api', ['namespace' => 'App\Controllers\Api', 'filter' => 'apiauth'], function ($routes) {
$routes->resource('products');
});
$routes->get('admin/(:any)', 'Admin::$1', ['filter' => 'auth']);
入力とセキュリティ
// ALWAYS use $this->request — never raw $_POST/$_GET
$name = $this->request->getPost('name');
$id = $this->request->getGet('id');
$data = $this->request->getJSON(true); // JSON API bodies → array
// Output escaping in views — context-aware
<?= esc($user_input) ?>
<?= esc($value, 'attr') ?>
<?= esc($value, 'js') ?>
セッション
$session = session();
$session->set('user_id', $id);
$userId = $session->get('user_id');
$session->setFlashdata('success', 'Done.');
$session->setTempdata('otp', $code, 300); // auto-expires in 5 minutes
セキュリティチェックリスト(両バージョン共通)
- 生の
$_POST/$_GETは使用せず、入力クラス(CI3)または$this->request(CI4)を使用してください - CSRF 保護を有効にし、ステートレスな API エンドポイントのみを例外にしてください
- クエリビルダーまたはバインドパラメーターを使用し、SQL を連結しないでください
- ビューでは出力をエスケープしてください。CI3 では
html_escape()、CI4 ではesc()を使用します - 本番環境では
db_debug = FALSE(CI3)/CI_ENVIRONMENT = production(CI4)を設定してください -
system/(CI3)またはプロジェクトのルート(CI4)を Web ルートの外に配置してください - 一括代入を防ぐため、CI4 のモデルで
$allowedFieldsを使用してください - ファイルのアップロードを検証してください。拡張子だけでなく MIME タイプも確認します
- 安全なセッション設定を使用してください。
databaseドライバーとhttponly、secureフラグを設定します - HTML 属性には
esc($var, 'attr')、JS コンテキストにはesc($var, 'js')を使用してください - 本番環境では
Content-Security-Policyヘッダーを設定してください - すべての通信で HTTPS を使用してください。CI4 では
app.forceGlobalSecureRequests = trueを設定します - 本番環境でスタックトレースを公開しないでください
CI3 → CI4 クイックリファレンス
| CI3 | CI4 |
|---|---|
$this->load->model('x') | model('XModel') または DI |
$this->load->view('x', $data) | return view('x', $data) |
$this->load->library('x') | service('x') または new X() |
$this->input->post('x') | $this->request->getPost('x') |
$this->db->get('table') | $db->table('table')->get() |
$query->result() / ->row() | $query->getResult() / ->getRow() |
$query->result_array() | $query->getResultArray() |
redirect('url') | return redirect()->to('url') |
show_404() | throw PageNotFoundException::forPageNotFound() |
$this->session->userdata('x') | session()->get('x') |
html_escape($x) | esc($x) |
$route['x'] = 'y' | $routes->get('x', 'Y::method') |
フック($hook[]) | フィルター(ミドルウェア) |
CI_Controller / CI_Model | BaseController / CodeIgniter\Model |
| 名前空間なし | PSR-4 名前空間 |
| CLI ジェネレーターなし | php spark make:* |
30 項目以上のマッピングを含む移行対応表、サーバー設定、本番環境へのデプロイチェックリストの全容については、deployment.md を参照してください。
コメント
Markdown 対応