在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看起来像以下这样:
SELECT col1,col2 FROM table_name WHERE col1=:1I
execute()--函数execute()将执行query。如果它被prepare()函数过早地初始化为一个存储过程,任何传递进来的参数都会被作为存储过程的执行参数,否则,第一个参数只会被作为查询文本。函数execute()将返回执行查询后的结果。
compile()--函数compile()与函数execute()类似,实际上,query并没有执行,而是替换查询字符串中所有占位符,接受存储过程的参数,并返回query的编译版本。
受保护的成员 正如上面所提到的,可见性的概念可用于隐藏对象的内部工作,保护内部工作所需的数据完整性。前面已经解释,query返回的结果指针将会保存为protected属性,在此使用保护成员是因为从query对象派生出来的特定数据库query对象可能会重载某些核心功能。
深掘代码 理论说够了,现在开始编写代码,首先,创建一个例1所示的模板:
例1:数据库query类的一个模板
class DBQuery
{
/**
*保存一个实现了DB接口对象的引用。
*/
protected $db;
/**
*如果是一个存储过程,设为true。
*/
protected $stored_procedure = false;
/**
*保存一个删除了所有字符串的query。
*/
private $query;
/**
*用于在SQL中匹配引号。
*/
private static $QUOTE_MATCH = "/(".*(?db = $db;
}
public function prepare($query)
{
$this->stored_procedure = true;
}
public function compile($args)
{}
public function execute($query)
{}
}
函数prepare
为使用例1中的模板,你要做的第一件事是构建好prepare()函数,为确保无带引号的字符被偶然解析为占位符,函数应该移除query内所有字符串,并把它们临时存储在一个数组内。而字符串本身也会被占位符取代,其通常被识别为不应该在SQL语句中出现的的字符串序列。在query的编译期间,过程占位符会首先被替换,接着把字符串放回query中,这是通过preg_replace()函数,和另一个用作preg_replace()函数的helper回调函数完成的。
例2:prepare()函数
/**
* 把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' 无法识别。");
}
}
関数 copy() では、2 つの追加関数が使用されます。compile_callback() 関数は、クエリ内でプレースホルダーが見つかるたびに、preg_replace() 関数呼び出しのコールバック関数として使用されます。コンパイル関数に渡されると実行されます。
関数execute
最後に、関数execute()を構築する必要があります。関数execute()は、DBオブジェクトを使用してクエリをコンパイルし、実行します。 、DB オブジェクトは、DBQuery オブジェクトの初期化に使用されます。例 4 では、コンパイルされたクエリを取得するために関数 call_user_func_array() がどのように使用されているかに注意してください。その理由は、関数execute() が実行時まで渡される引数の数を決定できないためです。
例 4:execute() 関数
/**
*
* 現在のクエリを実行し、プレースホルダーを指定されたパラメーターに置き換えます。
*
* @parammixed$queryParams,... クエリパラメータ
* @return resource 実行されたクエリを表すリソースへの参照。
*/
public functionexecute($queryParams = '')
{
//例: SELECT * FROM table WHERE name=:1S AND type=:2I AND level=:3N
$args = func_get_args();
if ($this->stored_procedure) {
/* 関数compileを呼び出してクエリを取得します */
$query = call_user_func_array(array($this, 'compile'), $args);
} else {
/* If theストアド プロシージャが初期化されていない場合は、標準クエリとして実行します。 */
$query = $queryParams;
}
$this->result = $this->db->query($query);
return $this->result;
}
すべてを統合する クエリ オブジェクトの使用方法を示すために、以下に小さな例を構築します。 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('クエリの実行中にエラーが発生しました:' . $db->error());
}
この記事では、クラス変数を宣言するときに、アクセス修飾子 private、protected、public を使用してデータを保護し、同時にデータ オブジェクトの可視性を制限する方法を説明しました。他のデータ クラスにも使用して、重要な内部データを保護できます。