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