博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
php pdo bindValue / bindParam 中不能含有连字符
阅读量:4932 次
发布时间:2019-06-11

本文共 4436 字,大约阅读时间需要 14 分钟。

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

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] 

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 phpGNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-94.el7Copyright (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_paramsBreakpoint 1 at 0x696aa0: file /data/src/php-7.0.18/ext/pdo/pdo_sql_parser.c, line 379.

 然后执行 run

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

(gdb) until 423pdo_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”的长度,证明了我们的说法,我们的占位符被截断了。结束。

 

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

 

 

转载于:https://www.cnblogs.com/dilei/p/7243464.html

你可能感兴趣的文章
Sublime Text 3 搭建 React.js 开发环境
查看>>
hdu 1290_献给杭电五十周年校庆的礼物
查看>>
Nginx 入门
查看>>
openCR-用ROS代码点亮LED的方法
查看>>
豆瓣电影api
查看>>
BufferedInputStream和FileInputStream的区别
查看>>
二阶段之六
查看>>
微博爬虫 python
查看>>
中石油 【递归】普通递归关系
查看>>
vue报错Error in render: "TypeError: Cannot read property '0' of undefined"
查看>>
silverlight 隐藏ChildWindow 右上角的关闭按钮
查看>>
oracle获取子串
查看>>
List排序
查看>>
Javascript闭包(Closure)
查看>>
字符串操作
查看>>
redis
查看>>
likely() 和 unlikely()
查看>>
4. Median of Two Sorted Arrays
查看>>
03一些View总结
查看>>
每月一次,免费领取小米云服务会员
查看>>