PHP 抽象属性

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

abstract class Foo_Abstract {
abstract public $tablename;
}


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

正如您可以通过测试代码发现的那样:

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

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

然而,您可以实现一个 getter/setter 函数抽象,这可能就是您正在寻找的。

属性没有实现(特别是公共属性) ,它们只是存在(或不存在) :

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

根本没有定义属性这回事。

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

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

“抽象”只表示某个东西已经声明但没有定义,因此在使用它之前,您需要定义它,否则它就变得无用。

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

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

为了对所有的 Foo _  摘要的派生类强制执行这个命令,你必须使 Foo _ 抽象的构造函数 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;
}
}

如上所述,没有这样准确的定义。 然而,我使用这个简单的解决方案来强制子类定义“抽象”属性:

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

根据属性的上下文,如果我想在扩展类中强制声明一个抽象类属性,我喜欢在抽象对象构造函数或 setter/getter 方法中对属性使用带有 static关键字的常量。可以选择使用 final来防止在扩展类中重写该方法。

例子: https://3v4l.org/WH5Xl

abstract class AbstractFoo
{
public $bar;


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


class Foo extends AbstractFoo
{
//const BAR = 'foobar'; //uncomment to prevent exception
}
$foo = new Foo();
//Fatal Error: Undefined class constant 'BAR'

但是,如果重新定义,扩展类将重写父类属性和方法。
例如,如果一个属性在父类中声明为 protected,并在扩展类中重新定义为 public,则生成的属性为 public。否则,如果该属性在父类中声明为 private,它将保持为 private,并且不可用于扩展类。

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

我今天也问过自己同样的问题,我想补充一下我的看法。

我们希望使用 abstract属性的原因是确保子类定义它们,并在它们不定义时抛出异常。在我的特殊情况下,我需要的东西,可以与 statically 工作。

理想情况下,我会喜欢这样的东西:

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 getter 中使用它。因此,下面的代码可以工作

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...

如果表名值在对象的生存期内永远不会更改,那么下面将是一个简单而安全的实现。

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 ()中指定并返回字符串值“ users”。该函数模仿“ readonly”属性。

这与前面提到的使用附加变量的解决方案非常相似。我也喜欢马可的解决方案,虽然它可能有点复杂。

对抽象属性的需求可以指出设计问题。虽然许多答案都实现了某种 模板方法,而且很有效,但它看起来总是有点奇怪。

让我们看看最初的例子:

abstract class Foo_Abstract {
abstract public $tablename;
}


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

标记某样东西 abstract表示它是必须拥有的东西。好吧,必须拥有的价值(在这个例子中) 是必需的依赖项,因此应该在实例化期间将其传递给构造函数:

class Table
{
private $name;


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


public function name(): string
{
return $this->name;
}
}

然后,如果你真的想要一个更具体的命名类,你可以这样继承:

final class UsersTable extends Table
{
public function __construct()
{
parent::__construct('users');
}
}

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

PHP7使得制作抽象“属性”变得相当容易。和上面一样,您可以通过创建抽象函数来实现它们,但是在 PHP 7中,您可以为该函数定义返回类型,这使得构建任何人都可以扩展的基类变得非常容易。

<?php


abstract class FooBase {


abstract public function FooProp(): string;
abstract public function BarProp(): BarClass;


public function foo() {
return $this->FooProp();
}


public function bar() {
return $this->BarProp()->name();
}


}


class BarClass {


public function name() {
return 'Bar!';
}


}


class FooClass extends FooBase {


public function FooProp(): string {
return 'Foo!';
}


public function BarProp(): BarClass {
// This would not work:
// return 'not working';
// But this will!
return new BarClass();
}


}


$test = new FooClass();
echo $test->foo() . PHP_EOL;
echo $test->bar() . PHP_EOL;

可以在抽象类中定义静态属性。

<?php
abstract class Foo {
private static $bar = "1234";
    

public static function func() {
echo self::$bar;
}
}




Foo::func(); // It will be printed 1234

现在回答这个问题已经太晚了,但是您可以使用 selfstatic之间的区别,如下所示

<?php
class A { // Base Class
protected static $name = 'ClassA';
public static function getSelfName() {
return self::$name;
}
public static function getStaticName() {
return static::$name;
}
}


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


echo A::getSelfName(); // ClassA
echo A::getStaticName(); // ClassA


echo B::getSelfName(); // ClassA
echo B::getStaticName(); // ClassB