web-dev-qa-db-ja.com

PHP抽象プロパティ

PHPで抽象クラスのプロパティを定義する方法はありますか?

abstract class Foo_Abstract {
    abstract public $tablename;
}

class Foo extends Foo_Abstract {
    //Foo must 'implement' $property
    public $tablename = 'users';   
}
116
Tamás Pap

プロパティを定義するようなものはありません。

プロパティは、初期化時にメモリに予約されたデータのコンテナであるため、宣言することしかできません。

一方、関数は定義せずに(型、名前、パラメーター)宣言することができ(関数本体が欠落している)、したがって抽象化することができます。

「抽象」は、何かが宣言されたが定義されていないことを示すだけであるため、使用する前に定義する必要があるか、それが役に立たなくなります。

146

いいえ、コンパイラでそれを強制する方法はありません。$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を作成して、オーバーライドを防止する必要があります。

代わりに抽象ゲッターを宣言できます:

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

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

上記のように、そのような正確な定義はありません。ただし、この単純な回避策を使用して、子クラスに「抽象」プロパティを強制的に定義させます。

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

子オブジェクトの抽象オブジェクトプロパティの宣言を強制する場合、プロパティのコンテキストに応じて、抽象オブジェクトコンストラクターまたはセッター/ゲッターメソッドのプロパティにstaticキーワードと共に定数を使用します。

それ以外は、再定義された場合、子オブジェクトが親オブジェクトのプロパティとメソッドをオーバーライドします。たとえば、親でプロパティが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;
21
fyrye

コードをテストするだけでわかるように:

致命的エラー:行3で...でプロパティを抽象として宣言できません

いいえ、ありません。 PHPでプロパティを抽象として宣言することはできません。

ただし、getter/setter関数の抽象を実装できますが、これはあなたが探しているものかもしれません。

プロパティは実装されておらず(特にパブリックプロパティ)、存在する(または存在しない)だけです。

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

今日は同じ質問を自問しましたが、2セントを加算したいと思います。

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ゲッターで使用しています。したがって、次のコードが機能します

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...
6
Marco Pallante

抽象プロパティの必要性は、設計上の問題を示している可能性があります。答えの多くは Template method pattern のようなものを実装しており、動作しますが、常に奇妙に見えます。

元の例を見てみましょう。

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コンテナを使用し、オブジェクトごとに異なるテーブルを渡す必要がある場合に役立ちます。

5
sevavietl

PHP 7では、抽象的な「プロパティ」を作成するのがかなり簡単になります。上記と同じように、抽象関数を作成して作成しますが、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;
2
Dropa

テーブル名の値がオブジェクトの存続期間中に変更されない場合、以下はシンプルで安全な実装になります。

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';
    }
}

ここで重要なのは、文字列値「users」が指定され、子クラス実装のgetTablename()で直接返されることです。この関数は、「読み取り専用」プロパティを模倣します。

これは、追加の変数を使用する前述のソリューションにかなり似ています。少し複雑になる可能性もありますが、私はマルコのソリューションも気に入っています。

1
ck.tan