Есть ли способ определить свойства абстрактного класса в PHP?
abstract class Foo_Abstract { abstract public $tablename; } class Foo extends Foo_Abstract { //Foo must 'implement' $property public $tablename = 'users'; }
Нет такой вещи, как определение свойства.
Вы можете объявлять свойства только потому, что они являются контейнерами данных, зарезервированных в памяти при инициализации.
С другой стороны, функция может быть объявлена (типы, имя, параметры) без определения (тело функции отсутствует) и, следовательно, может быть сделана абстрактной.
«Абстрактный» указывает только на то, что что-то было объявлено, но не определено, поэтому перед его использованием вам необходимо определить его или оно становится бесполезным.
Нет, нет никакого способа обеспечить выполнение этого с помощью компилятора, вам нужно будет использовать проверки времени выполнения (скажем, в конструкторе) для переменной $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 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 { }
Как вы можете видеть, в 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...
если значение 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'; } }
ключевым здесь является то, что строковое значение «пользователи» указано и возвращается непосредственно в getTablename () в реализации дочернего класса. Функция имитирует свойство «только для чтения».
Это довольно похоже на решение, опубликованное ранее, на котором используется дополнительная переменная. Мне также нравится решение Марко, хотя это может быть немного сложнее.