web-dev-qa-db-fra.com

Laravel: Différence App :: bind et App :: singleton

Je suis un peu confus sur toutes les belles choses laravel a à offrir en termes de IOC conteneur et façades. Comme je ne suis pas un programmeur expérimenté, il devient accablant d'apprendre.

Je me demandais quelle est la différence entre ces deux exemples:

  1. Une façade à 'Foo' et enregistrée dans le container via App::bind()

  2. Une façade à 'Foo' et enregistrée dans le container via App::singleton()

Dans ma meilleure compréhension, Foo::method() sera réécrit en $app->make['foo']->method() donc dans le premier exemple, plusieurs instances de la classe Foo seront créées et dans le deuxième exemple, car il est lié via une App::singleton(), la même instance de Foo sera renvoyée à chaque appel d'une méthode sur cet objet.

Je suis désolé si la réponse à cette question est trop évidente, mais je ne trouve aucune confirmation à ce sujet et nulle part cela n'est clairement expliqué.

59
Luuk Van Dongen

C'est exactement comme ça.

Une preuve très simple est de tester le bevahior. Puisque l'application Laravel étend simplement Illuminate\Container\Container, nous n'utiliserons que le conteneur (dans mon cas, je n'ai même ajouté le conteneur que comme dépendance à mon composer.json) pour tester.

require __DIR__ . '/vendor/autoload.php';

class FirstClass
{
    public $value;
}

class SecondClass
{
    public $value;
}

// Test bind()
$container = new Illuminate\Container\Container();

$container->bind('FirstClass');

$instance = $container->make('FirstClass');
$instance->value = 'test';

$instance2 = $container->make('FirstClass');
$instance2->value = 'test2';

echo "Bind: $instance->value vs. $instance2->value\n";

// Test singleton()
$container->singleton('SecondClass');

$instance = $container->make('SecondClass');
$instance->value = 'test';

$instance2 = $container->make('SecondClass');
$instance2->value = 'test2'; // <--- also changes $instance->value

echo "Singleton: $instance->value vs. $instance2->value\n";

Le résultat est comme prévu:

Bind: test vs. test2

Singleton: test2 vs. test2

Cela peut être une sale preuve, mais en effet, c'est une preuve.

Toute la magie réside dans le Container::make méthode. Si la liaison est enregistrée comme partagée (ce qui signifie en tant que singleton), l'instance de classe est renvoyée, sinon une nouvelle instance à chaque fois.

Source: https://github.com/laravel/framework/blob/4.2/src/Illuminate/Container/Container.php#L442

BTW, Container::singleton est le même que Container::bind avec le troisième paramètre défini sur true.

65
niclasleonbock

Les façades fonctionnent en tant que singleton, même si la liaison sous-jacente n'est pas un singleton.

Disons que vous avez:

$app->bind('foo', 'FooConcrete'); // not a singleton

et:

class Foo extends \Illuminate\Support\Facades\Facade {
    protected static function getFacadeAccessor() { return 'foo'; }
}

Ensuite, cela créera 2 instances de FooConcrete, comme d'habitude:

app('foo');
app('foo');

Mais cela ne créera qu'une seule instance de FooConcrete et la réutilisera:

Foo::someMethod();
Foo::someMethod();

C'est parce que resolveFacadeInstance() stocke les instances résolues.


Il y a cependant une exception. La plupart du temps, la getFacadeAccessor() définie renvoie une chaîne , comme illustré ci-dessus, mais elle peut également renvoyer une chaîne objet . Exemple de la façade Schema:

protected static function getFacadeAccessor() {
    return static::$app['db']->connection()->getSchemaBuilder();
}

Dans un tel cas, resolveFacadeInstance() ne stocke pas l'instance.

Donc, si getFacadeAccessor() renvoie une nouvelle instance, chaque appel à la façade crée également une nouvelle instance.

14
Gras Double

Mais quelque part, j'ai lu que Laravel traite les classes appelées via les façades toujours comme des singletons?

De ce fait, j'ai rencontré ce problème:

J'ai une classe de démonstration normalement liée via

$ this-> app-> bind ('demo', function () {return new Demo ();}

Une mise en place d'une façade

fonction statique protégée getFacadeAccessor () {return 'demo'; }

La classe elle-même ressemble à ceci

démonstration de classe 
 {
 
 private $ value1; 
 privé $ valeur2; 
 
 fonction publique setVal1 ($ value) 
 {
 $ this-> value1 = $ value; 
} 
 
 fonction publique setVal2 ($ value) 
 {
 $ this-> value2 = $ value; 
} 
 
 getVals de fonction publique ( ) 
 {
 renvoie 'Val 1:'. $ this-> value1. "Val 2:". $ this-> value2; 
} 
 
}

Vous m'avez dit que si j'utilisais une façade sur cette classe, cela instancierait un objet de la classe et ensuite appeler la méthode sur cet objet.

Mais j'ai testé un peu plus et j'ai trouvé ce comportement très étrange (du moins pour moi):

Si je fais

Demo :: setVal1 ('13654');
Demo :: setVal2 ('chaîne aléatoire')

Je ne devrais pas être en mesure d'utiliser Demo :: getVals () pour récupérer les valeurs que je viens de créer, dois-je? Étant donné que chaque fois qu'une méthode de façade est utilisée, un nouvel objet sera instancié et comment un objet peut-il récupérer les propriétés d'un autre objet? Il devrait y avoir trois instances différentes mais je suis toujours en mesure de récupérer les propriétés de ces autres instances ...

3
Luuk Van Dongen