教程栏目

joomla中文网出品的官方教程

在 Joomla 4 中, Joomla 数据库查询转变为使用预编译语句,本文旨在阐明我们这样做的原因和方式。

为什么要使用预编译语句(Prepared Statements)?


简单的来说,预编译语句相比之前的字符串查询语句将会有两大优势:

  • 更加的高效
  • 更加的安全

预编译语句(Prepared Statements)是什么?


通常我们的一条sql在db接收到最终执行完毕返回可以分为下面三个过程:

  • 词法和语义解析
  • 优化sql语句,制定执行计划
  • 执行并返回结果

我们把这种普通语句称作Immediate Statements

但是很多情况,我们的一条sql语句可能会反复执行,或者每次执行的时候只有个别的值不同(比如query的where子句值不同,update的set子句值不同,insert的values值不同)。如果每次都需要经过上面的词法语义解析、语句优化、制定执行计划等,则效率就明显不行了。

所谓预编译语句就是将这类语句中的值用占位符替代,可以视为将sql语句模板化或者说参数化,一般称这类语句叫Prepared Statements或者Parameterized Statements 。

预编译语句的优势归纳为:一次编译、多次运行,省去了解析优化等过程;此外预编译语句能防止sql注入。

如何在Joomla中使用预编译语句(Prepared Statements)?


joomla在底层的JDatabaseDriver类中已经帮我们实现好了预编译语句这项功能,开发者只需要在写查询语句的时候调用即可。以下面的查询语句为例:

正常的SQL语句写法:

$query = $this->db->getQuery(true)
	->select($this->db->quoteName(array('id', 'password')))
	->from($this->db->quoteName('#__users'))
	->where($this->db->quoteName('username') . '=' . $this->db->quote($credentials['username']));

 转换为预编译语句的SQL语句写法:

$query = $this->db->getQuery(true)
	->select($this->db->quoteName(array('id', 'password')))
	->from($this->db->quoteName('#__users'))
	->where($this->db->quoteName('username') . ' = :username')
	->bind(':username', $credentials['username']);

通过在where中使用占位符,在bind中进行参数绑定就实现了预编译的功能。另外,请注意最后的bind方法,在这里我们不再使用$db的quote(加引号)的方法,因为数据库驱动将自动的帮我们加上。

Joomla中使用预编译语句的一些说明


 JDatabaseDriver 会对一些函数自动使用预编译语句,如 whereIn()和whereNotIn()方法将自动的使用。

典型的代码如下:

$query = $this->db->getQuery(true)
	->select($this->db->quoteName(array('id, password')))
	->from($this->db->quoteName('#__users'))
	->whereIn($this->db->quoteName('id'), [ 1, 2, 3 ]);

 这个查询将会转换为预编译语句的SQL如下:

SELECT 
  `id`, `password`
FROM
  `#__users`
WHERE
  `id` IN (
    :preparedArray1,
    :preparedArray2,
    :preparedArray3
  );

其中的占位符 :preparedArray1-3 将在执行时填充 1,2,3。

通过在where中使用占位符,在bind中进行参数绑定就实现了预编译的功能。另外,请注意最后的bind方法,在这里我们不再使用$db的quote(加引号)的方法,因为数据库驱动将自动的帮我们加上。

另外,以下函数接受数组以减少函数调用开销

  • bind()
  • bindArray()
  • whereIn()
  • whereNotIn()

另外在编程中使用预编译的一个好处是可以方便的使用循环,下面是一段实例代码:

$listOfUsernames = [ 'admin', 'user1' ];

$query = $this->db->getQuery(true)
	->select($this->db->quoteName(array('id', 'password')))
	->from($this->db->quoteName('#__users'))
	->where($this->db->quoteName('username') . ' = :username')
	->bind(':username', $username);

foreach($listOfUsernames as $name)
{
  $username = $name;
  $this->db->setQuery($query);
  $user = $this->db->loadObject();
  print_r($user);
}

在上面的循环中,给$username赋值,然后重新调用setQuery方法来获得数据,非常的方便。

同样,也可以使用数据组来完成参数的绑定,代码如下:

$query = $this->db->getQuery(true)
	->select($this->db->quoteName(array('id', 'password')))
	->from($this->db->quoteName('#__users'))
	->where($this->db->quoteName('username') . ' = :username')
	->where($this->db->quoteName('id') . ' = :id')
	->bind([':username', ':id'], [$credentials['username'], 42], [Joomla\Database\ParameterType::STRING, Joomla\Database\ParameterType::INTEGER]);

在上面的代码中添加 username 和 id 作为绑定参数,并为每个变量设置正确的 ParameterType。也可以对所有绑定值和 ParameterType 使用一个变量。 代码如下:

$query = $this->db->getQuery(true)
	->select($this->db->quoteName(array('id', 'password')))
	->from($this->db->quoteName('#__users'))
	->where($this->db->quoteName('username') . ' = :username')
	->where($this->db->quoteName('password') . ' = :password')
	->bind([':username', ':password], $credentials['username']);

 在上面参数 :username 和 :password 设置为相同的值和默认的参数类型。

 

作者: 樱木花道

Joomla程序员,从J1.5到J4.x始终都在做Joomla相关开发定制工作,有超过10年行业经验,国内Joomla扩展开发商ZMAX团队的核心成员

作者网站:ZMAX程序人

评论 (0)

  • 最新在前
  • 最佳在前

第3章 处理URL请求参数

第5章 日志

第6章 错误和调试

第10章 缓存

第14章 路由系统