PHP学习(8)——面向对象(下)

8.编写代码类php

  每一个分离的函数能够执行一个明确的任务。任务越简单,编写与测试这个函数就越简单,固然也不要将这个函数分得过小——若将程序分红太多的小个体,读起来就会很困难。html

  使用继承能够重载操做。咱们能够替换成一个大的Display()函数,可是改变整个页面的显示方式几乎是不可能的。将显示功能分红几个独立的任务则更好,这样咱们能够只需重载须要改变的部分。数组

  以下所示的page类提供了简单灵活的方法来建立页面:缓存

<?php
class Page
{
  // class Page's attributes
  public $content;  //页面的主要内容
  public $title = "TLA Consulting Pty Ltd";   //页面的标题
  public $keywords = "TLA Consulting, Three Letter Abbreviation, 
                     some of my best friends are search engines";   //metatags便于搜索引擎对其检索
  public $buttons = array("Home"   => "home.php", 
                        "Contact"  => "contact.php", 
                        "Services" => "services.php", 
                        "Site Map" => "map.php"
                    );    //使用一个数组来保存按钮的文本标签以及该按钮指向的URL

  // class Page's operations
  public function __set($name, $value)
  {
    $this->$name = $value;
  }   //能够从定义访问函数来设置和得到已定义的变量值开始

  public function Display()
  {
    echo "<html>\n<head>\n";
    $this -> DisplayTitle();
    $this -> DisplayKeywords();
    $this -> DisplayStyles();
    echo "</head>\n<body>\n";
    $this -> DisplayHeader();
    $this -> DisplayMenu($this->buttons);
    echo $this->content;
    $this -> DisplayFooter();
    echo "</body>\n</html>\n";
  }

  public function DisplayTitle()
  {
    echo "<title>".$this->title."</title>";
  }

  public function DisplayKeywords()
  {
    echo "<meta name=\"keywords\" 
          content=\"".$this->keywords."\"/>";
  }

  public function DisplayStyles()
  { 
?>   
  <style>
    h1 {
        color:white; font-size:24pt; text-align:center; 
        font-family:arial,sans-serif
    }
    .menu {
        color:white; font-size:12pt; text-align:center; 
        font-family:arial,sans-serif; font-weight:bold
    }
    td {    
        background:black
    }
    p {
        color:black; font-size:12pt; text-align:justify; 
           font-family:arial,sans-serif
    }
    p.foot {
        color:white; font-size:9pt; text-align:center; 
        font-family:arial,sans-serif; font-weight:bold
    }
    a:link,a:visited,a:active {
        color:white
    }
  </style>
<?php
  }

  public function DisplayHeader()
  { 
?>   
  <table width="100%" cellpadding="12" 
         cellspacing="0" border="0">
  <tr bgcolor ="black">
    <td align ="left"><img src = "logo.gif" /></td>
    <td>
        <h1>TLA Consulting Pty Ltd</h1>
    </td>
    <td align ="right"><img src = "logo.gif" /></td>
  </tr>
  </table>
<?php
  }

  public function DisplayMenu($buttons)
  {
    echo "<table width=\"100%\" bgcolor=\"white\" 
          cellpadding=\"4\" cellspacing=\"4\">\n";
    echo "<tr>\n";

    //calculate button size
    $width = 100/count($buttons);

    while (list($name, $url) = each($buttons)) {
      $this -> DisplayButton($width, $name, $url, 
               !$this->IsURLCurrentPage($url));
    }
    echo "</tr>\n";
    echo "</table>\n";
  }

  public function IsURLCurrentPage($url)
  {
    if(strpos($_SERVER['PHP_SELF'], $url )==false)
    {
      return false;
    }
    else
    {
      return true;
    }
  }

  public function 
      DisplayButton($width,$name,$url,$active = true)
  {
    if ($active) {
      echo "<td width = \"".$width."%\">
      <a href=\"".$url."\">
      <img src=\"s-logo.gif\" alt=\"".$name."\" border=\"0\" /></a>
      <a href=\"".$url."\"><span class=\"menu\">".$name."</span></a>
      </td>";
    } else {
      echo "<td width=\"".$width."%\">
      <img src=\"side-logo.gif\">
      <span class=\"menu\">".$name."</span>
      </td>";
    }  
  }

  public function DisplayFooter()
  {
?>
<table width="100%" bgcolor="black" cellpadding="12" border="0">
<tr>
<td>
    <p class="foot">&copy; TLA Consulting Pty Ltd.</p>
    <p class="foot">Please see our <a href ="">legal 
    information page</a></p>
</td>
</tr>
</table>
<?php
  }
}
?>

  请注意函数DisplayStyles()、DisplayHeader()和DisplayFooter()须要显示没有通过PHP处理的大量静态HTML。所以,咱们简单地使用了PHP结束标记(?>)、输入HTML,而后再在函数体内部使用一个PHP打开标记(<?php)。服务器

