第四章 高级特性

以下代码摘自:

MATTZANDSTRA. 深入PHP:面向对象、模式与实践[M]. 人民邮电出版社, 2011.

第四章:高级特性

第四章 高级特性 摘录

4.1 静态属性和静态方法

<?php
class StaticExample(){
    static public $aNum=0;
    static public function sayHello(){
        print "hello";
    }
}
?>

调用上述类中的静态属性和静态方法:

StaticExample::$aNum;
StaticExample::sayHello();

seif 和 ::

从类外部访问静态属性和方法时采用

  • ::

从类内部访问静态属性和方法时采用

  • self

这里再附带一句话,虽然是我自己编的:

非静态的常量和属性是属于对象的,包括伪变量:$this
静态的常量和属性是属于类的,所以不用实例化对象就能调用

4.2 常量属性

设置常量属性

  • const

4.3 抽象类

<?php
abstract class ShopProductWriter{
    protected $products=array();

    public function addProduct(ShopProductWriter $shopProduct){
        $this->products[]=$shopProduct;
    }
    abstract public function write();
}
//继承抽象类,并重写全部抽象方法
class XmlProductWriter() extends ShopProductWriter{
    public function write(){
        //code...
    }
}
?>

抽象类可以定义属性和方法,但是不能实例化。

抽象类还中抽象方法定义方法:

abestrct public/private/protectd function Function_Name();

注意,在括号后面使用;结尾,不是{}

4.4 接口

<?php
interface Chargeable{
    public function getPrice();
}
//实现接口
class ShopProduct implements Chargeable{
    public function getPrice(){
        return ($this->price - $this->discount);
    }
}
?>

一个类只可以继承一个类,但可以实现多个接口。

4.5 延迟静态绑定:static 关键字

static又登场了,欢迎!这里的static的作用很强大,而且有有趣。

代码1:

<?php
abstract class DomainObject{
}
class User extends DomainObject{
    public static function create(){
        return new User();
    }
}
class Document extends DomainObject{
    public static function create(){
        return new Document();
    }
}
?>

上面的代码是希望在调用各自静态的方法:create()的时候能返回一个自身实例化的对象。
但是有一个地方很麻烦,就是要在所有的类中编写该create()方法。

代码2:黑暗进化版(亚古兽完全体黑暗进化,丧尸暴龙兽)

<?php
abstract class DomainObject{
     public static function create(){
        return new self();
    }
}
class User extends DomainObject{}
class Document extends DomainObject{}
//当要调用create方法实例化自身类时,可以直接写
$Document=Document::create();
?>

Document::create()会实例化DomainObject,而其是抽象类,不能实例化。所以会报错。

代码3:正确进化版(亚古兽完全体进化,机械暴龙兽)

<?php
abstract class DomainObject{
     public static function create(){
        return new static();
    }
}
class User extends DomainObject{}
class Document extends DomainObject{}
//当要调用create方法实例化自身类时,可以直接写
$Document=Document::create();
?>

这里将原来的self换成了static,就具有静态延迟效果。

上面是将静态延迟使用在了类的实例化中,下面展示一段代码,是将静态延迟使用在方法中:

代码4:

<?php
abstract class DomainObject{
    private $group;
    public static function getGroup(){
        return "DomainObject";
    }
    public function __construct(){
        $this->group=static::getGroup();
    }
    public static function create(){
        return new static();
    }
}
//重写DomainObject
class Document extends DomainObject{
    public static function getGroup(){
        return "Document";
    }
}
class User extends DomainObject{
}    
echo Document::create();
echo User::create();
//结果输出
//Document
//DomainObject
?>

猜猜看最后的输出结果,什么?我已经写在上面了?我是让你们猜,你们知道答案再去猜怎么了?委屈你们了?

我知道你们一定猜的对,所以我直接来介绍原理。

Document重写了DomainObject中的方法,但是我们没有直接调用getGroup()方法来获取输出。

注意当继承了DomainObject的Document和User调用其父类的方法的顺序:

  • create()
  • __construct()
  • getGroup()

那么分歧出现在哪里呢?getGroup()上。

在DomainObject中使用

$this->group=static::getGroup();

的方式进行group参数的设定,参考

代码3:正确进化版(亚古兽完全体进化,机械暴龙兽)

中的内容我们可以理解,这里的getGroup()方法不仅仅表示getGroup()方法是静态方法,也具有一定的

延时性

当子类中有方法复写该方法时,该方法就调用子类的方法,而不是父类的方法。

