<?php
class thiswillfail {
public function foo() {
return "hello world!";
}
}
$class = new thiswillfail;
$class->bar();
?>
<?php
//裝入pear的 <a >db package</a>
require_once "db.php";
class persistable {
private $data = array();
private $table = "users";
public function __construct($user) {
$this->dbh = db::connect("mysql://user:[email protected]/database");
$query = "select id, name, email, country from " .
$this->table . " where name = ?";
$this->data = $this->dbh->getrow($query, array($user),
db_fetchmode_assoc);
}
public function __get($member) {
if (isset($this->data[$member])) {
return $this->data[$member];
}
}
public function __set($member, $value) {
// dataset的id是只讀的
if ($member == "id") {
return;
}
if (isset($this->data[$member])) {
$this->data[$member] = $value;
}
}
public function __destruct() {
$query = "update " . $this->table . " set name = ?,
email = ?, country = ? where id = ?";
$this->dbh->query($query, $this->name, $this->email,
$this->country, $this->id);
}
}
$class = new persistable("martin jansen");
$class->name = "john doe";
$class->country = "united states";
$class->email = "[email protected]";
?>
你遇到的第一個問題可能是__construct(),這是php 5中引入的新的構造器方法。在php 4時代,構造器總是與它們的類名相匹配。在php 5中已不再是這樣。你不需要對構造器方法有過多的了解,除了調用它可以創建一個類的實例外;并注意到,這里使用了一個參數 - 執行一個基于此參數的數據庫。此構造器把查詢結果賦值給類屬性$data。
接下來,程序定義了兩個特別的方法__get()和__set()。你應該對它們早已熟悉:__get()用于讀取未定義的屬性值,__set()用于修改未定義的屬性值。
這意味著無論什么時候從持續性存儲類中讀取/寫入一個未定義的屬性,由這些專門方法來負責管理在屬性數組變量$data中的信息,而不是直接改變類的屬性(切記:變量$data包含著來自于數據庫中的一行!)。
類中的最后一個方法是__construct()的對立者- 析構器__destruct()。php在"腳本關閉階段"調用析構器,典型地這是在php腳本執行快要結束的時候。析構器把來自于$data屬性的信息寫回到數據庫中去。這正是前面同步(synchronization )術語的含義。
你可能早已注意到,這里的代碼使用了pear的數據庫抽象層包(database abstraction layer package)。其實這無所謂,通過別的方式與數據庫通訊也一樣能說明本文的主題。
如果你細心觀察,會發現該持續性存儲類的描述比較簡單。例子中僅涉及了一個數據庫表,而沒有考慮更復雜的數據模型,如使用left join和其它復雜的數據庫操作技術。然而你不必受此約束,借助于屬性重載,你可以使用你自己理想的數據庫模型。只需要加入少許代碼,你即可以在該持續性存儲類中運用復雜的數據庫特性。
還存在一個小問題 - 當在析構器中查詢失敗時并沒有引入錯誤處理機制。是析構器的天性導致在這種情況下不可能顯示相應的錯誤信息,因為構建html標志常常在php調用構析器之前就已經結束了。
為解決這個問題,你可以把__destruct()重命名為象savedata()這樣的名字并在調用腳本的某處手工執行這一方法。這對于類的持續性存儲的概念并沒有任何改變;僅是多寫幾行代碼而已。作為選擇,你還可以在析構器中使用函數error_log()來記錄下屬于系統范圍的錯誤記錄文件中的錯誤信息。
屬性重載的工作機制就是這樣。下面我們討論一下方法重載。
四、方法重載舉例
1. 動態的getter/setter方法
下列代碼實現了"動態"getter/setter方法以借助于方法重載的幫助來控制類。下面我們結合源代碼進行分析:
<?php
class dynamicgettersetter {
private $name = "martin jansen";
private $starbucksdrink = "caramel cappuccino swirl";
function __call($method, $arguments) {
$prefix = strtolower(substr($method, 0, 3));
$property = strtolower(substr($method, 3));
if (empty($prefix) || empty($property)) {
return;
}
if ($prefix == "get" && isset($this->$property)) {
return $this->$property;
}
if ($prefix == "set") {
$this->$property = $arguments[0];
}
}
}
$class = new dynamicgettersetter;
echo "name: " . $class->getname() . "/n";
echo "favourite starbucks flavour: " . $class->getstarbucksdrink() . "/n/n";
$class->setname("john doe");
$class->setstarbucksdrink("classic coffee");
echo "name: " . $class->getname() . "/n";
echo "favourite starbucks flavour: " . $class->getstarbucksdrink() . "/n/n";
?>
$class->thismethoddoesnotexist("martin", 42);
/導向__call()的第二個參數
array
(
[0] => martin
[1] => 42
)
新聞熱點
疑難解答