68

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.

I'm sorry if the answer to this question is to obvious, but I can't find any confirmation on this matter and nowhere this is clearly explained.

Luuk Van Dongen
  • 2,131
  • 5
  • 20
  • 37

3 Answers3

78

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.

Justine M.
  • 320
  • 5
  • 21
niclasleonbock
  • 916
  • 10
  • 8
  • 1
    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? – Luuk Van Dongen Aug 10 '14 at 21:58
  • 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. – niclasleonbock Aug 11 '14 at 06:25
  • 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? – Luuk Van Dongen Aug 13 '14 at 23:32
  • 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](http://laravel.com/docs/facades#facade-class-reference)). – niclasleonbock Aug 29 '14 at 20:42
  • Can you give a real life example on bind and singleton difference? – subdesign Apr 29 '16 at 10:32
  • 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. https://github.com/laravel/framework/blob/5.2/src/Illuminate/Database/DatabaseServiceProvider.php#L50 – niclasleonbock May 14 '16 at 05:23
  • 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. – Yevgeniy Afanasyev Jul 31 '18 at 03:50
  • 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? – Amit Shah Oct 13 '18 at 06:35
  • It's singleton per http request, not per user or even laravel instance. – niclasleonbock Oct 13 '18 at 06:47
14

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.

Gras Double
  • 14,028
  • 7
  • 52
  • 50
  • 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. – Mohyaddin Alaoddin Oct 26 '16 at 11:52
  • 1
    You should rather use `clearResolvedInstance()` (no "s") to clear only what you need, not everything. Nevertheless, you'd be better just returning an instance. – Gras Double Oct 26 '16 at 15:54
4

But somewhere I read that Laravel treats classes called via facades always as singletons?

Thereby, I encountered this problem:

I have a demo class normally bound via

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

An sett up a facade

protected static function getFacadeAccessor() { return 'demo'; }

The class itself looks like this

class Demo 
    {

        private $value1;        
        private $value2;        

        public function setVal1($value)
        {
            $this->value1 = $value;
        }

        public function setVal2($value)
        {
            $this->value2 = $value;
        }

        public function getVals()
        {
            return 'Val 1: ' . $this->value1 . ' Val 2: ' . $this->value2;
        }   

    }

You told me that if I would use a facade on this class, it would instantiate an object of the class and then call the method on that object.

Butt I tested some more and found this very strange (at least to me) behavior:

If I do

Demo::setVal1('13654');
and
Demo::setVal2('random string')

I shouldn't be able to use Demo::getVals() to retrieve the values I just created, should I? Since every time a facade method is used a new object will be instantiated and how can one object retrieve properties of another object? There should be three different instances but still I'm able to retrieve the properties from those other instances...

Dushyant Joshi
  • 3,609
  • 2
  • 23
  • 47
Luuk Van Dongen
  • 2,131
  • 5
  • 20
  • 37
  • 1
    Gras Double answers this questions well in his solution... "Facades do work as singleton, even if the underlying binding is not a singleton." So when you're using facades, it doesn't matter if you used bind() or singleton(), either way the behavior is the same. The difference only matters if you're creating new objects, using App::make() for example. – orrd Jan 26 '15 at 00:10