Sujet : Hoa_Controller, ça devient amusant

Hey smile,

Comme vous le savez, Hoa_Controller est en refonte pour être plus léger. Non pas que l'ancien était mauvais mais il n'était pas assez souple à mon goût. Le nouveau est juste génial à mes yeux. Hoa s'emboîte parfaitement bien et montre toute sa force (tada !).

Hoa_Controller comporte les mêmes couches qu'avant mais présentées différement. On retrouve la requête, le routeur, le dispatcheur, la réponse et l'application. Fini les contrôleurs primaires et secondaires, c'était valable à l'époque, ça ne l'est plus aujourd'hui. PHP est plus rapide et les problématiques ne sont plus les mêmes. En effet, le but était d'éviter du calcul à PHP alors que maintenant, on veut éviter des accès disques (ma hantise du moment).

Que trouve-t-on alors dans Hoa_Controller ? Simplement : Hoa_Controller_Router, Hoa_Controller_Dispatcher et Hoa_Controller_Application. Ces trois éléments sont totalement dissociés et la configuration est carrément plus souple, ainsi que les possibilités que ça apporte.

Le dispatcheur a besoin d'un routeur et d'une vue pour fonctionner. Un routeur est Hoa_Controller_Router ou une fille. Une vue est n'importe quoi qui implémente Hoa_View_Viewable (le seul fichier de Hoa_View d'ailleurs).

Qu'est-ce qu'une vue ? Quelque chose qui porte des données, qui est capable de faire un rendu et qui a un flux de sortie (de type Hoa_Stream_Interface_Out) (en général, le rendu est fait sur le flux de sortie wink).

Qu'est-ce qu'un routeur ? Il prend un ensemble de 3-uplet (ou 4-uplet) qui forme des règles. Ça donne :

$router->addRule(pattern, controller, action[, extra])

pattern est une expression régulière dans laquelle on capture nommément des parties, comme ^foo/bar/(?<baz>[^\.]+)\.html$. Ici, quand on écrit foo/bar/the-cake-is-a-lie.html, baz vaudra the-cake-is-a-lie, tout bêtement. Ensuite, controller est le nom du contrôleur ou son instance. Et action est le nom de l'action, le nom d'une fonction ou une closure. Enfin, extra correspond à des données qu'on veut passer à notre action (méthode, fonction, closure etc.).
Exemple pour le couple controller/action :

  • 'gordon'/'freeman' : va appeler la classe du contrôleur gordon (par défaut GordonController) et la méthode de l'action freeman (par défaut FreemanAction) ;

  • $gordon/'freeman' : même chose, sauf que la classe est $gordon ;

  • null/'freeman' : va appeler la fonction appelée freeman ;

  • null/function ( ) { } : va appeler la fonction anonyme.

Si on appelle un objet ou une classe, il faut qu'il étende Hoa_Controller_Application, qui fournit juste 4 attributes : $router, $dispatcher, $view, $data (les données de la vue). Tout ce qu'il faut pour s'amuser.
Si on appelle une fonction (anonyme ou pas), si l'argument $_this est présent, il vaudra Hoa_Controller_Application.

