回答:
プロパティを定義するようなものはありません。
プロパティは、初期化時にメモリに予約されるデータのコンテナーであるため、宣言できるのはプロパティのみです。
一方、関数は定義せずに(型、名前、パラメーター)宣言でき(関数本体が欠落)、したがって抽象化できます。
「抽象」は、何かが宣言されたが定義されていないことを示しているだけなので、それを使用する前に、定義する必要があるか、役に立たなくなります。
いいえ、コンパイラを使用して、$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;
}
}
プロパティのコンテキストに応じて、子オブジェクトで抽象オブジェクトプロパティの宣言を強制したい場合static
は、抽象オブジェクトコンストラクターまたはセッター/ゲッターメソッドのプロパティのキーワードで定数を使用します。オプションでを使用final
して、拡張クラスでメソッドがオーバーライドされないようにすることができます。
それ以外では、子オブジェクトは、再定義された場合、親オブジェクトのプロパティとメソッドをオーバーライドします。たとえば、プロパティがprotected
親のように宣言さpublic
れ、子のように再定義された場合、結果のプロパティはパブリックになります。ただし、プロパティがprivate
親で宣言されている場合、プロパティはそのまま残りprivate
、子は使用できません。
http://www.php.net//manual/en/language.oop5.static.php
abstract class AbstractFoo
{
public $bar;
final 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;
上記のように、そのような正確な定義はありません。ただし、私はこの簡単な回避策を使用して、子クラスに「抽象」プロパティを定義させます。
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();
}
}
static
プロパティの問題は解決しません。
the only "safe" methods to have in a constructor are private and/or final ones
、私の回避策はそのような場合ではありませんか?
$name
。setName()
実際に設定せずに関数を実装できます$name
。
getName
代わりにを使用した$name
方がうまくいくと思います。abstract class Father { abstract protected function getName(); public function foo(){ echo $this->getName();} }
今日も同じ質問をしたので、2セントを加算します。
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()
例外を取得するにはメソッドを呼び出す必要がありますが、クラスの読み込み時に取得することはできませんが、少なくとも私の場合は、望ましい動作に非常に近いです。
私は、ある賢い人(別名6か月で私自身)がやる気を起こさせないように定義getProp()
していますfinal
class D extends A {
public static function getProp() {
// really smart
}
}
D::getProp();
// => no exception...
抽象プロパティの必要性は、設計の問題を示している可能性があります。多くの回答は一種のテンプレートメソッドパターンを実装して機能しますが、常に奇妙に見えます。
元の例を見てみましょう:
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コンテナーを使用していて、異なるオブジェクトに異なるテーブルを渡す必要がある場合に役立ちます。
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;
オブジェクトの存続期間中にtablenameの値が変更されない場合、以下は単純でありながら安全な実装になります。
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()に直接返されることです。この関数は「読み取り専用」プロパティを模倣します。
これは、追加の変数を使用する前述のソリューションとかなり似ています。マルコのソリューションも好きですが、少し複雑になる可能性があります。