web-dev-qa-db-fra.com

Comment obtenir un constructeur de classe PHP pour appeler le constructeur de son parent

J'ai besoin d'un constructeur de classe dans PHP appeler le constructeur parent (grand-parent?) De son parent sans appeler le constructeur parent.

// main class that everything inherits
class Grandpa 
{
    public function __construct()
    {

    }

}

class Papa extends Grandpa
{
    public function __construct()
    {
        // call Grandpa's constructor
        parent::__construct();
    }
}

class Kiddo extends Papa
{
    public function __construct()
    {
        // THIS IS WHERE I NEED TO CALL GRANDPA'S
        // CONSTRUCTOR AND NOT PAPA'S
    }
}

Je sais que c'est une chose bizarre à faire et j'essaie de trouver un moyen qui ne sent pas mauvais mais qui reste néanmoins curieux de savoir si c'est possible.

EDIT

Je pensais que je devrais publier la justification de la réponse choisie. La raison étant; C’est la solution la plus élégante au problème de vouloir appeler le constructeur du "grand-parent" tout en conservant toutes les valeurs. Ce n’est certainement pas la meilleure approche ni OOP amicale, mais ce n’est pas ce que la question demandait.

Pour toute personne rencontrant cette question à une date ultérieure - Veuillez trouver une autre solution . J'ai pu trouver une bien meilleure approche qui n'a pas bouleversé la structure de classe. Alors devriez-vous.

189
Paulo

La solution de contournement laide serait de passer un paramètre booléen à Papa indiquant que vous ne souhaitez pas analyser le code contenu dans son constructeur. c'est à dire:

// main class that everything inherits
class Grandpa 
{
    public function __construct()
    {

    }

}

class Papa extends Grandpa
{
    public function __construct($bypass = false)
    {
        // only perform actions inside if not bypassing
        if (!$bypass) {

        }
        // call Grandpa's constructor
        parent::__construct();
    }
}

class Kiddo extends Papa
{
    public function __construct()
    {
        $bypassPapa = true;
        parent::__construct($bypassPapa);
    }
}
141
Corey Ballou

Vous devez utiliser Grandpa::__construct(), il n'y a pas d'autre raccourci pour cela. En outre, cela ruine l'encapsulation de la classe Papa - lors de la lecture ou du travail sur Papa, il est prudent de supposer que la méthode __construct() sera appelée lors de la construction, mais le Kiddo class ne fait pas cela.

69
too much php
class Grandpa 
{
    public function __construct()
    {}
}

class Papa extends Grandpa
{
    public function __construct()
    {
        //call Grandpa's constructor
        parent::__construct();
    }
}

class Kiddo extends Papa
{
    public function __construct()
    {
        //this is not a bug, it works that way in php
        Grandpa::__construct();
    }
}
51
Alain57

J'ai fini par proposer une solution alternative qui résolve le problème.

  • J'ai créé une classe intermédiaire qui a étendu grand-père.
  • Ensuite, Papa et Kiddo ont étendu cette classe.
  • Kiddo nécessitait des fonctionnalités intermédiaires de Papa mais n'aimait pas son constructeur. La classe a donc cette fonctionnalité supplémentaire et les étend toutes les deux.

J'ai voté pour les deux autres réponses qui fournissaient des solutions valables mais laides pour une question plus laide :)

19
Paulo

Belle solution en utilisant Reflection.

<?php
class Grandpa 
{
    public function __construct()
    {
        echo "Grandpa's constructor called\n";
    }

}

class Papa extends Grandpa
{
    public function __construct()
    {
        echo "Papa's constructor called\n";

        // call Grandpa's constructor
        parent::__construct();
    }
}

class Kiddo extends Papa
{
    public function __construct()
    {
        echo "Kiddo's constructor called\n";

        $reflectionMethod = new ReflectionMethod(get_parent_class(get_parent_class($this)), '__construct');
        $reflectionMethod->invoke($this);
    }
}

$kiddo = new Kiddo();
$papa = new Papa();
18
Aleksei Akireikin

Une autre option qui n’utilise pas d’indicateur et qui pourrait fonctionner dans votre cas:

<?php
// main class that everything inherits
class Grandpa 
{
    public function __construct(){
        $this->GrandpaSetup();
    }

