利用PHP的OOP特性实现数据保护

  在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' 无法识别。");
 }
}
  函数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中,这些概念也可用于其他的数据类,保护其重要的内部数据。

时间: 2024-12-13 09:31:20

利用PHP的OOP特性实现数据保护的相关文章

ruby way之高级OOP特性之二

1 把代码像对象一样存储 当你想要以对象的形式存储一块代码的时候,ruby给了你几种方法.下面我们会介绍Proc 对象, Method 对象和 UnboundMethod 对象. 内置的Proc 类包装ruby block到一个对象.Proc对象,像一个blocks,是一个闭包而且保存了它定义 时的上下文: Ruby代码 myproc = Proc.new { |a| puts "Param is #{a}" } myproc.call(99) # Param is 99 当一个方法接

ruby way之高级OOP特性之一

1 发送一条消息给一个对象 当你调用一个方法时,你也就是发送了一条消息给一个对象,在ruby中我们能够在运行时决定那个方 法被调用.send 方法就是做这个的,他接受一个symbol为参数. 举个简单的例子,假设我们要写一个排序,我们想要使用不同的域作为比较的key.虽然我们这时可以 用block,可是如果使用send的话,我们能有一个更优美的写法: Java代码 class Person attr_reader :name, :age, :height def initialize(name,

给大家一个点子:利用MySQL的一个特性实现MySQL查询结果的分页显示

mysql|分页|显示 在mysql中利用select语句的一个特性就可以很方便地实现查询结果的分页,select语句的语法:    SELECT [STRAIGHT_JOIN] [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [HIGH_PRIORITY]           [DISTINCT | DISTINCTROW | ALL]        select_expression,...        [INTO OUTFILE 'file_name' expor

利用MySQL的一个特性实现MySQL查询结果的分页显示

在mysql中利用select语句的一个特性就可以很方便地实现查询结果的分页,select语句的语法: SELECT [STRAIGHT_JOIN] [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [HIGH_PRIORITY] [DISTINCT | DISTINCTROW | ALL] select_expression,... [INTO OUTFILE 'file_name' export_options] [FROM table_references [WHER

利用JDK8的新特性计算某个目录下的文件中包含字符串的次数

需求:计算某个字符串在某个文件夹中出现的次数.**这篇文章利用了JDK1.8的新特性Stream流和Lambda表达式并结合了线程池的使用.** package com.zkn.fullstacktraining.seventh; import javafx.util.Pair; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; im

利用 Java 平台的特性建造一个令人瞩目的系统

在过去的几年中,Java 平台技术取得了一些惊人进展.但这项技术在某些方面的广泛应用和它最初的设计目标完全不同.Java 平台技术最初是希望通过客户端运行 Applet 和 application,来给网页增加交互性.而现在该技术最常见的用途却是基于服务器的 J2EE 系统.为了让 Java 平台在客户端发挥它的最大潜力,人们开发了许多新技术.由于企业系统逐渐被互联网应用程序所取代,掌握这些新技术也就非常必要.在这篇文章中,你可以看到如何利用新旧技术来达到此目的. Applet 遇到什么问题?

利用Windows2000的RunAs特性进行远程管理

让我们先以正常的方法建立一个可以远程控制的"服务"控制台.首先,我们在"运行"窗口运行 mmc,调入微软管理控制台界面 在控制台(C)菜单中我们选择"添加/删除管理单元(M)" 然后,弹出对话窗: 按添加(D)钮,弹出对话窗,并选择"服务" 弹出对话窗

ASP.NET MVC3 SEO优化:利用Routing特性提高站点权重_实用技巧

简介 我们在开发互联网程序的时候,有个很重要的事情就是做搜索引擎优化(SEO),我们都知道ASP.NET MVC程序提供了友好的URL以及永久重定向的支持,这些友好的URL是利用Routing系统的特性来支持的,但是在这个Routing里有个问题,就是多个不同的地址和指向同一个action方法,那对于搜索引擎来说就意味着你的站点有很多地址的内容都是重复的. 本章内容将展示如果解决这一问题. 正文 对于SEO,一个地址对应一个唯一独立的内容是保证最好权重的一个重要步骤,所以我们需要确保每一个URL

Windows 10 安全新特性保障现代企业安全,掌握创新场景机遇

网络攻击总会给企业造成巨大损失.致同国际 2015 年<国际商业调查报告>[ 致同国际于 2014 至 2015 年对 35 个经济体的 2,500 家企业领导人进行调查,并于 2015 年 10 月发布<国际商业调查报告>.]结果显示,多达六分之一(15%)的被调查企业受到过网络攻击,网络攻击给企业造成的损失平均达到销售收入的 1.2%.其中在亚太地区,12 个月内黑客攻击给本地区企业造成 813 亿美元的损失,在全球则造成至少 3,150 亿美元的损失.网络攻击不仅造成企业的经