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

Contribuez / Téléchargez Sources et Outils PHP Discussion :

prototype.php, vous avez bien dit dynamisme ?


Sujet :

Contribuez / Téléchargez Sources et Outils PHP

  1. #1
    Expert éminent
    Avatar de Benjamin Delespierre
    Profil pro
    Développeur Web
    Inscrit en
    Février 2010
    Messages
    3 929
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Février 2010
    Messages : 3 929
    Points : 7 762
    Points
    7 762
    Par défaut prototype.php, vous avez bien dit dynamisme ?
    Bonjour à tous,

    Vous vous rappellez de cette actualité ? dhoston proposait un proof of concept d'implémentation des objets dynamiques en PHP à l'aide des closures de PHP 5.3. Cette idée me parait intéressante à plusieurs points de vue mais il manque tout de même un concept pourtant bien pratique: l'héritage.

    En m'inspirant de son code et de l'implémentation de l'héritage de prototypes de JavaScript, j'ai créé cette toute petite librairie - pour l'instant au stade expérimental - afin de voir ce que ça pouvait donner. A l'instar de la classe de dhotson, les classes sont maintenant des objets dont les membres sont manipulables dynamiquement mais désormais, il est aussi possible de manipuler les prototypes de ces objets et de les échanger d'un objet à l'autre.
    On peut bien évidement créer une chaine de prototypes de telle sorte que les filles héritent des membres de leur(s) mère(s).

    Concrêtement, ça permet (entre autres) de faire ça:
    Code PHP : 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
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    <?php
    require_once "prototype.php";
     
    // fabrique de classes
    $class = Object::create()
        ->fn('new', function ($that) {
            $object = new Object(clone $that->prototype);
            $args   = func_get_args();
            array_shift($args);
            if ($that->construct instanceof Method)
                $that->construct->apply($object, $args);
            return $object;
        });
     
    // créons la classe $string
    $string = new Object($class);
     
    // ajoutons lui quelques méthodes d'instance...
    $string->prototype->construct = function ($that, $str = "") {
        $that->str = $str;
    };
    $string->prototype->toString = function ($that) {
        return (string)$that->str;
    };
    $string->prototype->toUpperCase = function ($that) use (&$string) {
        return $string->new(strtoupper($that->str));
    };
    $string->prototype->toLowerCase = function ($that) use (&$string) {
        return $string->new(strtolower($that->str));
    };
     
    // ...et quelques méthodes de classes
    // notez qu'elles portent le même nom
    $string->toUpperCase = function ($that, $str) use (&$string) {
        return $string->new($str)->toUpperCase();
    };
    $string->toLowerCase = function ($that, $str) use (&$string) {
        return $string->new($str)->toLowerCase();
    };
     
    // les méthodes de classes s'utilisent un peu de la même façon qu'en OOP classique
    echo $string->toUpperCase('lowercase') . "\n"; // affiche "LOWERCASE"
    echo $string->toLowerCase('uppercase') . "\n"; // affiche "uppercase"
     
    // maintenant crééons une instance de $string
    $my_string = $string->new('hello world !');
    echo $my_string->toUpperCase() . "\n"; // affiche "HELLO WORLD !"
    echo $my_string->toUpperCase()->toLowerCase() . "\n"; // affiche "hello world !"
     
    // c'est là que ça devient intéressant, nous allons ajouter une nouvelle
    // méthode d'instance à $string et nous allons l'appeller dans le contexte
    // de $my_string
    $string->prototype->replace = function ($that, $search, $replace) {
        return $that->new(str_replace($search, $replace, $that->str));
    };
     
    try {
        // cet appel va échouer parce que le prototype de $my_string est obsolète
        // car il s'agit d'un clone donc quand $string change, $my_string ne change
        // pas
        echo $my_string->replace('hello', 'strange') . "\n";
    }
    catch (Exception $e) {
        echo "Error: " . $e->getMessage() . "\n";
    }
     
    // corrigeons ça en mettant à jour le prototype de $my_string
    $my_string->prototype = $string->prototype;
     
    // et maintenant nous pouvons faire
    echo $my_string->replace('hello', 'strange')->toUpperCase() . "\n"; // affiche "STRANGE WORLD !"
     
    // on peut aussi appliquer les méthode de $string à des instances qui n'en hérite pas
    $obj_a = Object::createFromArray(array('str' => 'abc'));
    $obj_b = Object::createFromArray(array('str' => 'DEF'));
     
    echo $string->prototype->toUpperCase->call($obj_a) . "\n";
    echo $string->prototype->toLowerCase->call($obj_b) . "\n";

    Plutôt sympa non ? Les développeurs JavaScript reconnaîtrons sûrement cette syntaxe, mais je tiens à rappeller que je me suis inspiré du fonctionnement de JavaScript, ce n'est pas une copie parfaite, loin de là (et je ne pense pas qu'on y arrive correctement avec PHP 5.3).

    Quelques explication s'imposent: l'exemple ci-dessus montre comment créer de nouvelles classes à l'aide d'un objet $class (qui est en fait notre fabrique de classes), cet objet class ne porte que la méthode new qui sert à la création de nouvelles instance (de $class ou de ses filles). On note au passage que c'est le prototype qui est utilisé pour la création de nouvelles instance (en réalité, par clonage du prototype ce qui évite de modifier les membres d'un objet créé si on change sa classe - si c'est au contraire ce que vous voulez, enlevez le mot clé clone). On créé une nouvelle classe en instanciant un Object et en lui passant $class comme prototype, donc votre nouvelle classe ($string dans l'exemple) pourra utiliser new() dans son contexte. Il ne reste qu'a dotter notre nouvelle classe d'un constructeur (la méthode construct) et des méthodes qu'on veut et c'est fini.

    Important: on se place ici dans un contexte PHP 5.3, le support de Closure::bind qui permet d'utiliser le mot clé $this dans une fonction annonyme ne vient qu'avec PHP 5.4. A cet effet vous devez impérativement dotter vos méthodes d'un premier paramètre $that qui tiens lieu de $this pour le corp de la méthode. Dans les faits, quand vous invoquez $objet->method($a,$b,$c), vous invoquez method($objet,$a,$b,$c), la mécanique qui injecte l'instance courante en tant que premier paramètre de la méthode est fait par la méthode Object::__call.

    A noter également qu'a l'instar de l'OOP en JavaScript, on ne dispose plus de visibilité sur les membres, tout deviens public.

    Pour être tout à fait honnête avec vous, je doute moi-même de l'utilité d'un tel concept en PHP vu qu'on ne peut pas sérialiser les Closures (du moins, pas simplement). Donc on perd systématiquement tous nos objets à la fin du script. Enfin, c'était marrant à écrire et si quelqu'un lui trouve une utilité ce sera toujours ça de pris.

    Amusez-vous bien avec cette lib et n'hésitez pas à me faire part de vos retours. Je publierai bientôt le code sur GitHub une fois que j'aurais fini de tester tout ça (eh oui, je n'ai même pas encore écrit mes tests unitaires).
    Fichiers attachés Fichiers attachés

  2. #2
    Expert éminent sénior

    Profil pro
    Inscrit en
    Septembre 2010
    Messages
    7 920
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2010
    Messages : 7 920
    Points : 10 726
    Points
    10 726
    Par défaut
    et juste ça

    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
    class Object
    {
        public function __call($method, $args)
        {
            return call_user_func_array($this->$method, $args);
        }
    }
     
    $object = new Object;
     
    $object->hello = function ($name) {
        echo 'hello ', $name;
    };
     
    echo $object->hello('world');

  3. #3
    Expert éminent
    Avatar de Benjamin Delespierre
    Profil pro
    Développeur Web
    Inscrit en
    Février 2010
    Messages
    3 929
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Février 2010
    Messages : 3 929
    Points : 7 762
    Points
    7 762
    Par défaut
    ça c'est déjà fait

    il manque tout de même un concept pourtant bien pratique: l'héritage

  4. #4
    Expert éminent
    Avatar de Benjamin Delespierre
    Profil pro
    Développeur Web
    Inscrit en
    Février 2010
    Messages
    3 929
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Février 2010
    Messages : 3 929
    Points : 7 762
    Points
    7 762
    Par défaut
    Le projet est désormais disponible sur GitHub

Discussions similaires

  1. Réponses: 4
    Dernier message: 01/04/2009, 20h53
  2. Intel vous avez dit Intel
    Par venomelektro dans le forum OpenGL
    Réponses: 7
    Dernier message: 14/10/2004, 20h25

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