导 航

3、高级特性

3.1、静态方法和属性

我们不仅可以通过对象来访问方法和属性,还可以通过类来访问它们。这样的方法和属性是“静态的”
static关键词来声明。

example:

php class StaticExample { static public $aNum; static public function sayHi() { echo "hi,xujiajun :)" } }
如何访问?

因为通过类而不是实例来访问静态元素,所以访问静态元素时,不需要引用对象的变量,用::(双冒号)来连接
StaticExample::$aNum;
echo StaticExample::sayHi(); //输出 hi,xujiajun :)

在StaticExample内部,用关键词self关键词:

php class StaticExample { static public $aNum = 0; static public function sayHi() { self::$aNum++; echo "hi,xujiajun :)".self::$aNum."\n"; } }

3.2、常量属性

example:

  1. class ShopProduct
  2. {
  3. const AVAILABLE = 0;
  4. }

Q:如何使用?

A:Shopproduct::AVAILABLE;

Q:什么时候需要使用他?

A当需要类的所有实例中都能访问某个属性,并且属性值无需改变时,应该使用常量。

3.3、抽象类

注意抽象类不能被直接实例化。

抽象类中只定义(或者部分实现)子类需要的方法。子类可以继承他并且通过实现其中的抽象方法,使其具体化。

使用abstract关键词来定义一个抽象类。

example:

  1. abstract class ShopProductWriter
  2. {
  3. protected $product = array();
  4. public function addProduct(ShopProduct $shopProduct)
  5. {
  6. $this->products[] = $shopProduct;
  7. }
  8. }

在声明抽象方法不是以方法体结束,以分号结束。

  1. abstract class ShopProductWriter
  2. {
  3. protected $product = array();
  4. public function addProduct(ShopProduct $shopProduct)
  5. {
  6. $this->products[] = $shopProduct;
  7. }
  8. abstract public function write();
  9. }

创建抽象方法后,要确保所有子类中都实现该方法,但实现的细节可以先不确定。

  1. class ErrorWriter extends ShopProductWriter{};
  2. //Fatal error: Class ErrorWriter contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (ShopProductWriter::write) in ...

改成如下,就ok了:

  1. class TextProductWriter extends ShopProductWriter
  2. {
  3. public function write()
  4. {
  5. }
  6. }
3.4、接口

抽象类提供了具体的实现的标准,而接口则是纯粹的模板

example:

  1. interface Chargeable
  2. {
  3. public function getPrice();
  4. }

任何实现接口都要实现接口中所定义的所有方法,否则该类必须声明为abstract

关键词implements来实现某一个接口。

example:

  1. class ShopProduct implements Chargeable
  2. {
  3. //...
  4. public function getPrice()
  5. {
  6. return ($this->price - $this->discount);
  7. }
  8. }

一个类可以实现一个父类。多个接口
example:

  1. interface run
  2. {
  3. //...
  4. }
  5. interface fly
  6. {
  7. //...
  8. }
  9. class People
  10. {
  11. //...
  12. }
  13. class Xujiajun extends People implements run,fly
  14. {
  15. //...
  16. }

这里 Xujiajun这个类 继承了People类,实现了不止一个接口

注意:PHP只支持继承一个父类,因此extends关键词只能在一个类名之前。

3.5、错误处理

文件放错地方、数据库服务区未初始化、URL变动、XML文件损坏、权限设置得不对、磁盘空间限制等,这些问题,时常发生。
一个类里面 我们会充满了错误处理的代码。

异常 。这是一种完全不同的处理错误的方式。异常能解决刚提到的所有问题。

Exception类的public的方法


































