# 增強PHP語言
Nette Framework使用一些特殊語法candy擴展了PHP的對象模型。 你喜歡candy嗎? 繼續閱讀,你會發現更多好處。
1、為什么你應該使用Nette \ Object
2、什么是屬性
3、如何調用事件
4、如何向類添加方法
5、如何使用@annotations
在本章中,我們關注Nette \ Object,一個擴展PHP對象模型的類。 它是Nette Framework中幾乎所有類的祖先。 作為透明和不沖突的,它可以作為每個類的祖先。
## 嚴格的類
PHP給開發人員一個巨大的自由空間,這使得它語言很易犯錯誤。 但是你可以阻止這種不良行為,從開始編寫應用程序,而不會發現錯誤。 你想知道怎么樣做? 這很簡單 - 你只需要有更嚴格的規則。
你能在這個例子中找到一個錯誤嗎?
~~~
class Circle
{
public $radius;
public function getArea()
{
return $this->radius * $this->radius * M_PI;
}
}
$circle = new Circle;
$circle->raduis = 10;
echo $circle->getArea(); // 102 * π ≈ 314
~~~
首先看看代碼將打印出來314; 但它返回0。這怎么可能? 意外,$ circle-> radius被錯誤地輸入raduis。 只是一個小的錯字,這將給你一個很難糾正它,因為PHP不會說出那里不對。 甚至不是警告或通知的錯誤消息。 因為PHP不認為這是一個錯誤。
上述錯誤可以立即糾正,如果Circle類將是Nette \ Object的后代:
~~~
class Circle extends Nette\Object
{
...
~~~
雖然前面的代碼執行成功(雖然它包含一個錯誤),后者沒有:

Nette \ Object類制作 Circle更嚴格,并且當您嘗試訪問未聲明的屬性時拋出異常。 Tracy顯示了關于它的錯誤信息。 具有致命錯字的代碼行現在突出顯示,并且錯誤消息具有有意義的描述:無法寫入未聲明的屬性Circle :: $ raduis。 程序員現在可以解決他可能會錯過的錯誤,這可能是一個真正的痛苦,以后找到。
Nette \ Object的許多顯著的能力之一是在訪問未聲明的成員時拋出異常。
~~~
$circle = new Circle;
echo $circle->undeclared; // throws Nette\MemberAccessException
$circle->undeclared = 1; // throws Nette\MemberAccessException
$circle->unknownMethod(); // throws Nette\MemberAccessException
~~~
但它有更多的提供!
## Properties, getters and setters
在現代面向對象語言中,屬性描述類的成員,它們看起來像變量,但是由方法表示。 當讀取或賦值給這些“變量”時,會調用方法(所謂的getter和setter)。 這是非常有用的功能,它允許我們控制對這些變量的訪問。 使用它,我們可以驗證輸入或推遲這些變量的值的計算到實際訪問的時間。
任何作為Nette \ Object的后代的類都獲得了模仿屬性的能力。 只有你需要做的是保持簡單的約定:
* Getter和setter必須是公共方法。
* getter的名字是getXyz()或isXyz(),setter的是setXyz()
* Getter和setter是可選的,因此可以具有只讀或只寫屬性
* 屬性名稱區分大小寫(第一個字母為異常)
我們將使用Circle類中的屬性來確保變量$ radius只包含非負數:
~~~
class Circle extends Nette\Object
{
private $radius; // not public anymore!
public function getRadius()
{
return $this->radius;
}
public function setRadius($radius)
{
// sanitizing value before saving it
$this->radius = max(0.0, (float) $radius);
}
public function getArea()
{
return $this->radius * $this->radius * M_PI;
}
public function isVisible()
{
return $this->radius > 0;
}
}
$circle = new Circle;
// the classic way using method calls
$circle->setRadius(10); // sets circle's radius
echo $circle->getArea(); // gets circle's area
// the alternative way using properties
$circle->radius = 10; // calls setRadius()
echo $circle->area; // calls getArea()
echo $circle->visible; // calls $circle->isVisible()
~~~
屬性主要是一個語法candy,美化代碼,使程序員的生活更輕松。 你不必使用它們,如果你不想。
## 事件
現在我們要創建函數,當邊界半徑改變時調用。 讓我們調用它change事件和那些函數事件處理程序:
~~~
class Circle extends Nette\Object
{
/** @var array */
public $onChange;
public function setRadius($radius)
{
// call events in onChange
$this->onChange($this, $this->radius, $radius);
$this->radius = max(0.0, (float) $radius);
}
}
$circle = new Circle;
// adding an event handler
$circle->onChange[] = function($circle, $oldValue, $newValue) {
echo 'there was a change!';
};
$circle->setRadius(10);
~~~
setRadius的代碼中還有一個語法candy。 而不是在$ onChange數組上的迭代,并調用每個方法一個接一個與不可靠(不報告如果回調有任何錯誤)function call_user_func,你只需要寫簡單的onChange(...)和給定的參數將被移交給 處理程序。
## 擴展方法
你需要在運行時向現有對象或類添加一個新方法嗎? 擴展方法就是你所需要的。
~~~
// declaration of future method Circle::getCircumference()
Circle::extensionMethod('getCircumference', function (Circle $that) {
return $that->radius * 2 * M_PI;
});
$circle = new Circle;
$circle->radius = 10;
echo $circle->getCircumference(); // ≈ 62.8
~~~
擴展方法也可以采取參數。 它們不會打破封裝,因為它們只能訪問類的公共成員。 您還可以將它們與接口連接,因此實現該接口的每個類都將具有該方法。
## 反射
如果你需要找到關于任何類的每一個信息,反射是正確的工具。 你可以很容易地找出任何類有哪些方法,這些方法接受什么參數等等。Nette \ Object使用getReflection()方法簡化類的自我反射的訪問,返回一個ClassType對象:
~~~
$circle = new Circle;
echo $circle->getReflection()->hasMethod('getArea'); // does method 'test' exist?
echo $circle->getReflection()->getName(); // returns the name of the class ('Circle')
~~~
即使在這種情況下,我們可以使用屬性約定,并將最后一行更改為:
~~~
echo $circle->reflection->name; // returns 'Circle'
~~~
可以獲得反射而不使用Nette \ Object:
~~~
// getting PDO class reflection
$classReflection = new Nette\Reflection\ClassType('PDO');
// getting PDO::query method reflection
$methodReflection = new Nette\Reflection\Method('PDO', 'query');
~~~
## 注釋
反射與注釋有很多關系。 注釋寫入phpDoc注釋(兩個開頭的星號是必須的!),以@開頭。 您可以注釋類,變量和方法:
~~~
/**
* @author John Doe
* @author Tomas Marny
* @secured
*/
class FooClass
{
/** @Persistent */
public $foo;
/** @User(loggedIn, role=Admin) */
public function bar() {}
}
~~~
在代碼中有這些注釋:
* @author John Doe – string, contains text value 'John Doe'
* @Persistent – boolean, its presence means TRUE
* @User(loggedIn, role=Admin) – contains associative ['loggedIn', 'role' => 'Admin']
類注解的存在可以通過hasAnnotation()方法檢查:
~~~
$fooReflection = new Nette\Reflection\ClassType('FooClass');
$fooReflection->hasAnnotation('author'); // returns TRUE
$fooReflection->hasAnnotation('copyright'); // returns FALSE
~~~
可以使用getAnnotation()獲取值:
~~~
$fooReflection->getAnnotation('author'); // returns string 'Tomas Marny'
$fooReflection->getMethod('bar')->getAnnotation('User');
// returns ['loggedIn', 'role' => 'Admin']
~~~
所有注釋都可以通過getAnnotations()獲取:
~~~
array(3) {
"author" => array(2) {
0 => string(8) "John Doe"
1 => string(11) "Tomas Marny"
}
"secured" => array(1) {
0 => bool(TRUE)
}
}
~~~
- Nette簡介
- 快速開始
- 入門
- 主頁
- 顯示文章詳細頁
- 文章評論
- 創建和編輯帖子
- 權限驗證
- 程序員指南
- MVC應用程序和控制器
- URL路由
- Tracy - PHP調試器
- 調試器擴展
- 增強PHP語言
- HTTP請求和響應
- 數據庫
- 數據庫:ActiveRow
- 數據庫和表
- Sessions
- 用戶授權和權限
- 配置
- 依賴注入
- 獲取依賴關系
- DI容器擴展
- 組件
- 字符串處理
- 數組處理
- HTML元素
- 使用URL
- 表單
- 驗證器
- 模板
- AJAX & Snippets
- 發送電子郵件
- 圖像操作
- 緩存
- 本土化
- Nette Tester - 單元測試
- 與Travis CI的持續集成
- 分頁
- 自動加載
- 文件搜索:Finder
- 原子操作