Explain the attribute injection of component behavior in PHP's Yii framework and the method injection _php technique

Source: Internet
Author: User
Tags dsn yii

Properties of behavior and method of injection principle

We learned from the above that the intent of the act is to inject its own properties and methods into the dependent classes. So how does Yii inject an action-yii\base\behavior attribute and method into a yii\base\component? For attributes, this is done through the __get () and __set () magic methods. For methods, it is through the __call () method.

Injection of attributes

For example, if you visit $Component->property1, what does Yii do behind the scenes? This look at Yii\base\component::__get ()

Public Function __get ($name)
{
  $getter = ' get '. $name;
  if (Method_exists ($this, $getter)) {return
    $this-> $getter ();
  } else {
    //Note the contents of this else branch, exactly with Yii\base\ Object::__get ()
    ///Difference
    $this->ensurebehaviors ();
    foreach ($this->_behaviors as $behavior) {
      if ($behavior->cangetproperty ($name)) {

        //property must be public in the behavior. Otherwise it is not possible to access the form below. return
        $behavior-> $name
  }}} if (method_exists ($this, ' Set '. $name)) {
    throw new invalidcallexception (' Getting write-only property: ').
      Get_class ($this). '::' . $name);
  } else {
    throw new unknownpropertyexception (' Getting unknown property: '.
      Get_class ($this). '::' . $name);
  }


Focus on the difference between Yii\base\compoent::__get () and Yii\base\object::__get (). It's just that for processing after not defining the Getter function, Yii\base\object throws an exception directly, telling you that the property you want to access does not exist. But Yii\base\component has to look at the properties of the injected behavior after the getter is not present:

First, the $this->ensurebehaviors () is invoked. This method has been mentioned earlier, primarily to ensure that the behavior is bound.
After making sure that the behavior is bound, begin traversing the $this->_behaviors. Yii saves the behavior of all bindings of a class in a yii\base\compoent::$_behaviors[] array.
Finally, the property is judged by the Cangetproperty () of the behavior, whether it is a readable property of the bound behavior, and if so, returns this property of the behavior $behavior->name. Completes the read of the property. As for the Cangetproperty () is already in: Ref::p Roperty Part has been simply said, and later there will be a targeted introduction.
For setters, the code is similar, and there is no space.

Injection of methods

The injection with the attribute is similar through the __get () __set () Magic method, in which Yii uses the __call () Magic method to inject the method into the behavior:

Public Function __call ($name, $params)
{
  $this->ensurebehaviors ();
  foreach ($this->_behaviors as $object) {
    if ($object->hasmethod ($name)) {return
      Call_user_func_array ( $object, $name], $params);
    }
  throw new Unknownmethodexception (' Calling unknown Method: '.
    Get_class ($this). ":: $name ()");
}

As you can see from the code above, Yii still invokes the $this->ensurebehaviors () to ensure that the behavior is bound.

Then, too, is the traversal yii\base\component::$_behaviros[] array. The Hasmethod () method is used to determine whether the method exists. If the method being invoked in the bound behavior exists, it is invoked using the Call_user_func_array () of PHP. As for the Hasmethod () method, we'll talk about it later.

Access control of injection properties and methods

In the previous example, we gave specific examples of whether public and private, protected members in the behavior were accessible in the bound class. Here we parse the reason from the code level.

In the above content, we know that an attribute can be inaccessible, primarily to see the behavior of Cangetproperty () and Cansetproperty (). And a method can not be invoked, mainly to see the behavior of the Hasmethod (). Since Yii\base\behavior inherited from our old friend Yii\base\object, the three methods mentioned above, the fact that the code is in Object. Let's take a look at one:

Public Function Cangetproperty ($name, $checkVars = True)
{return
  method_exists ($this, ' get '. $name) | | $checkVa RS &&
    property_exists ($this, $name);
}

Public Function Cansetproperty ($name, $checkVars = True)
{return
  method_exists ($this, ' Set '. $name) | | $checkVa RS &&
    property_exists ($this, $name);
}

Public Function Hasmethod ($name)
{return
  method_exists ($this, $name);
}

These three methods are really far from complicated. In this respect, we can draw the following conclusions:

When a property is read (written) to a component-bound behavior, it can be accessed if the behavior defines a getter (setter) for the property. Alternatively, if the behavior does have the member variable, it can be judged by the above, at which point the member variable is public, private, and protected. But eventually only public member variables can be accessed correctly. The reason for this is explained in the principle of injection.
When you invoke a method of component bound behavior, you can pass the above judgement if the behavior has already defined the method. At this point, this method can be public, private, protected. But ultimately, only public methods can be invoked correctly. If you understand the reason for the last one, then it is understood here.


