Maison >développement back-end >tutoriel php >Brève analyse du processus en cours d'exécution de Pimple (conteneur PHP)
Points de connaissances requis
Clôture
Forfaits de clôture et anonymes les fonctions ont été introduites dans PHP5.3.0
.
La fermeture fait référence à :
Une fonction qui encapsule l'état environnant lors de sa création. Même si l’environnement dans lequel se situe la fermeture n’existe plus, l’état encapsulé dans la fermeture existe toujours.
Théoriquement, les fermetures et les fonctions anonymes sont des concepts différents. Mais PHP le traite comme le même concept.
En fait, les fermetures et les fonctions anonymes sont des objets déguisés en fonctions. Ce sont des instances de la classe Closure
.
Les fermetures, comme les chaînes et les entiers, sont des types de valeurs de première classe.
Créer une fermeture :
<?php $closure = function ($name) { return 'Hello ' . $name; }; echo $closure('nesfo');//Hello nesfo var_dump(method_exists($closure, '__invoke'));//true
La raison pour laquelle nous pouvons appeler la variable $closure
est que la valeur de cette variable est une fermeture et que l'objet de fermeture implémente __invoke()
Méthode magique. Tant qu'il y a () après le nom de la variable, PHP trouvera et appellera la méthode __invoke()
.
Habituellement, les fermetures PHP sont utilisées comme rappels de fonctions. Les méthodes
array_map()
, preg_replace_callback()
utilisent toutes des fonctions de rappel. C'est le meilleur moment pour utiliser les fermetures !
Par exemple :
<?php $numbersPlusOne = array_map(function ($number) { return $number + 1; }, [1, 2, 3]); print_r($numbersPlusOne);
obtient le résultat :
[2, 3, 4]
Avant les fermetures, vous ne pouviez créer que des fonctions nommées individuellement, puis référencer cette fonction par son nom. En faisant cela, l'exécution du code sera légèrement plus lente et l'implémentation du rappel sera isolée du scénario d'utilisation.
<?php function incrementNum ($number) { return $number + 1; } $numbersPlusOne = array_map('incrementNum', [1, 2, 3]); print_r($numbersPlusOne);
SPL
ArrayAccess
implémente l'interface ArrayAccess
, qui permet aux objets de fonctionner comme des tableaux. L'interface ArrayAccess contient quatre méthodes qui doivent être implémentées :
interface ArrayAccess { //检查一个偏移位置是否存在 public mixed offsetExists ( mixed $offset ); //获取一个偏移位置的值 public mixed offsetGet( mixed $offset ); //设置一个偏移位置的值 public mixed offsetSet ( mixed $offset ); //复位一个偏移位置的值 public mixed offsetUnset ( mixed $offset ); }
SplObjectStorage
SplObjectStorage
La classe implémente une carte avec des objets comme clés ou une collection d'objets ( Si vous ignorez les données correspondant à l'objet comme clé) cette structure de données. Une instance de cette classe ressemble beaucoup à un tableau, mais les objets qu’elle stocke sont tous uniques. Une autre fonctionnalité de cette classe est que vous pouvez directement supprimer l'objet spécifié sans parcourir ni rechercher dans l'intégralité de la collection.
::class
Syntaxe
Parce que ::class
représente une chaîne. L'avantage d'utiliser ::class
est que vous pouvez le renommer directement en class
dans l'IDE, puis l'IDE gérera automatiquement les références pertinentes.
En même temps, lorsque PHP exécute le code concerné, il ne chargera pas d'abord la classe concernée.
De même, l'inspection automatisée du code inspect
peut également identifier correctement les classes.
Une brève analyse du processus du conteneur Pimple
Pimpl est un conteneur populaire dans la communauté PHP. Il n'y a pas beaucoup de code, voir
https://github.com/silexphp/Pimple/blob/master/src/Pimple/Container.php pour plus de détails.
Notre application peut être développée sur la base de Pimple :
namespace EasyWeChat\Foundation; use Pimple\Container; class Application extends Container { /** * Service Providers. * * @var array */ protected $providers = [ ServiceProviders\ServerServiceProvider::class, ServiceProviders\UserServiceProvider::class ]; /** * Application constructor. * * @param array $config */ public function __construct($config) { parent::__construct(); $this['config'] = function () use ($config) { return new Config($config); }; if ($this['config']['debug']) { error_reporting(E_ALL); } $this->registerProviders(); } /** * Add a provider. * * @param string $provider * * @return Application */ public function addProvider($provider) { array_push($this->providers, $provider); return $this; } /** * Set providers. * * @param array $providers */ public function setProviders(array $providers) { $this->providers = []; foreach ($providers as $provider) { $this->addProvider($provider); } } /** * Return all providers. * * @return array */ public function getProviders() { return $this->providers; } /** * Magic get access. * * @param string $id * * @return mixed */ public function __get($id) { return $this->offsetGet($id); } /** * Magic set access. * * @param string $id * @param mixed $value */ public function __set($id, $value) { $this->offsetSet($id, $value); } }
Comment utiliser notre application :
$app = new Application([]); $user = $app->user;
Après cela, nous pouvons utiliser la méthode de l'objet $user . Nous avons constaté qu'il n'y a pas d'attribut $this->user, mais il peut être utilisé directement. Principalement le rôle de ces deux méthodes :
public function offsetSet($id, $value){} public function offsetGet($id){}
Ci-dessous nous expliquerons ce que fait Pimple lors de l'exécution de ces deux lignes de code. Mais avant d’expliquer cela, examinons quelques concepts fondamentaux des conteneurs.
Fournisseur de services
Le fournisseur de services est le pont entre le conteneur et la classe d'implémentation de fonction spécifique. Les fournisseurs de services doivent implémenter l'interface ServiceProviderInterface
:
namespace Pimple; /** * Pimple service provider interface. * * @author Fabien Potencier * @author Dominik Zogg */ interface ServiceProviderInterface { /** * Registers services on the given container. * * This method should only be used to configure services and parameters. * It should not get services. * * @param Container $pimple A container instance */ public function register(Container $pimple); }
Tous les fournisseurs de services doivent implémenter la méthode de registre d'interface.
Il y a 2 fournisseurs de services par défaut dans notre application :
protected $providers = [ ServiceProviders\ServerServiceProvider::class, ServiceProviders\UserServiceProvider::class ];
En prenant UserServiceProvider
comme exemple, regardons son implémentation de code :
namespace EasyWeChat\Foundation\ServiceProviders; use EasyWeChat\User\User; use Pimple\Container; use Pimple\ServiceProviderInterface; /** * Class UserServiceProvider. */ class UserServiceProvider implements ServiceProviderInterface { /** * Registers services on the given container. * * This method should only be used to configure services and parameters. * It should not get services. * * @param Container $pimple A container instance */ public function register(Container $pimple) { $pimple['user'] = function ($pimple) { return new User($pimple['access_token']); }; } }
On voit que Le La méthode d'enregistrement du fournisseur de services ajoutera des attributs user
au conteneur, mais ce qui est renvoyé n'est pas un objet, mais une fermeture. J'expliquerai cela plus tard.
Enregistrement des services
Nous utilisons Application
dans le constructeur en $this->registerProviders();
pour enregistrer tous les prestataires de services :
private function registerProviders() { foreach ($this->providers as $provider) { $this->register(new $provider()); } }
Regardez attentivement, nous trouvons que le fournisseur de services est instancié ici et que la méthode d'enregistrement du conteneur Pimple est appelée :
public function register(ServiceProviderInterface $provider, array $values = array()) { $provider->register($this); foreach ($values as $key => $value) { $this[$key] = $value; } return $this; }
Et la méthode register
du fournisseur de services est appelée ici, ce que nous avons mentionné dans la section précédente Arrivé : La méthode d'enregistrement ajoute des attributs user
au conteneur, mais elle ne renvoie pas un objet, mais une fermeture.
Lorsque nous ajouterons l'attribut user au conteneur Pimple, la méthode offsetSet($id, $value)
sera appelée : attribuer des valeurs aux attributs Pimple
et aux clés du conteneur values
respectivement :
$this->values[$id] = $value; $this->keys[$id] = true;
Ici, nous La classe EasyWeChatUserUsr
qui fournit réellement la fonctionnalité réelle n'a pas encore été instanciée. Cependant, l'enregistrement du fournisseur de services est terminé.
Lorsque nous exécutons ici :
$user = $app->user;
appellera offsetGet($id) et instanciera la vraie classe :
$raw = $this->values[$id]; $val = $this->values[$id] = $raw($this); $this->raw[$id] = $raw; $this->frozen[$id] = true; return $val;
$raw obtient la fermeture :
$pimple['user'] = function ($pimple) { return new User($pimple['access_token']); };
$raw($this)
返回的是实例化的对象User
。也就是说只有实际调用才会去实例化具体的类。后面我们就可以通过$this['user']
或者$this->user
调用User
类里的方法了。
当然,Pimple里还有很多特性值得我们去深入研究,这里不做过多讲解。
更多相关php知识,请访问php教程!
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!