# 增强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(); // 10² * π ≈ 314
~~~
首先看看代码将打印出来314; 但它返回0。这怎么可能? 意外,$ circle-> radius被错误地输入raduis。 只是一个小的错字,这将给你一个很难纠正它,因为PHP不会说出那里不对。 甚至不是警告或通知的错误消息。 因为PHP不认为这是一个错误。
上述错误可以立即纠正,如果Circle类将是Nette \ Object的后代:
~~~
class Circle extends Nette\Object
{
...
~~~
虽然前面的代码执行成功(虽然它包含一个错误),后者没有:
![](https://box.kancloud.cn/944d1d93be63c0339bf9b16154db6a5b_490x360.png)
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
- 原子操作