Подтвердить что ты не робот

Laravel: Difference App:: bind и приложение:: singleton

Я немного запутался во всех приятных вещах, которые laravel может предложить с точки зрения контейнера и фасадов МОК. Поскольку я не опытный программист, он становится невероятным, чтобы учиться.

Мне было интересно, в чем разница между этими двумя примерами:

  • Фасад к "Foo" и зарегистрированный в контейнере через App::bind()

  • Фасад к 'Foo' и зарегистрированный в контейнере через App::singleton()

В лучшем понимании Foo::method() будет переписано как $app->make['foo']->method(), поэтому в первом примере будут созданы несколько экземпляров класса Foo, а во втором примере, поскольку он связан через App::singleton(), тот же экземпляр Foo будет возвращаться каждый раз, когда вызывается метод для этого объекта.

Прошу прощения, если ответ на этот вопрос очевиден, но я не могу найти подтверждения по этому вопросу, и нигде это не объяснено.

4b9b3361

Ответ 1

Это точно так.

Очень простое доказательство - проверить бевахиор. Поскольку приложение Laravel просто расширяет Illuminate\Container\Container, мы будем использовать только контейнер (в моем случае я даже добавлял контейнер как зависимость от моего composer.json) для тестирования.

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

Результат будет таким, как ожидалось:

Bind: test vs. test2

Singleton: test2 vs. test2

Возможно, это грязное доказательство, но действительно оно одно.

Вся магия лежит в методе Container::make. Если привязка зарегистрирована как shared (что означает singleton), экземпляр класса возвращается, в противном случае новый экземпляр каждый раз.

Источник: https://github.com/laravel/framework/blob/4.2/src/Illuminate/Container/Container.php#L442

BTW, Container::singleton совпадает с Container::bind, а третий параметр равен true.

Ответ 2

Фасады работают как одиночные, даже если базовое связывание не является одиночным.

Скажем, у вас есть:

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

и

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

Затем это создаст 2 экземпляра FooConcrete, как обычно:

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

Но это создаст только один экземпляр FooConcrete и повторное его использование:

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

Это потому, что resolveFacadeInstance() хранит разрешенные экземпляры.


Однако есть исключение. В большинстве случаев определенный getFacadeAccessor() возвращает строку, как показано выше, но также может возвращать объект. Пример из Schema Фасад:

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

В таком случае resolveFacadeInstance() не сохраняет экземпляр.

Итак, если getFacadeAccessor() возвращает новый экземпляр, каждый вызов в Facade также создает новый экземпляр.

Ответ 3

Но где-то я читал, что Ларавель относится к классам, называемым через фасады, всегда в виде одиночек?

Таким образом, я столкнулся с этой проблемой:

У меня есть демонстрационный класс, обычно связанный через

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

Устанавливает фасад

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

Сам класс выглядит так:

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

    }

Вы сказали мне, что если я буду использовать фасад этого класса, он создаст объект класса, а затем вызовет метод на этом объекте.

Butt Я проверил еще несколько и нашел это очень странное (по крайней мере для меня) поведение:

Если я делаю

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

Я не могу использовать Demo:: getVals() для извлечения значений, которые я только что создал, должен ли я? Поскольку каждый раз, когда используется метод фасада, создается экземпляр нового объекта и как один объект может извлекать свойства другого объекта? Там должно быть три разных экземпляра, но все же я могу получить свойства из этих других экземпляров...