Si le routeur capture d'autres parties, elles seront distribuées sur les arguments du même nom de l'action. Un exemple : si notre règle capture act et ion (de cette façon : ^foo/bar/(?<act>.*)-(?<ion>.*)\.html$, alors les valeurs seront disponibles de cette façon :

class GordonController extends Hoa_Controller_Application {

    public function FreemanAction ( $act, $ion ) {

        var_dump($act, $ion);
    }
}

Bien sûr, si ion n'est pas capturée et qu'on a déclaré $ion = 'tada' en argument, alors tout ira bien. Bref, c'est naturel quoi, faut pas trop réfléchir, c'est une distribution bête et méchante.
Et dans le cas d'une fonction (anonyme ou pas) :

function f ( Hoa_Controller_Application $_this, $act, $ion ) {

}

$this dans une méthode est identique à $_this dans une fonction. Ça, c'est le côté pratique.
Dois-je préciser que si on capture (?<controller>) et (?<action>) ça va changer le contrôleur et l'action ? Rien de magique ou de particulier, il faut regarder comment fonctionne les règles (par « composant »/parties) et on va trouver ça logique.

Donc on avait dit qu'on a un dispatcheur. De quoi a-t-il besoin ? D'un routeur et d'une vue. Le routeur, c'est fait, on passe à la vue.
Prenons un exemple qui nous intéresse : Hoa_Xyl. XYL a besoin d'un flux d'entrée pour lire un document XYL, un flux de sortie et un interpréteur (comment interpréter XYL ? en HTML5, en PDF, en texte … ?). Alors, on va écrire ceci :

$dispatcher = new Hoa_Controller_Dispatcher();
$dispatcher->dispatch(
    new Hoa_Controller_Router(),
    new Hoa_Xyl(
        new Hoa_File_Read('hoa://Application/View/Main.xyl'),
        new Hoa_Http_Response(),
        new Hoa_Xyl_Interpreter_Html5()
    )
);

Ça peut paraître un brin compliqué comme ça, mais on découple au maximum et au final, c'est extrêmement modulaire.

Au final, dans notre action (classe, méthode, fonction, closure, peu importe !), on va taper sur $this ou $_this pour avoir les données et faire le rendu, un truc du genre :

$this->data->foobar = 'Hello world!';
$this->view->render();

Et c'est fini.
Notez que si on utilise XYL, on peut désormais ajouter des <?xyl-use href="…"?> dynamiquement avec addUse($href), ainsi :

$this->view->addUse('hoa://Application/View/Use1.xyl');
$this->view->render();

et le tour est joué. Pareil avec addOverlay($href).

Et maintenant, on veut s'amuser !
Comment passer d'une action à une autre ? On va utiliser le routeur et le dispatcheur qu'on a sous la main ! Un petit exemple :

$this->dispatcher->dispatch(
    $this->router->route('/new/path')
);

Si on ne donne pas la vue en second paramètre de dispatch(), on va prendre la vue courante (ça va de soit …). On peut échanger de routeur, échanger de vue, on fait ce qu'on veut, c'est très très souple. Libre à vous d'imaginer ce que vous voulez smile.
Le fait que XYL gère énormément de chose nous simplifie grandement la vie. Par exemple, les formulaires seront gérés par XYL.

Ensuite, petit truc amusant. La gestion de l'asynchrone par Hoa_Controller.
Vous savez que tout est paramétrable dans Hoa grâce à Hoa_Core_Parameterizable et le zFormat. Les paramètres de Hoa_Controller_Dispatcher sont les suivants (au format JSON) :

{
    "keywords": {
        "controller": "main",
        "action":     "main",
        "method":     null
    },

    "parameters": {
        "synchronous.file"       : "hoa://Application/Controller/(:controller:U:).php",
        "synchronous.controller" : "(:controller:U:)Controller",
        "synchronous.action"     : "(:action:U:)Action",

        "asynchronous.file"      : "(:%synchronous.file:)",
        "asynchronous.controller": "(:%synchronous.controller:)",
        "asynchronous.action"    : "(:%synchronous.action:)Async"
    }
}

Bien sûr, vous pouvez vous amuser comme vous voulez, mais par défaut, si vous appelez une action de manière asynchrone (via XHR par exemple), alors on va chercher la méthode suffixée de Async. Par exemple : FreemanActionAsync.
On peut aller plus loin avec le mot-clé method. Si on écrit :

"asynchronous.action": "(:action:U:)Action(:method:U:)Async"

alors ça pourrait donner FreemanActionGetAsync ou FreemanActionPostAsync etc. On parle bien des méthodes HTTP.

Bref. J'oublie encore des choses mais c'est déjà assez pour ce soir smile.

J'ai alors une question. Maintenant que le contrôleur et la vue sont bien foutus (d'après moi, votre avis ?), il me reste le modèle. Comment souhaitez-vous que le modèle soit géré ?

« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

2

Re : Hoa_Controller, ça devient amusant

Un petit Hello world avec le nouveau contrôleur pour s'amuser ? Aller, même pas peur. Surtout que j'ai améliorer le routeur pour qu'il sache router des requêtes en CLI et par derrière CGI.

C'est parti. Ça se teste en 2 minutes et ça tient en quelques lignes de code. Allons-y ! Ouvrons un fichier nommé index.php par exemple :

<?php

require_once '/var/hoa/Core.php';

import('Controller.Router');
import('Controller.Dispatcher');

$router = new Hoa_Controller_Router();
$router->addRule('^$', null, function ( ) {

    echo 'Hello world!' . "\n";
});

$controller = new Hoa_Controller_Dispatcher();
$controller->dispatch($router);

