Ответы:
Не существует такой вещи, как определение свойства.
Вы можете объявлять свойства только потому, что они представляют собой контейнеры данных, зарезервированные в памяти при инициализации.
С другой стороны, функция может быть объявлена (типы, имя, параметры) без определения (тело функции отсутствует) и, таким образом, может быть абстрактной.
«Абстрактное» указывает только на то, что что-то было объявлено, но не определено, и поэтому перед его использованием вам необходимо определить это, иначе оно станет бесполезным.
Нет, нет способа принудительно применить это с помощью компилятора, вам придется использовать проверки времени выполнения (скажем, в конструкторе) для $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();} }
Я задал себе тот же вопрос сегодня и хочу добавить свои два цента.
Причина, по которой нам нужны 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()
метод, чтобы получить исключение, и я не могу получить его при загрузке класса, но это довольно близко к желаемому поведению, по крайней мере, в моем случае.
Я определяю , getProp()
как , final
чтобы избежать , что некоторые умный парень (он же сам в течение 6 месяцев) искушается сделать
class D extends A {
public static function getProp() {
// really smart
}
}
D::getProp();
// => no exception...
Как вы могли понять, просто протестировав свой код:
Неустранимая ошибка: свойства не могут быть объявлены абстрактными в ... в строке 3
Нет, нет. Свойства не могут быть объявлены абстрактными в PHP.
Однако вы можете реализовать абстрактную функцию получения / установки, это может быть то, что вы ищете.
Свойства не реализованы (особенно общедоступные), они просто существуют (или нет):
$foo = new Foo;
$foo->publicProperty = 'Bar';
Потребность в абстрактных свойствах может указывать на проблемы дизайна. Хотя многие ответы реализуют своего рода шаблон метода шаблона, и он работает, это всегда выглядит странно.
Давайте посмотрим на исходный пример:
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';
}
}
Ключевым моментом здесь является то, что строковое значение «пользователи» указывается и возвращается непосредственно в getTablename () в реализации дочернего класса. Функция имитирует свойство «только для чтения».
Это довольно похоже на решение, опубликованное ранее, в котором используется дополнительная переменная. Мне также нравится решение Марко, хотя оно может быть немного сложнее.