Soutenez-nous
Publicité
+ Répondre à la discussion
Affichage des résultats 1 à 4 sur 4
  1. #1
    Expert Confirmé Sénior
    Avatar de Benjamin Delespierre
    Profil pro Benjamin Delespierre
    Développeur Web
    Inscrit en
    février 2010
    Messages
    3 932
    Détails du profil
    Informations personnelles :
    Nom : Benjamin Delespierre
    Âge : 26
    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 932
    Points : 8 705
    Points
    8 705

    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 :
    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
    Modérateur

    Inscrit en
    septembre 2010
    Messages
    7 957
    Détails du profil
    Informations forums :
    Inscription : septembre 2010
    Messages : 7 957
    Points : 9 507
    Points
    9 507

    Par défaut

    et juste ça

    Code :
    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 Confirmé Sénior
    Avatar de Benjamin Delespierre
    Profil pro Benjamin Delespierre
    Développeur Web
    Inscrit en
    février 2010
    Messages
    3 932
    Détails du profil
    Informations personnelles :
    Nom : Benjamin Delespierre
    Âge : 26
    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 932
    Points : 8 705
    Points
    8 705

    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 Confirmé Sénior
    Avatar de Benjamin Delespierre
    Profil pro Benjamin Delespierre
    Développeur Web
    Inscrit en
    février 2010
    Messages
    3 932
    Détails du profil
    Informations personnelles :
    Nom : Benjamin Delespierre
    Âge : 26
    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 932
    Points : 8 705
    Points
    8 705

    Par défaut

    Le projet est désormais disponible sur GitHub

Liens sociaux

Règles de messages

  • Vous ne pouvez pas créer de nouvelles discussions
  • Vous ne pouvez pas envoyer des réponses
  • Vous ne pouvez pas envoyer des pièces jointes
  • Vous ne pouvez pas modifier vos messages
  •