2011-10-03 10 views
96

Esiste un modo per definire proprietà di classe astratta in PHP?Proprietà abstract PHP

abstract class Foo_Abstract { 
    abstract public $tablename; 
} 

class Foo extends Foo_Abstract { 
    //Foo must 'implement' $property 
    public $tablename = 'users'; 
} 

risposta

121

Non esiste una cosa come la definizione di una proprietà.

È possibile dichiarare proprietà solo perché sono contenitori di dati riservati in memoria all'inizializzazione.

Una funzione invece può essere dichiarata (tipi, nome, parametri) senza essere definita (manca il corpo della funzione) e quindi può essere resa astratta.

"Astratto" indica solo che qualcosa è stato dichiarato ma non definito e quindi prima di utilizzarlo, è necessario definirlo o diventa inutile.

+7

+1: Ben spiegato. – hakre

+34

Non vi è alcun motivo ovvio per cui la parola "abstract" non possa essere utilizzata su proprietà * static * - ma con un significato leggermente diverso. Ad esempio potrebbe indicare che una sottoclasse deve fornire un valore per la proprietà. – frodeborli

5

Come si potrebbe avere scoperto da solo test il tuo codice:

Fatal error: Properties cannot be declared abstract in ... on line 3

No, non c'è. Le proprietà non possono essere dichiarate astratte in PHP.

Tuttavia è possibile implementare una funzione getter/setter in modo astratto, questo potrebbe essere quello che stai cercando.

Proprietà non sono implementate (specialmente le proprietà pubbliche), hanno appena esistono (o non):

$foo = new Foo; 
$foo->publicProperty = 'Bar'; 
42

No, non c'è modo di far rispettare che con il compilatore, che avrebbe dovuto utilizzare i controlli in fase di esecuzione (diciamo, nel costruttore) per la variabile $tablename, 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 far rispettare questo per tutte le classi derivate di Foo_Abstract dovresti rendere il costruttore di Foo_Abstract final, impedendo l'override.

Si potrebbe dichiarare un getter astratto invece:

abstract class Foo_Abstract { 
    abstract public function get_tablename(); 
} 

class Foo extends Foo_Abstract { 
    protected $tablename = 'tablename'; 
    public function get_tablename() { 
    return $this->tablename; 
    } 
} 
+0

Bella funzionalità, mi piace come si implementano proprietà astratte. –

+4

Ciò richiederebbe di rendere finale il costruttore nella classe base astratta. – hakre

+0

@hakre: non sono sicuro di averti capito? – connec

19

Come detto in precedenza, non esiste tale definizione esatta. Io, invece, utilizzare questo semplice soluzione per forzare la classe figlia per definire il "astratto" proprietà:

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(); 
    } 
} 
+0

Per quanto riguarda i workaround, questo è abbastanza elegante. –

+0

Elegante, ma non risolve il problema delle proprietà 'static'. – Robbert

+1

Non penso che tu possa avere un privato per i metodi astratti. – Zorji

10

A seconda del contesto della proprietà se voglio forzare dichiarazione di proprietà di un oggetto astratto in un bambino oggetto, mi piace usare una costante con la parola chiave static per la proprietà nel costruttore di oggetti astratti o nei metodi setter/getter.

Oltre a ciò l'oggetto figlio esegue l'override della proprietà dell'oggetto principale e dei metodi se ridefinito. Ad esempio se una proprietà viene dichiarata come protected nella parent e definita come public nel figlio, la proprietà risultante è pubblica. Tuttavia, se la proprietà è dichiarata private nel genitore rimarrà private e non disponibile per il bambino.

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; 
+1

La soluzione più elegante qui –

4

Ho chiesto la stessa domanda oggi, e vorrei aggiungere i miei due centesimi.

La ragione per cui desideriamo le proprietà abstract è 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 l'alleato static.

Idealmente vorrei qualcosa di simile:

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 si può vedere, in A io non definisco $prop, ma lo uso in un getter static. Pertanto, il seguente codice funziona

B::getProp(); 
// => 'B prop' 

$b = new B(); 
$b->getProp(); 
// => 'B prop' 

In C, d'altra parte, non mi definisco $prop, in modo da ottenere eccezioni:

C::getProp(); 
// => Exception! 

$c = new C(); 
$c->getProp(); 
// => Exception! 

devo chiamare il metodo getProp() per ottenere l'eccezione e non riesco a farlo caricare in classe, ma è abbastanza vicino al comportamento desiderato, almeno nel mio caso.

mi definiscono getProp() come final per evitare che alcuni ragazzo intelligente (aka me stesso in 6 mesi) è tentati di fare

class D extends A { 
    public static function getProp() { 
     // really smart 
    } 
} 

D::getProp(); 
// => no exception... 
+0

Questo è un trucco molto ingegnoso. Speriamo che questo non debba essere fatto in futuro. – CMCDragonkai

1

se tablename valore non cambierà mai durante la vita dell'oggetto, in seguito sarà 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 'utenti' il valore della stringa è specificato e restituito direttamente a getTablename() nella implementazione della classe del bambino. La funzione riproduce una proprietà "readonly".

Questo è abbastanza simile a una soluzione pubblicata in precedenza su cui viene utilizzata una variabile aggiuntiva. Mi piace anche la soluzione di Marco anche se può essere un po 'più complicata.