    public function GrandpaSetup(){
        $this->prop1 = 'foo';
        $this->prop2 = 'bar';
    }
}

class Papa extends Grandpa
{
    public function __construct()
    {
        // call Grandpa's constructor
        parent::__construct();
        $this->prop1 = 'foobar';
    }

}
class Kiddo extends Papa
{
    public function __construct()
    {
        $this->GrandpaSetup();
    }
}

$kid = new Kiddo();
echo "{$kid->prop1}\n{$kid->prop2}\n";
16
MitMaro

Je suis d'accord avec "too php too", essayez ceci:

class Grandpa 
{
    public function __construct()
    {
        echo 'Grandpa<br/>';
    }

}

class Papa extends Grandpa
{
    public function __construct()
    {
        echo 'Papa<br/>';
        parent::__construct();
    }
}

class Kiddo extends Papa
{
    public function __construct()
    {
        // THIS IS WHERE I NEED TO CALL GRANDPA'S
        // CONSTRUCTOR AND NOT PAPA'S
        echo 'Kiddo<br/>';
        Grandpa::__construct();
    }
}

$instance = new Kiddo;

J'ai eu le résultat attendu:

Gamin

Grand-père

Ceci est une fonctionnalité et non un bug, vérifiez ceci pour votre référence:

https://bugs.php.net/bug.php?id=42016

C'est juste la façon dont cela fonctionne. S'il voit qu'il vient du bon contexte, cette version de l'appel n'impose pas un appel statique.

Au lieu de cela, il va simplement garder cela et en être heureux.

parent :: method () fonctionne de la même manière, vous n'avez pas à définir la méthode comme statique, mais vous pouvez l'appeler dans le même contexte. Essayez ceci pour plus intéressant:

class Grandpa 
{
    public function __construct()
    {
        echo 'Grandpa<br/>';
        Kiddo::hello();
    }

}

class Papa extends Grandpa
{
    public function __construct()
    {
        echo 'Papa<br/>';
        parent::__construct();
    }
}

class Kiddo extends Papa
{
    public function __construct()
    {
        // THIS IS WHERE I NEED TO CALL GRANDPA'S
        // CONSTRUCTOR AND NOT PAPA'S
        echo 'Kiddo<br/>';
        Grandpa::__construct();
    }

    public function hello()
    {
        echo 'Hello<br/>';
    }
}

$instance = new Kiddo;

Cela fonctionne aussi comme prévu:

Gamin

Grand-père

Salut

Mais si vous essayez d’initialiser un nouveau Papa, vous obtiendrez une erreur E_STRICT:

$papa = new Papa;

Normes strictes: La méthode non statique Kiddo :: hello () ne devrait pas être appelée de manière statique, en supposant que $ this provient d'un contexte incompatible

Vous pouvez utiliser instanceof pour déterminer si vous pouvez appeler un Children :: method () dans une méthode parent:

if ($this instanceof Kiddo) Kiddo::hello();
9
Fishdrowned

Il existe une solution plus simple pour cela, mais cela nécessite que vous sachiez exactement combien d'héritage votre classe actuelle a subi. Heureusement, les arguments de get_parent_class () permettent à votre membre du tableau de classe d’être le nom de la classe en tant que chaîne, ainsi que l’instance elle-même.

Gardez à l'esprit que cela implique également d'appeler de manière statique la méthode __construct () d'une classe, bien que, dans la portée instanced d'un objet héritant, la différence dans ce cas particulier soit négligeable (ah, PHP).

Considérer ce qui suit:

class Foo {
    var $f = 'bad (Foo)';

    function __construct() {
        $this->f = 'Good!';
    }
}

class Bar extends Foo {
    var $f = 'bad (Bar)';
}

class FooBar extends Bar {
    var $f = 'bad (FooBar)';

    function __construct() {
        # FooBar constructor logic here
        call_user_func(array(get_parent_class(get_parent_class($this)), '__construct'));
    }
}

$foo = new FooBar();
echo $foo->f; #=> 'Good!'

Encore une fois, ce n'est pas une solution viable pour une situation dans laquelle vous n'avez aucune idée du montant de l'héritage, en raison des limitations de debug_backtrace (), mais dans des circonstances contrôlées, cela fonctionne comme prévu.