Depuis notre navigateur, on va voir le site, et on verra Hello world!. Je sais, c'est incroyable smile.

Le mieux, c'est qu'il encaisse jusqu'à 300 requêtes chez moi (configuration basique). C'est assez fort smile.

« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

3

Re : Hoa_Controller, ça devient amusant

OK big_smile
Je n'ai pas regardé le code (pas fait d'update du svn), je réagis donc uniquement par rapport à ce que tu as écrit ici.

Lorsque le controller est une fonction, $_this est optionnel si j'ai bien compris. Si cet argument est présent dans la signature (c'est bien ça?), alors il se réfère à une instance de Hoa_Controller_Application. Cette instance n'a d'utilité que par la présence des 4 membres $router, $dispatcher, $view et $data. C'est toujours bien ça?

Si $_this est optionnel (pour une func), je me demande quel est l'intérêt de forcer/obliger les controllers objets à dériver de Hoa_Controller_Application. OK, ça permet d'avoir accès aux "4 membres", mais ne serait-il pas envisageable de faire en sorte que l'utilisateur puisse fournir n'importe quelle instance, pour peu qu'elle contienne les méthodes d'actions (ou un __call adéquat, peu importe) ?

La question serait de savoir comment on "récupère" ne serait-ce que le $view quand on est dans une méthode d'un controller qui, par hypothèse, serait de n'importe quel type objet. Je sais pas, peut être comme pour les controllers fonctions avec leur paramètre $_this qui serait en quelque sorte le "contexte" ? Ou faire en sorte que ce paramètre (place holder) soit explicitement déclaré auprès du router pour que le dispatcher sache quoi faire et uniquement si l'utilisateur en a besoins...
De proche en proche, on se retrouve à imaginer un "factory" pour que le controller objet soit créé dans les bonnes conditions (avec les bon paramètres) en amont du dispatch effectif (le fait d'appeller la méthode d'action).

