Laravel:差异App :: bind和App :: singleton

时间:2014-08-10 13:28:59

标签: php oop laravel-4 ioc-container facade

我对laravel在IOC容器和外墙方面提供的所有好东西感到有些困惑。由于我不是一位经验丰富的程序员,因此学习起来势不可挡。

我想知道,这两个例子之间的区别是什么:

  1. “Foo”的立面。并通过App::bind()

  2. 在容器中注册
  3. “Foo”的立面。并通过App::singleton()

  4. 在容器中注册

    在我的最佳理解中,Foo::method()将被重写为$app->make['foo']->method(),因此在第一个示例中,将创建Foo类的多个实例,而在第二个示例中,因为它是'通过App::singleton()进行绑定,每次调用该对象上的Method时,都会返回Foo的相同实例。

    对不起,如果这个问题的答案显而易见,但我无法就此问题找到任何确认,而且无法明确解释。

3 个答案:

答案 0 :(得分:57)

就是这样。

一个非常简单的证明是测试bevahior。由于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::singletonContainer::bind相同,第三个参数设置为true。

答案 1 :(得分:14)

外墙确实可以作为单身人士工作,即使底层绑定不是单身人士。

假设你有:

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

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

然后这将像往常一样创建FooConcrete的2个实例:

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都会创建一个新实例。

答案 2 :(得分:3)

但在某处,我读到Laravel将通过外墙调用的课程视为单身人士?

因此,我遇到了这个问题:

我有一个通常通过

绑定的演示类
$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;
        }   

    }

你告诉我如果我在这个类上使用一个Facade,它将实例化该类的一个对象,然后在该对象上调用该方法。

我试了一下,发现这个非常奇怪(至少对我而言)的行为:

如果我

Demo::setVal1('13654');

Demo::setVal2('random string')

我不应该使用Demo :: getVals()来检索我刚创建的值,是吗?由于每次使用facade方法时都会实例化一个新对象,一个对象如何检索另一个对象的属性?应该有三个不同的实例,但我仍然可以从其他实例中检索属性......