- 后端
- Web框架
【免费下载链接】CodeIgniter4
Open Source PHP Framework (originally from EllisLab)
2023 年 1 月 10 日发布的 CodeIgniter 4.3.0 是 4.x 系列中功能密度极高的一个版本,核心主题是"数据库层重构"与"默认配置安全化"。本指南以官方变更日志(changelogs/v4.3.0.rst)为主体,结合仓库源码逐项讲解upsert()、deleteBatch()、Forge 索引管理等新特性,并完整梳理从 4.2 升级到 4.3 时必须关注的破坏性变更(异常模型统一、HTTP 状态码/退出码规则、Time 类不可变、Spark 命令处理重构等),帮助你评估升级影响并快速上手新 API。
版本亮点(Highlights)
4.3.0 的六个核心亮点直接决定了本次升级的工作量:
- Query Builder 新增
upsert()、upsertBatch()、deleteBatch()方法,且*Batch()系列方法现在可以通过setQueryAsData()以查询结果作为数据源; - Database Forge 支持在已有表上添加索引、为索引命名,并新增
processIndexes(); - 默认校验规则切换为 Strict Rules,默认配置更安全;
- 数据库错误的异常模型被统一:无论驱动如何,都抛出
CodeIgniter\Database\Exceptions\DatabaseException; - Exception Handler 不再用异常码推导 HTTP 状态码与退出码,默认 HTTP 状态码固定为
500、退出码为EXIT_ERROR(1); - Time 类改为完全不可变(继承
DateTimeImmutable),并新增TimeLegacy类做向后兼容。
下文将按"先破坏性变更、再接口/签名变更、最后增强功能"的顺序展开,便于你在升级时逐项对照检查。
BREAKING:行为变更详解
数据库错误时的异常模型统一
4.3.0 之前,不同数据库驱动(MySQLi、Postgre、SQLSRV、OCI8、SQLite3)在出错时可能抛出各自不同的异常类,行为不一致。4.3.0 将所有数据库异常统一收敛为CodeIgniter\Database\Exceptions\DatabaseException(定义于 system/Database/Exceptions/DatabaseException.php),涉及:
- 数据库连接类抛出的异常;
- Prepared Query 的
execute()方法抛出的异常(此前部分驱动不抛异常,现在统一抛出DatabaseException); BaseBuilder中抛出的DatabaseException现在以$DBDebug为判定条件(此前以CI_DEBUG为条件)。
DBDebug 与 CI_DEBUG 的语义调整
这是 4.3.0 最容易被忽略的行为变化,核心是让DBDebug在不同环境下行为一致:
Config\Database::$default['DBDebug']与Config\Database::$tests['DBDebug']默认值改为true(见 app/Config/Database.php),此前仅在"生产环境"下为false;现在只要数据库出错就会抛异常;BaseConnection::$DBDebug默认值改为true;- 调整后,
DBDebug的含义就是"出错时是否抛异常",与调试无关但名称保留; - 事务中的行为变化:当
DBDebug为true时,事务内发生查询错误默认不再抛异常。此前一旦出错会回滚所有查询并抛异常,导致官方文档中的"事务错误处理"(transactions-managing-errors)和"手动事务"(transactions-manual-transactions)两种模式失效。现在可以配合新增的BaseConnection::transException()(见 system/Database/BaseConnection.php)在事务中主动抛出异常; - Model 删除行为:
Model中无 WHERE 子句的删除操作现在总是抛出DatabaseException,即使CI_DEBUG为false也不例外(此前仅CI_DEBUG为true时抛出)。
异常时的 HTTP 状态码与退出码规则
旧版 Exception Handler 在部分场景下用异常码推导HTTP 状态码和退出码,但二者本无逻辑关联。4.3.0 起:
- 默认 HTTP 状态码固定为
500; - 默认退出码固定为常量
EXIT_ERROR(值为1); - 如需自定义,可在异常类中实现
HTTPExceptionInterface(控制 HTTP 状态码)或HasExitCodeInterface(控制退出码),对应实现分别位于 system/Exceptions/HTTPExceptionInterface.php 与 system/Exceptions/HasExitCodeInterface.php。
退出码的具体变化示例(对比 4.2):
| 未捕获异常 | 4.3.0 退出码 | 4.2 退出码 |
|---|---|---|
ConfigException | EXIT_CONFIG(3) | 12 |
CastException | EXIT_CONFIG(3) | 9 |
DatabaseException | EXIT_DATABASE(8) | 17 |
Time 类变为完全不可变
此前Time::add()、modify()、setDate()、setISODate()、setTime()、sub()存在修改当前对象状态的缺陷。4.3.0 将 system/I18n/Time.php 改为继承DateTimeImmutable,实现完全不可变——所有修改方法返回新实例。同时新增 system/I18n/TimeLegacy.php,该类继承DateTime,行为与未修改前的Time完全一致,供需要旧行为的场景做向后兼容。
其他行为变更
- Helper:
script_tag()与safe_mailto()不再在<script>标签中输出type="text/javascript"; - CLI:
spark文件因 Spark 命令处理方式变更而调整; - CLI 测试:
CITestStreamFilter::$buffer = ''不再触发过滤器注册,改为显式调用CITestStreamFilter::registration()(实现见 system/Test/Filters/CITestStreamFilter.php); - Database:
BaseBuilder::_whereIn()抛出的InvalidArgumentException(LogicException子类)不再被配置抑制,即使CI_DEBUG为false也照常抛出; - Database:
getForeignKeyData()返回的数据结构发生变化,所有 DBMS 现在返回相同结构; - Database:
CodeIgniter\Database\BasePreparedQuery对写类型查询返回bool值,不再返回Result类对象; - Model:
Model::update()若生成不带 WHERE 子句的 SQL 将抛出DatabaseException——Model 不支持全表更新操作; - Routing:
RouteCollection::resetRoutes()现在会重置路由自动发现。此前一旦发现过路由文件,即使调用resetRoutes()也不再重新发现。
接口变更(Interface Changes)
只要没有继承相关核心类或实现这些接口,以下变更均向后兼容,无需干预。
OutgoingRequestInterface
- 新增
OutgoingRequestInterface表示"出站请求",见 system/HTTP/OutgoingRequestInterface.php; - 新增实现类
OutgoingRequest(见 system/HTTP/OutgoingRequest.php); RequestInterface现在继承OutgoingRequestInterface;CURLRequest与Request现在都继承OutgoingRequest。
其他接口变更
- HTTP:
MessageInterface补全了缺失的getProtocolVersion()、getBody()、hasHeader()、getHeaderLine()方法(见 system/HTTP/MessageInterface.php); - HTTP:
ResponseInterface现在继承MessageInterface; - HTTP:
ResponseInterface新增getCSP()(Response::getCSP()同步新增)、getReasonPhrase()、getCookieStore()方法; - Database:
CodeIgniter\Database\ResultInterface补全缺失的getNumRows()方法; - 另见下方 Validation 变更。
方法签名变更(Method Signature Changes)
Validation 变更
ValidationInterface被重构以消除接口与Validation类之间的不一致(实现见 system/Validation/ValidationInterface.php 与 system/Validation/Validation.php):
ValidationInterface::run()新增第三个参数$dbGroup;- 接口新增以下方法:
setRule()、getRules()、getRuleGroup()、setRuleGroup()、loadRuleGroup()、hasError()、listErrors()、showError(); - 行为调整:
Validation::loadRuleGroup()在$group为空时返回[]而非null。
Database 相关签名变更
BasePreparedQuery::close()与PreparedQueryInterface的返回类型改为bool(此前无类型);Database::loadForge()返回类型改为Forge;Database::loadUtils()返回类型改为BaseUtils;Table::dropForeignKey()参数名从$column改为$foreignName;BaseBuilder::updateBatch()第二个参数从$index改为$constraints,现在接受array、string或RawSql类型;BaseBuilder::insertBatch()与updateBatch()的$set参数现在接受单行数据对象;BaseBuilder::_updateBatch():第二个参数$values改为$keys,第三个参数$index改为$values且类型固定为array。
Database Forge 签名变更
Forge::dropKey()新增可选参数$prefixKeyName;Forge::addKey()、addPrimaryKey()、addUniqueKey()均新增可选参数$keyName(源码见 system/Database/Forge.php);_processPrimaryKeys()新增$asQuery参数,设为true时返回独立可执行的 SQL;_processIndexes()、_processForeignKeys()在新增$asQuery参数的同时改为返回数组。
其他签名变更
- API:
API\ResponseTrait::failServerError()返回类型改为ResponseInterface; Debug\Exceptions::__construct()与Services::exceptions()的参数从Response改为ResponseInterface;- Request:
IncomingRequest::getJsonVar()的$index参数现在接受array、string或null。
增强功能(Enhancements)
Commands(Spark 命令)
4.3.0 对 Spark 命令做了一次结构性重构,以降低控制台调用开销:
- 将 Spark 命令的调用处理从
CodeIgniter\CodeIgniter类中抽离(因此CodeIgniter::isSparked()被移除,CodeIgniter\CLI\CommandRunner类被删除); - 新增
spark filter:check命令,用于检查某个路由的过滤器; - 新增
spark make:cell命令,一键生成 Cell 文件及其视图; spark routes命令现在显示路由名称,并支持按 Handler 排序输出;- 支持用
--help选项查看任意 spark 命令的帮助信息,例如php spark serve --help; - 新增
CLI::promptByMultipleKeys()方法,支持一次输入多个值(区别于promptByKey()的单值输入),实现见 system/CLI/CLI.php; - HTTP/3 现在被视为有效协议。
Testing(测试设施增强)
- 新增
StreamFilterTrait(见 system/Test/StreamFilterTrait.php),简化捕获 STDOUT/STDERR 输出流的测试写法;CITestStreamFilter也实现了向流注册过滤器的方法; - 新增
PhpStreamWrapper(见 system/Test/PhpStreamWrapper.php),方便向php://stdin注入测试数据; Timer::record()新增在可调用对象中测量性能的方法,通用函数timer()同步支持可选的可调用参数;TestLogger::didLog()新增第三个布尔参数$useExactComparison(默认true),控制日志消息是否逐字比对;- 新增
CIUnitTestCase::assertLogContains()(见 system/Test/CIUnitTestCase.php),按片段而非全文比对日志消息。
Database:Query Builder
以下方法全部实现在 system/Database/BaseBuilder.php 中:
upsert()/upsertBatch()(第 1956、1992 行)
upsert()在记录存在时执行更新、不存在时执行插入,是"有则改、无则加"的标准做法:
$db->table('my_table')->upsert([ ['id' => 1, 'name' => 'John', 'email' => 'john@example.com'], ['id' => 2, 'name' => 'Jane', 'email' => 'jane@example.com'], ]);upsertBatch()的第三个参数$batchSize(默认100)控制单批处理的行数,便于大体积数据分批执行。从源码看,upsert()会先合并QBSet与绑定参数,再经由setData()走_upsertBatch()批处理管线,最终返回受影响行数(testMode 下返回 SQL 数组)。
deleteBatch()(第 2885 行)
按批量条件删除数据:
$db->table('my_table') ->deleteBatch([ ['id' => 1], ['id' => 2], ], 'id'); // 第二个参数 $constraints 指定约束列when()/whenNot()
这两个方法允许条件化地向查询追加子句,避免在 PHP 层用if手工拼接:
$builder->when($isActive, static function ($builder) { $builder->where('status', 'active'); });当第一个参数为真时执行回调;whenNot()则在为假时执行。
setQueryAsData()(第 2156 行)
这是 4.3.0 的数据库层王牌功能:允许insertBatch()、updateBatch()、upsertBatch()、deleteBatch()以查询结果作为数据源。$query参数接受BaseBuilder或RawSql,可配合$alias指定子查询别名、$columns指定列:
$query = $db->table('source_table')->select('id, name'); $db->table('target_table') ->setQueryAsData($query, 'alias', ['id', 'name']) ->insertBatch();源码显示该方法会将BaseBuilder编译为 SELECT 字符串存入QBOptions['setQueryAsData'],后续各*Batch()方法检测到该选项后直接以子查询方式生成 SQL(upsertBatch()中对setQueryAsData分支的处理见第 1994-2008 行)。
同时,updateBatch()的 SQL 结构得到改进,setUpdateBatch()、setInsertBatch()被弃用,统一使用setData()(见下方 Deprecations)。
Database:Forge(索引管理)
Forge 相关实现集中在 system/Database/Forge.php:
processIndexes()(第 1142 行)——在已有表上添加索引
$forge->addKey(['first_name', 'last_name'], false, false, 'name_index'); $forge->processIndexes('my_table');该方法会先读取目标表的字段元数据(getFieldData()),再依次处理普通索引(_processIndexes())、主键(_processPrimaryKeys())与外键(_processForeignKeys()),逐个执行ALTER TABLE ... ADD ...SQL。
为索引命名
addKey()、addPrimaryKey()、addUniqueKey()都新增了可选的$keyName参数(第 331、353、365 行),未命名时主键默认使用pk_<table>形式(见_processPrimaryKeys()第 1130-1133 行的兜底逻辑)。
dropPrimaryKey()(第 511 行)与dropKey()(第 454 行)
dropPrimaryKey()用于删除表的主键;dropKey()修复了无法删除唯一索引的问题,这需要DROP CONSTRAINTSQL 命令(dropKeyAsConstraint()见第 489 行);新增的$prefixKeyName参数控制是否自动加表前缀。
其他 Forge 变化
addForeignKey()新增名称参数用于手动设置外键名(SQLite3 不支持);- SQLSRV 驱动在使用
Forge::dropColumn()时自动删除DEFAULT约束。
Database:其他
- SQLite3:新增配置项
busyTimeout,用于设置表被锁定时等待的超时时间(毫秒),示例配置见 app/Config/Database.php; - RawSql 支持:
BaseConnection::escape()不再转义RawSql数据类型,允许直接向数据中传入 SQL 字符串; - 元数据:
getForeignKeyData()返回结构在各 DBMS 间统一;SQLite 的getIndexData()可为AUTOINCREMENT列返回名为PRIMARY的伪索引,且每条索引数据带有type属性; - Prepared Query:
BasePreparedQuery::close()现在在所有 DBMS 中都会释放 prepared statement(此前 Postgre、SQLSRV、OCI8 不释放); - 事务异常:新增
BaseConnection::transException()(见 system/Database/BaseConnection.php),在事务中主动抛出异常,配合上文 DBDebug 事务行为变化使用。
Model
BaseModel::insertBatch()与updateBatch()新增 before/after 事件回调;- 新增
Model::allowEmptyInserts()方法(见 system/BaseModel.php),允许插入空数据:
$model->allowEmptyInserts()->insert([]);- Entity 新增
IntBoolCast转换类(见 system/Entity/Cast/IntBoolCast.php),用于在整数与布尔值之间互转。
Libraries
- Publisher:新增
replace()、addLineAfter()、addLineBefore()方法,可对已发布的文件做内容替换与行级插入(见 system/Publisher/Publisher.php); - Encryption:现在可以解密使用 CI3(CodeIgniter 3)加密的数据;
- CURLRequest:新增 HTTP/2 版本选项(
$options['version'] = CURL_HTTP_VERSION_2_0)。
Helpers 与全局函数
- Helper 自动加载:现在可以通过 app/Config/Autoload.php 自动加载 Helper;
- 表单验证辅助函数:新增
validation_errors()、validation_list_errors()、validation_show_error(),用于在视图中展示校验错误; route_to():最后一个参数传入 locale 值时可以为路由指定区域设置;request()/response():新增两个全局函数,分别返回当前请求与响应服务实例;decamelize():将 camelCase 转换为 snake_case,实现见 system/Helpers/inflector_helper.php;is_windows():检测 Windows 平台,实现见 system/Common.php,它取代了被弃用的CLI::isWindows()。
HTML5 兼容
通过 app/Config/DocTypes.php 的$html5属性控制 void 元素(如<input>)的闭合写法:设为true时输出不含/的 HTML5 风格标签(如<br>)。受影响的组件包括:Typography 类的br标签生成、View Parser 的nl2br过滤器、Honeypot 的input标签、Form helper、HTML helper 以及 Common Functions。
错误处理
- 弃用警告改为可记录日志:
logDeprecations配置开启后,弃用警告以日志形式记录而非抛异常;日志记录默认开启; - 如需临时恢复抛异常行为,设置环境变量
CODEIGNITER_SCREAM_DEPRECATIONS为真值即可(源码判定见 system/Debug/Exceptions.php); Config\Logger::$threshold现在按环境区分默认值:生产环境仍为4,其他环境改为9(即记录更多级别的日志)。
多域名支持(Multiple Domain Support)
新增Config\App::$allowedHostnames(见 app/Config/App.php),用于声明 baseURL 之外的合法主机名:
public array $allowedHostnames = ['admin.example.com'];当当前请求 URL 匹配列表中的主机名时,base_url()、current_url()、site_url()等 URL 相关函数返回的 URL 将使用该主机名,而不是 baseURL 中的主机名。
其他增强
- Routing:
$routes->useSupportedLocalesOnly(true)启用后,若 URL 中的 locale 不在Config\App::$supportedLocales中,Router 直接返回 404(见 system/Router/RouteCollection.php); - Routing:新增
$routes->view()方法,直接返回视图内容(见 system/Router/RouteCollection.php):
$routes->view('/about', 'about_page');- View:View Cells 升级为一等公民,可存放在app/Cells目录;新增 "Controlled Cells",为 Cell 提供更多结构与灵活性;
- Validation:新增 Closure(闭包)校验规则;
- Config:可以手动指定要自动发现的 Composer 包;
- Config:新增
Config\Session类统一管理会话配置(此前会话配置散落在Config\App中,相关属性随之弃用); - Debug:Kint 更新至 5.0.2;
- Request:新增
$request->getRawInputVar()方法,从原始流中返回指定变量;新增$request->is()方法查询请求类型。
消息文案变更(Message Changes)
- 更新了英文语言字符串,使其更一致;
- 新增
CLI.generator.className.cell、CLI.generator.viewName.cell两个语言键; - 新增en/Errors.php语言文件。
其他变更(Changes)
- Config:
Config类中所有原子类型属性均已显式声明类型;默认值变更详见官方升级指南(user_guide_src/source/installation/upgrade_430.rst); - Spark 命令处理重构:
CodeIgniter\CodeIgniter不再处理 Spark 命令;- 移除
CodeIgniter::isSparked()方法; - 删除
CodeIgniter\CLI\CommandRunner类; - 删除系统路由配置文件
system/Config/Routes.php; - app/Config/Routes.php 已相应修改,不再包含系统路由配置。
弃用项(Deprecations)
升级到 4.3.0 后以下 API 已弃用,请在下一个大版本前完成迁移:
RouteCollection::localizeRoute();RouteCollection::fillRouteParams()——改用RouteCollection::buildReverseRoute();BaseBuilder::setUpdateBatch()与BaseBuilder::setInsertBatch()——改用BaseBuilder::setData();Response::$CSP公开属性——改用Response::getCSP();CodeIgniter::$path与CodeIgniter::setPath()(已不再使用);IncomingRequest::$uri公开属性——改用IncomingRequest::getUri();IncomingRequest::$config公开属性(将改为 protected);CLI::isWindows()——改用is_windows();Config\App中的会话相关属性——改用新的Config\Session类。
修复的 Bug 摘要
- 修复了所有类型 Prepared Query 在写操作查询时返回
Result对象而非bool的问题; - 修复了
IncomingRequest::getVar()/getJsonVar()在 JSON 请求中的变量过滤问题; - 修复了使用指定索引时变量类型可能被改变的问题;
- 修复了启用 CSP 时 Honeypot 字段消失的问题。
完整 Bug 修复清单见仓库根目录的 CHANGELOG.md。
升级建议
综合来看,4.3.0 的升级路径可以归纳为三条主线:
- 数据库层:统一异常为
DatabaseException、DBDebug语义变化、事务内默认不抛异常、写查询返回bool,这些都需要回归测试数据库相关代码路径;如果你的代码曾依赖"生产环境不抛数据库异常"的行为,务必重点验证; - 异常输出层:HTTP 状态码与退出码不再由异常码推导,依赖 CLI 退出码做脚本判断的场景需要重新核对(尤其是
EXIT_CONFIG、EXIT_DATABASE的变化); - 命令与配置层:Spark 命令处理重构后,
app/Config/Routes.php与spark文件均已调整,自定义 spark 命令或依赖CommandRunner的扩展需要适配。
新功能方面,upsert()/upsertBatch()/deleteBatch()、setQueryAsData()与 Forge 的processIndexes()是处理批量数据同步与表结构演进的利器,建议在升级后的第一个迭代中优先引入。
- 后端
- Web框架
【免费下载链接】CodeIgniter4
Open Source PHP Framework (originally from EllisLab)
相关推荐
CodeIgniter 1.7.2 升级至 2.0.0 完整迁移指南:9 大步骤与破坏性变更全解析
CodeIgniter 1.7.2 升级至 2.0.0 完整迁移指南:9 大步骤与破坏性变更全解析 本指南以 CodeIgniter 官方升级文档 upgrad
后端Web框架微信聊天记录怎么导出到电脑?WeChatMsg 免费导出完全指南
微信聊天记录怎么导出到电脑?WeChatMsg 免费导出完全指南 微信聊天记录加密存在手机里,默认状态是搜不到、拿不走、统计不了,换机或重装系统时就等于消失。
TanStack Query v5 迁移完全指南:React Query 破坏性变更详解与升级实战
TanStack Query v5 迁移完全指南:React Query 破坏性变更详解与升级实战 本篇迁移指南基于本仓库中 docs/framework/re
前端缓存状态管理
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考