关于奇矩互动奇矩互动招贤纳士奇矩互动优质虚拟主机Discuz!商业用户享有本站VIP服务LAMP环境配置手册(CentOS5.1)
 13 12
发新话题
打印

PHP学习专题--第12期:PHP5 Web开发详解应用篇

本主题由 Edwin 于 2008-4-7 09:45 加入精华

PHP学习专题--第12期:PHP5 Web开发详解应用篇

随着《PHP5权威编程》中文版的推出,为了使大家对于php5的新特性有一个全面、深刻的理解,将其优势更好的应用到Web开发中。使得新的面向对象模型让我们在开发过程中,更加得心应手,游刃有余。这期专题我们将详细介绍PHP5的一些基础性知识,让大家对PHP5有一个初步的了解和认识。

PHP 5高级面向对象编程和设计模式
早在PHP3版本中,PHP就支持面向对象的编程(OOP)。虽然当时可以用面向对象编程,但是PHP对其的支持是非常简单的,而且到PHP4时也没有得到大幅度的改进,这主要是考虑到向后兼容性的问题。后来因为广泛地提高了对OOP支持的要求,PHP5才对整个面向对象的模型重新进行了设计,增加了大量的特性并且更改了“对象”本身的基础运行机制。


PHP5 OOP编程之代理与定制异常(1)

一、 DBQuery对象

  现在,我们的DBQuery对象简单地模仿一个存储过程—一旦被执行,即返回一个必须进行保存的结果资源;并且如果你想使用该结果集上的函数(例如num_rows()或fetch_row())的话,你必须传递MySqlDB对象。那么,如果由DBQuery对象来实现MySqlDB对象(其设计目的是对一个执行查询的结果进行操作)实现的函数,效果如何呢?让我们继续使用上一篇示例中的代码;并且让我们假定,现在由DBQuery对象管理我们的结果资源。DBQuery类的源码如列表1所示。

  列表1.使用DBQuery类。

require 'mysql_db.php';
require_once 'query.php';
$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');
try {
 if($query->execute("visualad", "apron", time()))->num_rows() == 1) {
  echo('Correct Credentials');
 } else {
  echo('Incorrect Credentials / Session Expired');
 }
} catch (QueryException $e) {
 echo('Error executing query: ' . $e);
}

  上面修改后的代码中我们最感兴趣的是,catch语句和execute语句。

  · execute语句不再返回一个结果资源,现在它返回DBQuery对象本身。

  · DBQuery对象现在实现num_rows()函数—我们从DB接口中已经熟悉。

  · 如果查询执行失败,它抛出一个QueryException类型的异常。当被转换成一个字符串时,它将返回发生的错误的细节信息。

  为此,你需要使用代理。事实上,你在我们的DBQuery对象中已经使用代理了,但是现在将更为深入地使用它来把它与MySqlDB对象紧密绑定。该DBQuery对象已经被使用一个实现DB接口的对象初始化,并且它已经包含一个成员函数execute—由它调用DB对象的query()方法来执行该查询。这个DBQuery对象本身并不实际地查询数据库,它把这项任务交由DB对象来完成。这就是代理,其实是一个进程—借助于这个进程,通过把消息发送给另一个实现相同的或类似行为的对象,一个对象可以实现一个特别的行为。

  为此,你需要修改DBQuery对象以便包括所有的函数—它们操作一个来自DB对象的结果资源。当执行查询以调用DB对象的相应函数并且返回它的结果时,你需要使用存储的结果。下列函数将被添加:

  列表2:使用代理扩展DBQuery类。

class DBQuery
{
 .....

 public function fetch_array()
 {
  if (! is_resource($this->result)) {
   throw new Exception('Query not executed.');
  }
  return $this->db->fetch_array($this->result);
 }

 public function fetch_row()
 {
  if (! is_resource($this->result)) {
   throw new Exception('Query not executed.');
  }
  return $this->db->fetch_row($this->result);
 }

 public function fetch_assoc()
 {
  if (! is_resource($this->result)) {
   throw new Exception('Query not executed.');
  }
  return $this->db->fetch_assoc($this->result);
 }

 public function fetch_object()
 {
  if (! is_resource($this->result)) {
   throw new Exception('Query not executed.');
  }
  return $this->db->fetch_object($this->result);
 }

 public function num_rows()
 {
  if (! is_resource($this->result)) {
   throw new Exception('Query not executed.');
  }
  return $this->db->num_rows($this->result);
 }
}

  每个函数的实现相当简单。它首先进行检查,以确保已经执行查询,然后把任务代理到DB对象,返回它的结果就好象它是查询对象本身(称作是基本数据库函数)一样。

  二、 类型提示(Type Hinting)

  为了使代理能够工作,我们需要确保DBQuery对象的$db变量是一个实现了DB接口的对象的实例。类型提示是PHP5中的一种新特征,它能够使你把函数参数强制转换成特定类型的对象。在PHP5之前,唯一的确保函数参数是一个特定对象类型的方法是使用PHP中所提供的类型检查函数(也即是is_a())。现在,你可以简单地强制转换对象类型—通过在函数参数的前面加上类型名。你已经从我们的DBQuery对象中看到了类型提示,这样可以确保一个实现DB接口的对象被传递到对象构造器中。

public function __construct(DB $db)
{
 $this->db = $db;
}

  当使用类型提示时,你不仅可以指定对象类型,还可以指定抽象类和接口。

TOP

PHP5 OOP编程之代理与定制异常(2)

三、 抛出异常

  你可能已经从上面的代码中注意到,你捕获的是一个称为QueryException(我们将在后面实现这个对象)的异常。一个异常类似于一个错误,然而却更具有一般性。描述一个异常的最好的方法是使用emergency。尽管一个emergency可以不会是“致命的”,但是还是必须处理它。当在PHP中抛出一个异常时,执行的当前范围很快地被终止,不管它是一个函数,try..catch块还是脚本本身。然后,该异常遍历调用栈—终止每个执行范围,直到或者在一个try..catch块中捕获它或者它到达调用栈的顶部—此时它将生成一个致命错误。

  异常处理是PHP 5中的另外一个新特征,当与OOP联用时,它能够实现良好地控制错误处理和报告。一个try..catch块是一种处理异常的重要机制。一旦被捕获,脚本将会从异常被捕获和被处理的代码的下一行继续执行。

  如果查询失败,你需要改变你的execute函数以抛出一个异常。你将抛出一个称为QueryException的定制异常对象—导致错误的DBQuery对象被传递给它。

  列表3.抛出一个异常。

