IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage PHP Discussion :

get_called_class en PHP <= 5.2


Sujet :

Langage PHP

  1. #1
    Membre habitué Avatar de alejandro
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Septembre 2004
    Messages
    167
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Tarn (Midi Pyrénées)

    Informations professionnelles :
    Activité : Chef de projet NTIC
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Septembre 2004
    Messages : 167
    Points : 188
    Points
    188
    Par défaut get_called_class en PHP <= 5.2
    Bonjour,

    Le titre du post résume la question !

    Pour pouvoir utiliser la fonction get_called_class() en php <= 5.2 on peut se baser sur la
    fonction utilisateur donnée dans la documentation officielle :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    /******************************** 
     * Retro-support of get_called_class() 
     * Tested and works in PHP 5.2.4 
     * http://www.sol1.com.au/ 
     ********************************/ 
    if(!function_exists('get_called_class')) { 
    function get_called_class($bt = false,$l = 1) { 
        if (!$bt) $bt = debug_backtrace(); 
        if (!isset($bt[$l])) throw new Exception("Cannot find called class -> stack level too deep."); 
        if (!isset($bt[$l]['type'])) { 
            throw new Exception ('type not set'); 
        } 
        else switch ($bt[$l]['type']) { 
            case '::': 
                $lines = file($bt[$l]['file']); 
                $i = 0; 
                $callerLine = ''; 
                do { 
                    $i++; 
                    $callerLine = $lines[$bt[$l]['line']-$i] . $callerLine; 
                } while (strpos($callerLine,$bt[$l]['function']) === false); 
                preg_match('/([a-zA-Z0-9\_]+)::'.$bt[$l]['function'].'/', 
                            $callerLine, 
                            $matches); 
                if (!isset($matches[1])) { 
                    // must be an edge case. 
                    throw new Exception ("Could not find caller class: originating method call is obscured."); 
                } 
                switch ($matches[1]) { 
                    case 'self': 
                    case 'parent': 
                        return get_called_class($bt,$l+1); 
                    default: 
                        return $matches[1]; 
                } 
                // won't get here. 
            case '->': switch ($bt[$l]['function']) { 
                    case '__get': 
                        // edge case -> get class of calling object 
                        if (!is_object($bt[$l]['object'])) throw new Exception ("Edge case fail. __get called on non object."); 
                        return get_class($bt[$l]['object']); 
                    default: return $bt[$l]['class']; 
                } 
     
            default: throw new Exception ("Unknown backtrace method type"); 
        } 
    } 
    } 
    ?>
    Seul problème, cette fonction ne prends pas en compte le mécanisme de Late State Binding sur lequel est justement basée la fonction get_called_class() disponible à partir de PHP 5.3.

    Cette fonctionnalité a été baptisée "late static bindings", d'un point de vue interne. "Late binding", littéralement compilation tardive, vient du fait que les éléments static:: ne seront plus résolus en utilisant la classe où la méthode a été définie, mais celle qui est active durant l'exécution. L'adjectif statique a été ajouté car ce problème s'applique aux méthodes statiques, mais pas seulement.
    Dans mon cas j'utilise un objet qui hérite d'un singleton abstrait dont voici la structure :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    if( ! class_exists( 'Singleton_Abstract' ) )
    {
        abstract class Singleton_Abstract
        implements Singleton_Interface
        {
            protected static $i_Counter = 0;
            protected static $s_Fline = null;
     
            final private function __clone(){}
            protected function __construct(){}
     
            final public static function GetInstance()
            {
                static $h_Instances = array();
                $s_Class = self::GetChild();
     
                if( ! isset( $h_Instances[ $s_Class ] ) )
                    $h_Instances[ $s_Class ] = new $s_Class;
     
                return $h_Instances[ $s_Class ];
            }
     
            final protected static function GetChild()
            {
                $h_Backtrace = debug_backtrace();
     
                $i_Line = $h_Backtrace[1]['line'];
                $s_File = $h_Backtrace[1]['file'];
                $s_Function = $h_Backtrace[1]['function'];
     
                if( self::$s_Fline == $s_File . $i_Line ) self::$i_Counter++;
                else self::$i_Counter = 0;
     
                $s_Search = "/([a-zA-Z0-9\_]+)::$s_Function/";
     
                $a_Lines = file( $s_File );
                $s_Line = $a_Lines[ --$i_Line ];
     
                preg_match_all( $s_Search, $s_Line, $a_Matches );
     
                return $a_Matches[1][self::$i_Counter];
            }
     
        }
    Et puisque je suis en PHP 5.2, et que je ne peux pas instancier mes objets singletons comme suit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    return $s_Class::GetInstance()
    je vais utiliser la fonction call_user_func() comme suit

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    return call_user_func( array( $s_Class, "GetInstance" ) );
    Dans une fabrique dont je vous fournis le code ici :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
            public function Factory( $s_Path )
            {
                $s_FrameworkPath = dirname( dirname( __FILE__ ) ) . DIRECTORY_SEPARATOR;
                $s_Path = str_replace( '/', DIRECTORY_SEPARATOR, $s_Path );
                $s_Class = str_replace( DIRECTORY_SEPARATOR, '_', $s_Path );
     
                if( file_exists( APPLICATION_PATH . "$s_Path.php" )
                  OR file_exists( $s_FrameworkPath . "$s_Path.php" ) )
                    return call_user_func( array( $s_Class, "GetInstance" ) );
                    //eval( "$s_Class::GetInstance();" );
     
                throw new Exception( "PAGE_NOT_FOUND", 404 );
            }

    Et là j'obtiens une suite d'erreurs :

    Notice: Undefined index: line in C:\Documents and Settings\alejandro\Mes documents\...\Framework\Singleton\Abstract.php on line 37

    Notice: Undefined index: file in C:\Documents and Settings\alejandro\Mes documents\...\Framework\Singleton\Abstract.php on line 38

    Notice: Undefined offset: 1 in C:\Documents and Settings\alejandro\Mes documents\...\Framework\Singleton\Abstract.php on line 51

    Fatal error: Class name must be a valid object or a string in C:\Documents and Settings\alejandro\Mes documents\...\Framework\Singleton\Abstract.php on line 28
    C'est ma fonction GetChild(), dérivée du get_called_class fait maison dans le manuel PHP qui, comme la fonction dont elle dérive, ne trouve pas les index file et line lorsque la trace a été générée lors d'un appel de fonction statique réalisé par call_user_func().

    Si vous avez une solution ...

  2. #2
    Membre chevronné Avatar de nosferapti
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    1 157
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 1 157
    Points : 1 895
    Points
    1 895
    Par défaut
    regarde le contenu de $h_Backtrace, l'information dont tu as besoin est peut-être présente dans une autre case du tableau

  3. #3
    Membre habitué Avatar de alejandro
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Septembre 2004
    Messages
    167
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Tarn (Midi Pyrénées)

    Informations professionnelles :
    Activité : Chef de projet NTIC
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Septembre 2004
    Messages : 167
    Points : 188
    Points
    188
    Par défaut
    Bonjour,

    Citation Envoyé par nosferapti Voir le message
    regarde le contenu de $h_Backtrace, l'information dont tu as besoin est peut-être présente dans une autre case du tableau
    Merci de ta réponse mais ...

    La fonction de remplacement à partir de laquelle je me suis inspiré pour récupérer le nom de la classe qui fait l'appel utilise backtrace afin de récupérer le nom du fichier et la ligne dans laquelle l'appel est fait.

    Puis avec une expression régulière on retrouve le nom de la classe et on retourne le résultat. Le problème avec call_user_func() c'est que l'appel n'est pas réalisé dans un fichier, et dans ce cas la technique du backtrace devient obsolète.

    Donc cela fonctionne bien si tu appelle ta classe par exemple comme suit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    return MaClasse::GetInstance();
    Mais cela ne fonctionnera pas si par exemple tu génère ton nom de classe de manière dynamique comme suit :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    return $maclasse::GetInstance();
    De plus, étant en php 5.2 je ne peux pas procéder comme dans l'exemple ci-dessus (car je n'ai pas le Late State Binding), je suis obligé de faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    return call_user_func( array( $maclasse, "GetInstance" ) );
    Et a part avec un debug_backtrace(), je ne vois pas d'autre solution. Autrement dit, cela me parait très difficile, voire impossible si l'on est dans une version de php <= 5.2.

    Votre avis ?

  4. #4
    Membre chevronné Avatar de nosferapti
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    1 157
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2009
    Messages : 1 157
    Points : 1 895
    Points
    1 895
    Par défaut
    Citation Envoyé par alejandro Voir le message
    et dans ce cas la technique du backtrace devient obsolète.
    et non
    dans ce cas tu as bien "call_user_func" dans $h_Backtrace[1]['function'] mais regarde ce que tu as dans $h_Backtrace[1]['args']

Discussions similaires

  1. [EDI] Quel est l'éditeur que vous recommandez pour PHP ?
    Par Lana.Bauer dans le forum EDI, CMS, Outils, Scripts et API
    Réponses: 400
    Dernier message: 10/04/2018, 20h08
  2. Quel est le meilleur script PHP de portail (CMS) ?
    Par Lana.Bauer dans le forum EDI, CMS, Outils, Scripts et API
    Réponses: 187
    Dernier message: 18/10/2012, 07h45
  3. [PHP 5.2] équivalent get_called_class() en PHP 5.2 ?
    Par basket dans le forum Langage
    Réponses: 2
    Dernier message: 03/04/2009, 19h24
  4. L'avenir est-il au PHP ?
    Par Manolo dans le forum Langage
    Réponses: 468
    Dernier message: 11/02/2008, 18h54

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo