Maison > développement back-end > tutoriel php > Une introduction au motif du contrôleur avant, partie 2

Une introduction au motif du contrôleur avant, partie 2

Lisa Kudrow
Libérer: 2025-02-26 09:55:45
original
815 Les gens l'ont consulté

An Introduction to the Front Controller Pattern, Part 2

Points de base

  • Le contrôleur frontal agit comme un proxy centralisé pour l'application, attribuant des commandes aux gestionnaires prédéfinis, tels que les contrôleurs de page ou les ressources de repos.
  • Le contrôleur frontal peut maintenir une structure compacte, une route et des demandes entrants, et peut également être étendu à un contrôleur reposant entièrement fonctionnel, analyser les verbes HTTP, s'adapter aux crochets pré / après la répartition, etc.
  • Cet article montre comment déployer un cadre HTTP petit mais évolutif qui fonctionne avec les contrôleurs frontaux, les routeurs autonomes et les planificateurs tout en gérant les cycles de demande / réponse indépendamment.
  • L'auteur a également introduit le processus de construction d'un contrôleur frontal à partir de zéro, y compris la définition de classes pour simuler les données et les comportements des cycles de demande / réponse HTTP typiques, de création de modules de routage et de configuration des planificateurs.
  • Le mode contrôleur frontal a les avantages du contrôle centralisé, de la réduction de la duplication du code et de l'amélioration de la modularité et de la séparation des préoccupations, mais il peut ne pas convenir à toutes les applications Web et peut entraîner un seul point de défaillance s'il est implémenté.

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>
Copier après la connexion
La classe

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>
Copier après la connexion

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>
Copier après la connexion

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>
Copier après la connexion

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!

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Derniers articles par auteur
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal