Відповіді:
Немає такого поняття, як визначення властивості.
Можна оголошувати властивості лише тому, що вони є контейнерами даних, зарезервованими в пам'яті при ініціалізації.
Функція з іншого боку може бути оголошена (типи, ім'я, параметри), не визначаючись (тіло функції відсутнє), і, таким чином, може бути абстрактно.
"Анотація" вказує лише на те, що щось було оголошено, але не визначено, і тому, перш ніж використовувати його, вам потрібно визначити це, або воно стане марним.
Ні, немає ніякого способу переконатись у тому, що для компілятора вам доведеться використовувати перевірки виконання (скажімо, у конструкторі) для $tablename
змінної, наприклад:
class Foo_Abstract {
public final function __construct(/*whatever*/) {
if(!isset($this->tablename))
throw new LogicException(get_class($this) . ' must have a $tablename');
}
}
Щоб застосувати це для всіх похідних класів Foo_Ab абстракт, вам доведеться створити конструктор Foo_Ab абстракт 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
ключовим словом для властивості в конструкторі абстрактних об'єктів або методах setter / getter. Ви можете додатково використовувати, 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;
якщо значення імені таблиці ніколи не зміниться протягом життя об’єкта, наступне буде простою, але безпечною реалізацією.
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 () у реалізації дочірнього класу. Функція імітує властивість "лише для читання".
Це досить схоже на рішення, розміщене раніше, в якому використовується додаткова змінна. Мені також подобається рішення Марко, хоча воно може бути трохи складніше.