小池有话说

PHP 类, 邪恶的继承

2014-12-29

之前就再犹豫要不要写这一部分, 因为如果不是要写框架, 这其中的东西, 一般的程序员是不需要理解的.

$o = new stdClass();
$o = new stdClass; // exactly the same

当然, 很多人都认为不应该使用第二种方式, 但我认为, 第二种方式更加优雅.

好的, 私货结束.

关于类的继承问题. 动态的方法很容易理解, 我们可以理解成随着继承链往上走, 直到遇到第一个同名方法.

class A { public function foo(){} }
class B extends A { public function foo(){} }
class C extends A {}
(new B)->foo();
(new C)->foo();

那么, 如果调用 B 的 foo 方法, 调用的是B自身的方法. 如果调用C的 foo 方法, 调用的是A的方法.

接下来要讲的是邪恶的继承. 邪恶的继承是可以破坏封装的.

class A { public function foo() { $this->bar(); } }
class B extends A { public function bar(){ // do some evil } }
(new B)->foo();

foo函数是无辜的, 它不知道自己做了邪恶的事情. 所以大神们会推荐用组合而非继承.

static 方法可以被继承.

class A { public static function foo(){} }
class B extends A { public static function foo(){} }
class C extends A {}
B::foo();
C::foo();

和预想的一样.

接下来就要讲一下不邪恶的继承:

class A {
    public static function foo() { self::bar(); }
    public static function bar() { // good }
}
class B extends A { public static function bar(){ // do some evil } }
B::foo(); // I am good

但这显然不灵活, 失去了多态.

class A { public static function foo() { static::bar(); } }

你可能会怀疑这个有什么用. 假设你写一个ORM的类库. 你希望用户的表名可以默认不配置.

class UserTask extends OrmBase {} // 默认表名 user_task
class UserHost extends OrmBase {
    public static $tableName = 'userhost';
} // 指定表名 userhost

那么假设 getTableName() 方法可以获取表名, 那么这个方法该怎么写呢?

class OrmBase()
{
    public static function getTableName()
    {
        if (static::$tableName) return static::$tableName;
        return strtolower(preg_replace('/[A-Z]/', '$0_', get_called_class()));
    }
}

当然, 如果你这个是是个效率党人, 那么你一定不能错过cache的机会

public static function getTableName()
{
    if (!static::$tableName)
        static::$tableName = strtolower(preg_replace('/[A-Z]/', '$0_', get_called_class()));
    return static::$tableName; 
}

属性赋值规则

  1. 如果有定义, 一切好说. (可见范围按照继承关系)
  2. 如果有 __set() 魔术方法, 使用此.
  3. 指定 public 属性(但如果有private, 会报错)

属性获取规则

  1. 如果有 public(真属性)
  2. 如果有 __get()
  3. 报错

isset 规则和此类似

方法调用规则

  1. 如果有定义
  2. 如果有 __call __callStatic()
  3. 报错

专辑: