3、高级特性

    3.1、静态方法和属性

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

    example:


    如何访问?

    因为通过类而不是实例来访问静态元素,所以访问静态元素时,不需要引用对象的变量,用::(双冒号)来连接
    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、常量属性

    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. }

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

    example:

    一个类可以实现一个父类。多个接口
    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的方法


































    ①抛出异常

    关键词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..

    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. 下面尝试生成SuperU子类
    4. class SuperuChild extends SuperU
    5. }
    6. 将会报错。

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

    但是,如果你要覆写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. function __unset($property)
    23. $method = "set".ucfirst($argument);
    24. if (method_exists($this, $method)) {
    25. return $this->$method(null);
    26. }
    27. }
    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是两个不同的对象了