而在User中并没有复写该方法,所以直接调用父类的方法。

当然,重点来了,这玩意有什么用呢?答案是我也不知道,总感觉是很牛逼的东西,所以记下来先,反正脑子存储空间足够大。

4.6 错误处理

Exception类的public方法

<?php   
class DIY_Exception{
    public function hello_bug(){
        throw new Exception("hello expaction", 1);        
    }
}
$new_world=new DIY_Exception();
try{
    $new_world->hello_bug();
    echo "when exception start,the code will stop and never show this text";
}catch(Exception $e){
    echo "<pre>";
    var_dump($e);
}
?>
  • getMessage()
  • getCode()
  • getFile()
  • getLine()
  • getPrevious() 获得一个嵌套的异常对象组合
  • getTrace()  获得一个多维数组
  • getTraceAsString() 上面信息的字符串形式
  • __toString() 返回一个描述异常的字符串

其实上述方法只是把Exception中的一些参数进行了返回,而同时,

Exception $e是一个实例化的Exception对象,其具体对象就是在DIY_Expection

throw new Exception("hello expection");

的对象。其实我有个猜想,如果说上面介绍的函数都只是获取$e的部分信息,那么为什么不直接将$e打印出来,直接查看呢?

echo "<pre>";
var_dump($e);

Exception类的继承

<?php
class XmlException extends Exception{
    private $error;
    function __construct(LibXmlError $error){
        $msg="错误提示信息";
        $this->error=$error;
        parent::__construct($msg,$error->code);
    }
    public function getLibXmlError(){
        return $this->error;
    }
?>

这里我把Exception的构造函数的代码贴一下:

public Exception::__construct([String $message=''[,int $code=0[,Throwable $previous=NULL]]])

注意点:

  • $message,$code,$previous都是可选值
  • $code的类型是int,不是出错误的代码
  • 如果子类的$code和$message属性已经设置过了,在调用Exception父类的构造函数时可以省略默认参数

所以上面代码也可以这样写:

<?php
class XmlException extends Exception{
    private $error;
    function __construct(Exception $error){
        $this->message="错误提示信息";
        $this->error=$error;
        $this->code=$error->code;
        parent::__construct();
    }
    public function getLibXmlError(){
        return $this->error;
    }
}
class FileException extends Exception {}
class ConfException extends Exception {}    
?>

<?php
class Conf {
    function __construct(){
        if(){
            throw new XmlException();
        }else if(){
            throw new FileException();
        }else if(){
            throw new ConfException();
        }
    }
}
?>

<?php
class Runner {
    public static function init(){
        try{
            $conf=new Conf();
        }catch(XmlException $e){
            //code...
        }catch(FileException $e){
            //code...
        }catch(ConfException $e){
            //code...
        }catch(Exception $e){
            //这个是到最后才运行的,保证异常能被处理
        }
    }
}
?>

4.7 Final类和方法

  • final类不能有子类
  • final方法不能被覆写

4.8 使用拦截器

  • __get($property)
  • __set($property,$value)
  • __isset($property)
  • __unset($property)
  • __call($method,$arg_array)

4.9 析构方法

<?php
class Person{
    private $name;
    private $age;
    private $id;
    function __construct($name,$age){
        $this->name=$name;
        $this->age=$age;
    }
    function setId($id){
        $this->id=$id;
    }
    function __destruct(){
        if(!empty($this->id)){
            echo "数据已经存入数据库";
        }
    }
}
?>

当外部删除该实例化的类时,__destruct()就会被调用。

<?php
$Person=new Person();
$Person->setId(100);
unset($Person);
//__destruct()函数被调用
?>

4.10 使用__clone()复制对象

对象被clone函数调用时进行调用。

<?php
class Person{
    private $name;
    private $age;
    private $id;
    function __construct($name,$age){
        $this->name=$name;
        $this->age=$age;
    }
    function setId($id){
        $this->id=$id;
    }
    function __clone(){
        $this->id=0;
    }
}
$Person=new Person('Name','Age');
$Person->setId(100);
$Person2=clone $Person;   
?>

上段代码的运行顺序如下:

  • 实例化Person类,Person类中的__construct()被调用。
  • 通过Person类的setId方法,将id属性设置为100
  • $Person2通过clone函数复制$Person类
  • 在$Person2中的__clone被调用,将自身的$id属性设置为0

注意,是$Person2的__clone方法,不是$Person的__clone方法,__clone是在新生成的对象中被调用的,不是在被复制的对象中调用的。

所以,原始$Person的$id属性仍为100,而$Person2的$id属性为0。

<?php
class Account{
    public $balace;
    function __construct($balace){
        $this->balace=$balace;
    }
}
class Person{
    private $name;
    private $age;
    private $id;
    public $account;        
    function __construct($name,$age,Account $account){
        $this->name=$name;
        $this->age=$age;
        $this->account=$account;
    }
    public function setId($id){
        $this->id=$id;
    }
    function __clone(){
        $this->id=0;
    }
    $Person=new Person("Name","Age",new Account(200));
    $Person->setId(343);
    $Person2=clone $Peron;
    $Person->account->balace+=10;
    echo $Person2->account->balace;
    //结果是210,不是200
}
?>

上面代码运行顺序:

  • 实例化Person类——$Person
  • 将$Person类的id属性设置为343,这个没有关系
  • $Person2浅复制(shallow copy)$Person中的属性,这里的浅复制表示$Person2中的属性与$Person中的属性指向同一个对象
  • 在对$Person中的属性进行处理后,会同样作用于$Person2中的属性

为了避免上述的情况发生,即:我们希望在克隆的$Person2中能有一个新的Account属性

<?php
function __clone(){
    $this->account=new Account();
}
?>

在__clone()函数中,为复制的$Person2新建一个Account对象。

注意,__clone()函数是在复制的$Person2中进行运行的!

4.11 定义对象的字符串

当把对象传递给print或echo时,会自动调用这个方法,并用方法的返回值来代替默认的输出内容。

注意,在PHP 5.2起,打印一个对象会报错!除非该对象设置有__toString()方法

<?php
class Person{
    function getName(){
        return "Name";
    }
    function getAge(){
        return "Age";
    }
    function __toString(){
        $desc.=$this->getName();
        $desc.=this->getAge();
        return desc;
    }
}
$Person=new Person();
echo $Person;
?>

4.12 回调、匿名函数和闭包

回调函数

<?php
class Product{
    public $name;
    public $price;
    function __construct($name,$price){
        $this->name=$name;
        $this->price=$price;
    }
}
class ProcessSale{
    private $callback;
    function registerCallback($callback){
        if(!is_callable($callback)){
            throw new Exception("function error");
        }
        $this->callback[]=$callback;           
    }
    function sale($product){
        echo $product->name."<br>";
        foreach($this->callback as $callback){
            call_user_func($callback,$product);
        }
    }
}
//在外部创建回调函数后传入ProcessSale类中处理
$logger=function($product){
    print "logging({$product->name})";
};
$processor=new ProcessSale();
try{
    $processor->registerCallback($logger);
}catch(Exception $e){
    echo "<pre>";
    var_dump($e);
}
$processor->sale(new Product("Name","Price"));
echo "<hr>";
$processor->sale(new Product("Name2","Price2"));
?>

或者上面的代码也可以是下面的形式:

<?php
function logging2($product){
    echo "logging({$product->name})";
}
try{
    $processor->registerCallback("logging2");
}catch(Exception $e){
     echo "<pre>";
     var_dump($e);
}
?>

上面代码运行顺序:

  • 设置匿名函数:$logger
  • 实例化ProcessSale()类
  • 设置回调函数,这里的回调函数就是$logger
  • 调用ProcessSale类的sale函数,sale函数中对传入的$product参数遍历$callback数组中的函数

匿名函数

形式1:

<?php
$logger=function($product){
    echo "logging({$product->product})";
}
?>

形式2:

<?php
class Mailer{
    function doMail($product){
        echo "mailing({$product->name})";
    }
}
try{
    $processor->registerCallback(array(new Mailer(),"doMail"));
}catch(Exception $e){
    echo "<pre>";
    var_dump($e);
    }
?>

形式3:

<?php
class Totalize{
    static function warnAmount(){
        return function($product){
            echo "Price:{$product->price}";
        }
    };
    //注意上面的分号!
}
try{
    $processor->registerCallback(Totalize::warnAmount());
}catch(Exception $e){
    echo "<pre>";
    var_dump($e);
}
?>    

方式4:

<?php
class Totalize{
    static function warnAmount($amt){
        $count=0;
        return function($product) use ($amt,$count){
            $count+=$product->price+$amt;
            echo $count;
        }
    }
}
try{
    $processor->registerCallback(Totalize::warnAmount(9));
}catch(Exception $e){
    echo "<pre>";
    var_dump($e);
}
?>

这里与形式3的方式是一样的,只是添加了调用类中方法参数的use选项!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值