/**
*执行当前查询
*
* 执行当前查询—用提供的参数代替任何点位符
* .
*
* @参数: mixed $queryParams,... 查询参数
* @返回:资源A—参考描述执行查询的资源。
*/
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 = call_user_func_array(array($this, 'compile'), $args);
 } else {
  /*一个存储过程没被初始化,因此,作为一种标准查询来执行之*/
  $query = $queryParams;
 }
 $result = $this->db->query($query);
 if (! $result) {
  throw new QueryException($this);
 }
 $this->result = $result;
 /* 注意现在我们怎么返回对象本身,这使我们能够从这个函数的返回结果中调用成员函数
 */
 return $this;
}

  四、 使用继承抛出定制异常

  在PHP中,你可以抛出任何对象作为一个异常;但是,首先该异常应该继承自PHP的内置异常类。通过创建你自己的定制异常,你可以记录其它有关于该错误的信息,例如在一个日志文件中创建一个入口,或做你喜欢做的任何事情。你的定制异常将要做如下几件事情:

  · 记录由查询产生的来自DB对象的错误消息。

  · 给出查询错误发生所在行代码的准确细节—通过检查调用栈。

  · 显示错误消息和查询文本—当被转换成一个字符串时。

  为了得到错误信息和查询文本,需要对DBQuery对象作多处更改。

  1. 一个新的protected属性—compiledQuery—需要被添加到类中。

  2. compile()函数使用查询文本更新查询compiledQuery属性。

  3. 应该加入一个检索编译的查询文本的函数。

  4. 还应该加入一个函数—它得到当前的与DBQuery对象相关联的DB对象。

  列表4.抛出一个异常。

class DBQuery
{
 /**
 *在调用compile()或execute()之后存储查询的编译版本
 *
 * @var string $compiledQuery
 */
 protected $compiledQuery;
 /**
 * 返回编译的查询而不执行它。
 * @参数:mixed $params,...查询参数
 * @返回:字符串—编译的查询
 */
 public function compile($params='')
 {
  if (! $this->stored_procedure) {
   throw new Exception("存储过程没被初始化.");
  }
  /*代替参数*/
  $params = func_get_args(); //得到函数参数
  $query = preg_replace("/(?compile_callback($params, 1, "2")', $this->query);
  return ($this->compiledQuery = $this->add_strings($query)); //把字符串放回查询中
 }
 public function getDB()
 {
  return $this->db;
 }
 public function getCompiledQuery()
 {
  return $this->compiledQuery;
 }
}

  现在,你可以实现QueryException类。注意你是如何遍历调用栈以在脚本中查找实际导致错误的位置的。这正好适用于当抛出异常的DBQuery对象是一个继承自DBQuery对象的子类的情况。

  列表5ueryException类。

/**
*查询异常
*
*当试图执行一个查询时,如果一个错误发生,将由{@link DBQuery}对象抛出错误
*/
class QueryException extends Exception
{
 /**
 * 查询文本
 *
 * @var字符串$QueryText;
 */
 protected $QueryText;
 /**
 *来自数据库的错误号/代码
 *
 * @var字符串$ErrorCode
 */
 protected $ErrorNumber;
 /**
 *来自数据库的错误消息
 *
 * @var字符串$ErrorMessage
 */
 protected $ErrorMessage;
 /**
 *类构造器
 *
 * @参数:DBQuery $db,是抛出异常的查询对象
 */
 public function __construct(DBQuery $query)
 {
  /*得到调用栈*/
  $backtrace = $this->GetTrace();
  /*把行和文件设置到错误实际发生的位置*/
  if (count($backtrace) > 0) {
   $x = 1;
   /*如果查询类被继承,那么我们需要忽略由子类所进行的调用*/
   while((! isset($backtrace[$x]['line'])) ||
(isset($backtrace[$x]['class']) && is_subclass_of($backtrace[$x]['class'], 'DBQuery')) ||
(strpos(strtolower(@$backtrace[$x]['function']), 'call_user_func')) !== false ) {
    /*循环执行,只要没有行号或调用的函数是DBQuery类的一个子类*/
    ++$x;
    /*如果我们到达栈底,那么我们使用第一个调用者*/
    if (($x) >= count($backtrace)) {
     $x = count($backtrace);
     break;
    }
   }
   /*如果上面的循环至少执行一次,那么我们可以把它减1以查找实际的引起错误的代码行
   */
   if ($x != 1) {
    $x -= 1;
   }
   /*最后,我们可以设置文件和行号,这应该可以反映出引起错误的SQL语句*/
   $this->line = $backtrace[$x]['line'];
   $this->file = $backtrace[$x]['file'];
  }
  $this->QueryText = $query->getCompiledQuery();
  $this->ErrorNumber = $query->getDB()->errno();
  $this->ErrorMessage = $query->getDB()->error();
  /*调用超类的异常构造器*/
  parent::__construct('Query Error', 0);
 }
 /**
 *得到查询文本
 *
 * @返回字符串查询文本
 */
 public function GetQueryText()
 {
  return $this->QueryText;
 }
 /**
 *得到错误号
 *
 * @返回字符串错误号
 */
 public function GetErrorNumber()
 {
  return $this->ErrorNumber;
 }
 /**
 *得到错误消息
 *
 * @返回字符串错误消息
 */
 public function GetErrorMessage()
 {
  return $this->ErrorMessage;
 }
 /**
 *当对象被转换为一个字符串时调用。
 * @返回字符串
 */
 public function __toString()
 {
  $output = "Query Error in {$this->file} on line {$this->line}nn";
  $output .= "Query: {$this->QueryText}n";
  $output .= "Error: {$this->ErrorMessage} ({$this->ErrorNumber})nn";

  return $output;
 }
}

  至此,在本节开始看到的代码可以工作了。

  五、 结论

  在本文中,你看到了代理是怎样把与查询相联系的DB接口映射到针对一个特定的查询结果上的操作。DBQuery对象暴露相同的函数,例如fetch_assoc(),作为DB对象。然而,这些都是针对单个查询起作用。你还学习了如何使用定制异常来给出详细信息—一个错误发生在何时何地,以及它们怎样更好地控制错误的处理。

TOP

使用PHP5创建图形巧法(一)

本文将展示如何使用 PHP 构建面向对象的图形层。使用面向对象的系统可以用来构建复杂的图形,这比使用标准 PHP 库中所提供的基本功能来构建图形简单很多。

  我将图形编辑程序分为两类:一类是绘图程序,利用这种程序可以一个像素一个像素地绘制图像;另外一类是制图程序,这种程序提供了一组对象,例如线、椭圆和矩形,您可以使用这些对象来组合成一幅大图像,例如JPEG。绘图程序非常适合进行像素级的控制。但是对于业务图形来说,制图程序是比较好的方式,因为大部分图形都是由矩形、线和椭圆组成的。

  PHP内置的制图基本操作与绘图程序非常类似。它们对于绘制图像来说功能非常强大;但是如果您希望自己的图像是一组对象集合时,这就不太适合了。本文将向您展示如何在 PHP 图形库的基础上构建一个面向对象的图形库。您将使用 PHP V5 中提供的面向对象的扩展。

  具有面向对象的图形支持之后,您的图形代码就非常容易理解和维护了。您可能还需要从一种单一的图形源将图形合成为多种类型的媒介:Flash 电影、SVG 等等。

  目标

  创建一个图形对象库包括 3 个主要的目标:

  从基本操作切换到对象上

  它不使用 imageline、imagefilledrectangle 以及其他图形函数,这个库应该提供一些对象,例如 Line、Rectangle 和 Oval,它们可以用来制作图像。它应该还可以支持构建更大的复杂对象或对对象进行分组的功能。

  可以进行 z 值排序

  制图程序让画家可以在画面表面上上下移动图形对象。这个库应该可以支持将一个对象放到其他对象前后的功能:它使用了一个 z 值,用来定义对象从制图平面开始的高度。z 值越大的对象被画得越晚,也就出现在那些 z 值较小的对象之上。

