PHP静态方法和属性、延迟静态绑定

静态方法和属性php

静态方法是以类做为做用域的函数。静态方法不能访问这个类中的普通属性,由于那些属性属于一个对象,但能够访问静态属性。若是修改了一个静态属性,那么这个类的全部实例都能访问到这个新值。
由于是经过类而不是实例来访问静态元素,因此访问静态元素时再也不须要引用对象的变量,而是使用::来链接类名和属性或类名和方法。函数

class StaicExample {
    static public $aNum = 0;
    static public function sayHello() {
        print "hello";
    }
}

print StaicExample::$aNum;
StaicExample::sayHello();

一个子类可使用parent关键字来访问父类,而不使用其类名。要从当前类(不是子类)中访问静态方法或属性,可使用self关键字。self指向当前类,就像伪变量$this指向当前对象同样。所以,在StaticExample类的外部可使用其类名访问属性$aNum:this

StaicExample::$aNum;

而在StaicExample类内部,可使用self关键字:code

class StaicExample {
    static public $aNum = 0;
    static public function sayHello() {
        self::$aNum++;
        print "hello (".self::$aNum.")\n";
    }
}

只有在使用parent关键字调用方法的时候,才能对一个非静态方法进行静态形式的调用(使用::)。除非是访问一个被覆写的方法,不然永远只能使用::访问被明确声明为static的方法或属性。有时看到使用static语法来引用方法或属性,可能并不意味着其中的方法或属性必须是静态的,只不过说明它属于特定的类。对象

根据定义,不能在对象中调用静态方法。所以静态方法和属性又被称为类变量和属性,也就不能在静态方法中使用伪变量$this继承

为何要使用静态方法或属性呢?作用域

  1. 在代码中的任何地方均可用(假设你能够访问该类)。也就是说,你不须要在对象间传递类的实例,也不须要将实例存放在全局变量中,就能够访问类中方法。
  2. 类的每一个实例均可以访问类中定义的静态属性,因此能够利用静态属性来设置值,该值能够被类的全部对象使用。
  3. 不须要实例对象就能访问静态属性或方法,这样就不用为了获取一个简单的功能而实例化对象。

延迟静态绑定:static关键字
静态方法能够用做工厂方法,工厂方法是生成包含类的实例的一种方法。
先看下面的重复代码:get

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();
    }
}

想必你们都不想为每一个DomainObject子类都建立与上面代码相似的标准代码。若是把create()放在超类呢?io

abstract class DomainObject {
    public static function create() {
        return new self();
    }
}
    
class User extends DomainObject {
        
}
    
class Document extends DomainObject {
        
}
Document::create();

这回看起来简洁多了。如今把常见的代码放在一个位置,并使用self做为对该类的引用。实际上,self对该类所起的做用与$this对对象所起的做用并不彻底相同。self指的不是调用上下文,而是解析上下文。所以,运行刚才上面的代码会获得:function

PHP Fatal error: Cannot instantiate abstract class DomainObject in ...

所以,self被解析为定义create()的DomainObject,而不是解析为调用self的Document类。PHP5.3以前,在这方面有严格的限制,产生不少笨拙的解决方案。PHP5.3引入了延迟静态绑定的概念。该特性最明显的标志就是新关键字static。static相似于self,但它指的是被调用的类而不是包含类

在本例中,它的意思是调用Document::create()将生成一个新的Document对象,而不是试图实例化一个DomainObject对象。
所以,如今在静态上下文使用继承关系。

abstract class DomainObject {
    public static function create() {
        return new static();
    }
}
    
class User extends DomainObject {
        
}
    
class Document extends DomainObject {
        
}
print_r(Document::create());//Document Object {}

static关键字不单单能够用于实例化。和self和parent同样,static还能够做为静态方法调用的标识符,甚至是从非静态上下文中调用。

若是想为DomainObject引入组(group)的概念。默认状况下,全部类都属于default类别,但想能够为继承层次结构的某些分支重写类别。

abstract class DomainObject {
    private $group;
    
    public function __construct() {
        $this->group = static::getGroup();
    }
    
    public static function create() {
        return new static();
    }
    
    static function getGroup() {
        return "default";
    }
}
    
class User extends DomainObject {
        
}
    
class Document extends DomainObject {
    static function getGroup() {
        return "document";
    }
} 

class SpreadSheet extends Document {

}

print_r(User::create());
print_r(SpreadSheet::create());

在DomainObject类中定义了构造函数。该构造函数使用static关键字调用静态方法getGroup()。DomainObject提供了默认实现,但Document将其覆盖了。建立的SpreadSheet新类扩展了Document类。下面是打印结果:

User Object
(
    [group:DomainObject:private] =>  default
)
SpreadSheet Object
(
    [group:DomainObject:private] => document
)

最后根据本身的理解,写个例子:

<?php
class Model {
    protected $model;
    
    public function __construct() {
        $this->model = static::getModel(); //延迟绑定
    }
    
    public function __get($name) {
        return $this->{'get'.ucfirst($name)}();
    }
    
    public static function getModel() {
        return 'Model';     
    } 
    
    public function getModelName()
    {
        return $this->model;
    }
    
    public function __toString() {
        return $this->modelName;
    }
}

class User extends Model {
    public static function getModel() {
        return 'User';
    }
}

class Book extends Model {
    public static function getModel() {
        return 'Book';
    }
}


$model = new Model;
$user = new User;
$book = new Book;
echo $model . PHP_EOL; // Model
echo $user . PHP_EOL; // User
echo $book . PHP_EOL; // Book

当把Model类的构造方法中的static::getModel()改成self::getModel()后:

echo $model . PHP_EOL; // Model
echo $user . PHP_EOL; // Model
echo $book . PHP_EOL; // Model
相关文章
相关标签/搜索