Le dispatcher requière-t-il qu'on lui passe un Hoa_View_Viewable obligatoirement avant l'appelle à dispatch() ? (en tout cas le fait qu'il y ait un Hoa_View_Viewable quelque part dans le contexte). Le controller ne pourrait-il pas choisir lui-même si besoins le type de view lorsque l'on est dans la méthode (ou func) d'action ?

En fait, comment je vois les choses pour un MVC: (je ne cherche en rien à t'influencer)
Je vire le M et le V lol On se concentre sur le D R et C (dispatcher/router/controller). Les features telles que Hoa_Controller_Application seraient optionnelles car construites sur un socle le plus light possible de DRC. Au minimum, on dispatch à peu près ce qu'on veut (classe, instance, fonction), et c'est ton idée, mais l'utilisateur doit pouvoir travailler à un niveau assez bas, c'est à dire avec le minimum de contraintes (et possiblement le maximum de choses à faire/écrire soi même). Pour pallier à ces limitations, on ajoute graduellement des facilités comme pourrait l'être Hoa_Controller_Application avec ses 4 membres. La "pile" de facilités peut aboutir in fine à un CMS complet. J'exagère un peu, c'est pour donner l'idée quoi. Du coup, il devient plus rassurant de "mettre le paquet" pour certains composants, comme par exemple vérifier que les fichiers existent avant de les charger, ou lancer 50 exceptions différentes selon 50 types d'erreurs différents. On "paye" en toute connaissance de cause. Certes, un dispatcher/router très light ne sera pas suffisant pour supporter les features additionnelles requises par des composants de plus haut niveau. Rien n'empêche de raffiner (au sens OOP) les entités ou carrément les réécrire. Le point important, toujours selon moi, c'est que le socle soit vraiment simple et performant/efficace pour qu'on ait le luxe de batir diverses extensions au dessus.
Quelque chose de simple et que tu proposes déjà est le fait de passer les paramètres provenant/extraits par le router en tant que arguments des actions ($act, $ion). Difficile de faire plus simple. Je pense que c'est un très bon point. Au dessus, on imagine créer une classe de "request" qui encapsule tout ça (ainsi que d'autres trucs en plus). Ca peut être utile, plein de fw le font. Bref, c'était juste pour illustrer mon point de vue.

"Comment souhaitez-vous que le modèle soit géré ?"
Laisser le user utiliser la librairie de son choix? (pdo, propel ou n'importe quel autre truc). Pour moi le model ne se réduit pas à une librairie ou un système qui implémente un unique pattern (généralement orm) du genre active record et compagnie. Je ne dis pas que c'est ce que tu veux faire. Du coup, au lieu de parler de modèle, je préfère parler de "facilités" pour accéder à une db, des webservices ou n'importe quelle resource. Bref, le model est trop custom pour être réduit à un unique composant qui serait aussi circonscrit que le View par exemple (mais cela ne veut pas dire pour autant que des "models" ne puissent être réutilisés d'appli en appli).

4

Re : Hoa_Controller, ça devient amusant

Hey smile,

Beaucoup de réponses sont dans le code. Néanmoins, voici une réponse rapide smile.

$_this est effectivement optionnelle. Elle prend Hoa_Controller_Application en paramètre. C'est une structure portant plusieurs informations pratiques pour les actions.

Ta question sur l'utilité qu'un contrôleur hérite de Hoa_Controller_Application est légitime. Surtout que entre obtenir (en paramètre) cette structure, ou en hériter, il y a un monde en terme de conception !
Je voulais que les contrôleurs héritent de Hoa_Controller_Application pour structurer un peu plus les contrôleurs. Pourquoi ? On voit que quand on fait du routing, on peut router les données sur n'importe quoi. Sauf que si on le fait sur une action appartenant à une contrôleur, alors on offre un cadre un peu plus solide, un peu plus sécurisant.
Les actions en tant que fonction ou closure sont rares, alors que des actions dans des contrôleurs sont plus répandues. On va donc offrir un peu plus de souplesse et décharger les actions de certains paramètres (plus de $_this quand on est dans un contrôleur, c'est $this tout court).

Si on ne connait pas Hoa_Controller_Application, alors notre action perd tout contexte. Elle ne connaît ni la vue, ni le routeur, ni le dispatcheur, ni rien du tout.

L'image de la factory est juste mais trop connotée. Néanmoins je vois où tu veux en venir wink. Un système très générique quoi. Un dispatcheur en fait tongue.

Le dispatcheur ne requiert pas de vue. C'est une facilité d'utilisation, mais elle n'est pas obligatoire. Dans mon précédent message, tu vois qu'on ne donne pas de vue, on fait simplement un echo smile. Et ensuite, avec XYL, on a la méthode render(). Cette méthode peut prendre un nouveau flux de sortie. On peut également réinterpréter XYL autrement grâce à interpreteAs(). Bref, ce genre de contraintes sont portées par la vue. Le DRC n'a aucune connaissance de la vue ! Il veut juste une bête interface avec 3 méthodes.

Ensuite, tu verras que Hoa_Controller est suffisamment générique pour répondre à ton paragraphe sur le DRC. J'aime bien cette idée en fait, sauf que c'est plus RDA (mais là, ça craint) : Router-Dispatcher-Action. Les actions peuvent être portées par des contrôleurs, mais pas nécessairement wink. Mais RDA ça craint … tongue. Du coup, si cette idée devient plus structurée, plus besoin de parler de contrôleur. On aura Hoa_Dispatcher, avec Hoa_Dispatcher_Router et Hoa_Dispatcher_Context (pour remplacer Hoa_Controller_Application), et hop. T'en penses quoi ? Ça ne serait pas trop déroutant pour les nouveaux venus ?

Enfin, sur les modèles, voici comment je procèdais avant. Hoa_Controller proposait un autoload pour les modèles et s'arrêtait là. Je pense que je vais faire pareil, et le reste est géré par l'utilisateur. S'il utilise Propel ou MongoDB par exemple, il saura se débrouiller tout seul, ou alors créer ses classes Hoathis pour se simplifier la tâche. Hoa est là pour ça, faciliter le développement, et non pas tout automatiser (enfin, dans le sens où on intègre un outil de A à Z comme Symfony le fait avec Doctrine).

« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

5

Re : Hoa_Controller, ça devient amusant

Quand je parlais du "factory", je pensais plus à un mécanisme optionnel (injectable) qui viendrait se greffer au dispatcher. Disons que le dispatcher pourrait déléguer à ce "factory" la tâche d'instancier les controllers objets (et pourquoi pas les autres). Je vais continuer à parler de factory dans ce qui suit, mais nous sommes d'accord, ça ne désigne pas le pattern commun factory du gof mais ça s'en approche.

Toujours selon moi, l'opération qui consiste à "créer" le controller est vraiment cruciale. Il y a trop de possibilités différentes et d'approches différentes pour que cette responsabilité soit "codée en dur" dans le dispatcher.
Pêle-Mêle, diverses considérations: autoloading vs require_once, tester ou non l'existence de fichiers avant de les charger, utiliser la reflection ou pas, tester l'existence de méthodes ou pas, quelles exceptions, quels paramètres sont passés au constructeur, quelle est la base classe du controller etc. Ca fait beaucoup de possibilités dont certaines impactent fortement les perfs et/ou le degré de liberté laissé à l'utilisateur.

Un "factory" un peu élaboré fournirait en terme d'heuristique et d'implémentation ce que fait ton dispatcher actuellement, c'est à dire la partie qui charge la classe avec un require, instancie le controller en lui passant le router, le dispatcher et la vue, puis teste/traite les paramètres de méthode avec la reflection (j'ai regardé le code).

Un "factory" beaucoup plus simple et tout aussi optionnel que les autres pourrait se contenter, schématiquement, de faire new $class puis call_user_func_array. Les méthodes du controller recevraient les paramètres "packés" extraits du router sous forme d'array. Autolading, pas de testes de fichiers, pas de testes de méthodes. RAS. Une appli bien testée (en amont) peut tout à fait fonctionner ainsi.

Hoa_Dispatcher_Context, pour reprendre ce qui a été évoqué précédemment, serait une possibilité offerte par un type particulier de "factory" (arf décidément il faudrait que je trouve un terme adéquat). Ce "contexte" serait une sorte de container réunissant tout ce qu'on juge utile pour ce type particulier et qui serait passé d'une manière ou d'une autre aux controllers (par constructeur, setter,  argument de methode, peu importe).

Est-ce que tu vois où je veux en venir? Bien sûr, un fw doit à moment ou un autre inculquer une philosophie ou disons privilégier une certaine façon de procéder. Ce qui reviendrait, pourquoi pas, à mettre en avant un "factory" compatible/spécialisé pour des controllers dérivant de Hoa_Controller_Application (et ses fameux 4 membres).

6

Re : Hoa_Controller, ça devient amusant

Je reprends mon schéma initial.
J'avais une couche requête, une couche routeur, une couche dispatcheur, une couche action, une couche réponse. On reste sur un schéma HTTP classique, on reprend la terminologie du protocole.

Ici, on dit que la couche requête et routeur sont fusionnées. Ça facilite l'instanciation d'une part, et surtout l'utilisation. PHP fonctionnera toujours en mode serveur, même s'il est en client ! Le router sait détecter s'il est utilisé derrière un CGI ou en CLI par exemple, et il va piocher les données différemments.

Le dispatcheur a potentiellement un très grand nombre de traitements possibles différents. Mais j'estime que dans le cadre d'un framework, tout comme d'une bibliothèque, il faut instaurer un minimum de savoir-vivre et surtout de savoir-programmer. C'est pourquoi on trouve une légère structuration imposée. Elle est suffisamment souple pour laisser beaucoup de liberté, et suffisamment rigide pour ... bah elle n'est pas rigide en fait !

Enfin, la couche réponse appartient à la vue. C'est son flux de sortie. La réponse peut être alors récupérer par ce qu'on veut, un fichier, une socket ..., bref : un flux quoi.
Ce n'est pas parce qu'on est sur un schéma HTTP que l'application doit produire un résultat HTTP obligatoirement.

Pour moi, c'est au dispacheur de faire les tests sur les fichiers, savoir s'ils existent etc. Pour l'auto-chargement des actions, je ne comprends pas trop où tu veux en venir. Une action doit fonctionner seule, sans vraiment connaître celle qui l'entour, sauf si elles appartiennent au même groupe (par exemple, au même contrôleur). Hmm, en écrivant ça, je n'arrive pas moi-même à me persuader.
Mais de toute façon, comme une action peut récupérer le routeur et le dispatcheur (grâce à son contexte, précisemment Hoa_Controller_Application), elle est capable de relancer les données sur autre chose, sur une autre action. N'est-ce pas suffisant ?

Si on veut changer le fonctionnement du dispatcheur ou du routeur, il suffit de l'étendre, de réécrire des points critiques (qui n'apparaissent pour l'instant pas : dispatch() et route() sont d'un seul bloc) et de les utiliser. N'est-ce pas suffisant ?

