Risposte:
Non esiste una definizione di proprietà.
È possibile dichiarare le proprietà solo perché sono contenitori di dati riservati in memoria durante l'inizializzazione.
D'altra parte una funzione può essere dichiarata (tipi, nome, parametri) senza essere definita (corpo della funzione mancante) e quindi può essere resa astratta.
"Astratto" indica solo che qualcosa è stato dichiarato ma non definito e quindi prima di usarlo, è necessario definirlo o diventa inutile.
No, non c'è modo di imporlo con il compilatore, dovresti usare i controlli di runtime (diciamo, nel costruttore) per la $tablenamevariabile, ad esempio:
class Foo_Abstract {
public final function __construct(/*whatever*/) {
if(!isset($this->tablename))
throw new LogicException(get_class($this) . ' must have a $tablename');
}
}
Per imporre questo per tutte le classi derivate di Foo_Abstract dovresti creare il costruttore di Foo_Abstract final, prevenendo l'override.
Invece potresti dichiarare un getter astratto:
abstract class Foo_Abstract {
abstract public function get_tablename();
}
class Foo extends Foo_Abstract {
protected $tablename = 'tablename';
public function get_tablename() {
return $this->tablename;
}
}
A seconda del contesto della proprietà se voglio forzare la dichiarazione di una proprietà di oggetto astratto in un oggetto figlio, mi piace usare una costante con la staticparola chiave per la proprietà nel costruttore di oggetti astratti o nei metodi setter / getter. È possibile utilizzare facoltativamente finalper impedire che il metodo venga sovrascritto in classi estese.
Oltre a ciò, l'oggetto figlio sovrascrive la proprietà e i metodi dell'oggetto padre se ridefinito. Ad esempio, se una proprietà viene dichiarata come protectednel padre e ridefinita come publicnel figlio, la proprietà risultante è pubblica. Tuttavia, se la proprietà è dichiarata privatenel genitore rimarrà privatee non sarà disponibile per il figlio.
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;
Come detto sopra, non esiste una definizione così precisa. Tuttavia, utilizzo questa semplice soluzione per forzare la classe figlio a definire la proprietà "astratta":
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();
}
}
staticproprietà.
the only "safe" methods to have in a constructor are private and/or final ones, non è il mio modo di aggirare il caso?
$name. È possibile implementare la setName()funzione senza che sia effettivamente impostata $name.
getNameinvece di $namefunzioni meglio. abstract class Father { abstract protected function getName(); public function foo(){ echo $this->getName();} }
Oggi mi sono posto la stessa domanda e vorrei aggiungere i miei due centesimi.
Il motivo per cui vorremmo le abstractproprietà è assicurarsi che le sottoclassi le definiscano e generino eccezioni quando non lo fanno. Nel mio caso specifico, avevo bisogno di qualcosa che potesse funzionare con un staticalleato.
Idealmente, vorrei qualcosa del genere:
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.
}
Ho finito con questa implementazione
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
{
}
Come puoi vedere, Anon lo definisco $prop, ma lo uso in modo staticgetter. Pertanto, il seguente codice funziona
B::getProp();
// => 'B prop'
$b = new B();
$b->getProp();
// => 'B prop'
D' Caltra parte, non definisco $prop, quindi ottengo delle eccezioni:
C::getProp();
// => Exception!
$c = new C();
$c->getProp();
// => Exception!
Devo chiamare il getProp() metodo per ottenere l'eccezione e non riesco a ottenerlo durante il caricamento della classe, ma è abbastanza vicino al comportamento desiderato, almeno nel mio caso.
Definisco getProp()come finalevitare che un ragazzo in gamba (alias me stesso in 6 mesi) sia tentato di farlo
class D extends A {
public static function getProp() {
// really smart
}
}
D::getProp();
// => no exception...
Come avresti potuto scoprire testando il tuo codice:
Errore irreversibile: le proprietà non possono essere dichiarate astratte in ... alla riga 3
No non c'è. Le proprietà non possono essere dichiarate astratte in PHP.
Tuttavia, puoi implementare un estratto della funzione getter / setter, questo potrebbe essere quello che stai cercando.
Le proprietà non sono implementate (in particolare le proprietà pubbliche), esistono solo (o meno):
$foo = new Foo;
$foo->publicProperty = 'Bar';
La necessità di proprietà astratte può indicare problemi di progettazione. Mentre molte risposte implementano una specie di modello di metodo Template e funziona, sembra sempre strano.
Diamo un'occhiata all'esempio originale:
abstract class Foo_Abstract {
abstract public $tablename;
}
class Foo extends Foo_Abstract {
//Foo must 'implement' $property
public $tablename = 'users';
}
Contrassegnare qualcosa abstractsignifica indicarlo come un must. Bene, un valore indispensabile (in questo caso) è una dipendenza richiesta, quindi dovrebbe essere passato al costruttore durante l'istanza :
class Table
{
private $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function name(): string
{
return $this->name;
}
}
Quindi, se in realtà vuoi una classe di nome più concreta, puoi ereditare in questo modo:
final class UsersTable extends Table
{
public function __construct()
{
parent::__construct('users');
}
}
Ciò può essere utile se si utilizza un contenitore DI e si devono passare tabelle diverse per oggetti diversi.
PHP 7 rende un po 'più semplice la creazione di "proprietà" astratte. Proprio come sopra, li creerai creando funzioni astratte, ma con PHP 7 puoi definire il tipo di ritorno per quella funzione, il che rende le cose molto più facili quando stai costruendo una classe base che chiunque può estendere.
<?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;
se il valore di tablename non cambierà mai durante il ciclo di vita dell'oggetto, seguirà un'implementazione semplice ma sicura.
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';
}
}
la chiave qui è che il valore della stringa 'utenti' è specificato e restituito direttamente in getTablename () nell'implementazione della classe figlio. La funzione imita una proprietà "sola lettura".
Questo è abbastanza simile a una soluzione pubblicata in precedenza in cui utilizza una variabile aggiuntiva. Mi piace anche la soluzione di Marco, sebbene possa essere un po 'più complicata.