  提供 viewport 的转换

  通常,数据的坐标空间与图像的坐标空间是不同的。PHP 中的图形基本操作是对图像的坐标平面进行操作的。这个图形库应该支持 viewport 的规范,这样您就可以在一个程序员熟悉的坐标系统中指定图形了,并且可以自动进行伸缩来适应任何图像的大小。

  由于这里有很多特性,您将一步步地编写代码来展示这些代码如何不断增加功能。

  基础知识

  让我们首先来看一个图形环境对象和一个名为 GraphicsObject 的接口,它是使用一个 Line 类实现的,功能就是用来画线。UML 如图 1 所示。

图 1. 图形环境和图形对象接口


  GraphicsEnvironment 类中保存了图形对象和一组颜色,还包括宽度和高度。saveAsPng 方法负责将当前的图像输出到指定的文件中。

  GraphicsObject 是任何图形对象都必须使用的接口。要开始使用这个接口,您所需要做的就是使用 render方法来画这个对象。它是由一个 Line 类实现的,它利用 4 个坐标:开始和结束的 x 值,开始和结束的 y 值。它还有一个颜色。当调用render 时,这个对象从 sx,sy 到 ex,ey 画一条由名字指定的颜色的线。

  这个库的代码如清单 1 所示。

  清单 1. 基本的图形库

            
<?php
class GraphicsEnvironment
{
  public $width;
  public $height;
  public $gdo;
  public $colors = array();

  public function __construct( $width, $height )
  {
    $this->width = $width;
    $this->height = $height;
    $this->gdo = imagecreatetruecolor( $width, $height );
    $this->addColor( "white", 255, 255, 255 );
    imagefilledrectangle( $this->gdo, 0, 0,
      $width, $height,
      $this->getColor( "white" ) );
  }

  public function width() { return $this->width; }

  public function height() { return $this->height; }

  public function addColor( $name, $r, $g, $b )
  {
    $this->colors[ $name ] = imagecolorallocate(
      $this->gdo,
      $r, $g, $b );
  }

  public function getGraphicObject()
  {
    return $this->gdo;
  }

  public function getColor( $name )
  {
    return $this->colors[ $name ];
  }

  public function saveAsPng( $filename )
  {
    imagepng( $this->gdo, $filename );
  }
}

abstract class GraphicsObject
{
  abstract public function render( $ge );
}

class Line extends GraphicsObject
{
  private $color;
  private $sx;
  private $sy;
  private $ex;
  private $ey;

  public function __construct( $color, $sx, $sy, $ex, $ey )
  {
    $this->color = $color;
    $this->sx = $sx;
    $this->sy = $sy;
    $this->ex = $ex;
    $this->ey = $ey;
  }

  public function render( $ge )
  {
    imageline( $ge->getGraphicObject(),
      $this->sx, $this->sy,
      $this->ex, $this->ey,
      $ge->getColor( $this->color ) );
  }
}
?>
            

  测试代码如清单 2 所示:

  清单 2. 基本图形库的测试代码

            
<?php
require_once( "glib.php" );

$ge = new GraphicsEnvironment( 400, 400 );

$ge->addColor( "black", 0, 0, 0 );
$ge->addColor( "red", 255, 0, 0 );
$ge->addColor( "green", 0, 255, 0 );
$ge->addColor( "blue", 0, 0, 255 );

$gobjs = array();
$gobjs []= new Line( "black", 10, 5, 100, 200 );
$gobjs []= new Line( "blue", 200, 150, 390, 380 );
$gobjs []= new Line( "red", 60, 40, 10, 300 );
$gobjs []= new Line( "green", 5, 390, 390, 10 );

foreach( $gobjs as $gobj ) { $gobj->render( $ge ); }

$ge->saveAsPng( "test.png" );
?>
            

  这个测试程序创建了一个图形环境。然后创建几条线,它们指向不同的方向,具有不同的颜色。然后,render 方法可以将它们画到图形平面上。最后,这段代码将这个图像保存为 test.png。

  在本文中,都是使用下面的命令行解释程序来运行这段代码,如下所示:

            
% php test.php
%
            

  图 2 显示了所生成的 test.png 文件在 Firefox 中的样子。

  图2. 简单的图形对象测试



  这当然不如蒙娜丽莎漂亮,但是可以满足目前的工作需要。

TOP

使用PHP5创建图形巧妙方法(二)

添加维数

  我们的第一个需求 —— 提供图形对象的能力 —— 已经满足了,现在应该开始满足第二个需求了:可以使用一个 z 值将一个对象放到其他对象的上面或下面。

  我们可以将每个 z 值当作是原始图像的一个面。所画的元素是按照 z 值从最小到最大的顺序来画的。例如,让我们画两个图形元素:一个红色的圆和一个黑色的方框。圆的 z 值是 100,而黑方框的 z 值是 200。这样会将圆放到方框之后,如图 3 所示:

  图3. 不同 z 值的面



  我们只需要修改一下 z 值就可以将这个红圆放到黑方框之上。要实现这种功能,我们需要让每个 GraphicsObject 都具有一个 z()方法,它返回一个数字,就是 z 值。由于您需要创建不同的图形对象(Line、Oval 和 Rectangle),您还需要创建一个基本的类BoxObject,其他 3 个类都使用它来维护起点和终点的坐标、z 值和这个对象的颜色(请参看图 4)。

  图 4. 给系统添加另外一维:z 值


  这个图形库的新代码如清单 3 所示:

  清单 3. 可以处理 z 信息的图形库

            
<?php
class GraphicsEnvironment
{
  public $width;
  public $height;
  public $gdo;
  public $colors = array();

  public function __construct( $width, $height )
  {
    $this->width = $width;
    $this->height = $height;
    $this->gdo = imagecreatetruecolor( $width, $height );
    $this->addColor( "white", 255, 255, 255 );
    imagefilledrectangle( $this->gdo, 0, 0,
      $width, $height,
      $this->getColor( "white" ) );
  }

  public function width() { return $this->width; }

  public function height() { return $this->height; }

  public function addColor( $name, $r, $g, $b )
  {
    $this->colors[ $name ] = imagecolorallocate(
      $this->gdo,
      $r, $g, $b );
  }

  public function getGraphicObject()
  {
    return $this->gdo;
  }

  public function getColor( $name )
  {
    return $this->colors[ $name ];
  }

  public function saveAsPng( $filename )
  {
    imagepng( $this->gdo, $filename );
  }
}

abstract class GraphicsObject
{
  abstract public function render( $ge );
  abstract public function z();
}

abstract class BoxObject extends GraphicsObject
{
  protected $color;
  protected $sx;
  protected $sy;
  protected $ex;
  protected $ey;
  protected $z;

  public function __construct( $z, $color, $sx, $sy, $ex, $ey )
  {
    $this->z = $z;
    $this->color = $color;
    $this->sx = $sx;
    $this->sy = $sy;
    $this->ex = $ex;
    $this->ey = $ey;
  }

