2011-10-03 80 views
96

有没有什么办法可以在PHP中定义抽象类属性?PHP抽象属性

abstract class Foo_Abstract { 
    abstract public $tablename; 
} 

class Foo extends Foo_Abstract { 
    //Foo must 'implement' $property 
    public $tablename = 'users'; 
} 

回答

121

有没有这样的东西定义一个属性。

您只能声明属性,因为它们是初始化时在内存中保留的数据的容器。

另一方面,函数可以被声明(类型,名称,参数)而不被定义(函数体缺失),因此可以被抽象化。

“摘要”只是表示声明了一些内容,但没有定义,因此在使用它之前,您需要定义它或者它变得毫无用处。

+7

+1:很好解释。 – hakre

+34

没有明显的原因,'抽象'这个词不能用在* static *属性上 - 但意思稍有不同。例如,它可能表明一个子类必须为该属性提供一个值。 – frodeborli

5

正如你可以通过只是测试你的代码已经发现:

Fatal error: Properties cannot be declared abstract in ... on line 3

没有,没有。不能在PHP中声明属性。

但是,你可以实现一个getter/setter函数的抽象,这可能是你在找什么。

属性不落实(特别是公共性),他们只是存在(或不):

$foo = new Foo; 
$foo->publicProperty = 'Bar'; 
42

没有,有没有办法强制执行与编译器,你就必须使用运行时检查(比如,在构造函数中)为$tablename变量,如:

class Foo_Abstract { 
    public final function __construct(/*whatever*/) { 
    if(!isset($this->tablename)) 
     throw new LogicException(get_class($this) . ' must have a $tablename'); 
    } 
} 

要强制执行对于Foo_Abstract的所有派生类,您必须使Foo_Abstract的构造函数为final,从而防止重写。

你可以声明一个抽象的getter来代替:

abstract class Foo_Abstract { 
    abstract public function get_tablename(); 
} 

class Foo extends Foo_Abstract { 
    protected $tablename = 'tablename'; 
    public function get_tablename() { 
    return $this->tablename; 
    } 
} 
+0

不错的功能,我喜欢你如何实现抽象属性。 –

+4

这将要求您在抽象基类中使构造函数最终。 – hakre

+0

@hakre:我不确定我了解你吗? – connec

19

如上所述,不存在这样的确切定义。 我,但是,使用这种简单的解决办法来迫使子类来定义“抽象”属性:

abstract class Father 
{ 
    public $name; 
    abstract protected function setName(); // now every child class must declare this 
             // function and thus declare the property 

    public function __construct() 
    { 
    $this->setName(); 
    } 
} 

class Son extends Father 
{ 
    protected function setName() 
    { 
    $this->name = "son"; 
    } 

    function __construct(){ 
    parent::__construct(); 
    } 
} 
+0

就解决方法而言,这是相当优雅的。 –

+0

优雅,但不解决'static'属性的问题。 – Robbert

+1

我不认为你可以有私人的抽象方法。 – Zorji

10

根据财产的情况下,如果我想强制在孩子的抽象对象属性的声明对象,我喜欢在抽象对象构造函数或setter/getter方法中使用关键字为static的常量。

除此之外,如果重新定义,子对象将覆盖父对象属性和方法。 例如,如果在父级中声明一个属性为protected,并且在孩子中重新定义为public,则生成的属性是公共的。但是,如果该房产在父母中被宣布为private,则该房产将保持private,并且对于该孩子不可用。

http://www.php.net//manual/en/language.oop5.static.php

abstract class AbstractFoo 
{ 
    public $bar; 

    public function __construct() 
    { 
     $this->bar = static::BAR; 
    } 
} 

class Foo extends AbstractFoo 
{ 
    //const BAR = 'foobar'; 
} 

$foo = new Foo; //Fatal Error: Undefined class constant 'BAR' (uncomment const BAR = 'foobar';) 
echo $foo->bar; 
+1

这里最优雅的解决方案 –

4

我今天问自己同样的问题,我想补充我的两分钱。

我们希望abstract属性的原因是要确保子类定义它们,并在它们没有的时候抛出异常。在我的具体情况中,我需要一些可以与static盟友合作的东西。

理想我想是这样的:

abstract class A { 
    abstract protected static $prop; 
} 

class B extends A { 
    protected static $prop = 'B prop'; // $prop defined, B loads successfully 
} 

class C extends A { 
    // throws an exception when loading C for the first time because $prop 
    // is not defined. 
} 

我结束了这个实现

abstract class A 
{ 
    // no $prop definition in A! 

    public static final function getProp() 
    { 
     return static::$prop; 
    } 
} 

class B extends A 
{ 
    protected static $prop = 'B prop'; 
} 

class C extends A 
{ 
} 

正如你所看到的,在A我不定义$prop,我却用它在static获得者。因此,下面的代码工作

B::getProp(); 
// => 'B prop' 

$b = new B(); 
$b->getProp(); 
// => 'B prop' 

C,在另一方面,我不定义$prop,所以我得到异常:

C::getProp(); 
// => Exception! 

$c = new C(); 
$c->getProp(); 
// => Exception! 

我必须调用getProp()方法来获取异常我无法在课堂上加载它,但至少在我的情况下,它非常接近理想的行为。

我定义getProp()final避免一些聪明的家伙(又称自己在6个月)的诱惑做

class D extends A { 
    public static function getProp() { 
     // really smart 
    } 
} 

D::getProp(); 
// => no exception... 
+0

这是非常巧妙的破解。希望这不需要在未来完成。 – CMCDragonkai

1

如果表名的值将不会在对象的生命周期内发生变化,以下将是一个简单但安全的实施。

abstract class Foo_Abstract { 
    abstract protected function getTablename(); 

    public function showTableName() 
    { 
     echo 'my table name is '.$this->getTablename(); 
    } 
} 

class Foo extends Foo_Abstract { 
    //Foo must 'implement' getTablename() 
    protected function getTablename() 
    { 
     return 'users'; 
    } 
} 

的关键在这里是指定的字符串值“用户”和子类实现在getTableName时()直接返回。该函数模仿“只读”属性。

这与之前发布的使用附加变量的解决方案非常相似。我也喜欢Marco的解决方案,尽管它可能会更复杂一点。