Points de base
Le contrôleur frontal est comme un indicateur centralisé dans une application, et son principal domaine d'intérêt est d'attribuer des commandes statiquement ou dynamiquement . Construire au moins un contrôleur frontal simple est une expérience très utile pour comprendre ses détails, et pour promouvoir cette idée d'un point de vue pragmatique, j'ai introduit dans un article d'introduction à la mise en œuvre d'un contrôleur frontal artificiel qui s'ouvrira et Toute la logique requise pour expédier les demandes est emballée dans les limites d'une classe. L'un des meilleurs aspects des contrôleurs frontaux est que vous pouvez les conserver en tant que structures compactes, des demandes entrantes d'itinéraire et d'envoi unique Envoyez des crochets, etc., tous derrière une API unifiée. Cependant, bien que cette approche soit attrayante, elle viole le principe de responsabilité unique (SRP) et la nature de la POO elle-même en déléguant de manière proactive différentes tâches à plusieurs objets à grain fin. Alors, cela signifie-t-il que je suis juste une autre âme pécheresse qui ose briser les préceptes du SRP? Dans un sens, oui. Je veux donc éliminer mon péché en vous montrant comment déployer facilement un cadre HTTP petit mais évolutif qui est capable de fonctionner avec des contrôleurs frontaux avec des routeurs et planificateurs autonomes. De plus, l'ensemble du cycle de demande / réponse sera géré indépendamment par plusieurs classes réutilisables qui peuvent naturellement être ajustées à volonté. Étant donné qu'il existe un grand nombre de frameworks HTTP emballés entièrement composés disponibles, il semble ridicule de mettre en œuvre un contrôleur frontal qui achemine et envoie des demandes à partir de zéro, même si ces classes conservent la nature du SRP. Pour éviter d'être jugé pour réinventer la roue, certaines parties de mon implémentation personnalisée seront inspirées par la bibliothèque EphPMVC intelligente écrite par Lars Strojny.
Analyser le cycle de demande / de routage / planification / réponse
La tâche que nous devons résoudre d'abord est de définir plusieurs classes responsables de la simulation des données et du comportement des cycles de demande / réponse HTTP typiques. Ceci est la première classe, et l'interface qu'il implémente:
<code>class Request { public function __construct($uri, $params) { $this->uri = $uri; $this->params = $params; } public function getUri() { return $this->uri; } public function setParam($key, $value) { $this->params[$key] = $value; return $this; } public function getParam($key) { if (!isset($this->params[$key])) { throw new \InvalidArgumentException("The request parameter with key '$key' is invalid."); } return $this->params[$key]; } public function getParams() { return $this->params; } }</code>
La classe de demande résume le réseau URI et de paramètres entrant et simule une demande HTTP extrêmement simple. Pour Brevity, des membres de données supplémentaires tels que la méthode définie associée à la demande pertinente ont été intentionnellement exclues. Si vous voulez les ajouter à la classe, continuez. Avoir une fine enveloppe de demande HTTP qui existe indépendamment est bon, mais il finira par être inutile sans s'accoupler avec les parties correspondantes des données et le comportement qui simule une réponse HTTP typique. Corrigeons et construisons ce composant supplémentaire:
<code>class Response { public function __construct($version) { $this->version = $version; } public function getVersion() { return $this->version; } public function addHeader($header) { $this->headers[] = $header; return $this; } public function addHeaders(array $headers) { foreach ($headers as $header) { $this->addHeader($header); } return $this; } public function getHeaders() { return $this->headers; } public function send() { if (!headers_sent()) { foreach($this->headers as $header) { header("$this->version $header", true); } } } }</code>
La classe de réponse est sans aucun doute plus active que sa demande de partenaire. Il agit comme un conteneur de base qui vous permet d'empiler les en-têtes HTTP à volonté et est en mesure de les envoyer au client. Étant donné que ces classes effectuent leurs opérations indépendamment, il est temps de commencer à construire la partie suivante du contrôleur frontal. Dans une implémentation typique, le processus de routage / répartition est principalement encapsulé dans la même approche, ce qui n'est franchement pas trop mal du tout. Cependant, dans ce cas, il est préférable de décomposer ces processus et de les déléguer à différentes classes. De cette façon, les choses seront plus équilibrées en termes de responsabilité égale. Voici les lots de classes qui font fonctionner le module de routage:
<code>class Route { public function __construct($path, $controllerClass) { $this->path = $path; $this->controllerClass = $controllerClass; } public function match(RequestInterface $request) { return $this->path === $request->getUri(); } public function createController() { return new $this->controllerClass; } } class Router { public function __construct($routes) { $this->addRoutes($routes); } public function addRoute(RouteInterface $route) { $this->routes[] = $route; return $this; } public function addRoutes(array $routes) { foreach ($routes as $route) { $this->addRoute($route); } return $this; } public function getRoutes() { return $this->routes; } public function route(RequestInterface $request, ResponseInterface $response) { foreach ($this->routes as $route) { if ($route->match($request)) { return $route; } } $response->addHeader("404 Page Not Found")->send(); throw new \OutOfRangeException("No route matched the given URI."); } }</code>
Comme on pourrait s'y attendre, il existe de nombreuses options lors de la mise en œuvre de mécanismes de routage fonctionnel. Au moins à mon avis, la méthode ci-dessus est à la fois pratique et directe. Il définit une classe d'itinéraire distincte qui lie le chemin d'accès à un contrôleur d'opération donné et un simple routeur dont les responsabilités se limitent à vérifier si l'itinéraire stocké correspond à l'URI associé à un objet de demande spécifique. Pour enfin résoudre le problème, nous devons configurer un planificateur rapide qui peut être utilisé côte à côte avec la classe précédente. C'est ainsi que fait la classe suivante:
<code>class Dispatcher { public function dispatch($route, $request, $response) $controller = $route->createController(); $controller->execute($request, $response); } }</code>
Scannez le répartiteur et vous remarquerez deux choses. Premièrement, il ne porte aucun état. Deuxièmement, il suppose implicitement que chaque contrôleur d'opération fonctionnera sous la surface de la méthode EXECUTE (). Cela peut être refactorisé à un modèle légèrement plus flexible si vous le souhaitez (la première chose qui me vient à l'esprit est de modifier la mise en œuvre de la classe d'itinéraire), mais pour la simplicité, je garderai le planificateur inchangé. Jusqu'à présent, vous vous demandez peut-être comment placer un contrôleur frontal capable de combiner toutes les classes précédentes ensemble. Ne vous inquiétez pas, le prochain est!
(En raison des limitations de l'espace, le contenu ultérieur sera tronqué. Veuillez fournir le reste du contenu et je continuerai à terminer le pseudo-original.)
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!