静态方法和属性
静态方法是以类作为作用域
的函数。静态方法不能访问这个类中的普通属性
,因为那些属性属于一个对象
,但可以访问静态属性。如果修改了一个静态属性,那么这个类的所有实例
都能访问到这个新值。
因为是通过类而不是实例来访问静态元素,所以访问静态元素时不再需要引用对象的变量,而是使用::来连接类名和属性或类名和方法。
class StaicExample {
static public $aNum = 0;
static public function sayHello() {
print "hello";
}
}
print StaicExample::$aNum;
StaicExample::sayHello();
一个子类可以使用parent
关键字来访问父类,而不使用其类名。要从当前类(不是子类)中访问静态方法或属性,可以使用self
关键字。self
指向当前类,就像伪变量$this
指向当前对象一样。因此,在StaticExample
类的外部可以使用其类名访问属性$aNum
:
StaicExample::$aNum;
而在StaicExample
类内部,可以使用self
关键字:
class StaicExample {
static public $aNum = 0;
static public function sayHello() {
self::$aNum++;
print "hello (".self::$aNum.")\n";
}
}
只有在使用parent
关键字调用方法的时候,才能对一个非静态方法进行静态形式的调用(使用::
)。除非是访问一个被覆写的方法,否则永远只能使用::
访问被明确声明为static
的方法或属性。有时看到使用static
语法来引用方法或属性,可能并不意味着其中的方法或属性必须是静态的,只不过说明它属于特定的类。
根据定义,不能在对象中调用静态方法。因此静态方法和属性又被称为类变量和属性
,也就不能在静态方法中使用伪变量$this
。
为什么要使用静态方法或属性呢?
- 在代码中的任何地方都可用(假设你可以访问该类)。也就是说,你不需要在对象间传递类的实例,也不需要将实例存放在全局变量中,就可以访问类中方法。
- 类的每个实例都可以访问类中定义的静态属性,所以可以利用静态属性来设置值,该值可以被类的所有对象使用。
- 不需要实例对象就能访问静态属性或方法,这样就不用为了获取一个简单的功能而实例化对象。
延迟静态绑定:static关键字
静态方法可以用作工厂方法,工厂方法是生成包含类的实例的一种方法。
先看下面的重复代码:
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()
放在超类呢?
abstract class DomainObject {
public static function create() {
return new self();
}
}
class User extends DomainObject {
}
class Document extends DomainObject {
}
Document::create();
这回看起来简洁多了。现在把常见的代码放在一个位置,并使用self
作为对该类的引用。实际上,self对该类所起的作用与$this对对象所起的作用并不完全相同。self指的不是调用上下文,而是解析上下文
。因此,运行刚才上面的代码会得到:
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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。