操做IsURLCurrentPage()将判断按钮URL是否指向当前页。ide

  这里,咱们使用了字符串函数strpos(),它能够查看给定的URL是否包含在服务器设置的变量中。strpos($__SERVER[‘PHP_SELF’], $url)语句将返回一个数字(若是$url中的字符串包含在全局变量$_SERVER[‘PHP_SELF’])或者false(若是没有包含在全局变量中)。函数

  首页使用page类完成生成页面内容的大部分工做:测试

<?php
  require("page.inc");

  $homepage = new Page();

  $homepage->content ="<p>Welcome to the home of TLA Consulting.
                       Please take some time to get to know us.</p>
                       <p>We specialize in serving your business needs
                       and hope to hear from you soon.</p>";
  $homepage->Display();
?>

  在以上的程序清单中能够看出,若是使用Page类,咱们在建立新页面的时候只要作少许工做。经过这种方法使用类意味着全部页面都必须很类似。网站

  若是但愿网站的一些地方使用不一样的标准页,只要将page.inc复制到名为page2.inc的新文件里,并作一些改变就能够了。这意味着每一次更新或修改page.inc时,要记得对page2.inc进行一样的修改。ui

  一个更好的方法是用继承来建立新类,新类从Page类里继承大多数功能,可是必须重载须要修改的部分。

  Services页面继承了Page类,可是重载了Display()操做,从而改变了其输出结果:

<?php
  require ("page.inc");

  class ServicesPage extends Page
  {
    private $row2buttons = array(
                           "Re-engineering" => "reengineering.php",
                           "Standards Compliance" => "standards.php",
                           "Buzzword Compliance" => "buzzword.php",
                           "Mission Statements" => "mission.php"
                           );

    public function Display()
    {
      echo "<html>\n<head>\n";
      $this -> DisplayTitle();
      $this -> DisplayKeywords();
      $this -> DisplayStyles();
      echo "</head>\n<body>\n";
      $this -> DisplayHeader();
      $this -> DisplayMenu($this->buttons);
      $this -> DisplayMenu($this->row2buttons);
      echo $this->content;
      $this -> DisplayFooter();
      echo "</body>\n</html>\n";
    }
  }

  $services = new ServicesPage();

  $services -> content ="<p>At TLA Consulting, we offer a number
  of services.  Perhaps the productivity of your employees would
  improve if we re-engineered your business. Maybe all your business
  needs is a fresh mission statement, or a new batch of
  buzzwords.</p>";

  $services -> Display();
?>

  经过PHP类建立页面的好处是显而易见的,经过用类完成了大部分工做,在建立页面的时候,咱们就能够作更少的工做。在更新页面的时候,只要简单地更新类便可。经过继承,咱们还可从最初的类派生出不一样版本的类而不会破坏这些优点。

  不过,用脚本建立网页要求更多计算机处理器的处理操做,应该尽可能使用静态HTML网页,或者尽量缓存脚本输出,从而减小在服务器上的载入操做。

 

9.PHP面向对象的高级功能

  9.1 使用Pre-Class常量

  能够在不须要初始化该类的状况下使用该类中的常量

class Math {

    const pi = 3.14159; //定义常量

}

echo Math::pi;

  能够经过使用::操做符指定常量所属的类来访问Per-Class常量。

  9.2 实现静态方法

  和Pre-Class常量的思想同样,能够在未初始化类的状况下直接调用这个方法,不过,在这个静态方法中,不容许使用 this 关键字,由于可能会没有能够引用的对象。

class Math {

    static function squared($input) {

        return $input * $input;

    }

}

echo Math::squared(8);

  9.3 检查类的类型和类型提示

  instanceof 关键字容许检查一个对象的类型。能够检查一个对象是不是特定类的实例,是不是从某个类继承过来或者是否实现了某个接口。

  另外,类型检查等价于 instanceof 的做用。

function check_hint(B $someclass){

  // ...

}

  以上示例将要求$someclass必须是类B的实例。若是按以下方式传入了类A的一个实例:

check_hint($a);

  将产生以下所示的致命错误:

Fatal error: Argument 1 must be an instance of B

  9.4 延迟静态绑定

  PHP 5.3版本引入了延迟静态绑定(late static binding)的概念,该特性容许在一个静态继承的上下文对一个被调用类的引用。父类可使用子类重载的静态方法。以下所示的是PHP手册提供的延迟静态绑定示例:

<?php

class A{

  public static function who(){

    echo __CLASS__;

  }

  public static function test(){

    static::who(); // Here comes Late Static Bindings

  }

}

class B extends A{

  public static function who(){

    echo __CLASS__;

  }

}

B::test();

?>

  通俗的说,就是B经过继承走的A里的test(),而后经过静态延迟走的B里重载的who()。

  不管类是否被重载,容许在运行时调用类的引用将为你的类提供更多的功能。

  9.5 克隆对象

  PHP提供了 clone 关键字,该关键字容许复制一个已有的对象。

