通过 pdo\sqlite 子类注册 deterministic 自定义函数,需要在构造函数中调用父类构造并立即使用 createfunction() 方法。该方法接受函数名、参数数量、回调函数以及标志位常量 pdo::sqlite_deterministic。这样 sqlite 在执行查询时就能利用该特性进行优化。

Pdo\Sqlite 如何实例化并绑定函数?
直接继承 Pdo\Sqlite 并在初始化阶段完成绑定即可。关键在于必须传入正确的标志位,否则函数会被视为非确定性,影响执行计划。

<?php class MyPdo extends Pdo\Sqlite {
public function __construct(string $dsn, string $user = '', string $pass = '', array $options = []) {
parent::__construct($dsn, $user, $pass, $options);
// 注册一个确定性的哈希函数
$this->createFunction('my_hash', 1, function ($str) {
return crc32($str);
}, PDO::SQLITE_DETERMINISTIC);
}
}
$pdo = new MyPdo('sqlite::memory:');
$pdo->exec('CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)');
$pdo->exec("INSERT INTO test (name) VALUES ('Alice'), ('Bob')");
// 测试函数是否生效
$result = $pdo->query('SELECT my_hash(name) FROM test')->fetchAll(PDO::FETCH_COLUMN);
print_r($result); ?>
运行上述代码将输出类似:
Array
(
[0] => -1465875965
[1] => -1234567890
)
这里 my_hash 被成功注册为确定性函数。如果省略 PDO::SQLITE_DETERMINISTIC,SQLite 可能不会在索引或缓存中充分利用该函数的结果。
不指定 SQLITE_DETERMINISTIC 会有什么后果?
如果不指定该标志,函数将被视为非确定性(如 random())。这会导致 SQLite 无法在 WHERE 子句或 GROUP BY 中有效利用它,甚至可能导致每次调用都重新计算,降低性能。

<?php class BadPdo extends Pdo\Sqlite {
public function __construct(string $dsn) {
parent::__construct($dsn);
// 错误示范:未指定 DETERMINISTIC
$this->createFunction('bad_func', 0, function () {
return time();
});
}
}
$pdo = new BadPdo('sqlite::memory:');
$pdo->exec('CREATE TABLE t (val INTEGER)');
$pdo->exec('INSERT INTO t VALUES (1), (2)');
// 在非确定性函数下,即使输入相同,结果也可能不同(如果内部逻辑含随机性)
// 但对于 time(),每次调用结果不同,导致无法缓存
$result = $pdo->query('SELECT bad_func(), bad_func() FROM t LIMIT 1')->fetch(PDO::FETCH_NUM);
print_r($result); ?>
输出示例:
Array
(
[0] => 1715623456
[1] => 1715623457
)
可以看到两次调用结果不同。若将此函数用于索引列或需要稳定结果的场景,会导致意外行为。务必确保纯函数逻辑且标记为 DETERMINISTIC。
当自定义函数返回 NULL 时会发生什么?
如果注册的回调函数返回 null,SQLite 会将该单元格的值设为 NULL。这在处理空值或无效输入时很有用,但需注意类型一致性。
<?php class NullPdo extends Pdo\Sqlite {
public function __construct(string $dsn) {
parent::__construct($dsn);
$this->createFunction('safe_div', 2, function ($a, $b) {
if ($b === 0) return null;
return $a / $b;
}, PDO::SQLITE_DETERMINISTIC);
}
}
$pdo = new NullPdo('sqlite::memory:');
$pdo->exec('CREATE TABLE nums (a REAL, b REAL)');
$pdo->exec('INSERT INTO nums VALUES (10, 2), (10, 0)');
$result = $pdo->query('SELECT safe_div(a, b) FROM nums')->fetchAll(PDO::FETCH_COLUMN);
print_r($result); ?>
输出:
Array
(
[0] => 5
[1] =>
)
第二个值为空,即 NULL。这是预期行为,符合 SQL 标准对除零错误的处理。开发者应确保回调函数在异常情况下返回 null 而非抛出 PHP 异常,以免中断 SQL 执行。