  public function z() { return $this->z; }
}

class Line extends BoxObject
{
  public function render( $ge )
  {
    imageline( $ge->getGraphicObject(),
      $this->sx, $this->sy,
      $this->ex, $this->ey,
      $ge->getColor( $this->color ) );
  }
}

class Rectangle extends BoxObject
{
  public function render( $ge )
  {
    imagefilledrectangle( $ge->getGraphicObject(),
      $this->sx, $this->sy,
      $this->ex, $this->ey,
      $ge->getColor( $this->color ) );
  }
}

class Oval extends BoxObject
{
  public function render( $ge )
  {
    $w = $this->ex - $this->sx;
    $h = $this->ey - $this->sy;
    imagefilledellipse( $ge->getGraphicObject(),
      $this->sx + ( $w / 2 ),
      $this->sy + ( $h / 2 ),
      $w, $h,
      $ge->getColor( $this->color ) );
  }
}
?>
            

  测试代码也需要进行更新,如清单 4 所示。

  清单 4. 更新后的测试代码

            
<?php
require_once( "glib.php" );

function zsort( $a, $b )
{
  if ( $a->z() < $b->z() ) return -1;
  if ( $a->z() > $b->z() ) return 1;
  return 0;
}

$ge = new GraphicsEnvironment( 400, 400 );

$ge->addColor( "black", 0, 0, 0 );
$ge->addColor( "red", 255, 0, 0 );
$ge->addColor( "green", 0, 255, 0 );
$ge->addColor( "blue", 0, 0, 255 );

$gobjs = array();
$gobjs []= new Oval( 100, "red", 50, 50, 150, 150 );
$gobjs []= new Rectangle( 200, "black", 100, 100, 300, 300 );

usort( $gobjs, "zsort" );

foreach( $gobjs as $gobj ) { $gobj->render( $ge ); }

$ge->saveAsPng( "test.png" );
?>
            

  此处需要注意两件事情。首先是我们添加了创建 Oval 和 Rectangle 对象的过程,其中第一个参数是 z 值。其次是调用了 usort,它使用了 zsort 函数来对图形对象根据 z 值进行排序。

  在运行这个程序时,test.png 文件应该如图 5 所示。

  图5. 红圆在黑方框之后



  现在修改下面的代码:

            
$gobjs []= new Oval( 200, "red", 50, 50, 150, 150 );
$gobjs []= new Rectangle( 100, "black", 100, 100, 300, 300 );
            

  再次运行这个代码,突然这个椭圆就在这个方框上面了,如图 6 所示。

  图6. 红圆现在在黑方框上面了



  红圆现在就出现在黑方框上面了,尽管它是先创建的,也是首先添加到数组中的。这就是 z 值的实际价值:您可以按照任何顺序来创建对象,并可以通过调整每个对象的 z 值来调整彼此之间的相对位置。

  在这段代码中,z 值排序是在这个库之外实现的。让我们通过创建一个新容器对象 Group 来实现这种功能,其中保存了一组 GraphicsObject 对象。Group 对象然后再处理排序的问题。

  Group 类的代码如清单 5 所示。

  清单 5. Group 类

            
function zsort( $a, $b )
{
  if ( $a->z() < $b->z() ) return -1;
  if ( $a->z() > $b->z() ) return 1;
  return 0;
}

class Group extends GraphicsObject
{
  private $z;
  protected $members = array();
  public function __construct( $z )
  {
    $this->z = $z;
  }
  public function add( $member )
  {
    $this->members []= $member;
  }
  public function render( $ge )
  {
    usort( $this->members, "zsort" );
    foreach( $this->members as $gobj )
    {
      $gobj->render( $ge );
    }
  }
  public function z() { return $this->z; }
}
            

  Group 对象的任务是保持一个对象数组,然后在画图时,逐个对对象zo进行排序和画图。

  更新后的测试代码如清单 6 所示。

  清单 6. 更新后的测试代码

            
<?php
require_once( "glib.php" );

$ge = new GraphicsEnvironment( 400, 400 );

$ge->addColor( "black", 0, 0, 0 );
$ge->addColor( "red", 255, 0, 0 );
$ge->addColor( "green", 0, 255, 0 );
$ge->addColor( "blue", 0, 0, 255 );

$g1 = new Group( 0 );
$g1->add( new Oval( 200, "red", 50, 50, 150, 150 ) );   
$g1->add( new Rectangle( 100, "black", 100, 100, 300, 300 ) );

$g1->render( $ge );

$ge->saveAsPng( "test.png" );
?>
            

  现在所有的客户机需要做的是创建一个 Group 对象。它会处理排序和其他操作。

TOP

使用PHP5创建图形的巧妙方法三

创建 viewport

  viewport是一个人造的坐标系统,可以转换成图像的物理坐标系统。viewport 的扩展可以是您希望的任何东西。例如,x 和 y 轴的起点和终点可以是-2 和 2,这样 viewport 坐标平面的中心就是 0, 0。这对于三角图形(例如 sin 和 cosine)来说是很好的一个viewport。或者,这个 viewport 也可以是不对称的,其中 y 值的范围从 -1 到 1,x 值的范围是从 0 到10,000,这取决于您的需要。

  这个 viewport 的其他值可以确保构建一个 400X400的图像所采用的逻辑与构建一个 4000X2000 的图像所采用的逻辑是相同的。代码负责向这个 viewport 中写入数据,然后这个viewport 自动实现到图像的物理尺寸的自动映射。

  要让您的 viewport 正常工作,您需要将这个 viewport 的范围从 0,0 修改为 1,1,这可以让图形对象回调图形环境,从而将 viewport 的坐标转换成物理坐标。您可以将所有的代码都放到 BoxObject 基类中进行简化。

  图 7 显示了有关新添加的代码的两个内容。首先是添加的 tx 和 ty 方法,这会将 x 和 y 坐标从 viewport转换成物理图像的坐标。第二个是对 BoxObject 增加了 draw 方法,它的派生类应该用来进行制图。BoxObject 在 render方法中实现 viewport 的转换,并使用物理坐标来调用 draw 方法。使用这种方法,Line、Oval 和 Rectangle类都可以利用 viewport 坐标,而不需要担心坐标转换的问题。

图 7. 所添加的图形环境 viewport 转换


  这个新库的代码如清单 7 所示:

  清单 7. 具有 viewport 支持的图形库

            
<?php
class GraphicsEnvironment
{
  public $width;
  public $height;
  public $gdo;
  public $colors = array();

  public function __construct( $width, $height )
  {
    $this->width = $width;
    $this->height = $height;
    $this->gdo = imagecreatetruecolor( $width, $height );
    $this->addColor( "white", 255, 255, 255 );
    imagefilledrectangle( $this->gdo, 0, 0,
      $width, $height,
      $this->getColor( "white" ) );
  }

  public function width() { return $this->width; }

  public function height() { return $this->height; }

  public function addColor( $name, $r, $g, $b )
  {
    $this->colors[ $name ] = imagecolorallocate(
      $this->gdo,
      $r, $g, $b );
  }

  public function getGraphicObject()
  {
    return $this->gdo;
  }

  public function getColor( $name )
  {
    return $this->colors[ $name ];
  }

  public function saveAsPng( $filename )
  {
    imagepng( $this->gdo, $filename );
  }
   
  public function tx( $x )
  {
    return $x * $this->width;
  }
   
