Laravel: Difference App::bind and App::singleton
Asked Answered
E

3

82

I get a bit confused over all the nice things laravel has to offer in terms of the IOC container and facades. Since I'm not an experienced programmer it gets overwhelming to learn.

I was wondering, what is the difference between these two examples:

  1. A facade to 'Foo' and registered in the container via App::bind()

  2. A facade to 'Foo' and registered in the container via App::singleton()

In my best understanding Foo::method() will be rewritten as $app->make['foo']->method() so in the first example multiple instances of the Foo class will be created and in the second example, since it's bound via an App::singleton(), the same instance of Foo will be returned every time a Method on that object is called.

Is my assumption correct?

East answered 10/8, 2014 at 13:28 Comment(0)
Y
90

It's exactly like that.

A very simple proof is to test out the behavior. Since the Laravel Application simply extends Illuminate\Container\Container, we'll use just the container (in my case I even only added the container as a dependency to my composer.json) to test.

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";

The result is as expected:

Bind: test vs. test2

Singleton: test2 vs. test2

Might be a dirty proof, but indeed it is one.

All the magic lies in the Container::make method. If the binding is registered as shared (which means as singleton), the class instance is returned, otherwise a new instance every time.

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

BTW, Container::singleton is the same as Container::bind with the third parameter set to true.

Yeargain answered 10/8, 2014 at 15:1 Comment(10)
Thank you very much, this is exactly what I was looking for! Would it be I much to ask for a good resource on when to use singletons and when it's better to instantiate multiple objects?East
If you need the same instance of a class over the whole request, you should consider using the singleton method (e.g. a cart), for everything else bind should just be fine.Yeargain
Correct me if I'm wrong, but if a facade is used on a underlying class that is bound via App::bind(), then Laravel will instantiate a new object of the underlying class? If so, is that object itself accessible since it's instantiated every time a method on the facade is used?East
Sorry for my late response. As the documentation points out facades only "provide a "static" interface to classes that are available in the application's IoC container" (source).Yeargain
Can you give a real life example on bind and singleton difference?Notwithstanding
Let's take a look at the database connection manager. The connection manager registers itself as singleton, because you want keep the connections saved inside of it over the whole request. Let's say you have two databases, using the connection manager you can use them both. Thanks to the singleton registration, these are actually only two connections and not more. Just one for every resolve call and connection to the (always-the-same) manager. github.com/laravel/framework/blob/5.2/src/Illuminate/Database/…Yeargain
Until you assign an instance to the app like so app()->instance(Address::class, $address); after that, you will have this instance back no matter if it was bind or singleton in the begining. But only Until you would not decide to use app()->makeWith(...) in witch case you will make an object for you self no matter if it was bind or singleton or even assigned as instance... there are conditions after conditions... I love Laravel.Calculate
My question on this will sound stupid but still wants to know, is singleton instance is single per http request / per request of a user / for every http reqest from all user? meaning a same singleton instance can be shared between two different http request from single/different end users?Beaumarchais
It's singleton per http request, not per user or even laravel instance.Yeargain
@niclasleonbock, what is scoped binding in service container and what it should be used?Kiley
D
16

Facades do work as singleton, even if the underlying binding is not a singleton.

Let's say you have:

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

and:

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

Then this will create 2 instances of FooConcrete, as usual:

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

But this will create only one instance of FooConcrete and reuse it:

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

It is because resolveFacadeInstance() stores the resolved instances.


There is an exception though. Most of the time the defined getFacadeAccessor() returns a string, as shown above, but it can also return an object. Example from the Schema Facade:

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

In such a case, resolveFacadeInstance() doesn't store the instance.

So if getFacadeAccessor() returns a new instance, each call to the Facade creates a new instance as well.

Devotee answered 29/12, 2014 at 12:53 Comment(2)
That's why if needed you can use clearResolvedInstances() method to remove the stored instances, so if you'd like to get a new object on every call on the Facade you can use that method in the getFacadeAccessor() method right before returning the concrete string.Tannie
You should rather use clearResolvedInstance() (no "s") to clear only what you need, not everything. Nevertheless, you'd be better just returning an instance.Devotee
C
-1

In summary:

bind usage

Use bind when you need a fresh instance of a service on every request or when the service has mutable state.

singleton usage

Use singleton when you want to share the same instance across the application, which can improve performance and reduce resource usage.

But I still had a question: the application reloads on every request, so what difference does it make in practice?

1 - Instance creation

bind Creates a new instance every time the service is resolved within the same request. If the service is requested multiple times during a single request, you will get different instances. singleton: Creates a single instance that is shared across the application’s lifetime, but within the same request, if you resolve the service multiple times, you will get the same instance.

2 - Performance:

If your service is expensive to create (like a service that establishes a database connection or performs complex initializations), using singleton can save on performance by avoiding repeated instantiations.

3 - State management:

If your service maintains state (e.g., caches data, holds configuration), using singleton ensures that all parts of your application use the same state. This can prevent bugs related to inconsistent state when using bind.

Claymore answered 12/7 at 8:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.