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開發》

相關文章
相關標籤/搜索