Dependency Injection Container
a Dependency injection (Dependency Injection,di) container is an object that knows how to initialize and configure objects and all objects they depend on. Martin's article has explained why DI containers are useful. Here we mainly explain the use of DI containers provided by YII.

Dependency Injection

YII provides DI container characteristics through the Yii\di\container class. It supports several types of dependency injection:

    • construction method injection;
    • Setter and attribute injection;
    • PHP callback injection.
    • Construction Method Injection

With the help of the parameter type hint, the DI container implements the construction method injection. When a container is used to create a new object, the type hint tells it what class or interface to rely on. The container attempts to get an instance of the class or interface on which it relies, and then injects it into the new object through the constructor. For example:

Class Foo
{public
  function __construct (Bar $bar)
  {
  }
}

$foo = $container->get (' Foo ');
The above code is equivalent to:
$bar = new Bar;
$foo = new Foo ($bar);

Setter and attribute Injection

Setter and attribute injection are supported through configuration. When registering for a dependency or creating a new object, you can provide a configuration that will be provided to the container to inject dependencies through the appropriate Setter or attribute. For example:

Use Yii\base\object;

Class Foo extends Object
{public
  $bar;

  Private $_qux;

  Public Function Getqux ()
  {return
    $this->_qux;
  }

  Public Function Setqux (Qux $qux)
  {
    $this->_qux = $qux;
  }
}

$container->get (' Foo ', [], [
  ' Bar ' => $container->get (' Bar '),
  ' Qux ' => $container->get (' Qux '),
]);

PHP Callback Injection

In this case, the container will use a registered PHP callback to create a new instance of the class. The callback is responsible for resolving dependencies and injecting them appropriately into the newly created object. For example:

$container->set (' Foo ', function () {return
  new Foo (new Bar);
});

$foo = $container->get (' foo ');

Registration dependencies

You can register dependencies with Yii\di\container::set (). Registration uses a dependency name and the definition of a dependency relationship. A dependency name can be a class name, an interface name, or an alias. The definition of a dependency can be a class name, an array of configurations, or a PHP callback.

$container = new \yii\di\container;
Register a similar name as a dependency, this can be omitted.

$container->set (' yii\db\connection ');
Register an interface//When a class relies on this interface, the corresponding class is initialized as a dependent object.

$container->set (' yii\mail\mailinterface ', ' Yii\swiftmailer\mailer ');
Registers an alias.

You can use $container->get (' foo ') to create a Connection instance $container->set (' foo ', ' yii\db\connection ');
The configuration is used when you initialize a class//by configuring registration by using get (). $container->set (' yii\db\connection ', [' DSN ' => ' Mysql:host=127.0.0.1;dbname=demo ', ' username ' => ' root ', '

Password ' => ', ' CharSet ' => ' UTF8 ',]; Registering an alias through the configuration of a class//In this case, you need to specify this class by a "class" element $container->set (' db ', [' Class ' => ' yii\db\connection ', ' DSN ' =& Gt

' Mysql:host=127.0.0.1;dbname=demo ', ' username ' => ' root ', ' password ' => ', ' CharSet ' => ' UTF8 ',];
Registers a PHP callback//Every time the call to the $container->get (' db '), the callback function is executed.

$container->set (' db ', function ($container, $params, $config) {return new \yii\db\connection ($config);}); Register a component instance//$container->get ('Pagecache ') will return the same instance each time it is invoked.

 $container->set (' Pagecache ', new Filecache);

Tip: If the dependency name and dependency definitions are the same, you do not need to register the dependency through the DI container.
A dependency that is registered through set () produces a new instance each time it is used. You can use Yii\di\container::setsingleton () to register a single instance of dependencies:

