Cet article présente principalement l'analyse du processus en cours du conteneur PHP Pimple, qui a une certaine valeur de référence. Maintenant, je le partage avec tout le monde. Les amis dans le besoin peuvent s'y référer
Les fermetures et les fonctions anonymes ont été introduites dans PHP5.3.0.
Une 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 parce que la valeur de cette variable est une fermeture, et l'objet de fermeture implémente la magie__invoke()
méthode. 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);
Obtenir 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);
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 ); }
La classe SplObjectStorage implémente une carte avec des objets comme clés ou une collection d'objets (si les objets comme clés sont ignorés données correspondantes) 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 en 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 directement renommer une classe dans l'EDI, 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 peut également identifier correctement les classes.
Pimpl est un conteneur populaire dans la communauté PHP. Il n'y a pas beaucoup de code, voir https://github.com/silexphp/P... 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 les méthodes du $user
objet . 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.
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 les méthodes d'interface register
.
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']); }; } }
Nous voyons , 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.
Nous utilisons $this->registerProviders();
pour enregistrer tous les fournisseurs de services dans le constructeur de l'application :
private function registerProviders() { foreach ($this->providers as $provider) { $this->register(new $provider()); } }
En regardant attentivement, nous avons trouvé cette instance Le fournisseur de services est transformé et la méthode register
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 ici la méthode register
du fournisseur de services est appelée, ce que nous avons mentionné dans la section précédente : L'enregistrement. La méthode ajoute des attributs user
au conteneur, mais renvoie non pas un objet, mais une fermeture.
Lorsque nous ajouterons des attributs user
au conteneur Pimple, la méthode offsetSet($id, $value)
sera appelée : attribuer des valeurs aux attributs values
et keys
du conteneur Pimple respectivement :
$this->values[$id] = $value; $this->keys[$id] = true;
À ce stade, nous n'avons pas instancié la classe EasyWeChatUserUsr
qui fournit réellement la fonctionnalité réelle. Cependant, l'enregistrement du fournisseur de services est terminé.
Lorsque nous courons 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 le package de fermeture :
$pimple['user'] = function ($pimple) { return new User($pimple['access_token']); };
$raw($this)
renvoie l'objet instancié User. Autrement dit, seul l’appel réel instanciera la classe spécifique. Plus tard, nous pourrons appeler des méthodes dans la classe User via $this['user']
ou $this->user
.
Bien sûr, il existe de nombreuses fonctionnalités dans Pimple qui méritent notre étude approfondie, nous ne les expliquerons donc pas trop ici.
Ce qui précède représente l'intégralité du contenu de cet article. J'espère qu'il sera utile à l'étude de chacun. Pour plus de contenu connexe, veuillez faire attention au site Web PHP chinois !
Recommandations associées :
Comment wordpress utilise la fonction wp_head()
Portée, globale, statique des variables PHP Attendez pour les mots clés
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!