Est-il possible de créer des classes statiques en PHP (comme en C #)?

139

Je veux créer une classe statique en PHP et la faire se comporter comme elle le fait en C #, donc

  1. Le constructeur est automatiquement appelé au premier appel de la classe
  2. Aucune instanciation requise

Quelque chose de ce genre ...

static class Hello {
    private static $greeting = 'Hello';

    private __construct() {
        $greeting .= ' There!';
    }

    public static greet(){
        echo $greeting;
    }
}

Hello::greet(); // Hello There!
Aleemb
la source
Pourriez-vous expliquer brièvement comment une classe statique est censée se comporter? Est-ce la mise en œuvre d'un utilitaire?
xtofl
Juste en jetant ma propre opinion là-bas, mais d'après mon expérience en PHP, pour la raison, la testabilité et l'évolutivité, les classes statiques devraient être à peu près entièrement sans état, présenter une API plus fonctionnelle de type programmation qu'une API orientée objet, et sont généralement mieux utilisé comme façades d'accessibilité pour des objets entièrement instanciés ou des enveloppes d'utilitaires pour des helpers ou des constructions similaires s'ils sont même utilisés.
mopsyd

Réponses:

200

Vous pouvez avoir des classes statiques en PHP mais elles n'appellent pas automatiquement le constructeur (si vous essayez d'appeler, self::__construct()vous obtiendrez une erreur).

Par conséquent, vous devrez créer une initialize()fonction et l'appeler dans chaque méthode:

<?php

class Hello
{
    private static $greeting = 'Hello';
    private static $initialized = false;

    private static function initialize()
    {
        if (self::$initialized)
            return;

        self::$greeting .= ' There!';
        self::$initialized = true;
    }

    public static function greet()
    {
        self::initialize();
        echo self::$greeting;
    }
}

Hello::greet(); // Hello There!


?>
Greg
la source
20
Je fais souvent cela juste pour regrouper les fonctions en un seul endroit. Utilitaire IE :: doSomethingUseful ();
smack0007
16
Au lieu de cela, Therefore you'd have to create an initialize() function and call it in each method:il serait plus facile de faire initializeune fonction publique et de l'appeler juste après la déclaration de la classe.
chacham15
4
Je sais que c'est assez ancien, mais maintenant vous pouvez utiliser la magie __callStatic, donc lorsque vous appelez une méthode statique ou quoi que ce soit, elle sera d'abord appelée __callStatic, vous pourrez voir si elle a été initialisée et faire self::$methodce que vous appelez. S'il appelle toujours la méthode directement, essayez de tout changer en privé et voyez là.
matiaslauriti
1
Que se passe-t-il si deux threads appellent saluer en même temps? Comme il n'y a pas de synchronisation, l'initialisation ne sera pas appelée deux fois (ce qui est correct dans ce cas, mais ce n'est pas le cas dans de nombreux autres cas). Ou est php single threaded et non préemptif comme node?
John Little
53

En plus de la réponse de Greg, je recommanderais de définir le constructeur private afin qu'il soit impossible d'instancier la classe.

Donc, à mon humble avis, c'est un exemple plus complet basé sur celui de Greg:

<?php

class Hello
{
    /**
     * Construct won't be called inside this class and is uncallable from
     * the outside. This prevents instantiating this class.
     * This is by purpose, because we want a static class.
     */
    private function __construct() {}
    private static $greeting = 'Hello';
    private static $initialized = false;

    private static function initialize()
    {
        if (self::$initialized)
            return;

        self::$greeting .= ' There!';
        self::$initialized = true;
    }

    public static function greet()
    {
        self::initialize();
        echo self::$greeting;
    }
}

Hello::greet(); // Hello There!


?>
Phil
la source
1
C'est une excellente approche, la fonction construct ne peut cependant pas être implémentée si votre singelton hérite de certains objets qui nécessitent un constructeur public.
Eric Herlitz
4
@EricHerlitz Cette question ne concerne pas les singletons, mais les classes statiques. Pourquoi voudriez-vous créer une classe statique qui hérite d'une classe qui est censée être instanciée?
Mark Amery
3
De même, déclarer la classe comme abstraite pour l'empêcher d'être instanciée et autoriser toujours les appels aux méthodes statiques.
bstoney
24

vous pouvez avoir ces classes "statiques". mais je suppose qu'il manque quelque chose de vraiment important: en php, vous n'avez pas de cycle d'application, vous n'obtiendrez donc pas de véritable statique (ou singleton) dans toute votre application ...

voir Singleton en PHP

Andreas Niedermair
la source
1
Les classes statiques et les singletons ne sont que 2 choses différentes.
Max Cuttins
4
final Class B{

    static $staticVar;
    static function getA(){
        self::$staticVar = New A;
    }
}

la structure de b est appelée un gestionnaire singeton, vous pouvez également le faire dans un

Class a{
    static $instance;
    static function getA(...){
        if(!isset(self::$staticVar)){
            self::$staticVar = New A(...);
        }
        return self::$staticVar;
    }
}

c'est l'usage singleton $a = a::getA(...);

Borrel
la source
3

Je préfère généralement écrire des classes non statiques régulières et utiliser une classe factory pour instancier des instances uniques (sudo static) de l'objet.

De cette façon, le constructeur et le destructeur fonctionnent comme d'habitude, et je peux créer des instances non statiques supplémentaires si je le souhaite (par exemple une deuxième connexion DB)

Je l'utilise tout le temps et est particulièrement utile pour créer des gestionnaires de session de magasin de base de données personnalisés, car lorsque la page se termine, le destructeur pousse la session vers la base de données.

Un autre avantage est que vous pouvez ignorer l'ordre dans lequel vous appelez les choses, car tout sera configuré à la demande.

class Factory {
    static function &getDB ($construct_params = null)
    {
        static $instance;
        if( ! is_object($instance) )
        {
            include_once("clsDB.php");
            $instance = new clsDB($construct_params);   // constructor will be called
        }
        return $instance;
    }
}

La classe DB ...

class clsDB {

    $regular_public_variables = "whatever";

    function __construct($construct_params) {...}
    function __destruct() {...}

    function getvar() { return $this->regular_public_variables; }
}

Partout où vous voulez l'utiliser, appelez simplement ...

$static_instance = &Factory::getDB($somekickoff);

Ensuite, traitez simplement toutes les méthodes comme non statiques (car elles le sont)

echo $static_instance->getvar();
dave.zap
la source
1
Il s'agit en fait d'une implémentation de modèle singleton, et ne devrait vraiment pas être utilisé - tenez-vous-en à l'injection de dépendances à la place, qui est testable et facilite le débogage.
Thomas Hansen
1
Pouvez-vous donner un exemple d'utilisation de l'injection de dépendances pour cette réponse et comment cela la rend plus testable?
cjsimon
2

l'objet ne peut pas être défini de manière statique mais cela fonctionne

final Class B{
  static $var;
  static function init(){
    self::$var = new A();
}
B::init();
Borrel
la source
1
Andreas Niedermair: c'est ainsi que fonctionne php (cycle d'application = une seule requête) Mais un singleton (sur celui qui vit dans la requête) est une possebility en php (en php un singleton est un objet qui a 1 instance (dans l'application- cycle)
borrel