Injection of attributes
Take read as an example, what does Yii do behind the scenes if it accesses $Component->property1? Take a look at Yii\base\component::__get ()
Public Function __get ($name)
{
$getter = ' Get '. $name;
if (Method_exists ($this, $getter)) {
return $this $getter ();
} else {
Note that the content of this else branch is exactly the same as the Yii\base\object::__get ()
The difference
$this->ensurebehaviors ();
foreach ($this->_behaviors as $behavior) {
if ($behavior->cangetproperty ($name)) {
The property must be public in the behavior. Otherwise it is impossible to access it through the following form.
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 differences between Yii\base\compoent::__get () and Yii\base\object::__get (). That is, for processing after the Getter function is undefined, yii\base\object throws the exception directly, telling you that the property you want to access does not exist. But yii\base\component is the attribute of the behavior that is not injected after the getter is not present:
First, the $this->ensurebehaviors () is called. This method has been described earlier, mainly to ensure that the behavior is bound.
After ensuring that the behavior has been bound, start traversing the $this->_behaviors. Yii stores the behavior of all binding classes in the yii\base\compoent::$_behaviors[] array.
Finally, the behavior of Cangetproperty () determines whether this property 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 Cangetproperty () already in: Ref::p The Roperty section has been briefly described, followed by a targeted introduction.
For setter, the code is similar, here is no space.
Injection of methods
Similar to the injection of attributes through the __get () __set () Magic method, Yii implements the injection of the methods in the behavior by means of the __call () Magic Method:
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 can be seen from the above code, YII first calls the $this->ensurebehaviors () to ensure that the behavior is bound.
Then, it is also the traversal yii\base\component::$_behaviros[] array. Determine whether a method exists by using the Hasmethod () method. If the method being invoked in the bound behavior exists, it is invoked using PHP's Call_user_func_array (). As for the Hasmethod () method, we'll talk about it later.
Access control for injected properties and methods
In the preceding section we cite specific examples of whether public and private, protected members in the behavior are accessible in the class being bound. Here we parse the reason from the code level.
In the above content, we know that an attribute can be inaccessible, mainly looking at the behavior of Cangetproperty () and Cansetproperty (). And a method can not be called, mainly see the behavior of the Hasmethod (). Since Yii\base\behavior inherits from our old friend Yii\base\object, the three judgments mentioned above are in fact the code in Object. Let's look at it one by one:
Public Function Cangetproperty ($name, $checkVars = True)
{
Return method_exists ($this, ' get '. $name) | | $checkVars &&
Property_exists ($this, $name);
}
Public Function Cansetproperty ($name, $checkVars = True)
{
Return method_exists ($this, ' Set '. $name) | | $checkVars &&
Property_exists ($this, $name);
}
Public Function Hasmethod ($name)
{
Return method_exists ($this, $name);
}
These three methods are really not 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 this member variable to pass the above judgment, at this point, the member variable can be public, private, protected. However, only the member variables of public can be accessed correctly. The reason is explained in the above-mentioned principle of injection.
When calling a method of the behavior of the component binding, if the behavior already defines the method, it can be judged by the above. At this point, this method can be public, private, protected. However, only public methods can be called correctly in the end. If you understand the reason for the previous paragraph, it will be understood here.
Dependency Injection Container
A dependency injection (Dependency Injection,di) container is an object that knows how to initialize and configure the object and all of its dependent objects. Martin's article has explained why DI containers are useful. Here we mainly explain the use of the DI container provided by YII.
Dependency Injection
Yii provides DI container properties through the Yii\di\container class. It supports several types of dependency injection as follows:
1. Construction method Injection;
2.Setter and attribute injection;
3.PHP callback injection.
4. Construction Method Injection
With the help of parameter type hints, 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 depend on. The container attempts to obtain an instance of the class or interface on which it depends, 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 a dependency or creating a new object, you can provide a configuration that is provided to the container for use by the appropriate Setter or property injection dependency. 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. Callbacks are responsible for resolving dependencies and injecting them appropriately into newly created objects. For example:
$container->set (' Foo ', function () {
return new Foo (new Bar);
});
$foo = $container->get (' foo ');
Registering dependency relationships
Dependencies can be registered with Yii\di\container::set (). Registration uses a dependency name and a 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, a configuration array, or a PHP callback.
$container = new \yii\di\container;
Register a dependency of a similar name, 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 the $container->get (' foo ') to create a Connection instance
$container->set (' foo ', ' yii\db\connection ');
Registering a class with a configuration
When initialized with Get (), the configuration will be used.
$container->set (' yii\db\connection ', [
' DSN ' = ' Mysql:host=127.0.0.1;dbname=demo ',
' Username ' = ' root ',
' Password ' = ',
' CharSet ' = ' utf8 ',
]);
Registering an alias with the configuration of the class
In this case, you need to specify this class with a "class" element
$container->set (' db ', [
' Class ' = ' yii\db\connection ',
' DSN ' = ' Mysql:host=127.0.0.1;dbname=demo ',
' Username ' = ' root ',
' Password ' = ',
' CharSet ' = ' utf8 ',
]);
Registering a PHP callback
Each time a call is $container->get (' db '), the callback function is executed.
$container->set (' db ', function ($container, $params, $config) {
return new \yii\db\connection ($config);
});
Registering a component instance
$container->get (' Pagecache ') will return the same instance each time it is called.
$container->set (' Pagecache ', new Filecache);
Tip: If the dependency name and dependency definitions are the same, you do not need to register the dependency with the DI container.
A dependency that is registered through set () will produce a new instance each time it is used. You can use Yii\di\container::setsingleton () to register a singleton dependency relationship:
$container->setsingleton (' yii\db\connection ', [
' DSN ' = ' Mysql:host=127.0.0.1;dbname=demo ',
' Username ' = ' root ',
' Password ' = ',
' CharSet ' = ' utf8 ',
]);
Resolving dependencies
Once you have registered a dependency, you can use the DI container to create a new object. The container automatically resolves the dependency, instantiating the dependency and injecting the newly created object. The dependency resolution is recursive, and if there are other dependencies in one dependency, the dependencies will be resolved automatically.
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 an alias previously defined
$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 much more work than creating objects. The container first checks the constructor of the class, 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, and the Userfinder class implements the interface and relies on a Connection object. All of these dependencies are defined by the type hint of the class constructor parameter. With the registration of 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);
}
Public Function Finduser ()
{
}
}
Class Userlister 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 the yii.php file is introduced into the application's portal script, Yii creates a DI container. This DI container can be accessed via Yii:: $container. When Yii::createobject () is called, this method actually calls the container's Yii\di\container::get () method to create a new object. As mentioned 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 benefit of using the automatic construction method injection in the DI container. Suppose your controller class relies on some other object, such as a hotel booking service. You can declare dependencies through a constructor parameter, and then let the DI container help you resolve this dependency automatically.
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 into the controller's constructor as a third parameter.
When to register a dependency
Because dependencies need to be addressed when creating new objects, their registrations should be completed as early as possible. The following are recommended practices:
If you are a developer of an application, you can register dependencies in the application's portal script or script introduced by the portal script.
If you are a developer who can redistribute extensions, you can register dependencies in the extended boot class.
Reprinted from QandA. REN
PHP Framework prevents injected code