$c = clone $b;

  将建立与对象 $b 具备相同类的副本,并且具备相同的属性值。

  固然,能够本身在类中从新定义 __clone 函数,来控制克隆的过程。

  9.6 使用抽象类

  PHP提供了抽象类。这些类不能被实例化,一样类方法也没有实现,只是提供类方法的声明,没有具体实现。

abstract operationX($param1, $param2);

  包含抽象方法的任何类自身必须是抽象的。

  抽象方法和抽象类主要用于复杂的类层次关系中,该层次关系须要确保每个子类都包含并重载了某些特性的方法,这也能够经过接口来实现。

  9.7 使用__call()重载方法

  在PHP中,__call()方法用来实现方法的重载。

<?php

class overload {

    public function displayArray($array) {

        foreach($array as $print) {

            echo $print;

            echo "<br />";

        }

    }

    public function displayScalar($scalar) {

        echo $scalar;

        echo "<br />";

    }

    public function __call($method, $p) {

        if ($method == "display") {

            if (is_object($p[0])) {

                $this->displayObject($p[0]);

            } else if (is_array($p[0])) {

                $this->displayArray($p[0]);

            } else {

                $this->displayScalar($p[0]);

            }

        }

    }

}

$ov = new overload;

$ov->display(array(1, 2, 3));

$ov->display('cat');

 ?>

  __call()方法必须带有两个参数。第一个包含了被调用的方法名称,而第二个参数包含了传递给该方法的参数数组。

  使用 __call 方法,不须要实现任何 display() 方法。

  9.8 使用__autoload()方法

  __autoload()函数将在实例化一个尚未被声明的类时自动调用。

  __autoload()方法的主要用途是尝试包含或请求任何用来初始化所需类的文件。

  9.9 实现迭代器和迭代

  可使用foreach()方法经过循环方式取出一个对象的全部属性,就像数组方式同样。

<?php

class myClass{

  public $a = "5";

  public $b = "7";

  public $c = "9";

}

$x = new myClass;

foreach($x as $attribute){

  echo $attribute."<br />";

}

?>

  若是须要一些更加复杂的行为,能够实现一个iterator(迭代器)。要实现一个迭代器,必须将要迭代的类实现IteratorAggregare接口,而且定义一个可以返回该迭代类实例的getIterator方法。这个类必须实现Iterator接口,该接口提供了一系列必须实现的方法。

  迭代器和迭代的示例基类:

 

<?php
class ObjectIterator implements Iterator {  //迭代器 这个类实现了interator接口

   private $obj;
   private $count;
   private $currentIndex;

   function __construct($obj)
   {
     $this->obj = $obj;
     $this->count = count($this->obj->data);
   }
   function rewind()
   {
     $this->currentIndex = 0;
   }
   function valid()
   {
     return $this->currentIndex < $this->count;
   }
   function key()
   {
     return $this->currentIndex;
   }
   function current()
   {
     return $this->obj->data[$this->currentIndex];
   }
   function next()
   {
     $this->currentIndex++;
   }
}

class Object implements IteratorAggregate   //接口
{
  public $data = array();

  function __construct($in)
  {
    $this->data = $in;
  }

  function getIterator()
  {
    return new ObjectIterator($this);   //返回迭代示例的方法
  }
}

$myObject = new Object(array(2, 4, 6, 8, 10));

$myIterator = $myObject->getIterator();
for($myIterator->rewind(); $myIterator->valid(); $myIterator->next())
{
  $key = $myIterator->key();
  $value = $myIterator->current();
  echo $key." => ".$value."<br />";
}

?>

  ObjectIterator类具备Iterator接口所要求的一系列函数:

    · 构造函数并非必需的,可是很明显,它是设置将要迭代的项数和当前数据项连接的地方。

    · rewind()函数将内部数据指针设置回数据开始处。

    · valid()函数将判断数据指针的当前位置是否还存在更多数据。

    · key()函数将返回数据指针的值。

    · value()函数将返回保存在当前数据指针的值。

    · next()函数在数据中移动数据指针的位置。

  像这样使用Iterator类的缘由就是即便潜在的实现发生了变化,数据的接口仍是不会发生变化。

  9.10 将类转换成字符串

  __toString()函数的全部返回内容都将被echo语句打印。

<?php

$p = new Printable;

echo $p;

class Printable{

  public $testone;

  public $testtwo;

  public function __toString(){

    return(var_export($this, TRUE));

  }

}

?>

  var_export()函数打印出了类中的全部属性值。

  9.11 使用Reflection(反射)API

  PHP的面向对象引擎还包括反射API。反射是经过访问已有类和对象来找到类和对象的结构和内容的能力。

  显示关于Page类的信息:

<?php

require_once("page.inc");

$class = new ReflectionClass("Page");

echo "<pre>".$class."</pre>";

?>

  这里使用了Reflection类的__toString()方法来打印这个数据。注意,<pre>标记位于不一样的行上,不要与__toString()方法混淆。

 

整理自《PHP和MySQL Web开发》

相关文章
相关标签/搜索