Skip to content

Instantly share code, notes, and snippets.

@bombless
Created March 7, 2013 06:05
Show Gist options
  • Save bombless/5105869 to your computer and use it in GitHub Desktop.
Save bombless/5105869 to your computer and use it in GitHub Desktop.
PHP5.3开始,ext/mysqli的mysqli_stmt::bind_param不能正确的处理双重引用下的字符串导致MySQL数据库中VARCHAR型域的值被错误的更新成"0"这个值。调这个问题调的我蛋都碎了。
<?php
/**
* ext/mysqli不像PDO支持命名的变量绑定
* 因此这里弄了个helper类来生成mysqli_stmt::bind_param所需的调用参数
* 关于ext/mysqli可参考[http://codular.com/php-mysqli]
*/
class BindParam{
private $values = array(),
$names = array(),
$types = '',
$question_marks = array(),
$table_name,
$outputNames = array(),
$outputValues = array(),
$conditionNames = array(),
$conditionValues = array(),
$conditionTypes = '';
public function __construct($table){
$this->table_name = $table;
}
public function add( $type, $name, $value ){
$this->types .= $type;
$this->names[] = $name;
$this->values[] = $value;
$this->question_marks[] = '?';
//echo $type, $name, $value;
/*
if(defined('DEBUG')){
echo '<pre>';
print_r($this->names);
print_r($this->values);
echo '</pre>';
}
echo 'adding '.$name.':';
var_dump($value);
echo '<br>';*/
}
public function addCondition($type, $name, $value){
$this->conditionTypes .= $type;
$this->conditionNames[] = $name;
$this->conditionValues[] = $value;
/*
if(defined('DEBUG')){
echo '<pre>';
print_r($this->conditionNames);
print_r($this->conditionValues);
echo '</pre>';
}
echo 'adding condition '.$name.':';
var_dump($value);
echo '<br>';*/
}
public function output($name, &$value){
$this->outputNames[] = $name;
$this->outputValues[] = &$value;
}
public function prepareUpdate(){
$setArray = array();
foreach($this->names as $item){
$setArray[] = $item . ' = ?';
}
$condition = '';
for($i = 0; $i < count($this->conditionNames); ++$i){
$condition .= ' AND ' . $this->conditionNames[$i] . ' = ?';
}
$query = 'UPDATE '.($this->table_name).' SET '.implode(',', $setArray).
' WHERE TRUE'.$condition;
return $query;
}
public function prepareInsert(){
$query = 'INSERT INTO '.($this->table_name).' ('.implode(',', $this->names).
') VALUES('.implode(',',$this->question_marks).')';
return $query;
}
private function prepare_Select($like = '', $from = 0, $limit = 0, $orderby = ''){
$outputArray = array();
foreach($this->outputNames as $item){
$outputArray[] = '`'.$item.'`';
}
$query = 'SELECT '.implode(',', $outputArray).' FROM '.($this->table_name).' WHERE TRUE ';
foreach($this->names as $item){
$query .= 'AND '.$item.' '.($like? 'LIKE': '=').' ? ';
}
if($orderby)$query .= ' ORDER BY ' . $orderby;
$query .= ' LIMIT '.$from.($limit > 0? ','.$limit: '');
//echo $query;
return $query;
}
public function prepareLikeSelect($from = 0, $limit = 0, $orderby = ''){
return $this->prepare_Select('LIKE', $from, $limit, $orderby);
}
public function prepareSelect($from = 0, $limit = 0, $orderby = ''){
return $this->prepare_Select('', $from, $limit, $orderby);
}
public function bind_result($stmt){
if(!count($this->outputValues))return TRUE;
//print_r($this->outputValues);
//return call_user_func_array('mysqli_stmt_bind_result', array_merge(array($stmt), $this->outputValues));
return call_user_func_array(array($stmt, 'bind_result'), $this->outputValues);
}
public function bind_param($stmt){
$values = array();
$conditionValues = array();
foreach($this->values as $k => &$v){
$values[$k] = &$v;
}
//美国东海岸的PHP引用变量邪恶地扑哧一下翅膀,西伯利亚的小宝宝都要哭泣。
//谢天谢地,我们现在要把它unset
unset($v);
/*
* 在使用下面的循环引用赋值之前对$v做unset
* 这可不是开玩笑的
*
*/
foreach($this->conditionValues as $k => &$v){
$conditionValues[$k] = &$v;
}
unset($v);//保持良好的习惯
/*
* 想要知道这里为什么这么麻烦,这是因为从PHP5.3开始,ext/mysqli里对引用的处理就不像PHP5.2那样聪明了,
* 它开始在bind_param之后错误的使用对空字符串的引用,使得相应的字段实际被更新为字符串"0",这就像听上去那样让人崩溃
* PHP5.4和PHP5.3保持同样的行为。
*
*/
$args = array_merge(array(($this->types).($this->conditionTypes)), array_merge($values,$conditionValues));
//print_r($args);
/*
if(defined('DEBUG')){
echo '<pre>';
print_r($args);
print_r($this->conditionValues);
echo '</pre>';
}*/
if(!($this->types))return TRUE;
return call_user_func_array('mysqli_stmt_bind_param', array_merge(array($stmt), $args));
//return call_user_func_array(array($stmt, 'bind_param'), $args);
}
}
?>
@bombless
Copy link
Author

bombless commented Mar 7, 2013

现在想想的话这个问题可能存在于按引用传值的函数调用中。也许是函数调用中的引用传值与foreach中的引用传值的差异导致了代码效果的差异。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment