Created
March 7, 2013 06:05
-
-
Save bombless/5105869 to your computer and use it in GitHub Desktop.
PHP5.3开始,ext/mysqli的mysqli_stmt::bind_param不能正确的处理双重引用下的字符串导致MySQL数据库中VARCHAR型域的值被错误的更新成"0"这个值。调这个问题调的我蛋都碎了。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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); | |
} | |
} | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
现在想想的话这个问题可能存在于按引用传值的函数调用中。也许是函数调用中的引用传值与foreach中的引用传值的差异导致了代码效果的差异。