$container->setsingleton (' yii\db\connection ', [
  ' DSN ' => ' Mysql:host=127.0.0.1;dbname=demo ',
  ' Username ' => ' root ',
  ' password ' => ', ' CharSet ' => ' UTF8 '
  ,
];

Resolve Dependencies

Once a dependency is registered, you can create a new object using the DI container. The container automatically resolves the dependencies and relies on instantiating and injecting the newly created object. The resolution of dependencies is recursive, and if there are other dependencies in a dependency, those dependencies are automatically resolved.

You can use Yii\di\container::get () to create a new object. The method receives a dependency name, which can be a class name, an interface name, or an alias. The dependency name may be registered through set () or Setsingleton (). You can optionally provide a constructor argument list for a class and a configuration to configure the newly created object. For example:

"DB" is a previously defined alias
$db = $container->get (' db ');

Equivalent to: $engine = new \app\components\searchengine ($apiKey, [' type ' => 1]);
$engine = $container->get (' App\components\searchengine ', [$apiKey], [' type ' => 1]);

Behind the code, the DI container does a lot more work than creating objects. The container first examines the class's construction method, finds the dependent class or interface name, and then automatically recursively resolves the dependencies.

The following code shows a more complex example. The Userlister class relies on an object that implements the Userfinderinterface interface, the Userfinder class implements this interface, and relies on a Connection object. All of these dependencies are defined by the type hint of the class constructor argument. By registering the property dependencies, the DI container can automatically resolve these dependencies and create a new Userlister instance through a simple get (' Userlister ') call.

namespace App\models;
Use Yii\base\object;
Use yii\db\connection;

Use Yii\di\container;

Interface Userfinderinterface {function finduser ();}

  Class Userfinder extends Object implements Userfinderinterface {public $db;
    Public function __construct (Connection $db, $config = []) {$this->db = $db;
  Parent::__construct ($config);

  The Public Function Finduser () {}} class Userlister the extends Object {public $finder;
    Public function __construct (userfinderinterface $finder, $config = []) {$this->finder = $finder;
  Parent::__construct ($config);
}} $container = new container;
$container->set (' yii\db\connection ', [' DSN ' => ' ... ',]);
$container->set (' App\models\userfinderinterface ', [' Class ' => ' App\models\userfinder ',]);

$container->set (' Userlister ', ' app\models\userlister ');

$lister = $container->get (' Userlister ');
Equivalent to: $db = new \yii\db\connection ([' DSN ' => ' ... ']);
$finder = new Userfinder ($DB); $lister =New Userlister ($finder);

 

The application in practice

When a yii.php file is introduced into an application's portal script, Yii creates a DI container. This DI container can be accessed via Yii:: $container. When Yii::createobject () is invoked, this method actually invokes the Yii\di\container::get () method of the container to create a new object. As described above, the DI container automatically resolves the dependency (if any) and injects it into the newly created object. Because Yii uses Yii::createobject () to create new objects in most of its core code, you can customize these objects globally by using Yii:: $container.

For example, you can globally customize the default number of paging buttons in Yii\widgets\linkpager:

\yii:: $container->set (' Yii\widgets\linkpager ', [' Maxbuttoncount ' => 5]);

So if you use this pendant in a view with the following code, its Maxbuttoncount property is initialized to 5 instead of the default value 10 defined in the class.

Echo \yii\widgets\linkpager::widget ();

However, you can still override the values set by the DI container:

Echo \yii\widgets\linkpager::widget ([' Maxbuttoncount ' => 20]);

Another example is the benefits of using automatic construction method injection in the DI container. Suppose your controller class relies on some other object, such as a hotel reservation service. You can declare dependencies through a constructor parameter, and then let the DI container help you automatically resolve the dependency relationship.

namespace App\controllers;

Use Yii\web\controller;
Use App\components\bookinginterface;

Class Hotelcontroller extends Controller
{
  protected $bookingService;

  Public function __construct ($id, $module, Bookinginterface $bookingService, $config = [])
  {
    $this-> Bookingservice = $bookingService;
    Parent::__construct ($id, $module, $config);
  }


If you access this controller from the browser, you will see an error message reminding you that bookinginterface cannot be instantiated. This is because you need to tell the DI container how to handle this dependency.

\yii:: $container->set (' app\components\bookinginterface ', ' app\components\bookingservice ');
Now if you access the controller again, an instance of App\components\bookingservice will be created and injected as the third parameter into the controller's constructor.

When do I register a dependency relationship

Because dependencies need to be resolved when creating new objects, their registrations should be completed as soon as possible. The following are recommended practices:

If you are an application developer, you can register dependencies in the application's portal script or in scripts introduced by the portal script.
If you are a distributable extension developer, you can register dependencies in the extended boot class.
Summary

Dependency Injection and service locator are popular design patterns that allow you to build software in a fully decoupled and test-enabled style. You are strongly recommended to read Martin's article and have a deeper understanding of dependency injection and service locator.

YII implements its service locator on top of a dependency (DI) container. When a service locator attempts to create a new object instance, it forwards the call to the DI container. The latter will automatically resolve dependencies as described earlier.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.