☰
CodeIgniter 4.3.0 完整升级指南:Query Builder 批量写入、Forge 索引增强与破坏性变更解析
2026/10/12 2:04:12 网站建设 项目流程
  • 后端
  • Web框架

【免费下载链接】CodeIgniter4

Open Source PHP Framework (originally from EllisLab)

项目地址:https://gitcode.com/gh_mirrors/co/CodeIgniter4
点击查看免费下载

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 的六个核心亮点直接决定了本次升级的工作量:

  1. Query Builder 新增upsert()、upsertBatch()、deleteBatch()方法,且*Batch()系列方法现在可以通过setQueryAsData()以查询结果作为数据源;
  2. Database Forge 支持在已有表上添加索引、为索引命名,并新增processIndexes();
  3. 默认校验规则切换为 Strict Rules,默认配置更安全;
  4. 数据库错误的异常模型被统一:无论驱动如何,都抛出CodeIgniter\Database\Exceptions\DatabaseException;
  5. Exception Handler 不再用异常码推导 HTTP 状态码与退出码,默认 HTTP 状态码固定为500、退出码为EXIT_ERROR(1);
  6. 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 退出码
ConfigExceptionEXIT_CONFIG(3)12
CastExceptionEXIT_CONFIG(3)9
DatabaseExceptionEXIT_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 的升级路径可以归纳为三条主线:

  1. 数据库层:统一异常为DatabaseException、DBDebug语义变化、事务内默认不抛异常、写查询返回bool,这些都需要回归测试数据库相关代码路径;如果你的代码曾依赖"生产环境不抛数据库异常"的行为,务必重点验证;
  2. 异常输出层:HTTP 状态码与退出码不再由异常码推导,依赖 CLI 退出码做脚本判断的场景需要重新核对(尤其是EXIT_CONFIG、EXIT_DATABASE的变化);
  3. 命令与配置层:Spark 命令处理重构后,app/Config/Routes.php与spark文件均已调整,自定义 spark 命令或依赖CommandRunner的扩展需要适配。

新功能方面,upsert()/upsertBatch()/deleteBatch()、setQueryAsData()与 Forge 的processIndexes()是处理批量数据同步与表结构演进的利器,建议在升级后的第一个迭代中优先引入。

  • 后端
  • Web框架

【免费下载链接】CodeIgniter4

Open Source PHP Framework (originally from EllisLab)

项目地址:https://gitcode.com/gh_mirrors/co/CodeIgniter4
点击查看免费下载

相关推荐

上一篇:VoiceFixer终极指南:如何让任何受损语音重获新生
下一篇:Linux 内核 AF_ALG 用户空间加密接口完全指南:弃用背景、Socket 编程模型与迁移建议

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询