3、高级特性

优质
小牛编辑
136浏览
2023-12-01

3.1、静态方法和属性

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

example:

class StaticExample
{
    static public $aNum;
    static public function sayHi()
    {
        echo "hi,xujiajun :)"
    }
}

如何访问?

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

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

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

3.2、常量属性

example:

class ShopProduct
{
    const AVAILABLE = 0;
}

Q:如何使用?

A:Shopproduct::AVAILABLE;

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

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

3.3、抽象类

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

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

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

example:

abstract class ShopProductWriter
{
    protected $product = array();

    public function addProduct(ShopProduct $shopProduct)
    {
        $this->products[] = $shopProduct;
    }
}

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

abstract class ShopProductWriter
{
    protected $product = array();

    public function addProduct(ShopProduct $shopProduct)
    {
        $this->products[] = $shopProduct;
    }

    abstract public function write();
}

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

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

改成如下,就ok了:

class TextProductWriter extends ShopProductWriter
{
    public function write()
    {
    }
}

3.4、接口

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

example:

interface Chargeable
{
    public function getPrice();
}

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

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

example:

class ShopProduct implements Chargeable
{
    //...
    public function getPrice()
    {
        return ($this->price - $this->discount);  
    }
}

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


interface run
{
    //...
}

interface fly
{
    //...
}

class People
{
    //...
}

class Xujiajun extends People implements run,fly
{
    //...
}

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

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

3.5、错误处理

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

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

Exception类的public的方法

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

①抛出异常

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

example:

function write ()
{
    if (!is_writeable($this->file)) {
        throw new Exception("file '{$this->file}' is not writeable!");
    }
}

如何捕获异常?

try...catch..

example:


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

②异常子类化

exmaple:

class XmlException extends Exception{}

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

3.6、Final类和方法

Q:什么情况用到Final?

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

example:

final class Superu
{
}

下面尝试生成SuperU子类

class SuperuChild extends SuperU
{
}

将会报错。

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


class Superu
{
   final function getName();
}

class SuperUChild extends SuperU
{
}

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

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

3.7、使用拦截器

拦截器方法:

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

example:


//__get
class Person
{

    function __get($argument)
    {
        $method = "get".ucfirst($argument);
        if (method_exists($this, $method)) {
             return $this->$method();
        }
    }

    function getName()
    {
        return "xujiajun";
    }
}


$p = new Person();
echo $p->name; //输出xujiajun

注意:如果方法不存在,则什么也不做。


//__isset:

function __isset($argument)
{
   $method = "get".ucfirst($argument);
        if (method_exists($this, $method)) {
             return $this->$method();
   }
}

if (isset($p->name)) {
    return $p->getName();
}

//__set

class Person
{
    private $_name;

    function __set($argument,$value)
    {
        $method = "set".ucfirst($argument);
        if (method_exists($this, $method)) {
             return $this->$method($value);
        }
    }

    function setName($name)
    {
        $this->_name = $name;
        if (!$this->_name) {
            $this->_name = strtoupper($this->_name);
        }
    }
}


$p = new Person();
$p->name = "xujiajun";//注意 这个时候$_name 已经变成 xujiajun


//__unset 和__set向对应

function __unset($property)
{
    $method = "set".ucfirst($argument);
    if (method_exists($this, $method)) {
        return $this->$method(null);
    }
}
//__call
class PersonWriter
{
    function writeName(Person $p)
    {
        return $p->getName()."~\n";
    }
}

class person
{
    private $_writer;

    function __construct(PersonWriter $writer)
    {
        $this->_writer = $writer;
    }

    function __call($medthodName,$args)
    {
        if (method_exists($this->_writer, $medthodName)) {
            return $this->_writer->$medthodName($this);
        }
    }

    function getName()
    {
        return "xujiajun";
    }
}

$p = new Person(new PersonWriter());
echo $p->writeName(); //输出xujiajun~

3.8、析构方法

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


class Person
{
    private $name;
    private $age;
    private $id;

    function __construct($name,$age)
    {
        $this->name = $name;
        $this->age = $age;
    }

    function __destruct()
    {
        if(!empty($this->id))
        {
        //保存信息
            echo "saving person info";
        }
    }

    function setId($id)
    {
        $this->id = $id;
    }
}
$p = new Person("xujiajun",18);
$p->setId(18);
unset($p);//输出saving person info

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

3.9、克隆对象

class CopyMe
{
};
$firstClass = new CopyMe();
$secondClass = clone $firstClass;
//现在$firstClass、$secondClass是两个不同的对象了

__clone的使用

class Person
{
    private $name;
    private $age;
    private $id;

    function __construct($name,$age)
    {
        $this->name = $name;
        $this->age = $age;
    }
    function __clone()
    {
        $this->id = 0;
    }
}

$p = new Person("xujiajun",18);
$p2 = clone $p;// 这个时候 $p2的id为0,name:xujiajun,age:18