方法 描述
getMessage() 获得传递给构造方法的消息字符串
getCode() 获得传递给构造方法的错误代码
getFile() 获得产生异常的文件
getLine() 获得异常生成的行数
getTrace() 获得一个多维数组,追踪导致异常的方法调用,包含文件、类、文件、参数数据
getTraceAsString() 获得getTrace(返回的字符串版本
__toString() 在字符串中使用Exception对象时自动调用。返回一个描述异常细节的字符串

①抛出异常

关键词throw和Exception对象来抛出异常。

example:

  1. function write ()
  2. {
  3. if (!is_writeable($this->file)) {
  4. throw new Exception("file '{$this->file}' is not writeable!");
  5. }
  6. }

如何捕获异常?

try…catch..

example:

  1. try {
  2. $conf = new Conf(dirname(__FILE__)."/xujiajun.xml");
  3. $conf->write();
  4. } catch (Exception $e) {
  5. die($e->__toString());
  6. }

②异常子类化

exmaple:

  1. class XmlException extends Exception{}

通过这种方式你可以扩展异常类的功能和定义新的异常类型。

3.6、Final类和方法

Q:什么情况用到Final?

A: 如果希望类和方法完全确定不变的功能,担心覆写它会破坏这个功能。

example:

  1. final class Superu
  2. {
  3. }
  4. 下面尝试生成SuperU子类
  5. class SuperuChild extends SuperU
  6. {
  7. }
  8. 将会报错。

但是,注意如果final关键词 放在SuperU这个类的方法里面,继承是不会报错的:

  1. class Superu
  2. {
  3. final function getName();
  4. }
  5. class SuperUChild extends SuperU
  6. {
  7. }

但是,如果你要覆写SuperU类的getName方法,还是会报错的。

注意:高质量的面向对象代码往往强调定义明确的接口。声明类或方法为final,会减少其灵活性。不过有时候我们确实需要这样做。

3.7、使用拦截器

拦截器方法:

方法 描述
__get($property) 访问未定义的属性被调用
__set($property) 对未定义的属性赋值时被调用
__isset($property) 对未定义的属性调用isset()时被调用
__unset($property) 对未定义的属性调用unset()时被调用
__call($method,$arg_array) 调用未定义的方法时被调用

example:

  1. //__get
  2. class Person
  3. {
  4. function __get($argument)
  5. {
  6. $method = "get".ucfirst($argument);
  7. if (method_exists($this, $method)) {
  8. return $this->$method();
  9. }
  10. }
  11. function getName()
  12. {
  13. return "xujiajun";
  14. }
  15. }
  16. $p = new Person();
  17. echo $p->name; //输出xujiajun
  18. 注意:如果方法不存在,则什么也不做。
  19. //__isset:
  20. function __isset($argument)
  21. {
  22. $method = "get".ucfirst($argument);
  23. if (method_exists($this, $method)) {
  24. return $this->$method();
  25. }
  26. }
  27. if (isset($p->name)) {
  28. return $p->getName();
  29. }
  1. //__set
  2. class Person
  3. {
  4. private $_name;
  5. function __set($argument,$value)
  6. {
  7. $method = "set".ucfirst($argument);
  8. if (method_exists($this, $method)) {
  9. return $this->$method($value);
  10. }
  11. }
  12. function setName($name)
  13. {
  14. $this->_name = $name;
  15. if (!$this->_name) {
  16. $this->_name = strtoupper($this->_name);
  17. }
  18. }
  19. }
  20. $p = new Person();
  21. $p->name = "xujiajun";//注意 这个时候$_name 已经变成 xujiajun
  22. //__unset 和__set向对应
  23. function __unset($property)
  24. {
  25. $method = "set".ucfirst($argument);
  26. if (method_exists($this, $method)) {
  27. return $this->$method(null);
  28. }
  29. }
  1. //__call
  2. class PersonWriter
  3. {
  4. function writeName(Person $p)
  5. {
  6. return $p->getName()."~\n";
  7. }
  8. }
  9. class person
  10. {
  11. private $_writer;
  12. function __construct(PersonWriter $writer)
  13. {
  14. $this->_writer = $writer;
  15. }
  16. function __call($medthodName,$args)
  17. {
  18. if (method_exists($this->_writer, $medthodName)) {
  19. return $this->_writer->$medthodName($this);
  20. }
  21. }
  22. function getName()
  23. {
  24. return "xujiajun";
  25. }
  26. }
  27. $p = new Person(new PersonWriter());
  28. echo $p->writeName(); //输出xujiajun~
3.8、析构方法

比如有一个类需要把其自身的信息写入数据库。这时可以使用__destruct()方法在对象实例被删除时确保把自己保存在数据库中:

  1. class Person
  2. {
  3. private $name;
  4. private $age;
  5. private $id;
  6. function __construct($name,$age)
  7. {
  8. $this->name = $name;
  9. $this->age = $age;
  10. }
  11. function __destruct()
  12. {
  13. if(!empty($this->id))
  14. {
  15. //保存信息
  16. echo "saving person info";
  17. }
  18. }
  19. function setId($id)
  20. {
  21. $this->id = $id;
  22. }
  23. }
  24. $p = new Person("xujiajun",18);
  25. $p->setId(18);
  26. unset($p);//输出saving person info

注意:析构方法和__call一样这些都是魔术方法。需慎用。

3.9、克隆对象
  1. class CopyMe
  2. {
  3. };
  4. $firstClass = new CopyMe();
  5. $secondClass = clone $firstClass;
  6. //现在$firstClass、$secondClass是两个不同的对象了

__clone的使用

  1. class Person
  2. {
  3. private $name;
  4. private $age;
  5. private $id;
  6. function __construct($name,$age)
  7. {
  8. $this->name = $name;
  9. $this->age = $age;
  10. }
  11. function __clone()
  12. {
  13. $this->id = 0;
  14. }
  15. }
  16. $p = new Person("xujiajun",18);
  17. $p2 = clone $p;// 这个时候 $p2的id为0,name:xujiajun,age:18