  public function ty( $y )
  {
    return $y * $this->height;
  }
}

abstract class GraphicsObject
{
  abstract public function render( $ge );
  abstract public function z();
}

function zsort( $a, $b )
{
  if ( $a->z() < $b->z() ) return -1;
  if ( $a->z() > $b->z() ) return 1;
  return 0;
}

class Group extends GraphicsObject
{
  private $z;
  protected $members = array();
  public function __construct( $z )
  {
    $this->z = $z;
  }
  public function add( $member )
  {
    $this->members []= $member;
  }
  public function render( $ge )
  {
    usort( $this->members, "zsort" );
    foreach( $this->members as $gobj )
    {
      $gobj->render( $ge );
    }
  }
  public function z() { return $this->z; }
}

abstract class BoxObject extends GraphicsObject
{
  protected $color;
  protected $sx;
  protected $sy;
  protected $ex;
  protected $ey;
  protected $z;

  public function __construct( $z, $color, $sx, $sy, $ex, $ey )
  {
    $this->z = $z;
    $this->color = $color;
    $this->sx = $sx;
    $this->sy = $sy;
    $this->ex = $ex;
    $this->ey = $ey;
  }

  public function render( $ge )
  {
    $rsx = $ge->tx( $this->sx );
    $rsy = $ge->ty( $this->sy );
    $rex = $ge->tx( $this->ex );
    $rey = $ge->ty( $this->ey );
    $this->draw( $rsx, $rsy, $rex, $rey,
          $ge->getGraphicObject(),
          $ge->getColor( $this->color ) );
  }

  abstract public function draw( $sx, $sy,
    $ex, $ey, $gobj, $color );

  public function z() { return $this->z; }
}

class Line extends BoxObject
{
  public function draw( $sx, $sy, $ex, $ey,
    $gobj, $color )
  {
    imageline( $gobj, $sx, $sy, $ex, $ey,
       $color );
  }
}

class Rectangle extends BoxObject
{
  public function draw( $sx, $sy, $ex, $ey,
    $gobj, $color )
  {
    imagefilledrectangle( $gobj, $sx, $sy,
      $ex, $ey, $color );
  }
}

class Oval extends BoxObject
{
  public function draw( $sx, $sy, $ex, $ey,
    $gobj, $color )
  {
    $w = $ex - $sx;
    $h = $ey - $sy;
    imagefilledellipse( $gobj,
      $sx + ( $w / 2 ), $sy + ( $h / 2 ),
      $w, $h, $color );
  }
}
?>
            

  GraphicsEnvironment 类中的 viewport 转换代码是高亮显示的,正如 GraphicsObject 中的 render 代码一样,这会回调图形环境来进行坐标转换的工作。

  测试代码只需要稍加修改即可(请参看清单 8)。这些对象现在需要在 0,0 和 1,1 之间的 viewport 中进行指定。

  清单 8. 使用新 viewport 坐标的测试代码

            
$g1 = new Group( 0 );
$g1->add( new Oval( 200, "red", 0.1, 0.1, 0.5, 0.5 ) );   
$g1->add( new Rectangle( 100, "black", 0.4, 0.4, 0.9, 0.9 ) );
            

  这非常不错,但是您可能实际上并不希望使用一个 0,0 与 1,1 之间的viewport;而是希望使用任意的 viewport —— 例如,在 -1000,-1000 到 1000,1000之间。要让这成为可能,这个图形环境就需要知道 viewport 的起点和终点。

TOP

使用PHP5创建图形的巧妙方法四

图 8 显示了更新后的 GraphicsEnvironment 类,它具有几个成员变量,用来存储 viewport 的起点和终点坐标:vsx,vsy 和 vex,vey。图形对象并不需要进行修改。

  图 8. 具有灵活 viewport 规范的图形环境



  清单 9 显示了更新后的 GraphicsEnvironment 代码。

  清单 9. 更新后的 GraphicsEnvironment 代码

            
class GraphicsEnvironment
{
  public $vsx;
  public $vsy;
  public $vex;
  public $vey;
  public $width;
  public $height;
  public $gdo;
  public $colors = array();

  public function __construct( $width, $height,
    $vsx, $vsy, $vex, $vey )
  {
    $this->vsx = $vsx;
    $this->vsy = $vsy;
    $this->vex = $vex;
    $this->vey = $vey;
    $this->width = $width;
    $this->height = $height;
    $this->gdo = imagecreatetruecolor( $width, $height );
    $this->addColor( "white", 255, 255, 255 );
    imagefilledrectangle( $this->gdo, 0, 0,
      $width, $height,
      $this->getColor( "white" ) );
  }

  public function width() { return $this->width; }

  public function height() { return $this->height; }

  public function addColor( $name, $r, $g, $b )
  {
    $this->colors[ $name ] = imagecolorallocate(
      $this->gdo,
      $r, $g, $b );
  }

  public function getGraphicObject()
  {
    return $this->gdo;
  }

  public function getColor( $name )
  {
    return $this->colors[ $name ];
  }

  public function saveAsPng( $filename )
  {
    imagepng( $this->gdo, $filename );
  }
   
  public function tx( $x )
  {
    $r = $this->width / ( $this->vex - $this->vsx );
    return ( $x - $this->vsx ) * $r;
  }
   
  public function ty( $y )
  {
    $r = $this->height / ( $this->vey - $this->vsy );
    return ( $y - $this->vsy ) * $r;
  }
}
            

  现在这个构造函数可以利用另外 4 个参数了,它们分别是 viewport 的起点和终点。 tx 和 ty 函数使用新的 viewport 坐标,并将 viewport 坐标转换成物理坐标。

  测试代码如清单 10 所示。

  清单 10. viewport 测试代码

            
<?php
require_once( "glib.php" );

$ge = new GraphicsEnvironment( 400, 400,
   -1000, -1000, 1000, 1000 );

$ge->addColor( "black", 0, 0, 0 );
$ge->addColor( "red", 255, 0, 0 );
$ge->addColor( "green", 0, 255, 0 );
$ge->addColor( "blue", 0, 0, 255 );

$g1 = new Group( 0 );
$g1->add( new Oval( 200, "red", -800, -800, 0, 0 ) );   
$g1->add( new Rectangle( 100, "black", -400, -400, 900, 900 ) );

$g1->render( $ge );

$ge->saveAsPng( "test.png" );
?>
            

  这段测试代码会在 -1000,-1000 与 1000,000 之间创建一个 viewport。对象会被重新放置,以适合这个新的坐标系统。

  测试代码的输出如图 9 所示。

  图 9. viewport 绘制的图像转换为一个 400X400 的图像



  如果您希望图像的大小是 400X200,就可以采用下面的方法:

            
$ge = new GraphicsEnvironment( 400, 200,
  -1000, -1000, 1000, 1000 );
            

  您会得到一个纵向缩小后的图像,如图 10 所示。

  图 10. 图形的 400X200 版本



  这展示了代码如何自动调整图像的大小来适合所请求的图像。

  结束语

