mysql pdo bindvalue_php pdo bindValue / bindParam 中不能含有连字符

最近在使用pdo时,bindValue的第一个参数中有一个“-”,就触发了这个bug,

$dsn = 'mysql:dbname=cm_code;host=127.0.0.1';

$user = 'root';

$password = 'Dsdsd56';

try {

$dbh = new PDO($dsn, $user, $password);

} catch (PDOException $e) {

echo 'Connection failed: ' . $e->getMessage();

}

$name = "sdsds";

$phone = '13522222264';

$sth = $dbh->prepare('SELECT username, phone FROM user WHERE username =:userss-name');

$sth->bindValue(':userss-name', $name, PDO::PARAM_STR);

$sth->execute();

var_dump($sth->fetchAll());exit;

运行上面的代码,会出现

PHP Warning:  PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: parameter was not defined

Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: parameter was not defined

虽然很快就定位到了bug的原因,是含有“-”导致的,但是还是很不爽,为什么命名占位符中不能含有“-”呢?

当然第一就是google了,发现他是php很早之前的bug(PDO Common: Bug #43130 (Bound parameters cannot have - in their name)),也修复了(当出现“-”时,就报waring);

https://bugs.php.net/bug.php?id=43130

下面是pdo作者的回复

[2007-10-30 09:51 UTC] uw@php.net

I disagree with the decision to allow "-" in parameter names. Parameter names should consist of [a-zA-Z] and nothing else. "-" is an operator in most databases.

For BC compatibility I'm also fine with the old pattern [:][a-zA-Z0-9_]+ . Though I must say, that I'd prefer [:][a-zA-Z]+[a-zA-Z0-9_]+, don't allow ":0". ":0" looks a bit like "operator" + "number"...

However, the underlying problem here is that there is absolutely no specification for PDO. This makes PDO a guessing game and error prone.

大意是,他不允许变量名中出现‘-’,而且在大部分数据库中‘-’是作为运算符使用的。

但是,感觉还是如鲠在喉啊,他只是一个占位符,字符串,和“-”有什么关系呢,匹配替换不就行了吗? 但是网上没有对此的回答,所以只能去看源码

pdo的实现是在 phpsrc/ext/pdo/pdo_stmt.c 中,找到 execute 方法。读代码后找到可疑的地方, 在495行

495 ret = pdo_parse_params(stmt, stmt->query_string, stmt->query_stringlen,

&stmt->active_query_string, &stmt->active_query_stringlen);

pdo_parse_params方法的实现是在 pdo_sql_parser.c 中, 解析参数就在这个while循环中

/* phase 1: look for args */

while((t = scan(&s)) != PDO_PARSER_EOI) {

if (t == PDO_PARSER_BIND || t == PDO_PARSER_BIND_POS) {

if (t == PDO_PARSER_BIND) {

int len = s.cur - s.tok;

if ((inquery < (s.cur - len)) && isalnum(*(s.cur - len - 1))) {

continue;

}

query_type |= PDO_PLACEHOLDER_NAMED;

} else {

query_type |= PDO_PLACEHOLDER_POSITIONAL;

}

plc = emalloc(sizeof(*plc));

memset(plc, 0, sizeof(*plc));

plc->next = NULL;

plc->pos = s.tok;

plc->len = s.cur - s.tok;

plc->bindno = bindno++;

if (placetail) {

placetail->next = plc;

} else {

placeholders = plc;

}

placetail = plc;

}

}

其中的秘密就在 上面的 scan方法中,方法实在是太长,static int scan(Scanner *s),只放个声明吧,具体解析实现它是通过指针移动和goto来实现整个queryString的遍历,当遇到非法字符的时候,比如“-”,就会断开,所以最开始的代码命名占位符,就变成了“:userss”了,这就是参数未定义的原因了。这也解决了我的疑问,他不是匹配替换的,而是逐个字符遍历来实现解析的。

要更详细了解请移步源代码的实现。

static int scan(Scanner *s) {

char *cursor = s->cur;

s->tok = cursor;

{

YYCTYPE yych;

if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);

yych = *YYCURSOR;

switch (yych) {

case 0x00:

goto yy2;

case '"':

goto yy3;

case '\'':

goto yy5;

case '(':

case ')':

case '*':

case '+':

case ',':

case '.':

goto yy9;

case '-':

goto yy10;

case '/':

goto yy11;

case ':':

goto yy6;

case '?':

goto yy7;

default:

goto yy12;

}

..............

当然,以上都是属于结果部分了,不想深究的到此就可以了,想了解我是怎么定位找到的呢,接着往下看。

怀疑(断点)和 行动(gdb)

来运行我们的我们的神器gdb,加载我们的代码文件pdo.php

root@iZwz90qokcxygcqwhd7j6kZ ~]# gdb php

GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-94.el7

Copyright (C) 2013 Free Software Foundation, Inc.

License GPLv3+: GNU GPL version 3 or later

This is free software: you are free to change and redistribute it.

There is NO WARRANTY, to the extent permitted by law. Type "show copying"

and "show warranty" for details.

This GDB was configured as "x86_64-redhat-linux-gnu".

For bug reporting instructions, please see:

...

Reading symbols from /data/server/php7/bin/php...done.

(gdb) set args pdo.php

在我们怀疑的地方加断点

(gdb) b pdo_parse_params

Breakpoint 1 at 0x696aa0: file /data/src/php-7.0.18/ext/pdo/pdo_sql_parser.c, line 379.

然后执行 run

为了方便查看我们直接到while循环结束,当然你也可以一直next,看看到底是怎么执行的

(gdb) until 423

pdo_parse_params (stmt=stmt@entry=0x7fffeee85380, inquery=, inquery_len=, outquery=outquery@entry=0x7fffeee853e8, outquery_len=outquery_len@entry=0x7fffeee853f0)

at /data/src/php-7.0.18/ext/pdo/pdo_sql_parser.c:424

然后打印存储命名占位符的变量

(gdb) print *placetail

$1 = {pos = 0x7fffeee71171 ":userss-name", len = 7, bindno = 0, qlen = 0, quoted = 0x0, freeq = 0, next = 0x0

发现了吗? len长度是7,正好是“:userss”的长度,证明了我们的说法,我们的占位符被截断了。结束。

当然,正确的变量名命名规则,是不能出现“-”的,所以你有好的命名习惯,是完全可以避免这个问题的。希望对你有所帮助。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值