Réponses:
Il n’existe pas de définition d’une propriété.
Vous ne pouvez déclarer des propriétés que car ce sont des conteneurs de données réservés en mémoire lors de l'initialisation.
Une fonction par contre peut être déclarée (types, nom, paramètres) sans être définie (corps de fonction manquant) et donc, peut être rendue abstraite.
"Abstract" indique seulement que quelque chose a été déclaré mais pas défini et donc avant de l'utiliser, vous devez le définir ou il devient inutile.
Non, il n'y a aucun moyen d'imposer cela avec le compilateur, vous devriez utiliser des vérifications au moment de l'exécution (par exemple, dans le constructeur) pour la $tablename
variable, par exemple:
class Foo_Abstract {
public final function __construct(/*whatever*/) {
if(!isset($this->tablename))
throw new LogicException(get_class($this) . ' must have a $tablename');
}
}
Pour appliquer cela à toutes les classes dérivées de Foo_Abstract, vous devez créer le constructeur de Foo_Abstract, évitant ainsi le remplacement final
.
Vous pouvez plutôt déclarer un getter abstrait:
abstract class Foo_Abstract {
abstract public function get_tablename();
}
class Foo extends Foo_Abstract {
protected $tablename = 'tablename';
public function get_tablename() {
return $this->tablename;
}
}
En fonction du contexte de la propriété, si je veux forcer la déclaration d'une propriété d'objet abstrait dans un objet enfant, j'aime utiliser une constante avec le static
mot - clé de la propriété dans le constructeur d'objet abstrait ou les méthodes setter / getter. Vous pouvez éventuellement utiliser final
pour empêcher la méthode d'être remplacée dans les classes étendues.
À part cela, l'objet enfant remplace la propriété et les méthodes de l'objet parent s'il est redéfini. Par exemple, si une propriété est déclarée comme protected
dans le parent et redéfinie comme public
dans l'enfant, la propriété résultante est publique. Cependant, si la propriété est déclarée private
dans le parent, elle restera private
et ne sera pas disponible pour l'enfant.
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;
Comme indiqué ci-dessus, il n'existe pas de définition exacte. Cependant, j'utilise cette solution de contournement simple pour forcer la classe enfant à définir la propriété "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
propriétés.
the only "safe" methods to have in a constructor are private and/or final ones
, ma solution de contournement n'est -elle pas un tel cas?
$name
. Vous pouvez implémenter la setName()
fonction sans qu'elle soit réellement définie $name
.
getName
au lieu de $name
fonctionne mieux. abstract class Father { abstract protected function getName(); public function foo(){ echo $this->getName();} }
Je me suis posé la même question aujourd'hui et j'aimerais ajouter mes deux cents.
La raison pour laquelle nous aimerions des abstract
propriétés est de nous assurer que les sous-classes les définissent et lèvent des exceptions quand elles ne le font pas. Dans mon cas précis, j'avais besoin de quelque chose qui pourrait fonctionner avec un static
allié.
Idéalement, je voudrais quelque chose comme ceci:
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.
}
J'ai fini avec cette implémentation
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
{
}
Comme vous pouvez le voir, dans A
je ne définit pas $prop
, mais je l'utilise dans un static
getter. Par conséquent, le code suivant fonctionne
B::getProp();
// => 'B prop'
$b = new B();
$b->getProp();
// => 'B prop'
En C
, par contre, je ne définit pas $prop
, donc j'obtiens des exceptions:
C::getProp();
// => Exception!
$c = new C();
$c->getProp();
// => Exception!
Je dois appeler la getProp()
méthode pour obtenir l'exception et je ne peux pas l'obtenir lors du chargement de la classe, mais c'est assez proche du comportement souhaité, du moins dans mon cas.
Je définis getProp()
comme final
éviter qu'un mec intelligent (alias moi-même dans 6 mois) soit tenté de faire
class D extends A {
public static function getProp() {
// really smart
}
}
D::getProp();
// => no exception...
Comme vous auriez pu le découvrir en testant simplement votre code:
Erreur fatale: les propriétés ne peuvent pas être déclarées abstraites dans ... à la ligne 3
Non, il n'y en a pas. Les propriétés ne peuvent pas être déclarées abstraites en PHP.
Cependant, vous pouvez implémenter un résumé de la fonction getter / setter, c'est peut-être ce que vous recherchez.
Les propriétés ne sont pas implémentées (en particulier les propriétés publiques), elles existent juste (ou non):
$foo = new Foo;
$foo->publicProperty = 'Bar';
Le besoin de propriétés abstraites peut indiquer des problèmes de conception. Bien que de nombreuses réponses implémentent une sorte de modèle de méthode Template et que cela fonctionne, cela semble toujours étrange.
Jetons un coup d'œil à l'exemple original:
abstract class Foo_Abstract {
abstract public $tablename;
}
class Foo extends Foo_Abstract {
//Foo must 'implement' $property
public $tablename = 'users';
}
Marquer quelque chose, abstract
c'est l'indiquer comme un incontournable. Eh bien, une valeur indispensable (dans ce cas) est une dépendance obligatoire, elle doit donc être transmise au constructeur lors de l'instanciation :
class Table
{
private $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function name(): string
{
return $this->name;
}
}
Ensuite, si vous voulez réellement une classe nommée plus concrète, vous pouvez hériter comme suit:
final class UsersTable extends Table
{
public function __construct()
{
parent::__construct('users');
}
}
Cela peut être utile si vous utilisez un conteneur DI et devez passer différentes tables pour différents objets.
PHP 7 facilite un peu la création de "propriétés" abstraites. Tout comme ci-dessus, vous les ferez en créant des fonctions abstraites, mais avec PHP 7, vous pouvez définir le type de retour pour cette fonction, ce qui facilite beaucoup les choses lorsque vous créez une classe de base que tout le monde peut étendre.
<?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;
si la valeur de nom_table ne changera jamais pendant la durée de vie de l'objet, la suite sera une implémentation simple mais sûre.
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 clé ici est que la valeur de chaîne 'users' est spécifiée et renvoyée directement dans getTablename () dans l'implémentation de la classe enfant. La fonction imite une propriété "lecture seule".
Ceci est assez similaire à une solution publiée précédemment sur qui utilise une variable supplémentaire. J'aime aussi la solution de Marco même si elle peut être un peu plus compliquée.