  动态图可以为应用程序添加一个新的交互层。使用这种面向对象的系统可以让构建复杂图形变得非常简单,比使用标准的 PHP库中的基本操作来画图更加简单。另外,您还可以实现画不同大小或类型的图像,并且可以长期使用相同的代码来画不同类型的媒介,例如SVG、PDF、Flash 和其他类型的媒介。

TOP

用PHP5的DirectoryIterators递归扫描目录

PHP5中增加了Iterator,一组有助于导航和处理等级数据结构的现成接口,这是PHP5最有趣的新特性之一。
这些Iterator显著减少了处理XML文档树或文件集合所需的代码。PHP5中使用大量Iterator,包括ArrayIterator、 CachingIterator、LimitIterator、RecursiveIterator、SimpleXMLIterator和 DirectoryIterator。
通过DirectoryIterator可以迅速有效地对目录中的文件进行处理。在编码过程中稍微增加一些创造力,DirectoryIterator还可用于递归处理嵌套式目录树。这两个任务只需使用几行代码就可以完成,比“标准”处理方法有了显著提高。
处理单级目录首先我们从简单的任务着手:处理一个单级目录。输入(或复制)以下代码(列表A),修改目录路径以反映当地配置:
列表A
<?php

$it = new DirectoryIterator("/tmp/mystuff");

foreach($it as $file) {

if (!$it->isDot()) {

echo $file . "n";

}

}

?>在浏览器中查看这段代码的输出结果,你会在指定目录中看到一个文件列表。这一切是如何发生的呢?DirectoryIterator提供一个预先确 定的接口来重述一个目录的内容;示例目标目录的位置后,就可以把它当作一个标准的PHP数组来处理,每个元素代表目录中的一个文件。注意它使用isDot ()方法分别过滤掉“.”和“..”目录。
处理嵌套式目录树递归处理一个嵌套式目录树几乎同样简单。在这种情况下,DirectoryIterator需要检查它在单级目录中遇到的每一个对象,确定其是一个文件还是目录。如果是一个目录,就更深入一级检验下一级的内容。这听起来似乎相当复杂,在过去一般都需要15行以上的代码。
但是,使用PHP5,你只需要两个新的Iterator:RecursiveIterator和RecursiveIteratorIterator,它们组合了所有上述功能。见列表B:
列表B
<?php

$it = new RecursiveDirectoryIterator("/tmp");

foreach(new RecursiveIteratorIterator($it) as $file) {

echo $file . "n";

}

?>
这时,输入结果将列出起始目录下的所有文件和目录。不必说,如果需要处理某个特定目录级下的所有文件——例如,递归压缩一个目录树;或修改一系列嵌套文件的组/所有者许可时——使用这种递归内置接口就非常方便。
现实应用:打印一个目录树打印图形目录树是目录递归的一个常见应用。利用Iterator处理这个任务十分简单,因为Iterator类文档中包含一个专门为这个应用而编写的实例类。DirectoryTreeIterator(感谢MarcusBoerger)为前面讨论的RecursiveIteratorIterator提供了其它改进,特别是在树结构中代表深度和位置的ASCII标记。
列表C说明了DirectoryTreeIterator的用法。
列表C
<?php

$it = new DirectoryTreeIterator("/tmp/cookbook/");

foreach($it as $path) {

echo $path . "n";

}

?>
以下是你看到的一部分输出结果:
|-ch01

| |-recipe01

| | |-example01.php

| | -example02.php

| |-recipe02

| | |-example01.php

| | -example02.php

| |-recipe03

| | -example01.php

...
为更好了解这些DirectoryIterator的价值,尝试用标准的文件和目录函数对本教程中说明的三个应用编码。

TOP

PHP 5中处理XML
作为一个不同平台之间通信的通用语言,XML正在获得越来越多的支持;一些人甚至叫它“新的互联网革命”。XML有时用来作为数据库存放文档,但是数据存储永远不是它主要的用途。它是为了用通用的格式从一个系统传递信息到另一个系统中而开发的。

使用PHP 5.0 轻松解析XML文档

用sax方式的时候,要自己构建3个函数,而且要直接用这三的函数来返回数据, 要求较强的逻辑。 在处理不同结构的xml的时候, 还要重新进行构造这三个函数,麻烦!

  用dom方式,倒是好些,但是他把每个节点都看作是一个node,操作起来要写好多的代码, 麻烦!

  网上有好多的开源的xml解析的类库, 以前看过几个,但是心里总是觉得不踏实,感觉总是跟在别人的屁股后面.

  这几天在搞java, 挺累的,所以决定换换脑袋,写点php代码,为了防止以后xml解析过程再令我犯难,就花了一天的时间写了下面一个xml解析的类,于是就有了下面的东西。

  实现方式是通过包装"sax方式的解析结果"来实现的. 总的来说,对于我个人来说挺实用的,性能也还可以,基本上可以完成大多数的处理要求。

  功能:

  1、 对基本的xml文件的节点进行 查询 / 添加 / 修改 / 删除 工作.

  2、导出xml文件的所有数据到一个数组里面.

  3、整个设计采用了oo方式,在操作结果集的时候, 使用方法类似于dom

  缺点:

  1、 每个节点最好都带有一个id(看后面的例子), 每个“节点名字”=“节点的标签_节点的id”,如果这个id值没有设置,程序将自动给他产生一个id,这个id就是这个节点在他的上级节点中的位置编号,从0开始。

   2、 查询某个节点的时候可以通过用“|”符号连接“节点名字”来进行。这些“节点名字”都是按顺序写好的上级节点的名字。

  使用说明:

  运行下面的例子,在执行结果页面上可以看到函数的使用说明

  代码是通过php5来实现的,在php4中无法正常运行。

  由于刚刚写完,所以没有整理文档,下面的例子演示的只是一部分的功能,代码不是很难,要是想知道更多的功能,可以研究研究源代码。

  目录结构:
      test.php
      test.xml
      xml / SimpleDocumentBase.php
      xml / SimpleDocumentNode.php
      xml / SimpleDocumentRoot.php
      xml / SimpleDocumentParser.php

  文件:test.xml

            <?xml version="1.0" encoding="GB2312"?>
<shop>
<name>华联</name>
<address>北京长安街-9999号</address>
<desc>连锁超市</desc>
<cat id="food">
  <goods id="food11">
   <name>food11</name>
   <price>12.90</price>
  </goods>
  <goods id="food12">
   <name>food12</name>
   <price>22.10</price>
   <desc creator="hahawen">好东西推荐</desc>
  </goods>
</cat>
<cat>
  <goods id="tel21">
   <name>tel21</name>
   <price>1290</price>
  </goods>
</cat>
<cat id="coat">
  <goods id="coat31">
   <name>coat31</name>
   <price>112</price>
  </goods>
  <goods id="coat32">
   <name>coat32</name>
   <price>45</price>
  </goods>
</cat>
<special id="hot">
  <goods>
   <name>hot41</name>
   <price>99</price>
  </goods>
</special>
</shop>
            

   文件:test.php
复制内容到剪贴板
代码:
   
        
            
