ci中不能用new创建数据库类,因其依赖框架自动加载、配置注入和连接池管理;必须通过$this->load->database()(ci 3.x)或service('database')(ci 4.x)获取已初始化连接。

CI中用new创建数据库类会直接报错
CodeIgniter(尤其是 CI 3.x 和 CI 4.x)的数据库操作不支持直接 new 实例化内置数据库类(如 CI_DB 或 Connection),因为它们依赖框架的自动加载、配置注入和连接池管理。手动 new Database() 会缺失 $db 配置、驱动实例、查询编译器等关键依赖,立刻抛出 Fatal error: Uncaught Error: Call to undefined method __construct() 或 Undefined property: CI_DB::$conn_id。
常见错误写法:
$db = new \CodeIgniter\Database\MYSQLI\Connection(); // ❌ CI 4.x 中无效 $db = new \CI_DB(); // ❌ CI 3.x 中不存在该类名,且未初始化
- CI 3.x 的数据库类是工厂模式构建的,必须走
$this->load->database()或get_instance()->load->database() - CI 4.x 使用服务定位器 + 依赖注入,数据库连接由
Database服务提供,需通过service('database')获取 - 即使你封装了自定义数据库操作类,也**不要在构造函数里硬编码
new \CodeIgniter\Database\...**,而应接收已注入的Connection实例
自定义数据库操作类该怎么写才安全
真正可复用的自定义类,应该把数据库连接作为依赖传入,而不是自己创建。这样既解耦、又便于单元测试,还能复用 CI 的连接池和配置。
CI 4.x 示例(推荐):
class UserRepo
{
protected $db;
public function __construct(\CodeIgniter\Database\ConnectionInterface $db)
{
$this->db = $db; // ✅ 依赖注入,非 new
}
public function find($id)
{
return $this->db->table('users')->where('id', $id)->get()->getRow();
}
}
- 构造函数参数类型声明为
ConnectionInterface,CI 自动注入已配置好的连接 - 类本身不关心驱动类型(MySQLI / PDO / SQLite),只依赖接口契约
- 若在控制器中使用,直接
new UserRepo(service('database'))即可;更推荐用服务容器自动解析
CI 3.x 兼容写法(不推荐但可行):
class User_model extends CI_Model
{
public function __construct()
{
parent::__construct();
$this->load->database(); // ✅ 必须先加载,再用 $this->db
}
public function get_by_id($id)
{
return $this->db->get_where('users', ['id' => $id])->row();
}
}
为什么不能在自定义类里调用 $this->load->database()
在 CI 3.x 中,如果你把数据库加载逻辑写进一个普通 PHP 类(非继承 CI_Model),$this->load 是不存在的——因为 $this 不是 CI 的核心对象实例,没有 load 属性。此时调用会报 Call to a member function database() on null。
- 只有继承
CI_Model或CI_Controller的类,才拥有 CI 的超全局对象引用 - 普通类想访问 DB,要么传入
$this->db实例,要么用get_instance()->db(CI 3.x),但后者破坏封装性且难测试 - CI 4.x 完全弃用
get_instance(),强制走依赖注入或service()函数
CI 4.x 中 service('database') 和 new Connection 的区别
service('database') 返回的是已初始化、已连接、带缓存和事务上下文的完整连接实例;而手动 new \CodeIgniter\Database\MySQLi\Connection(...) 只是空壳,缺少配置解析、字符集设置、错误处理钩子等关键环节。
-
service('database')会读取app/Config/Database.php中的分组配置,自动选择默认组并建立连接 - 手动
new需要自己传入全部配置数组,且无法触发 CI 的连接事件(如postConnect) - 事务控制(
$db->transStart())依赖服务实例的内部状态,独立Connection实例无法参与同一事务
真正需要“新建”连接的极少数场景(比如跨库查询),也应通过 service('database', 'other_group') 获取另一个预配置的连接服务,而不是手动生成。











