关于 codeigniter
面向 CodeIgniter 开发者的开发指导,覆盖 CI3 和 CI4,并要求先识别项目版本再编写代码。内容包括框架模式、安全实践、常见功能及 CI3 到 CI4 的迁移参考。
主要功能
- 01识别 CI3 与 CI4 项目版本
- 02指导控制器、模型和路由实现
- 03涵盖查询、表单与会话模式
- 04提供两版本安全检查清单
- 05参考 CI3 到 CI4 迁移映射
适用场景
- 01为现有 CodeIgniter 项目编写功能
- 02确认代码应遵循的版本模式
- 03检查输入处理与输出转义安全
- 04规划 CI3 项目迁移到 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
- 转义输出:在视图中使用
html_escape()(CI3)或esc()(CI4) - 在生产环境中设置
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