              <?php
    require_once "xml/SimpleDocumentParser.php";
    require_once "xml/SimpleDocumentBase.php";
    require_once "xml/SimpleDocumentRoot.php";
    require_once "xml/SimpleDocumentNode.php";
   $test = new SimpleDocumentParser();
    $test->parse("test.xml");
    $dom = $test->getSimpleDocument();
    echo "<pre>";
    echo "<hr><font color=red>";
    echo "下面是通过函数getSaveData()返回的整个xml数据的数组";
    echo "</font><hr>";
    print_r($dom->getSaveData());
    echo "<hr><font color=red>";
    echo "下面是通过setValue()函数,给给根节点添加信息,添加后显示出结果xml文件的内容";
    echo "</font><hr>";
    $dom->setValue("telphone", "123456789");
    echo htmlspecialchars($dom->getSaveXml());
    echo "<hr><font color=red>";
    echo "下面是通过getNode()函数,返回某一个分类下的所有商品的信息";
    echo "</font><hr>";
    $obj = $dom->getNode("cat_food");
    $nodeList = $obj->getNode();
    foreach($nodeList as $node){
    $data = $node->getValue();
    echo "<font color=red>商品名:".$data[name]."</font><br>";
    print_R($data);
    print_R($node->getAttribute());
    }
    echo "<hr><font color=red>";
    echo "下面是通过findNodeByPath()函数,返回某一商品的信息";
    echo "</font><hr>";
    $obj = $dom->findNodeByPath("cat_food|goods_food11");
    if(!is_object($obj)){
    echo "该商品不存在";
    }else{
    $data = $obj->getValue();
    echo "<font color=red>商品名:".$data[name]."</font><br>";
    print_R($data);
    print_R($obj->getAttribute());
    }
   echo "<hr><font color=red>";
    echo "下面是通过setValue()函数,给商品\"food11\"添加属性, 然后显示添加后的结果";
    echo "</font><hr>";
    $obj = $dom->findNodeByPath("cat_food|goods_food11");
    $obj->setValue("leaveword", array("value"=>"这个商品不错",
          "attrs"=>array("author"=>"hahawen", "date"=>date('Y-m-d'))));
    echo htmlspecialchars($dom->getSaveXml());
    echo "<hr><font color=red>";
    echo "下面是通过removeValue()/removeAttribute()函数,
          给商品\"food11\"改变和删除属性, 然后显示操作后的结果";
    echo "</font><hr>";
    $obj = $dom->findNodeByPath("cat_food|goods_food12");
    $obj->setValue("name", "new food12");
    $obj->removeValue("desc");
    echo htmlspecialchars($dom->getSaveXml());
    echo "<hr><font color=red>";
    echo "下面是通过createNode()函数,添加商品, 然后显示添加后的结果";
    echo "</font><hr>";
    $obj = $dom->findNodeByPath("cat_food");
    $newObj = $obj->createNode("goods", array("id"=>"food13"));
    $newObj->setValue("name", "food13");
    $newObj->setValue("price", 100);
    echo htmlspecialchars($dom->getSaveXml());
    echo "<hr><font color=red>";
    echo "下面是通过removeNode()函数,删除商品, 然后显示删除后的结果";
    echo "</font><hr>";
    $obj = $dom->findNodeByPath("cat_food");
    $obj->removeNode("goods_food12");
    echo htmlspecialchars($dom->getSaveXml());
  ?>

TOP

使用PHP 5.0 轻松解析XML文档(2)

文件:SimpleDocumentParser.php

            <?php
/**
*=========================================================
*
* @author     hahawen(大龄青年)  
* @since      2004-12-04
* @copyright  Copyright (c) 2004, NxCoder Group
*
*=========================================================
*/
/**
* class SimpleDocumentParser
* use SAX parse xml file, and build SimpleDocumentObject
* all this pachage's is work for xml file, and method is action as DOM.
*
* @package SmartWeb.common.xml
* @version 1.0
*/
class SimpleDocumentParser
{
private $domRootObject = null;
private $currentNO = null;
private $currentName = null;
private $currentValue = null;
private $currentAttribute = null;
public function getSimpleDocument()
{
     return $this->domRootObject;
}
public function parse($file)
{
        $xmlParser = xml_parser_create();
     xml_parser_set_option($xmlParser,XML_OPTION_CASE_FOLDING, 0);
     xml_parser_set_option($xmlParser,XML_OPTION_SKIP_WHITE, 1);
     xml_parser_set_option($xmlParser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
     xml_set_object($xmlParser, $this);
     xml_set_element_handler($xmlParser, "startElement", "endElement");
     xml_set_character_data_handler($xmlParser, "characterData");
        if (!xml_parse($xmlParser, file_get_contents($file)))
            die(sprintf("XML error: %s at line %d", xml_error_string(xml_get_error_code($xmlParser)),
        xml_get_current_line_number($xmlParser)));
     xml_parser_free($xmlParser);
}
private function startElement($parser, $name, $attrs)
{
        $this->currentName = $name;
        $this->currentAttribute = $attrs;
        if($this->currentNO == null)
        {
         $this->domRootObject = new SimpleDocumentRoot($name);
         $this->currentNO = $this->domRootObject;
        }
        else
        {
         $this->currentNO = $this->currentNO->createNode($name, $attrs);
        }
}
    private function endElement($parser, $name)
    {
        if($this->currentName==$name)
        {
         $tag = $this->currentNO->getSeq();
         $this->currentNO = $this->currentNO->getPNodeObject();
            if($this->currentAttribute!=null && sizeof($this->currentAttribute)>0)
          $this->currentNO->setValue($name, array('value'=>$this->currentValue,
       'attrs'=>$this->currentAttribute));
         else
                $this->currentNO->setValue($name, $this->currentValue);
            $this->currentNO->removeNode($tag);
        }
        else
        {
      $this->currentNO = (is_a($this->currentNO, 'SimpleDocumentRoot'))? null:
        $this->currentNO->getPNodeObject();
        }
}
private function characterData($parser, $data)
{
        $this->currentValue = iconv('UTF-8', 'GB2312', $data);
}
function __destruct()
{
     unset($this->domRootObject);
}
}
?>
                  


  文件:SimpleDocumentBase.php
复制内容到剪贴板
代码:
   
        
            
            
            
