8 ответов:
нет такой вещи, как определение свойства.
свойства можно объявлять только потому, что они являются контейнерами данных, зарезервированных в памяти при инициализации.
функция, с другой стороны, может быть объявлена (типы, имя, параметры) без определения (отсутствует тело функции) и, таким образом, может быть сделана абстрактной.
"абстрактный" указывает только на то, что что-то было объявлено, но не определено, и поэтому перед его использованием вам нужно определить его или его становиться бесполезным.
нет, нет никакого способа обеспечить это с помощью компилятора, вам придется использовать проверки времени выполнения (скажем, в конструкторе) для
$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; } }
как указано выше, такого точного определения нет. Однако я использую этот простой обходной путь, чтобы заставить дочерний класс определить свойство "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(); } }
в зависимости от контекста свойства, если я хочу принудительно объявить свойство абстрактного объекта в дочернем объекте, мне нравится использовать константу с
staticключевое слово для свойства в конструкторе абстрактных объектов или методах setter / getter.кроме этого дочерний объект переопределяет родительское свойство объекта и методы, если переопределено. Например, если свойство объявлено как
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;
Как вы могли бы узнать, просто проверив свой код:
неустранимая ошибка: свойства не могут быть объявлены абстрактными ... в строке 3
нет, нет. Свойства не могут быть объявлены абстрактными в PHP.
однако вы можете реализовать абстрактную функцию getter/setter, это может быть то, что вы ищете.
свойства не реализованы (особенно публичные свойства), они просто существуют (или нет):
$foo = new Foo; $foo->publicProperty = 'Bar';
я задал себе тот же вопрос сегодня, и я хотел бы добавить свои два цента.
причина, по которой мы хотели бы
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 { }Как видите, в
AI не определяйте$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...
необходимость в абстрактных свойствах может указывать на проблемы проектирования. Хотя многие из ответов реализуют вид шаблон метода 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'); } }это может быть полезно, если вы используете контейнер внедрения и проходить разные таблицы для разных объектов.
если значение 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() в реализации дочернего класса. Функция имитирует свойство "только для чтения".
Это довольно похоже на решение, опубликованное ранее, в котором используется дополнительная переменная. Мне также нравится решение Марко, хотя это может быть немного больше сложный.
Comments