在php 4中,声明变量通常使用var,而在php 5中,可使用面向对象编程(oop)的特性来自定义数据的可见性--即可访问性,可见性在此与变量作用域非常类似,但提供了更好的控制机制,有以下三种类型的可见性修饰符:
public(默认)--变量可在全局范围内访问或修改。
protected--变量只能在类本身及直接派生(使用extends语句)类内访问或修改。
private--变量只能在类内部访问或修改。
与接口实现类似,在程序中违反这些规则将会导致严重的错误;且与接口类似的是,它们的存在纯粹是为了方便程序员。但这并不意味着可以忽略它们,指定某个类成员变量的可见性,可保护对象内的数据免受外界影响。
假设有一个mysqldb类,一个$link变量在其中声明为private,这意味着这个变量只能从对象内部使用$this变量访问,这防止了类外其他对象或函数的意外覆盖,在此,我们将使用可见性特性帮助我们创建一个query对象。
你可以把query当作一个单独的实体,它可以执行,并且返回结果。一些数据库系统也具有存储过程,存储过程与函数很相似,它们存储查询语句,并在调用时接受相应的参数,但mysql在5.1版本之前并没有提供类似功能,某些其他类型的数据库管理系统也没有。
在本文中,将把上述两个特性结合进示例的query对象中,示例将模拟一个基本的存储过程,并在内部保存结果指针。目前,重点是从对象中执行query,在此可以调用mysqldb对象的query()函数。
可在query对象中定义如下的public函数:
__construct()--构造函数接受一个包含了实现db接口对象实例引用的参数。
prepare()--函数prepare()初始化query的存储过程。它可能包含一个或多个有限的占位符,而其将会作为参数传递给execute()函数。占位符定义为与参数个数有关的一个冒号紧跟一个整数及与参数类型有关的一个字母。
包含占位符的一个简单的query看起来像以下这样:
/**
* 把query准备为一个存储过程。
* @param string $query prepared query text
* @return void
*/
public function prepare($query)
{
$this->stored_procedure = true;
$this->quote_store = array(); //清除引号
$this->query = preg_replace(self::$quote_match, '$this->sql_quote_replace("1"?"1":'2')', $query);
}
private function sql_quote_replace($match)
{
$number = count($this->query_strings);
$this->query_strings[] = $match;
return "$||$$number";
}
在此留意对静态quote_match属性private的使用,还有quote_store属性和sql_quote_replace()函数。相比protected,在此定义为private更能确保任何重载query类prepare()方法的子类使用其自身的机制来剔除引号。
函数compile 下一步是构建compile()与execute()函数。
函数compile()如例3中所示,功能如下:
·接受的参数数目可变(即可变参数),其将匹配query中的占位符。
·检查占位符是否为正确的数据类型,并把它替换为参数中的值。
·把query作为字符串返回,但不执行它。
·如果query对象没有使用prepare()函数初始化为一个存储过程,将抛出一个异常。
例3:compile()函数
/**
* 返回编译的query,但并不执行它。
* @param mixed $args,... query parameters
* @return string compiled query
*/
public function compile($params)
{
if (! $this->stored_procedure) {
throw new exception("存储过程未被初始化!");
}
/* 替代参数 */
$params = func_get_args(); // 取得函数参数
$query = preg_replace("/(?query);
return $this->add_strings($query); //把字符串放回query中
}
/**
* 重新插入被prepare()函数移除的字符串。
*/
private function add_strings($string)
{
$numbers = array_keys($this->query_strings);
$count = count($numbers);
$searches = array();
for($x = 0; $x < $count; $x++) {
$searches[$x] = "$||${$numbers[$x]}";
}
return str_replace($searches, $this->query_strings, $string);
}
/**
* 每次执行,存储过程中都有一个占位符被替换。
*/
protected function compile_callback($params, $index, $type)
{
--$index;
/* 抛出一个异常 */
if (! isset($params[$index])) {
throw new exception("存储过程未收到所需的参数数目!");
}
/* 可以在此添加别的类型,如日期和时间。 */
switch ($type) {
case 's':
return '"' . $this->db->escape_string($params[$index]) . '"';
break;
case 'i':
return (int) $params[$index];
break;
case 'n':
return (float) $params[$index];
default:
throw new exception("存储过程中指定的数据类型 '$type' 无法识别。");
}
}
函数compile()中使用了两个额外的函数,其中compile_callback()函数是作为在preg_replace()函数调用中的回调函数,每一次在query中查找到占位符,并把它替换为传给compile函数的值时,都会执行它。
函数execute
最后,还需要构建函数execute(),函数execute()编译query并且使用db对象执行它,而db对象在此是用于初始化dbquery对象的。请注意在例4中,是怎样运用函数call_user_func_array()来得到编译后的query的,而这样做的原因是,函数execute()要直到运行时,才能确定传递给它的参数数目。
例4:execute()函数
/**
*
* 执行当前query,并把占位符替换为所提供的参数。
*
* @param mixed $queryparams,... query parameter
* @return resource a reference to the resource representing the executed query.
*/
public function execute($queryparams = '')
{
//例如:select * from table where name=:1s and type=:2i and level=:3n
$args = func_get_args();
if ($this->stored_procedure) {
/* 调用函数compile以取得query */
$query = call_user_func_array(array($this, 'compile'), $args);
} else {
/* 如果存储过程未被初始化,就把它作为标准query执行。*/
$query = $queryparams;
}
$this->result = $this->db->query($query);
return $this->result;
}
全部整合起来 为演示怎样使用query对象,下面构造了一个小例子,其将把dbquery对象作为存储过程使用,并检查是否输入了正确的用户名与密码,请看例5:
例5:
require 'mysql_db.php5';
require_once 'query2.php5';
$db = new mysqldb;
$db->connect('host', 'username', 'pass');
$db->query('use content_management_system');
$query = new dbquery($db);
$query->prepare('select fname,sname from users where username=:1s and pword=:2s and expire_time<:3i');
if ($result = $query->execute("visualad", "apron", time())) {
if ($db->num_rows($result) == 1) {
echo('凭证正确。');
} else {
echo('凭证不正确,会话已过期。');
}
} else {
echo('执行query时发生错误:' . $db->error());
}
在本文中,你已看到了如何在声明类变量时,利用访问修饰符private、protected和public,保护数据和限制数据对象的可见性,同时,在php 5中,这些概念也可用于其他的数据类,保护其重要的内部数据。