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 :

Surcharge de méthode/classe


Sujet :

Langage PHP

  1. #1
    Membre régulier Avatar de Jiraiya42
    Profil pro
    Inscrit en
    Mai 2005
    Messages
    671
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 671
    Points : 114
    Points
    114
    Par défaut Surcharge de méthode/classe
    Salut,

    Je ne poste pas souvent pour demander de l'aide car j'arrive souvent à trouver une solution en creusant dans mon coin.
    Là, par contre, je suis totalement bloqué et je ne vois pas de solution.

    Je sais qu'en faisant hériter une classe d'une autre, il est possible de redéfinir une méthode.
    Je sais que PHP ne permet pas de surcharger une méthode d'une classe.

    Bien entendu, je me retrouve dans le second cas.

    Prenons l'exemple de deux classes :
    - Utilisateur
    - UtilisateurSurcharge

    Initialement, je définis une méthode vConnexion dans Utilisateur.
    Ensuite, je définis une méthode vConnexion dans UtilisateurSurcharge. C'est cette dernière que je souhaite utiliser.
    Il n'y a pas de notion d'héritage entre les deux car je ne souhaite pas redéfinir tous mes appels. Par exemple, dans mon code, je fais des new Utilisateur et je ne veux pas à avoir à passer dans tous le code.
    Cela ne me dérangerait pas ponctuellement mais le but est de généraliser le fonctionnement.

    Est-ce que quelqu'un aurait déjà mis en place un système permettant de pallier à ce manque de surcharge svp ? Même si cela n'est pas natif, un moyen malin de simuler la surcharge ?

    Merci de votre aide et de votre lecture
    "Vous qui entrez ici, abandonnez toute espérance." Dante

  2. #2
    Modérateur

    Avatar de MaitrePylos
    Homme Profil pro
    DBA
    Inscrit en
    Juin 2005
    Messages
    5 496
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : Belgique

    Informations professionnelles :
    Activité : DBA
    Secteur : Service public

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 496
    Points : 12 596
    Points
    12 596
    Par défaut
    Je ne comprend pas bien ta question car ceci fonctionne

    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
     
    <?php
    class a {
     
     
        function go(){
            echo 'go';
        }
     
        function start(){
     
            echo 'start';
        }
     
    }
     
    class b extends a {
     
        function start()
        {
     
            echo 'Run';
        }
     
     
     
    }
     
     
    $b = new b();
    $b->go(); // affiche go
    $b->start(); //affiche Run

  3. #3
    Modérateur
    Avatar de grunk
    Homme Profil pro
    Lead dév - Architecte
    Inscrit en
    Août 2003
    Messages
    6 691
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Lead dév - Architecte
    Secteur : Industrie

    Informations forums :
    Inscription : Août 2003
    Messages : 6 691
    Points : 20 222
    Points
    20 222
    Par défaut
    Je vois 2 solutions pour palier à l'absence de surcharge.

    1- Passer un tableau d'argument au lieu d'une liste d'argument et donc d'appeller la bonne méthode privé dans la classe.
    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
    class MaClasse
    {
        public function maFunction(array $param)
        {
            $nb = count($param);
            if($nb == 1)
                $this->maFunctionUn($param[0]);
            else if ($nb == 2)
                $this->maFunctionDeux($param[0],$param[1]);
     
        }
     
        private function maFunctionUn($param){}}
     
        private function maFunctionDeux($param1,$param2){}
    }

    2- Compter directement le nombre de paramètre passés :

    avec
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    $total = func_num_args();
    $args = func_get_args();
    @MaitrePylos , tu fais de la redéfinition dans ton exemple , pas de la surcharge
    Pry Framework php5 | N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  4. #4
    Membre régulier Avatar de Jiraiya42
    Profil pro
    Inscrit en
    Mai 2005
    Messages
    671
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 671
    Points : 114
    Points
    114
    Par défaut
    Merci de vos réponses.

    @MaitrePylos : Il s'agit d'une redéfinition de méthode liée à l'héritage, cela ne convient donc pas mais merci.

    @grunk: Cela ne me parait pas non plus satisfaisant car si ma méthode de surcharge contient le même nombre de paramètres cela ne fonctionne pas.
    De plus, comme indiqué, je souhaite généraliser le fonctionnement à toutes les méthodes de toutes les classes et cela ne m'inspire que très peu d'injecter une portion de code dans chaque.
    "Vous qui entrez ici, abandonnez toute espérance." Dante

  5. #5
    Membre émérite

    Profil pro
    Inscrit en
    Mai 2008
    Messages
    1 576
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 1 576
    Points : 2 440
    Points
    2 440
    Par défaut
    Peux-tu expliquer clairement ce que tu veux?

    Surtout ceci:
    Il n'y a pas de notion d'héritage entre les deux car je ne souhaite pas redéfinir tous mes appels. Par exemple, dans mon code, je fais des new Utilisateur et je ne veux pas à avoir à passer dans tous le code.
    S'il n'y a pas d'héritage, quel est le problème?

  6. #6
    Modérateur
    Avatar de grunk
    Homme Profil pro
    Lead dév - Architecte
    Inscrit en
    Août 2003
    Messages
    6 691
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Lead dév - Architecte
    Secteur : Industrie

    Informations forums :
    Inscription : Août 2003
    Messages : 6 691
    Points : 20 222
    Points
    20 222
    Par défaut
    Citation Envoyé par Jiraiya42 Voir le message
    si ma méthode de surcharge contient le même nombre de paramètres
    Si tu as le même nombre de paramètre et les même type ce n'est plus une surcharge

    Je fait pas de test sur les type dans mon exemple mais c'est bien évidemment possible.

    Y'a pas d'autre solution pour émuler la surcharge
    Pry Framework php5 | N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  7. #7
    Membre régulier Avatar de Jiraiya42
    Profil pro
    Inscrit en
    Mai 2005
    Messages
    671
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 671
    Points : 114
    Points
    114
    Par défaut
    Oui je ne suis peut-être pas assez clair donc je vais reprendre en prenant un cas précis.

    Lors d'un développement précédent, nous avons créé une classe Utilisateur possédant différentes méthodes, variables.
    Une de ces méthodes est vConnexion permettant de se connecter à la base de donnée.
    Il faut bien garder à l'esprit que ce module utilisateur est terminé et qu'il ne doit pas être modifié pour le développement spécifique.

    Je veux donc surcharger ma méthode Utilisateur::vConnexion pour l'adapter à mon développement spécifique.
    J'ai donc besoin de créer une classe de surcharge UtilisateurSurcharge et je crée ma méthode vConnexion dedans.

    Pour rappel, dans mon module initial, l'instanciation de ma classe se fait toujours de la manière suivante : new Utilisateur() et donc non pas new UtilisateurSurcharge() : voici donc le premier problème.
    Il faudrait donc que l'instanciation via new Utilisateur() soit interceptée pour faire un new UtilisateurSurcharge() à la place

    Quand je dis qu'il n'y a pas d'héritage c'est que UtilisateurSurcharge ne peut hériter de Utilisateur puisque je ne l'appelle jamais directement.

    Suis-je plus clair ?
    "Vous qui entrez ici, abandonnez toute espérance." Dante

  8. #8
    Membre émérite

    Profil pro
    Inscrit en
    Mai 2008
    Messages
    1 576
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 1 576
    Points : 2 440
    Points
    2 440
    Par défaut
    C'est pour ça que je recommande toujours de créer une interface même quand on fait une seule classe, et ne pas utiliser new() mais plutôt d'injecter un objet créé par un factory et on me dit souvent que c'est de l'overengineering. Une interface t'aurait aidée ici.

    Tout ça ne t'aide pas évidemment si tu ne peux modifier ni la classe Utilisateur ni le code qui utilise la classe Utilisateur. Peut-être que runkit peut t'aider, notamment runkit_method_redefine mais ça implique de pouvoir installer une extension pecl.

  9. #9
    Membre émérite

    Profil pro
    Inscrit en
    Mai 2008
    Messages
    1 576
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 1 576
    Points : 2 440
    Points
    2 440
    Par défaut
    Tu peux aussi essayer http://antecedent.github.io/patchwork/, qui fait la même chose que runkit_method_redefine, mais sans extension. Bien évidemment, le recours à des modifications dynamiques de code de ce genre apporte son lot d'embêtement si on les utilise excessivement.

  10. #10
    Membre régulier Avatar de Jiraiya42
    Profil pro
    Inscrit en
    Mai 2005
    Messages
    671
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 671
    Points : 114
    Points
    114
    Par défaut
    Alors là tu as grandement piqué ma curiosité ! Peux-tu m'en dire plus concernant ta solution via les interfaces stp ?

    Si c'est une modif structurelle applicable à tous les modules je peux faire une maintenance et définir le fonctionnement comme norme de codage. Après tout dépend si la solution est très contraignante pour les développeurs...
    "Vous qui entrez ici, abandonnez toute espérance." Dante

  11. #11
    Membre émérite

    Profil pro
    Inscrit en
    Mai 2008
    Messages
    1 576
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 1 576
    Points : 2 440
    Points
    2 440
    Par défaut
    Cherche "Inversion de dépendance" - et pas "injection de dépendance", qui n'a rien à voir. Le principe est d'utiliser des abstractions (classes abstraites, interfaces) plutôt que des classes concrètes lorsqu'on manipule des dépendances.

    Supposons que tu as une entité (au sens global, métier, pas au sens "entity" de MVC) qui s'appelle User. Le premier réflexe est de créer une classe concrète User, mais ça t'enferme dans une implémentation concrète, comme tu as pu expérimenter.

    Au lieu de créer une classe concrète User, tu crées une interface User.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    interface User
    {
        public function vConnexion();
    }
    Ensuite, tu crées une classe concrète normale, par exemple BasicUser. C'est l'équivalent de ta 1e classe Utilisateur.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    class BasicUser implements User
    {
        public function vConnexion()
        {
            //traitement spécifique à Basic User
        }
    }
    Maintenant, tu utilises bien l'utilisateur dans une autre classe, par exemple la classe Foo et sa méthode processUser. Sauf qu'au lieu de passer une instance de BasicUser dans la signature, tu passe une interface. processUser accepte donc n'importe quel objet pourvu qu'il implémente User, et appelera la méthode vConnexion() de cet objet, sans savoir quel est son type réel.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class Foo
    {
        public function processUser(User $user)
        {
            $vconnexion = $user->vConnexion();
            //etc...
        }
    }
    Tu fais après comme ceci dans le code qui utilise Foo:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    $foo = new Foo;
    $user = new BasicUser;
    $foo->processUser($user);
    Supposons après que les besoins changent et que tu doives modifier vConnexion. Eh bien, tu ne le fais pas, tu crées une autre classe qui implémente de BasicUser à la place
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class AdvancedUser implements User
    {
        public function vConnexion()
        {
            //traitement spécifique à Advanced User
        }
    }
    Après, tu n'est plus obligé de modifier la classe Foo puisqu'elle accepte n'importe quel objet ayant l'interface User. Tu dois juste modifier le code qui utilise Foo
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    $foo = new Foo;
    $user = new AdvancedUser;
    $foo->processUser($user);
    C'est bien, mais ça t'oblige encore à changer manuellement ce code à chaque nouvelle classe. D'où l'utilisation d'une factory, qui est une usine à fabriquer les Users.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class UserFactory
    {
     
        public function  make($type)
        {
            return new $type;
        }
    }
    Bien évidemment ceci est une version extrêmement simplifiée et sans contrôle d'erreur, vérification etc... une vrai factory fait et vérifie plus de choses (par exemple si la classe à créer existe bien).

    Du coup, ton code devient:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    $foo = new Foo;
    $type = 'AdvancedUser'; //<- fichier de config, database, etc...
    $user = (new UserFactory)->make($type); 
    $foo->processUser($user);
    L'astuce, c'est que tu peux obtenir la valeur de $type depuis un fichier de configuration, une base de données, des règles métiers, etc... Donc tu n'as plus besoin de modifier ton code, la nature de User peut être changée sans toucher à ton code.

  12. #12
    Membre régulier Avatar de Jiraiya42
    Profil pro
    Inscrit en
    Mai 2005
    Messages
    671
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 671
    Points : 114
    Points
    114
    Par défaut
    Merci pour le détail de ta réponse.

    Je suis beaucoup moins emballé du coup car il me semble que la mise en place sera quelque peu laborieuse pour chaque module...
    Concernant le patchwork, ce pourrait être une solution, après je crains pour les performances...
    "Vous qui entrez ici, abandonnez toute espérance." Dante

  13. #13
    Membre émérite

    Profil pro
    Inscrit en
    Mai 2008
    Messages
    1 576
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 1 576
    Points : 2 440
    Points
    2 440
    Par défaut
    Oui, c'est des décisions qu'il faut adopter à l'avance, avant l'écriture. Evidemment, au moment où on n'en a pas besoin, ce qui pousse certains à ne pas les faire parce que c'est une perte de temps. Jusqu'au jour où...

    Autrement, si tu peux poster des exemples de code concret, on trouvera peut-être d'autres solutions?

  14. #14
    Membre régulier Avatar de Jiraiya42
    Profil pro
    Inscrit en
    Mai 2005
    Messages
    671
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 671
    Points : 114
    Points
    114
    Par défaut
    D'après ce que tu me dis, est-ce que la solution suivante correspondrait à mon besoin :

    ControleurPrincipal
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class ControleurPrincipal
    {
        protected function oNewClass($szNomClasse = '')
        {
            $aClasse = explode('\\', $szNomClasse);
     
            if (class_exists($szNomClasse.'Surcharge')) {
                $szNomClasse .= 'Surcharge';
            }
     
            return new $szNomClasse;
        }
    }
    UtilisateurAction
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class Utilisateur extends ControlleurPrincipal
    {
        protected function vConnexion()
        {
            $oUtilisateur = $this->oNewClass('\APP\Modules\Authentification\Models\Utilisateur');
        }
    }
    UtilisateurSurcharge
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class UtilisateurSurcharge extends Utilisateur
    {
        protected function vConnexion()
        {
            // Connexion...
        }
    }
    Ça à l'air de bien fonctionner... il y a un truc que je ne comprends pas : si je mets l'alias de mon use (namespace) en tant que paramètre de oNew, il ne connait pas la classe alors que si je l'écris en dur il la connait. On dirait qu'à partir du moment où je fais un new $variable le contenu n'est pas interprété de la même manière... Quelqu'un a une piste ?

    J'avance enfin grâce à ta piste ! Je ne suis pas très loin d'obtenir une solution satisfaisante !! Il faut que je supprime l'appel au namespace complet en arrivant à utiliser l'alias du use et ça me parait pas mal. Après il faudra que j'en discute avec l'équipe mais de mon côté ça me parait bien plus propre que toutes les solutions/bidouilles trouvées...

    Si tu as une solution pour l'alias je suis preneur ! Je vais fouiller de mon côté, merci encore.
    "Vous qui entrez ici, abandonnez toute espérance." Dante

  15. #15
    Membre régulier Avatar de Jiraiya42
    Profil pro
    Inscrit en
    Mai 2005
    Messages
    671
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 671
    Points : 114
    Points
    114
    Par défaut
    Non ça n'ira pas non plus.

    En plus de ce problème, j'ai omis le passage de paramètres au constructeur de ma classe. Il va falloir ajouter les paramètres du constructeur dans celui de mon oNew, ça va être crade.
    "Vous qui entrez ici, abandonnez toute espérance." Dante

  16. #16
    Membre émérite

    Profil pro
    Inscrit en
    Mai 2008
    Messages
    1 576
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 1 576
    Points : 2 440
    Points
    2 440
    Par défaut
    Pour l'alias, c'est normal, puisque tu n'as pas déclaré l'alias APP\Modules\Authentification\Models\UtilisateurSurcharge avec "use" avant.

    Tu peux simplifier ça dans UtilisateurAction avec ceci (PHP 5.5 et +)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    $oUtilisateur = $this->oNewClass(Utilisateur::class);
    Ça t'évite de saisir le namespace au complet.

    Ta méthode oNewClass est une factory method, quel est le format des paramètres du constructeur de UtilisateurSurcharge? En quoi ça devient plus crade?

  17. #17
    Membre régulier Avatar de Jiraiya42
    Profil pro
    Inscrit en
    Mai 2005
    Messages
    671
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 671
    Points : 114
    Points
    114
    Par défaut
    Pour l'alias du namespace, j'ai défini le use justement et ça ne fonctionne pas...
    C'est justement pour ça que je trouve que c'est bizarre, j'ai l'impression qu'il n'est pas interprété de la même manière.

    Pour les paramètres, j'ai fais un truc de ce genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    $objUtilisateur = $this->oNew('AuthUtilisateur', array($szParam1, $szParam2));
    Et dans le oNew, j'ai fait :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    $reflected_class = new \ReflectionClass($szNomClasse);
    $szNomClasse = $reflected_class->newInstanceArgs($aParametres);
    Je ne pense que je pourrais avoir mieux que ça, en tout cas je n'ai pas d'autre idée...
    "Vous qui entrez ici, abandonnez toute espérance." Dante

Discussions similaires

  1. template class & surcharge de méthode
    Par Gébix dans le forum C++
    Réponses: 2
    Dernier message: 27/12/2011, 00h38
  2. [1.x] surcharger la méthode save() de la classe du modèle
    Par Mich972 dans le forum Symfony
    Réponses: 2
    Dernier message: 23/08/2010, 17h26
  3. Surcharger les méthodes d'une class mère
    Par Djobird dans le forum Langage
    Réponses: 8
    Dernier message: 06/06/2008, 16h54
  4. [Custom Tags] Problème avec une surcharge de méthode
    Par Strab dans le forum Taglibs
    Réponses: 19
    Dernier message: 26/08/2005, 16h34
  5. Comment surcharger la méthode OnClose d'un TFrame ?
    Par sdebrois dans le forum Composants VCL
    Réponses: 2
    Dernier message: 17/01/2005, 20h57

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