web-dev-qa-db-fra.com

Comment chaîner une méthode sur un objet nouvellement créé?

J'aimerais savoir s'il existe un moyen de chaîner des méthodes sur un objet nouvellement créé en PHP?

Quelque chose comme:

class Foo {
    public function xyz() { ... return $this; }
}

$my_foo = new Foo()->xyz();

Quelqu'un sait-il comment y parvenir?

54
aefxx

Dans PHP 5.4+, l'analyseur a été modifié pour que vous puissiez faire quelque chose comme ça

(new Foo())->xyz();

Enveloppez l'instanciation entre parenthèses et enchaînez. 

Avant PHP 5.4, lorsque vous utilisez le 

new Classname();

syntaxe, vous ne pouvez pas chaîner un appel de méthode hors de l’instanciation. C'est une limitation de la syntaxe de PHP 5.3. Une fois qu'un objet est instancié, vous pouvez chaîner.

Une méthode que j'ai vue utiliser pour contourner ce problème est une méthode d'instanciation statique. 

class Foo
{
    public function xyz()
    {
        echo "Called","\n";
        return $this;
    }

    static public function instantiate()
    {
        return new self();
    }
}


$a = Foo::instantiate()->xyz();

En encapsulant l'appel de new dans une méthode statique, vous pouvez instancier une classe avec un appel de méthode, et vous êtes ensuite libre de chaîner cela.

96
Alan Storm

Définissez une fonction globale comme ceci:

function with($object){ return $object; }

Vous pourrez alors appeler:

with(new Foo)->xyz();
23
Kenaniah

Dans PHP 5.4, vous pouvez chaîner un objet nouvellement instancié:

http://docs.php.net/manual/fr/migration54.new-features.php

Pour les anciennes versions de PHP, vous pouvez utiliser la solution d'Alan Storm.

11
Jackson

Cette réponse est obsolète - voulez donc la corriger.

Dans PHP 5.4.x, vous pouvez chaîner une méthode à un nouvel appel. Prenons cette classe comme exemple:

<?php class a {
    public function __construct() { echo "Constructed\n"; }
    public function foo() { echo "Foobar'd!\n"; }
}

Maintenant, nous pouvons utiliser ceci: $b = (new a())->foo();

Et le résultat est:

Constructed
Foobar'd!

Des informations complémentaires sont disponibles dans le manuel: http://www.php.net/manual/fr/migration54.new-features.php

6
Ingwie Phoenix

Eh bien, c’est peut-être une vieille question, mais comme pour beaucoup de choses en programmation, la réponse change finalement. 

En ce qui concerne PHP 5.3, non, vous ne pouvez pas chaîner directement à partir du constructeur. Cependant, pour développer la réponse acceptée, vous pouvez effectuer les tâches suivantes:

abstract class Foo 
{    
    public static function create() 
    {
        return new static;
    }
}

class Bar extends Foo
{
    public function chain1()
    {
        return $this;
    }

    public function chain2()
    {
        return $this;
    }
}

$bar = Bar::create()->chain1()->chain2();

Cela fonctionnera très bien et vous retournera une nouvelle instance de Bar (). 

Dans PHP 5.4, vous pouvez simplement faire:

$bar = (new Bar)->chain1()->chain2();

Espérons que cela aide quelqu'un à trébucher sur la question comme je l'ai!

3
Lukey

Il serait vraiment utile qu'ils résolvent ce problème dans une prochaine version. J'apprécie vraiment la possibilité de chaîner (surtout lorsque vous remplissez des collections):

J'ai ajouté une méthode à la classe de base de mon framework appelée create () sur laquelle vous pouvez chaîner. Devrait fonctionner avec toutes les classes descendantes automatiquement.

class baseClass
{
    ...
    public final static function create()
    {
        $class = new \ReflectionClass(get_called_class());
        return $class->newInstance(func_get_args());
    }
    ...
    public function __call($method, $args)
    {
        $matches = array();
        if (preg_match('/^(?:Add|Set)(?<prop>.+)/', $method, $matches) > 0)
        {
            //  Magic chaining method
            if (property_exists($this, $matches['prop']) && count($args) > 0)
            {
                $this->$matches['prop'] = $args[0];
                return $this;
            }
        }
    }
    ...
}

Class :: create () -> SetName ('Kris') -> SetAge (36);

1
Kris

Juste pour être complet (et pour le plaisir de le faire ...), personne ne semble avoir mentionné la solution avec le code le plus court (et le moins sophistiqué).

Pour les objets de courte durée fréquemment utilisés, en particulier lors de la rédaction de scénarios de test, où vous effectuez généralement de nombreuses créations d'objet, vous pouvez optimiser pour la commodité de la saisie (plutôt que la pureté) et combiner un peu la méthode de fabrique Foo::instantiate() d'Alan Storm avec la fonction with() globale de Kenaniah technique de fonction.

Il suffit de faire de la méthode usine une fonction globale avec le même nom que la classe! . ; -o (Ajoutez-le comme un wrapper de commodité autour du Foo::instantiate() statique correct ou déplacez-le simplement alors que personne ne le regarde.)

class Foo
{
    public function xyz()
    {
        echo "Called","\n";
        return $this;
    }
}

function Foo()
{
    return new Foo();
}

$a = Foo()->xyz();

REMARQUE:

  • Je ne voudrais pas faire cela sur le code de production. Bien que sexy, c’est un abus des principes de codage de base (comme "principe de moindre surprise" (même s’il s’agit d’une syntaxe plutôt intuitive), ou "ne vous répétez pas", surtout si vous envelopper une méthode Les paramètres qui, BTW, sont déjà un abus de DRY ...), plus PHP peuvent changer dans le futur pour casser un tel code de façon amusante.
0
Sz.