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