一尘不染

可以在PDO准备好的语句中参数化哪些令牌?

php

我在玩PHP / PDO中准备好的语句。基本查询工作正常,将值传递给WHERE子句:

$stmt = $db->prepare( 'SELECT title FROM episode WHERE id=:id' );
$stmt->bindParam( ':id', $id, PDO::PARAM_INT );
$id = 5;
$stmt->execute();

但是我有一种情况,我需要为字段名称传递变量。此查询(具有适当的绑定)可以正常工作:

SELECT :field FROM episode WHERE id=:id

这给出了一个错误:

SELECT title FROM :field WHERE id=:id

这个不会给出错误,但是不会返回任何行:

SELECT title FROM episode WHERE :field=:id

那么,哪些事情应该在准备好的语句中起作用?我可以“参数化”字段名称,表名称等吗?


阅读 218

收藏
2020-05-29

共2个答案

一尘不染

表名和列名不能用PDO中的参数替换。

在这种情况下,您只需要手动过滤和清理数据。一种实现方法是将简写参数传递给将动态执行查询的函数,然后使用一条switch()语句创建要用于表名或列名的有效值白名单。这样,就不会有用户输入直接进入查询。因此,例如:

function buildQuery( $get_var ) 
{
    switch($get_var)
    {
        case 1:
            $tbl = 'users';
            break;
    }

    $sql = "SELECT * FROM $tbl";
}

通过不保留默认大小写或使用返回错误消息的默认大小写,可以确保仅使用要使用的值。

2020-05-29
一尘不染

这种想法非常严格(在我看来,这只是太懒而无法实施可靠的解决方案的借口),尤其是对于数据库中表示的动态树结构。

考虑以下示例:

我的项目具有逻辑结构:

公司层次结构以实体表示。在一般情况下,每个实体都可以被视为层次结构的成员或特定层次结构的成员。层次结构本身在表中定义为单个树枝,如下所示:

entity_structure (
   id
   name
   parent_entity_structure_id
);

实体本身表示为:

entities (
   id
   name
   entity_structure_id
   parent_id
);

为了易于使用,我构建了一种算法,可创建树的平面视图。以下具体示例说明了我的意思:

SELECT * FROM entity_structure;

id      | name               | entity_structure_parent_id
-----------------------------------------------------------
1       | Company            | null    (special one that always exists)
2       | Division           | 1
3       | Area               | 2
4       | Store              | 3

这将导致生成以下平面表示形式:

entity_tree (
   entity_id
   division_id
   area_id
   store_id
)

处于划分级别的实体的divide_id,area_id和store_id为NULL,区域area_id和store_id为NULL,等等。

这样做的好处是,您可以使用类似于以下内容的语句来查询部门的所有子级:

SELECT * FROM entity_tree WHERE division_id = :division_id;

但是,这假定我知道我要查询的实体的结构级别。这样做很好:

SELECT * FROM entity_tree WHERE :structure = :entity_id;

我知道弄清单个实体的结构级别并不难,但是假设我正在遍历可能并非都处于同一级别的实体集合。现在,我必须为层次结构的每个级别构建一个单独的查询,但是如果可以参数化字段,则可以执行以下操作:

$children = array();
$stmt = $pdo->prepare('SELECT entity_id FROM entity_tree WHERE :structure = :entityId');
foreach ($entities AS $entity) {
   $stmt->execute(array(
      ':structure' = $entity->getEntityStructureId(),
      ':entityId'  = $entity->getId()
   ));

   $children[$entity->getId()] = $stmt->fetchAll(PDO::FETCH_COLUMN);
}

导致代码更简洁,只有一个准备好的语句。

整个示例不使用任何用户输入。

只是要考虑的事情。

2020-05-31