Avec Hoa_Controller, je voulais aller au plus simple, au plus souple et au plus puissant rapidement. Je n'ai pas trouvé de cas où ça pourrait ne pas fonctionner en fait. Est-ce que tu as un exemple ?

« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

7

Re : Hoa_Controller, ça devient amusant

Hywan a écrit:

Pour moi, c'est au dispacheur de faire les tests sur les fichiers, savoir s'ils existent etc. Pour l'auto-chargement des actions, je ne comprends pas trop où tu veux en venir. Une action doit fonctionner seule, sans vraiment connaître celle qui l'entour, sauf si elles appartiennent au même groupe (par exemple, au même contrôleur). Hmm, en écrivant ça, je n'arrive pas moi-même à me persuader.

Si on veut changer le fonctionnement du dispatcheur ou du routeur, il suffit de l'étendre, de réécrire des points critiques (qui n'apparaissent pour l'instant pas : dispatch() et route() sont d'un seul bloc) et de les utiliser. N'est-ce pas suffisant ?

Il y a de quoi se poser la question de savoir s'il vaut mieux avoir recours à l'héritage pour modifier les comportements, ou utiliser d'autres modes de "customisation". 

Scinder/éclater ce que fait dispatch() permettrait de plus facilement modifier le dispatcher par dérivation. Dans la pratique, pour ce cas précis du dispatcher, il me parait plus judicieux de procéder autrement que par dérivation, en tout cas lorsque la customisation ne concerne que la partie instanciation, checking et appel des controllers utilisateurs.