            <?php
/**
*=========================================================
*
* @author     hahawen(大龄青年)  
* @since      2004-12-04
* @copyright  Copyright (c) 2004, NxCoder Group
*
*=========================================================
*/
/**
* abstract class SimpleDocumentBase
* base class for xml file parse
* all this pachage's is work for xml file, and method is action as DOM.
*
* 1\ add/update/remove data of xml file.
* 2\ explode data to array.
* 3\ rebuild xml file
*
* @package SmartWeb.common.xml
* @abstract
* @version 1.0
*/
abstract class SimpleDocumentBase
{
private $nodeTag = null;
private $attributes = array();
private $values = array();
private $nodes = array();
    function __construct($nodeTag)
    {
        $this->nodeTag = $nodeTag;
    }
    public function getNodeTag()
    {
     return $this->nodeTag;
    }
    public function setValues($values){
     $this->values = $values;
    }
    public function setValue($name, $value)
    {
     $this->values[$name] = $value;
    }
    public function getValue($name=null)
    {
     return $name==null? $this->values: $this->values[$name];
    }
    public function removeValue($name)
    {
     unset($this->values["$name"]);
    }
    public function setAttributes($attributes){
        $this->attributes = $attributes;
    }
    public function setAttribute($name, $value)
    {
     $this->attributes[$name] = $value;
    }
    public function getAttribute($name=null)
    {
        return $name==null? $this->attributes: $this->attributes[$name];
    }
    public function removeAttribute($name)
    {
     unset($this->attributes["$name"]);
    }
    public function getNodesSize()
    {
        return sizeof($this->nodes);
    }
    protected function setNode($name, $nodeId)
    {
     $this->nodes[$name] = $nodeId;
    }
    public abstract function createNode($name, $attributes);
    public abstract function removeNode($name);
    public abstract function getNode($name=null);
    protected function getNodeId($name=null)
    {
     return $name==null? $this->nodes: $this->nodes[$name];
    }
    protected function createNodeByName($rootNodeObj, $name, $attributes, $pId)
    {
        $tmpObject = $rootNodeObj->createNodeObject($pId, $name, $attributes);
        $key = isset($attributes[id])? $name.'_'.$attributes[id]: $name.'_'.$this->getNodesSize();
        $this->setNode($key, $tmpObject->getSeq());
     return $tmpObject;
    }
    protected function removeNodeByName($rootNodeObj, $name)
    {
        $rootNodeObj->removeNodeById($this->getNodeId($name));
        if(sizeof($this->nodes)==1)
            $this->nodes = array();
     else
      unset($this->nodes[$name]);
    }
    protected function getNodeByName($rootNodeObj, $name=null)
    {
     if($name==null)
     {
            $tmpList = array();
            $tmpIds = $this->getNodeId();
            foreach($tmpIds as $key=>$id)
             $tmpList[$key] = $rootNodeObj->getNodeById($id);
            return $tmpList;
     }
     else
     {
      $id = $this->getNodeId($name);
      if($id===null)
      {
             $tmpIds = $this->getNodeId();
             foreach($tmpIds as $tkey=>$tid)
             {
              if(strpos($key, $name)==0)
              {
               $id = $tid;
               break;
              }
             }
      }
      return $rootNodeObj->getNodeById($id);
     }
    }
    public function findNodeByPath($path)
    {
     $pos = strpos($path, '|');
     if($pos<=0)
     {
      return $this->getNode($path);
        }
        else
        {
         $tmpObj = $this->getNode(substr($path, 0, $pos));
         return is_object($tmpObj)? $tmpObj->findNodeByPath(substr($path, $pos+1)): null;
        }
    }
    public function getSaveData()
    {
        $data = $this->values;
        if(sizeof($this->attributes)>0)
         $data[attrs] = $this->attributes;
        $nodeList = $this->getNode();
        if($nodeList==null)
         return $data;
        foreach($nodeList as $key=>$node)
        {
         $data[$key] = $node->getSaveData();
        }
     return $data;
    }
    public function getSaveXml($level=0)
    {
     $prefixSpace = str_pad("", $level, "\t");
     $str = "$prefixSpace<$this->nodeTag";
        foreach($this->attributes as $key=>$value)
         $str .= " $key=\"$value\"";
        $str .= ">\r\n";
        foreach($this->values as $key=>$value){
            if(is_array($value))
            {
                $str .= "$prefixSpace\t<$key";
                foreach($value[attrs] as $attkey=>$attvalue)
                 $str .= " $attkey=\"$attvalue\"";
                $tmpStr = $value[value];
            }
            else
            {
             $str .= "$prefixSpace\t<$key";
             $tmpStr = $value;
            }
            $tmpStr = trim(trim($tmpStr, "\r\n"));
            $str .= ($tmpStr===null || $tmpStr==="")? " />\r\n": ">$tmpStr</$key>\r\n";
        }
        foreach($this->getNode() as $node)
         $str .= $node->getSaveXml($level+1)."\r\n";
     $str .= "$prefixSpace</$this->nodeTag>";
     return $str;
    }
    function __destruct()
    {
     unset($this->nodes, $this->attributes, $this->values);
    }
}
?>
      
            
            

TOP

使用PHP 5.0 轻松解析XML文档(3)

文件:SimpleDocumentRoot.php

                        <?php
/**
*=========================================================
*
* @author     hahawen(大龄青年)  
* @since      2004-12-04
* @copyright  Copyright (c) 2004, NxCoder Group
*
*=========================================================
*/
/**
* class SimpleDocumentRoot
* xml root class, include values/attributes/subnodes.
* all this pachage's is work for xml file, and method is action as DOM.
*
* @package SmartWeb.common.xml
* @version 1.0
*/
class SimpleDocumentRoot extends SimpleDocumentBase
{
private $prefixStr = '<?xml version="1.0" encoding="utf-8" ?>';
private $nodeLists = array();
function __construct($nodeTag)
{
        parent::__construct($nodeTag);
}
    public function createNodeObject($pNodeId, $name, $attributes)
    {
     $seq = sizeof($this->nodeLists);
     $tmpObject = new SimpleDocumentNode($this, $pNodeId, $name, $seq);
     $tmpObject->setAttributes($attributes);
     $this->nodeLists[$seq] = $tmpObject;
     return $tmpObject;
    }
    public function removeNodeById($id)
    {
        if(sizeof($this->nodeLists)==1)
            $this->nodeLists = array();
     else
      unset($this->nodeLists[$id]);
    }
    public function getNodeById($id)
    {
     return $this->nodeLists[$id];
    }
    public function createNode($name, $attributes)
    {
        return $this->createNodeByName($this, $name, $attributes, -1);
    }
    public function removeNode($name)
    {
        return $this->removeNodeByName($this, $name);
    }
    public function getNode($name=null)
    {
        return $this->getNodeByName($this, $name);
    }
    public function getSaveXml()
    {
     $prefixSpace = "";
     $str = $this->prefixStr."\r\n";
     return $str.parent::getSaveXml(0);
    }
}
?>
                  
            
  
  文件:SimpleDocumentNode.php

            <?php
/**
*=========================================================
*
* @author     hahawen(大龄青年)  
* @since      2004-12-04
* @copyright  Copyright (c) 2004, NxCoder Group
*
*=========================================================
*/
/**
* class SimpleDocumentNode
* xml Node class, include values/attributes/subnodes.
* all this pachage's is work for xml file, and method is action as DOM.
*
* @package SmartWeb.common.xml
* @version 1.0
*/
class SimpleDocumentNode extends SimpleDocumentBase
{
private $seq = null;
private $rootObject = null;
    private $pNodeId = null;
    function __construct($rootObject, $pNodeId, $nodeTag, $seq)
    {
     parent::__construct($nodeTag);
        $this->rootObject = $rootObject;
        $this->pNodeId = $pNodeId;
        $this->seq = $seq;
    }
    public function getPNodeObject()
    {
     return ($this->pNodeId==-1)? $this->rootObject: $this->rootObject->getNodeById($this->pNodeId);
    }
    public function getSeq(){
     return $this->seq;
    }
    public function createNode($name, $attributes)
    {
        return $this->createNodeByName($this->rootObject, $name, $attributes, $this->getSeq());
    }
    public function removeNode($name)
    {
        return $this->removeNodeByName($this->rootObject, $name);
    }
    public function getNode($name=null)
    {
        return $this->getNodeByName($this->rootObject, $name);
    }
}
?>
                  

TOP

 13 12
发新话题