web-dev-qa-db-fra.com

PHP propriétés abstraites

Est-il possible de définir des propriétés de classes abstraites en PHP?

abstract class Foo_Abstract {
    abstract public $tablename;
}

class Foo extends Foo_Abstract {
    //Foo must 'implement' $property
    public $tablename = 'users';   
}
116
Tamás Pap

Il n’existe pas de définition d’une propriété.

Vous ne pouvez déclarer des propriétés que parce qu'il s'agit de conteneurs de données réservés en mémoire lors de l'initialisation.

Par contre, une fonction peut être déclarée (types, nom, paramètres) sans être définie (corps de la fonction manquant) et peut donc être rendue abstraite.

"Abstract" indique uniquement que quelque chose a été déclaré mais non défini et que par conséquent, avant de l'utiliser, vous devez le définir ou il devient inutile.

146
Mathieu Dumoulin

Non, il n'y a aucun moyen d'imposer cela avec le compilateur, vous devez utiliser des contrôles d'exécution (par exemple, dans le constructeur) pour le $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 final, empêchant ainsi le remplacement.

Vous pouvez déclarer un getter abstrait à la place:

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

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

Comme indiqué ci-dessus, il n’existe pas de définition aussi précise. Cependant, j’utilise cette solution de contournement simple pour forcer la classe enfant à définir la propriété "abstraite":

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();
  }
}
23
ulkas

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 mot clé static de la propriété dans le constructeur d'objet abstrait ou le setter/getter. méthodes.

Sinon, l'objet enfant remplace la propriété de l'objet parent et les méthodes s'il est redéfini. Par exemple, si une propriété est déclarée comme protected dans le parent et redéfinie comme étant 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;

    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;
21
fyrye

Comme vous avez 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 cherchez.

Les propriétés ne sont pas implémentées (surtout les propriétés publiques), elles existent (ou non):

$foo = new Foo;
$foo->publicProperty = 'Bar';
6
hakre

Je me suis posé la même question aujourd'hui et j'aimerais ajouter mes deux sous.

La raison pour laquelle nous voudrions que abstract properties soit, est de nous assurer que les sous-classes les définissent et génèrent des exceptions lorsqu'elles ne le font pas. Dans mon cas spécifique, j'avais besoin de quelque chose qui puisse fonctionner avec statically.

Idéalement, j'aimerais 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 constater, dans A, je ne définis 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'

Par contre, dans C, je ne définis pas $prop, Je reçois donc des exceptions:

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

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

Je dois appeler la méthode getProp() pour obtenir l'exception. Je ne peux pas l'obtenir lors du chargement de la classe, mais le comportement souhaité est proche, du moins dans mon cas.

Je définis getProp() comme final pour éviter que certains type malin (alias moi-même dans 6 mois) soit tenté de le faire

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

D::getProp();
// => no exception...
6
Marco Pallante

Le besoin de propriétés abstraites peut indiquer des problèmes de conception. Bien que beaucoup de réponses implémentent un type de modèle de méthode et que cela fonctionne, cela semble toujours étrange.

Jetons un coup d'oeil à 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 une chose indispensable. Eh bien, une valeur indispensable (dans ce cas) est une dépendance requise, elle doit donc être passée à le 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 transmettre différentes tables pour différents objets.

5
sevavietl

PHP 7 facilite la création de "propriétés" abstraites. Comme ci-dessus, vous les créerez en créant des fonctions abstraites, mais avec PHP 7, vous pouvez définir le type de retour pour cette fonction, ce qui facilite grandement la tâche lorsque vous construisez une classe de base. que n'importe qui peut prolonger.

<?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;
2
Dropa

si la valeur 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 'utilisateurs' est spécifiée et renvoyée directement dans getTablename () dans l'implémentation de la classe enfant. La fonction imite une propriété "en lecture seule".

Ceci est assez similaire à une solution publiée précédemment qui utilise une variable supplémentaire. J'aime aussi la solution de Marco, même si elle peut être un peu plus compliquée.

1
ck.tan