Pouvoir utiliser n'importe quel controller (dérivant ou pas de classes arbitraires) n'est pas un luxe. Je pense que c'est primordiale. Le framework devrait supporter cette feature nativement. On ne sait jamais comment le framework sera utilisé et quelles seront les contraintes spécifiques à l'appli. Par exemple, la différence de nature entre la partie publique et la partie admin ou la différence entre la partie html et la partie API du site.

Tel que c'est actuellement, je ne pourrai pas avoir par exemple un controller qui utilise __call pour certaines actions. Le test sur method_exists va échouer. Pour des questions de perf (je ne sais pas, disons que le site fournirait une API), je souhaiterai, pour certaines urls, bypasser la majeure partie des vérifs, tests et reflection pour simplement récupérer les paramètres provenant du router dans un controller objet simpliste.

Comment est-ce que tu envisagerais ça?

8

Re : Hoa_Controller, ça devient amusant

Hmm, j'y réfléchis j'y réfléchis. Cette question hante mes nuits maintenant, je tiens à te remercier wink.

Je pense qu'il faudrait faire la différence entre un système de contrôleur et un système de routing + dispatch. En tout cas dans ma tête, ça me permet de mieux raisonner.
Le truc, l'astuce, le tip, réside dans l'écriture des règles de routing + dispatch. Comment écrire des règles générales et des règles exceptionnelles (des règles particulières) ? Si tu as une idée, je la prends.

N'oublie pas que Hoa est hautement paramétrable et modulaire. C'est là un de ses points forts. On peut imaginer un peu tout et n'importe quoi.
Tu me parlais d'un système fortement dynamique qui reposait sur tout ce que PHP peut offrir en terme de dynamicité. C'est justement un bel exemple d'utilisation smile. Donc à toi de me proposer des idées wink.

« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »

9

Re : Hoa_Controller, ça devient amusant

Une idée, ça serait de passer au dispatcher lors de sa construction, non pas un array de parameters (pour Hoa_Core_Parameter), mais un object dont le role serait de faire une bonne partie de ce que fait dispatch() actuellement. Cet objet, c'est le "factory" de mes posts précédents, mais encore une fois, son nom n'est toujours pas complètement adéquat.

Le dispatcher devient en quelque sorte un "controller" (ou un manager) du router et de cet objet. C'est le dispatcher qui gère le flow du dispatch... L'interface utilisateur et l'utilisation tout court du dispatcher reste les mêmes (à ceci près qu'un factory est injecté).

Une fois que le dispatcher a obtenu l'array $components du router, il délègue au factory le soin de retourner un callable approprié. Les parameters qui étaient passés au dispatcher sont maintenant passés au factory à sa construction (en tout cas, les parameters dont le factory a besoins).