8
mway

Vous pouvez appeler Grandpa :: __ construct à partir de votre choix et le mot-clé $ this fera référence à votre instance de classe actuelle. Mais attention, avec cette méthode, vous ne pouvez pas accéder aux propriétés protégées et aux méthodes d'instance en cours à partir de cet autre contexte, uniquement aux éléments publics. => Tout travail et officiellement supporté .

Exemple

// main class that everything inherits
class Grandpa 
{
    public function __construct()
    {
        echo $this->one; // will print 1
        echo $this->two; // error cannot access protected property
    }

}

class Papa extends Grandpa
{
    public function __construct()
    {
        // call Grandpa's constructor
        parent::__construct();
    }
}

class Kiddo extends Papa
{
    public $one = 1;
    protected $two = 2;
    public function __construct()
    {
        Grandpa::__construct();
    }
}

new Kiddo();
7
Xorax

Détail amusant sur php: les classes étendues peuvent utiliser des fonctions non statiques d'une classe parente dans une matière statique. Dehors, vous aurez une erreur stricte.

error_reporting(E_ALL);

class GrandPa
{
    public function __construct()
    {
        print("construct grandpa<br/>");
        $this->grandPaFkt();
    }

    protected function grandPaFkt(){
        print(">>do Grandpa<br/>");
    }
}

class Pa extends GrandPa
{
    public function __construct()
    {   parent::__construct();
        print("construct Pa <br/>");
    }

    public function paFkt(){
        print(">>do Pa <br>");
    }
}

class Child extends Pa
{
    public function __construct()
    {
        GrandPa::__construct();
        Pa::paFkt();//allright
        //parent::__construct();//whatever you want
        print("construct Child<br/>");
    }

}

$test=new Child();
$test::paFkt();//strict error 

Donc, dans une classe étendue (Enfant), vous pouvez utiliser

parent::paFkt(); 

ou

Pa::paFkt();

pour accéder à une fonction parent (ou grandpa) (non privée).

En dehors de la classe

$test::paFkt();

va trow erreur stricte (fonction non statique).

4
Hauke

Ok, encore une autre solution laide:

Créez une fonction dans Papa comme:

protected function call2Granpa() {
     return parent::__construct();
}

Ensuite, dans Kiddo, vous utilisez:

parent::call2Granpa(); // au lieu d'appeler le constructeur dans Papa.

Je pense que cela pourrait fonctionner ... Je ne l'ai pas testé, je ne suis donc pas sûr que les objets soient créés correctement.

J'ai utilisé cette approche mais avec des fonctions non constructeurs.

2
lepe
<?php

class grand_pa
{
    public function __construct()
    {
        echo "Hey I am Grand Pa <br>";
    }
}

class pa_pa extends grand_pa
{
    // no need for construct here unless you want to do something specifically within this class as init stuff
    // the construct for this class will be inherited from the parent.
}

class kiddo extends pa_pa
{
    public function __construct()
    {
        parent::__construct();
        echo "Hey I am a child <br>";
    }
}

new kiddo();
?>

Bien entendu, cela n'implique pas que vous ayez besoin de faire quoi que ce soit dans le concept du pa_pa. En cours d'exécution, cela affichera:

Hey je suis grand papa Hey je suis un enfant

2
Anand P
// main class that everything inherits
class Grandpa 
{
    public function __construct()
    {
        $this->___construct();
    }

    protected function ___construct()
    {
        // grandpa's logic
    }

}

class Papa extends Grandpa
{
    public function __construct()
    {
        // call Grandpa's constructor
        parent::__construct();
    }
}

class Kiddo extends Papa
{
    public function __construct()
    {
        parent::___construct();
    }
}

notez que "___construct" n'est pas un nom magique, vous pouvez l'appeler "doGrandpaStuff".

1
luchaninov
    class Grandpa 
{
    public function __construct()
    {
        echo"Hello Kiddo";
    }    
}

class Papa extends Grandpa
{
    public function __construct()
    {            
    }
    public function CallGranddad()
    {
        parent::__construct();
    }
}

class Kiddo extends Papa
{
    public function __construct()
    {

    }
    public function needSomethingFromGrandDad
    {
       parent::CallGranddad();
    }
}
1
Crazy Alien