web-dev-qa-db-fra.com

Constantes abstraites dans PHP - Force une classe enfant à définir une constante

J'ai remarqué que vous ne pouvez pas avoir de constantes abstraites en PHP.

Existe-t-il un moyen de forcer une classe enfant à définir une constante (que je dois utiliser dans l'une des méthodes internes de la classe abstraite)?

43
Alex

Un constant est un constant; il n'y a pas de constantes abstract ou private dans PHP pour autant que je sache, mais vous pouvez contourner:

Exemple de classe abstraite

abstract class Hello {
    const CONSTANT_1 = 'abstract'; // Make Abstract
    const CONSTANT_2 = 'abstract'; // Make Abstract
    const CONSTANT_3 = 'Hello World'; // Normal Constant
    function __construct() {
        Enforcer::__add(__CLASS__, get_called_class());
    }
}

Cela fonctionnerait bien

class Foo extends Hello {
    const CONSTANT_1 = 'HELLO_A';
    const CONSTANT_2 = 'HELLO_B';
}
new Foo();

Bar retournerait une erreur

class Bar extends Hello {
    const CONSTANT_1 = 'BAR_A';
}
new Bar();

Songo retournerait une erreur

class Songo extends Hello {

}
new Songo();

classe Enforcer

class Enforcer {
    public static function __add($class, $c) {
        $reflection = new ReflectionClass($class);
        $constantsForced = $reflection->getConstants();
        foreach ($constantsForced as $constant => $value) {
            if (constant("$c::$constant") == "abstract") {
                throw new Exception("Undefined $constant in " . (string) $c);
            }
        }
    }
}
28
Baba

Cela peut être un peu un "hack", mais fait le travail avec très peu d'effort, mais juste avec un message d'erreur différent si la constante n'est pas déclarée dans la classe enfant.

Une déclaration constante auto-référentielle est syntaxiquement correcte et analyse sans problème, ne générant une erreur que si cette déclaration est réellement exécutée au moment de l'exécution, donc une déclaration auto-référentielle dans la classe abstraite doit être remplacée chez un enfant sinon, il y aura une erreur fatale: Cannot declare self-referencing constant.

Dans cet exemple, la classe parent abstraite Foo force tous ses enfants à déclarer la variable NAME. Ce code fonctionne correctement, produisant Donald. Cependant, si la classe enfant Fooling a pas déclaré la variable, l'erreur fatale serait déclenchée.

<?php

abstract class Foo {

    // Self-referential 'abstract' declaration
    const NAME = self::NAME;

}

class Fooling extends Foo {

    // Overrides definition from parent class
    // Without this declaration, an error will be triggered
    const NAME = 'Donald';

}

$fooling = new Fooling();

echo $fooling::NAME;
37
WebSmithery

Malheureusement non ... une constante est exactement ce qu'elle dit sur l'étain, constante. Une fois défini, il ne peut pas être redéfini, de cette façon, il est impossible d'exiger sa définition via l'héritage abstrait ou les interfaces de PHP.

Cependant ... vous pouvez vérifier si la constante est définie dans le constructeur de la classe parent. Si ce n'est pas le cas, lancez une exception.

abstract class A
{
    public function __construct()
    {
        if (!defined('static::BLAH'))
        {
            throw new Exception('Constant BLAH is not defined on subclass ' . get_class($this));
        }
    }
}

class B extends A
{
    const BLAH = 'here';
}

$b = new B();

C'est la meilleure façon de penser à cela à partir de votre description initiale.

18
Jamie Rumbelow

Non, mais vous pouvez essayer d'autres méthodes telles que les méthodes abstraites:

abstract class Fruit
{
    abstract function getName();
    abstract function getColor();

    public function printInfo()
    {
        echo "The {$this->getName()} is {$this->getColor()}";
    }
}

class Apple extends Fruit
{
    function getName() { return 'Apple'; }
    function getColor() { return 'red'; }

    //other Apple methods
}

class Banana extends Fruit
{
    function getName() { return 'banana'; }
    function getColor() { return 'yellow'; }

    //other banana methods
}  

ou membres statiques:

abstract class Fruit
{
    protected static $name;
    protected static $color;

    public function printInfo()
    {
        echo "The {static::$name} is {static::$color}";
    }
}

class Apple extends Fruit
{
    protected static $name = 'Apple';
    protected static $color = 'red';

    //other Apple methods
}

class Banana extends Fruit
{
    protected static $name = 'banana';
    protected static $color = 'yellow';

    //other banana methods
} 

Source

7
Songo

Testé en php 7.2 mais devrait le faire depuis 5.3, vous pouvez utiliser la liaison statique tardive pour archiver ce comportement. Il générera une erreur fatale atteignant la même chose qu'une exception car dans la plupart des cas, vous ne voulez pas gérer les erreurs Fatal lors de l'exécution. Si vous le souhaitez, vous pouvez facilement implémenter un gestionnaire d'erreurs personnalisé.

Donc, ce qui suit fonctionne pour moi:

<?php

abstract class Foo {

    public function __construct() {
        echo static::BAR;
    }

}


class Bar extends Foo {
    const BAR = "foo bar";
}

$bar = new Bar();    //foo bar

Si vous supprimez le const, vous obtiendrez un:

Fatal error: Uncaught Error: Undefined class constant 'BAR' in ...
1
Code Spirit