Le factory privilégié par le fw (les bonnes pratiques) pourrait faire exactement ce qui se passe dans dispatch() actuellement pour instancier/checker/introspecter et appeller les "controllers" (funcs ou classes). Le factory travaille à partir de ses parameters (passés à la construction surement) et les $components provenant du router. Le dispatcher doit donc passer ces éléments au factory, ainsi que lui-même pourquoi pas (il se passe lui même au factory, ça peut être à la construction si on veut que le factory ait un full access au dispatcher).

Les choix vraiment concrets concernant la manière dont les fichiers/classes sont chargés (require_once vs autoloading etc), les différents paramètres à passer aux constructeurs ainsi que la forme des paramètre à passer aux callables sont localisés dans le factory. Un factory vraiment laxiste pourrait se contenter de faire "new $controller; call_user_func_array($callable, $components);". Un autre plus "cossu" ferait tous les checks et error/exception reporting qu'on retrouve dans ton code.

Plus haut j'ai parlé d'un factory qui retourne un callable alors que dans mon paragraphe juste au-dessus, c'est le factory qui fait en sorte de caller ce callable. J'en sais trop rien. Par contre, à mon sens, c'est le seul factory qui "sait" comment organiser/trier/checker les arguments à passer à ce callable. Du coup, je me dis, laissons le choisir/faire l'appel lui même. Il retournerait le retour de l'appel.

Suivant le site (appli), un seul factory peut convenir. On choisit par avance le type qu'on veut (le type Hoa classique pour funcs et classes dérivant de Hoa_Controller_Application, ou un type super fast sans contrainte de base class etc.. peu importe). Le dispatcher et le router restent les mêmes.

Si on a besoins de règles exceptionnelles qui nécessitent, pour une même appli, un/des factory différents de celui par défaut (celui passé au dispatcher), on pourrait imaginer que c'est la règle qui fournit elle-même le factory. Ce factory spécifique aurait la priorité sur celui par défaut. C'est au dispatcher qu'incomberait le role d'appeller ce factory spécifique une fois que le router a fait son job.

Cette spécialisation pourrait être précisée dans le paramètre "[, extra]" du addRule. Ou encore une API spéciale, du genre beginGroup(), addRule(), endGroup() avec toutes les rules du groupe concernées par le même factory. La construction du factory pourrait être lazy également. Ca fait pas mal de possibilités, je te l'accorde ;-)

Ce qui me semble bien dans cette approche, c'est que les choix très spécifiques de chargement, instanciation, types des controllers supportés etc sont encapsulés dans des composants interchangeables. C'est plus simple de switcher de "factory" plutôt que de réimplémenter un dispatcher. Et du coup, on envisage la possibilité d'utiliser plusieurs "factory" pour une même appli si jamais on en a besoins (je suis d'accord que ça n'est probablement pas un besoins récurent).

Est-ce que tu penses que ça a un brin de sens? Mon point de vue est quand même assez marqué par le souhait de pouvoir dispatcher vers n'importe quoi (notamment des controllers objet sans contraintes particulières)...

10

Re : Hoa_Controller, ça devient amusant

Je résume.

Tu souhaites que le dispatcheur actuel devienne un front controller et qu'on créé un nouveau dispatcheur. Le soucis, c'est que je ne veux pas charger trop de fichiers pour la rapidité justement (c'est le plus long actuellement, toujours 80% d'accès disque et 20% de traitement …).

Et si on fait un contrôleur frontal, il faudrait associer à chaque dispatcheur une sorte d'identifiant que l'on retrouverait dans les règles pour savoir qu'elle dispatcheur utiliser. L'idée n'est pas bête je sais, j'y ai déjà pensé, c'est comme ça que ça marchait avant (sauf que c'était pas très bien fait wink).

Je vais être un peu bête, mais si une règle disait [pattern, foobar, __call], ça n'irait pas ? Ce serait tout aussi simple. Je te l'accorde, si on veut ajouter des données sur les arguments de notre méthode appelée, ce serait un peu galère. Mais entre créé un nouveau dispatcheur ou en étendre un, je pense que l'étendre serait plus aisé, surtout s'il est bien découpé. Mais si on étend, on reste à manipuler un seul dispatcheur …

Je ne persiste pas mais je veux rester dans la simplicité et la souplesse. Certes, l'idée d'un contrôleur frontal ajoute de la souplesse mais complexifie un brin le système. Qu'en penses-tu smile ?
Note, je dis ça, mais je vais sûrement le faire tongue.

« Un handicap est le résultat d'une rencontre entre une déficience ou différence et une incapacité de la société à répondre à celle-ci. »