PHP抽象属性

Tam*_*Pap 120 php oop abstract-class

有没有办法在PHP中定义抽象类属性?

abstract class Foo_Abstract {
    abstract public $tablename;
}

class Foo extends Foo_Abstract {
    //Foo must 'implement' $property
    public $tablename = 'users';   
}
Run Code Online (Sandbox Code Playgroud)

Mat*_*lin 150

没有定义属性的东西.

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

另一方面,可以声明函数(类型,名称,参数)而不定义(函数体缺失),因此可以使其成为抽象的.

"抽象"仅表示某些内容已声明但未定义,因此在使用之前,您需要定义它或它变得无用.

  • 没有明显的理由'abstract'这个词不能用在*static*属性上 - 但含义略有不同.例如,它可以指示子类必须为属性提供值. (57认同)
  • 在 TypeScript 中有 [抽象属性和访问器](https://github.com/Microsoft/TypeScript/wiki/What's-new-in-TypeScript#abstract-properties-and-accessors)。遗憾的是,在 php 中这是不可能的。 (3认同)

con*_*nec 49

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

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

要为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;
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 这将要求您在抽象基类中使构造函数final. (4认同)
  • 一些解释:如果在构造函数中进行检查,并且它是否必须是必需的,则需要确保在每个实例实例化中执行它.因此,您需要防止它被删除,例如通过扩展类并替换构造函数.您可以允许[最终关键字](http://php.net/manual/en/language.oop5.final.php). (3认同)
  • 我喜欢“抽象吸气剂”解决方案。当你在一个抽象类中声明一个函数时,你必须声明这个类本身是抽象的。这意味着除非扩展和完全实现,否则该类无法使用。扩展该类时,您必须为“getter”函数提供一个实现。这意味着您还必须在扩展类中创建相关属性,因为该函数必须有返回值。遵循此模式,您将获得与声明抽象属性相同的结果,这也是一种简洁明了的方法。这就是它实际完成的方式。 (2认同)

ulk*_*kas 23

如上所述,没有这样的确切定义.但是,我使用这个简单的解决方法来强制子类定义"abstract"属性:

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();
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 这看起来不错,但它不会强制子类实际设置`$ name`.您可以实现`setName()`函数,而不实际设置`$ name`. (4认同)
  • 我认为使用`getName`而不是`$ name`会更好.`abstract class Father {abstract protected function getName(); public function foo(){echo $ this-> getName();}}` (3认同)

fyr*_*rye 22

根据属性的上下文,如果我想在子对象中强制声明抽象对象属性,我喜欢static在抽象对象构造函数或setter/getter方法中使用带有关键字的常量.

除此之外,子对象重写父对象属性和方法(如果重新定义).例如,如果属性protected在父级中声明并在子级中重新定义public,则结果属性为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;
Run Code Online (Sandbox Code Playgroud)

  • 最优雅的解决方案 (3认同)

hak*_*kre 6

您可以通过测试代码找到答案:

致命错误:第3行的......中的属性不能被声明为抽象

不,那里没有.在PHP中,属性不能声明为abstract.

但是,您可以实现getter/setter函数抽象,这可能是您正在寻找的.

属性未实现(尤其是公共属性),它们只是存在(或不存在):

$foo = new Foo;
$foo->publicProperty = 'Bar';
Run Code Online (Sandbox Code Playgroud)


Mar*_*nte 6

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

我们想要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.
}
Run Code Online (Sandbox Code Playgroud)

我最终得到了这个实现

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
{
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,A我没有定义$prop,但我在staticgetter中使用它.因此,以下代码有效

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

$b = new B();
$b->getProp();
// => 'B prop'
Run Code Online (Sandbox Code Playgroud)

C另一方面,我没有定义$prop,所以我得到例外:

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

$c = new C();
$c->getProp();
// => Exception!
Run Code Online (Sandbox Code Playgroud)

我必须调用该getProp() 方法来获取异常,我无法在类加载时得到它,但它非常接近所需的行为,至少在我的情况下.

我定义getProp()final避免一些聪明的家伙(也就是我自己在6个月内)的诱惑

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

D::getProp();
// => no exception...
Run Code Online (Sandbox Code Playgroud)


sev*_*etl 6

对抽象属性的需求可以指示设计问题。尽管许多答案都实现了某种Template方法模式并且可以正常工作,但它看起来总是很奇怪。

让我们看一下原始示例:

abstract class Foo_Abstract {
    abstract public $tablename;
}

class Foo extends Foo_Abstract {
    //Foo must 'implement' $property
    public $tablename = 'users';   
}
Run Code Online (Sandbox Code Playgroud)

标记事物abstract是表示它必须具备的功能。好吧,必须具有的值(在这种情况下)是必需的依赖项,因此应在实例化期间将其传递给构造函数

class Table
{
    private $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    public function name(): string
    {
        return $this->name;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,如果您实际上想要一个更具体的命名类,则可以像这样继承:

final class UsersTable extends Table
{
    public function __construct()
    {
        parent::__construct('users');
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您使用DI容器并且必须为不同的对象传递不同的表,这将很有用。