1

Hello,

Attention, pavé !

Je relance un sujet que j'avais déjà abordé il y a quelques années (#vieuxsage#), mais pour lequel je n'ai toujours pas de solution. La question n'est pas "comment faire" puisqu'il y a 1000 et une façons de coder le petit exercice que je vais décrire, mais "comment bien faire" puisque je ne connais pas de solution qui me semble efficace. Vous avez peut-être pensé à des façons bien plus intelligentes que moi de structurer votre code, donc ça m'intéresse smile

Commençons par poser le vocabulaire. La grosse différence entre un site "non-ajax" et un site "ajax", c'est que dans le premier on a un certain nombre de pages entières et que chacune possède un point d'entrée : son URL. Dans un site "ajax", on a toujours des pages avec chacune son URL d'accès, mais il existe également d'autres points d'entrées pour des "fragments" de page, qui sont utilisés pour mettre à jour une partie du contenu de la page mais ne peuvent être affichés seuls. Jusqu'ici rien de neuf, mais maintenant que j'ai défini ce que j'appelle une page et un fragment on va pouvoir aborder l'exercice.

Prenons yAronet comme exemple, et imaginons que la page qui affiche la liste des topics utilise de l'ajax (en voilà une killer feature !). Cette page affiche initialement les 30 premiers topics d'un forum, avec des liens "précédent" et "suivant" qui permettent d'accéder aux 30 topics précédents ou suivants ceux qui sont actuellement affichés. J'ai donc une page "topics" (yn.com/topics.php?forum=X), et un fragment "topics_liste" (yn.com/topics_liste.php?forum=X&index=Y). La page affiche un document HTML habituel avec <head>, <body> & co, plus éventuellement plein d'autres informations comme mon login ou la liste des utilisateurs connectés, alors que le fragment affiche directement des balises HTML qui n'ont un sens que si elles sont insérées au bon endroit dans la page.

Puisque ma page affiche par défaut les 30 premiers topics, et que le fragment affiche "30 topics à partir d'un index donné", je suis très tenté d'utiliser le même code. Je vais donc avoir, quelque part, une fonction qui me récupère 30 topics à partir du N-ième, et qui va être appelée aussi bien dans ma page que mon fragment. Je conçois qu'on puisse vouloir désactiver le Javascript, et donc l'ajax, je voudrais en conséquence que ma page soit capable elle aussi d'afficher 30 topics à partir d'un index donné. Ça tombe bien, la fonction que je voulais réutiliser offre cette possibilité, autant ne pas s'en priver.

Je vais donc finalement avoir "yn.com/topics.php?forum=X&index=Y" qui affiche ma page entière avec 30 topics à partir du Y-ième, les utilisateurs connectés, et tout plein d'autres trucs, et "yn.com/topics_list.php?forum=X&index=Y" qui ne m'affiche qu'une liste de 30 topics à partir du Y-ième. Les bouts de code déclenchés par ces deux URLs vont faire appel à une même fonction "lister_topics" qui me donne une liste de 30 topics étant donné un forum et un index.

L'aspect de la liste des topics sera rigoureusement identique qu'elle soit affichée par ma page ou par mon fragment, je peux donc déjà écrire le "template" de ma liste (ou la "vue", si vous préférez) qui sera utilisé dans les deux cas. Mettons qu'il s'appelle "topics_liste.tpl.php", et qu'il ressemble à ça (faites abstraction de ce code hybride et imaginez un template réalisé à l'aide de votre framework de template favori) :

topics_liste.tpl.php: <a href="topics.php?forum=X&index=<?php echo $index_precedent; ?>" onclick="return ajax('topics_liste.php?forum=X&index=<?php echo $index_precedent; ?>');">Page précédente</a> <!-- TODO: la même avec $index_suivant, vous avez compris l'idée --> <ul> <?php foreach ($topics as $topic): ?> <li><a href="fixme"><?php echo $topic['name']; ?></a></li> <?php endforeach; ?> </ul>
Mes utilisateurs sont gentils et ne font pas d'injection dans les noms de topic.

Jusqu'ici tout va bien, mais il va falloir écrire le code qui va afficher ces deux templates, et c'est là que les choses se compliquent. Une chose est sûre, il va me falloir une fonction "afficher_topics ($forum_id, $index)" et une autre "afficher_topics_liste ($forum_id, $index)" qui vont être appelées quand quelqu'un accèdera à leurs URLs respectives. Pour le reste, plusieurs approches sont possibles, en voici quelques-unes et les défauts que je leur trouve :
Solution 1, la page effectue le rendu du fragment elle-même
topics.tpl.php: <html> <body> <h1>Affichage des topics du forum #<?php echo $forum_id; ?> :</h1> <div id="header"> <!-- TODO: Plein de trucs, affichage des utilisateurs connectés, d'un lien vers mon profil, d'un bouton "J'aime", etc. --> </div> <div id="topics"> <?php echo $fragment_topics ?> </div> </body> </html>

topics.php: <?php // Point d'entrée : yn.com/topics.php?forum=X&index=Y function afficher_topics ($forum_id, $index) { /* TODO: Vérifications d'usage, validité des arguments & co */ $utilisateurs = lister_utilisateurs (); $topics = lister_topics ($forum_id, $index); $fragment_topics = rendre ('topics_liste.tpl.php', array ( 'index_precedent' => max ($index - 30, 0), 'index_suivant' => $index + 30, 'topics' => $topics )); echo rendre ('topics.tpl.php', array ( 'utilisateurs' => $utilisateurs, 'fragment_topics' => $fragment_topics, 'forum_id' => $forum_id )); }?>

topics_liste.php: <?php // Point d'entrée : yn.com/topics_liste.php?forum=X&index=Y function afficher_topics_liste ($forum_id, $index) { /* TODO: Vérifications d'usage, validité des arguments & co */ $topics = lister_topics ($forum_id, $index); echo rendre ('topics_liste.tpl.php', array ( 'index_precedent' => max ($index - 30, 0), 'index_suivant' => $index + 30, 'topics' => $topics )); }?>
Pas terrible : j'ai dupliqué tout le code de rendu du fragment (l'appel à "rendre"). Durant tout le développement de ma future application il va falloir maintenir ces deux copies identiques, ce qui va devenir pénible quand j'aurai beaucoup de pages et de fragments. Les codes de rendu pouvant devenir facilement bien plus longs que ce cas d'école, je n'ai vraiment pas envie de les dupliquer.
Solution 2, on factorise le code de rendu dans une fonction
topics.tpl.php: <html> <body> <h1>Affichage des topics du forum #<?php echo $forum_id; ?> :</h1> <div id="header"> <!-- TODO: Plein de trucs, affichage des utilisateurs connectés, d'un lien vers mon profil, d'un bouton "J'aime", etc. --> </div> <div id="topics"> <?php echo $fragment_topics ?> </div> </body> </html>

topics.php: <?php include ('topics_liste.php'); // Point d'entrée : yn.com/topics.php?forum=X&index=Y function afficher_topics ($forum_id, $index) { /* TODO: Vérifications d'usage, validité des arguments & co */ $fragment_topics = rendre_topics_liste ($forum_id, $index); $utilisateurs = lister_utilisateurs (); echo rendre ('topics.tpl.php', array ( 'utilisateurs' => $utilisateurs, 'fragment_topics' => $fragment_topics, 'forum_id' => $forum_id )); }?>

topics_liste.php: <?php // Point d'entrée : yn.com/topics_liste.php?forum=X&index=Y function afficher_topics_liste ($forum_id, $index) { /* TODO: Vérifications d'usage, validité des arguments & co */ echo rendre_topics_liste ($forum_id, $index); } function rendre_topics_liste ($forum_id, $index) { $topics = lister_topics ($forum_id, $index); return rendre ('topics_liste.tpl.php', array ( 'index_precedent' => max ($index - 30, 0), 'index_suivant' => $index + 30, 'topics' => $topics )); }?>
C'est un peu mieux, je n'ai plus de code dupliqué. En revanche, je suis obligé de créer deux fonctions pour chaque fragment. Pire encore : si je veux ajouter un niveau d'abstraction et rester cohérent, la fonction "afficher_topics" ne devrait pas calculer le rendu elle-même. Il faudrait également la scinder en deux fonctions "afficher_topics" et "rendre_topics", la première appelant la seconde pour calculer le rendu. En gros, je viens de multiplier par deux le nombre de fonctions dans mon programme, et pour peu qu'il soit assez riche, ça représente un bon paquet de fonctions en plus dont j'aurais bien aimé me passer.
Solution 3, la page appelle l'affichage du fragment
topics.tpl.php: <html> <body> <div id="header"> <!-- TODO: Plein de trucs, affichage des utilisateurs connectés, d'un lien vers mon profil, d'un bouton "J'aime", etc. --> </div> <div id="topics"> <?php afficher_topics_liste ($forum_id, $index); ?> </div> </body> </html>

topics.php: <?php // Point d'entrée : yn.com/topics.php?forum=X&index=Y function afficher_topics ($forum_id, $index) { /* TODO: Vérifications d'usage, validité des arguments & co */ $utilisateurs = lister_utilisateurs (); echo rendre ('topics.tpl.php', array ( 'utilisateurs' => $utilisateurs, 'forum_id' => $forum_id, 'index' => $index )); }?>

topics_liste.php: <?php // Point d'entrée : yn.com/topics_liste.php?forum=X&index=Y function afficher_topics_liste ($forum_id, $index) { /* TODO: Vérifications d'usage, validité des arguments & co */ $topics = lister_topics ($forum_id, $index); echo rendre ('topics_liste.tpl.php', array ( 'index_precedent' => max ($index - 30, 0), 'index_suivant' => $index + 30, 'topics' => $topics )); }?>
Avec cette méthode, j'ai à nouveau une seule fonction par page ou fragment, c'est une bonne chose. J'ai aussi gagné quelque chose de très intéressant : le code de "afficher_topics" ne fait plus directement référence à "afficher_topics_liste", et ça c'est cool. J'ai repoussé cette liaison à la couche d'affichage, ce qui me permettra par exemple d'include de nouveaux fragments dans une page sans toucher à son code, c'est à dire du code qui n'a a priori aucun rapport direct avec les fragments en question. Bon point.
Par contre, je me tape les vérifications d'usage deux fois, c'est à dire que je dois m'assurer que $forum_id est un identifiant valide, qu'$index est cohérent, que l'utilisateur est bien connecté, etc. C'est d'autant plus dommage que je n'ai reçu qu'une seule requête, et que donc lors de mes deux vérifications $forum_id et $index n'ont pas changé de valeur. Mais à trop avoir voulu découpler mon code, j'ai perdu cette information, et je suis obligé de vérifier mes paramètres deux fois. Si ma page comportait 5 fragments au lieu d'un seul, ça pourrait devenir problématique, ne serait-ce qu'au niveau des performances.

Au final, la troisième approche me semble être la moins pire, mais elle garde un gros problème de code exécuté deux fois pour rien. Je pourrais limiter la casse avec un système de cache qui me permette de retenir qu'un paramètre est valide pour ne pas le vérifier à nouveau, mais ça va inévitablement allourdir mon code de rendu.

Et vous, vous faites/feriez comment ?
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

2

Je ne comprends pas tout vu que je ne connais pas trop PHP, mais si je comprends bien, topics c'est juste liste_topics avec des trucs en plus autour. Tu ne peux pas utiliser une seule et même fonction avec un switch pour lui dire de générer ou pas ce qui va autour de la liste ? (genre afficher_topics (forum_id, index, page_complete) avec page_complete qui vaut true ou false suivant que tu veux la page ou juste la liste ? et après tu fais un if dans le rendu ?)
et du coup ton url serait yn.com/topics.php?forum=X&index=Y&page=true pour la page et yn.com/topics.php?forum=X&index=Y&page=false pour le fragment.
avatar
« Le bonheur, c'est une carte de bibliothèque ! » — The gostak distims the doshes.
Membrane fondatrice de la confrérie des artistes flous.
L'univers est-il un dodécaèdre de Poincaré ?
(``·\ powaaaaaaaaa ! #love#

3

(désolé, j'ai choisi php en me disant que c'était ce qui serait compris par un maximum de gens ^^)

Sinon c'est une solution qui me semble trop figée. En particulier, ça devient assez peu pratique de réutiliser un fragment commun à plusieurs pages (par exemple le fragment qui affiche un post, que j'aimerais utiliser dans les topics, dans mes sujets, dans les liens automatiques style "./43", etc). En fait, c'est équivalent à la solution n°2 mais en remplaçant deux fonctions par une seule avec un switch, ce qui revient quasiment au même.
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

4

Est-ce obligatoirement du PHP ?
j'ai l'impression qu'avec les templates dans Django, ça irait tout seul : suivant l'url demandée tu choisis un template "fragment" ou "complet", et tu appelles la fonction qui calcule la liste des topics hum
avatar
<<< Kernel Extremis©®™ >>> et Inventeur de la différence administratif/judiciaire ! (©Yoshi Noir)

<Vertyos> un poil plus mais elle suce bien quand même la mienne ^^
<Sabrina`> tinkiete flan c juste qu'ils sont jaloux que je te trouve aussi appétissant

5

Non non, c'est pas du tout limité au PHP ! (au contraire, ça m'étonnerait beaucoup que les bonnes réponses puissent n'être valables que pour un seul langage).

Sinon je ne suis pas sûr de comprendre ce que tu veux dire. Avec Django j'aurais un "urls.py" qui me permettrait de définir quelle fonction "afficher_*" serait appelée en fonction de l'URL, mais ensuite je retombe directement dans la solution n°2, non ? (ou n°3 si je développe un template tag kivabien, c'est à dire la solution sur laquelle je suis actuellement)
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

6

moi je passe par un plugin de mon fw, ici se serais un plugin "topic" appelé par exemple comme ca : §topic|last|30§ c'est le plugin qui se chargera d'extraire les données et de remplir un design avec celles ci, puis il le retournera et le moteur le blitera ou il faut

dans le contenu de la page normale je l'insère avec : §topic|last|30§
la page normale à un squelette de page normal avec header/footer/flashchat etc ...

après pour la version "standalone" soit je fait une page dédié, avec un squelette complètement vide qui va juste appeler mon plugin de la même manière
soit je fait une page pareille, vide, commune à tous les bout de page ajax, prenant des args différents et appelants les divers plugin ou bout de code suivant ceux ci,
soit je fait juste un fichier ajax.php dédié au plugin dans le même répertoire qui, va simplement charger le moteur et le plugin, et retournera le contenu de l'appel du plugin

mais pour le futur il y aura un fichier ajax standard qui se chargera de charger un plugin de l'appeler et simplement retourner sa sortie texte.

après moi je suis partisan de générer/charger seulement le bout de page, d'autre te dirons qu'il est plus simple de faire un appel complet de la page normale, de la découper par du js et afficher le bout nécessaire
et la le mec il le pécho par le bras et il lui dit '

7

Alors pour la dernière suggestion (calculer la page entière et extraire un bout par JS) je trouve ça carrément ignoble sorry
Tu calcules potentiellement le rendu pour un truc extrêmement complexe alors que tu vas en extraire une toute petite partie. Ça contourne effectivement très bien le problème, mais à quel prix !

Sinon pour la solution avec ton fw, j'ai rien comprisgrin (mais je serais preneur d'une explication un peu plus abstraite que ta syntaxe perso que je ne connais pas ^^)
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

8

./3 > moui c'est vrai que c'est pas très différent du 2, mais à propos du 2 tu avais l'air de te plaindre principalement du nombre de fonctions en plus, donc je me disais que ça serait peut-être mieux cheeky

du coup je dirais que la bonne méthode est sans doute 2 ou 3 suivant le besoin, mais je ne vois pas tellement de solution intermédiaire.

est-ce que tu ne peux pas faire facilement une fonction php qui ne fait les vérifications d'usage sur ses arguments que si elle est appelée via URL et pas si elle est appelée par une autre fonction ?
avatar
« Le bonheur, c'est une carte de bibliothèque ! » — The gostak distims the doshes.
Membrane fondatrice de la confrérie des artistes flous.
L'univers est-il un dodécaèdre de Poincaré ?
(``·\ powaaaaaaaaa ! #love#

9

J'ai un peu du mal à lire (j'ai seulement mon téléphone), mais je ne vois pas trop pourquoi il faudrait deux fonctions par fragment.

Dans urls.py, tu aurais /fragments/topiclist/(\d+) qui appelle fragments_topiclist,
ainsi que /pages/topics/(\d+) qui appelle pages_topiclist.
Cette fonction appellerait check_topic_id, get_topic_list, render_from_template.

Quant à la fonction pages_topiclist, elle aurait appellerait les mêmes sous-fonctions ainsi que d'autres, et appellerait un template incluant celui du fragment.

Tu aurais une seule fonction par fragment, sans code dupliqué.



avatar
<<< Kernel Extremis©®™ >>> et Inventeur de la différence administratif/judiciaire ! (©Yoshi Noir)

<Vertyos> un poil plus mais elle suce bien quand même la mienne ^^
<Sabrina`> tinkiete flan c juste qu'ils sont jaloux que je te trouve aussi appétissant

10

./8 : C'est vrai que ta solution fait une fonction en moins, mais finalement ça oblige à ajouter un bloc "switch" avec deux "cases" constants dans chaque fonction afficher_*, du coup je ne suis pas sûr que le gain soit si intéressant que ça (il va falloir écrire autant voire davantage de code en plus que si on avait simplement scindé en deux fonctions).

Pour le deuxième point, j'ai peur que ce soit plus compliqué que "si elle est appelée via URL" : imaginons que le fragment possède 3 arguments en commun avec la page qui l'a appelé, et un 4ème qui lui est propre. On peut économiser les trois premiers arguments (pas besoin de les valider, la page l'a déjà fait) mais pas le 4ème qui est utilisé pour la première fois. Il y a peut-être des solutions élégantes pour gérer ce cas sans faire des tests partout, mais là encore je me demande si ça ne va pas provoquer un code plus long que scinder en deux fonctions comme dans la solution n°2.

[edit] Bon, ma deuxième hypothèse est débile, le fragment n'a aucune chance d'avoir un argument que la page ne lui a pas donné : il l'aurait obtenu d'où ? triso

Non en fait ça a l'air plutôt intéressant comme idée ^^

./9 : Soit y'a un truc que je comprends pas, soit c'est très exactement la solution n°2 ?
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

11

concrètement c'est similaire à ta solution 3, le code est mis à part dans une fonction et utilisé dans les deux cas

après, mon fw simplifie le chargement et l'appel de la fonction en y mettant une syntaxe particulière §nom-plugin|arg1|arg2|..argN§ et en séparant bien la fonction du code du moteur en le calant dans un répertoire et fichier particulier.

donc j'ai juste à inclure l'appel du plugin dans la grosse page, du moins dans son contenu réel, le moteur remplira l'ensemble de la page suivant son squelette, et appellera le plugin lui même, pour la page ajax ce sera pareil mais avec un squelette de page "vide"
après comme tu le dit en ./1 j'utilise accessoirement un système de cache, soit sur la page elle même soit sur l'appel au plugin, de plus j'utilise redis, qui permet de supprimer automatiquement une clef au bout d'un certain temps si nécessaire (ici ca buterais le cache des 30 derniers posts toute 30 secondes par exemple)
et la le mec il le pécho par le bras et il lui dit '

12

Mais en mettant de côté tous les aspects techniques qui sont propres à ton framework, genre syntaxe ou séparation du code en fichiers (a priori ça ne change rien à la question), finalement ça change quoi par rapport à la solution n°3 ? J'imagine que "arg1", "arg2" & co ce sont des ids d'objets et non pas des instances, et qu'il va falloir passer chaque fois par une étape de récupération/validation avant de pouvoir commencer les traitements ?
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

13

Je ne comprends pas trop les extraits de code que tu as donnés.

Voilà comment je ferais :

- Un template pour représenter la liste des topics (un fragment de page web, donc) ;
- Un template pour représenter la page entière, faisant appel au template précédant pour rendre la liste des topics ;
- Un point d’entrée pour afficher le fragment contentant certains topics, réalisant les vérifs nécessaires, et éventuellement en capturant certaines d’entre elles dans une fonction à part, réutilisable) ;
- Un point d’entrée pour afficher la page entière, réalisant les vérifs, réutilisant éventuellement la fonction définie précédemment.

Ça correspond à laquelle de tes solutions ?
avatar
« Quand le dernier arbre sera abattu, la dernière rivière empoisonnée, le dernier poisson capturé, alors vous découvrirez que l'argent ne se mange pas. »

14

(il me semble que c'est peu ou prou (mais plutôt prou) la solution 3, sauf si j'ai très mal compris ^^)
avatar
« Le bonheur, c'est une carte de bibliothèque ! » — The gostak distims the doshes.
Membrane fondatrice de la confrérie des artistes flous.
L'univers est-il un dodécaèdre de Poincaré ?
(``·\ powaaaaaaaaa ! #love#

15

Je ne sais pas si ça peut avoir un rapport avec ton problème, mais j'en ai déjà contourné un du même genre en supprimant complètement les vérifications pour chaque morceau.

En fait, j'ai fait un moteur de site dont toutes les adresses sont signées (en fait, pour chaque paramètre, on peut choisir de le signer ou non). À la réception de la requête, le moteur va constituer la liste des paramètres présents et les morceaux de page pourront alors les utiliser sans autres vérifications. Cette technique fonctionne si tu peux assurer que les adresses que ton site génère seront toujours valides.

Cette technique m'a également permis de générer certaines adresses avec une signature propre à l'utilisateur pour les actions d'administration, ce qui élimine certains risques pour la sécurité (personne ne peut générer le lien qui s'affiche sur la page d'un administrateur pour supprimer un message par exemple, on ne peut donc pas te faire faire une requête à son adresse par un moyen détourné).
avatar

16

C’est-à-dire « signées » ? chiffrées ?
avatar
« Quand le dernier arbre sera abattu, la dernière rivière empoisonnée, le dernier poisson capturé, alors vous découvrirez que l'argent ne se mange pas. »

17

Sally (./14) :
(il me semble que c'est peu ou prou (mais plutôt prou) la solution 3, sauf si j'ai très mal compris ^^)
Cependant je ne vois pas, dans la solution que je propose, de code exécuté inutilement deux fois (ce que Bob64 reprochait à la solution 3).

Sinon, pour info, vous développez vraiment avec PHP ?
avatar
« Quand le dernier arbre sera abattu, la dernière rivière empoisonnée, le dernier poisson capturé, alors vous découvrirez que l'argent ne se mange pas. »

18

il vaut mieux extraire le code de verif en dehors de la fonction, si justement le test est dupliqué c'est qu'il est inutile

> J'imagine que "arg1", "arg2" & co ce sont des ids d'objets et non pas des instances, et qu'il va falloir passer chaque fois par une étape de récupération/validation avant de pouvoir commencer les traitements ?
arg 1 ..n c'est ce que tu veut, le fw ne te force en rien, ici juste ca va appeler le code propre au plugin en lui donnant un array contenant tous les arguments, après oui bien sur c'est à toi de faire les test etc nécessaire, juste ca te propose un plus "haut niveau" que php smile
et la le mec il le pécho par le bras et il lui dit '

19

Sasume (./16) :
C'est-à-dire « signées » ? chiffrées ?
Non, j'ajoute une signature. Mais c'est probablement hors sujet pour le problème traité en fait grin
Et pourquoi ne pas utiliser cette solution ?
topics.tpl.php:<html> <body> <div id="header"> <!-- TODO: Plein de trucs, affichage des utilisateurs connectés, d'un lien vers mon profil, d'un bouton "J'aime", etc. --> </div> <div id="topics"> <?php echo $topics_liste; ?> </div> </body> </html>

forum.php:> <?php // Points d'entrée : - yn.com/forum.php?module=topics?forum=X&index=Y // - yn.com/forum.php?module=topics_liste?forum=X&index=Y function afficher_module ($module, $args) { /* TODO: Actions d'usage : connexion BDD, vérification de l'utilisateur... */ echo rendre ($module.'.php', $args); }?

topics.php<?php // Point d'entrée : fonction interne function rendre_topics ($args) { /* TODO: Vérifications d'usage, validité des arguments (aucun pour cet exemple) & co */ $utilisateurs = lister_utilisateurs (); $topics_liste = rendre ('topics_liste.php', $args); return rendre ('topics.tpl.php', array ( 'utilisateurs' => $utilisateurs, 'topics_liste' => $topics_liste )); }?>

topics_liste.php:<?php // Point d'entrée : fonction interne function rendre_topics_liste ($args) { $forum_id = $args['forum_id']; $index = $args['index']; /* TODO: Vérifications d'usage, validité des arguments & co */ $topics = lister_topics ($forum_id, $index); return rendre ('topics_liste.tpl.php', array ( 'index_precedent' => max ($index - 30, 0), 'index_suivant' => $index + 30, 'topics' => $topics )); }?>
avatar

20

./13 & ./14 : En effet, il me semble que c'est la solution 3 ^^

./15 & ./16 : "signés"... dans l'URL tu veux dire ? au lieu d'avoir "forum=156" tu as "forum=sq6ZDs545dsf", et pareil pour chaque paramètre ? Pourquoi pas, mais c'est une sacré incitation à essayer de casser le code, et ça expose le mécanisme de sécurité à l'extérieur. Quand c'est possible d'éviter, j'aime autant smile (et puis ça rend assez difficile de générer des requêtes côté client aussi, je sais que c'est censé ne jamais arriver, mais des fois ça rend bien des services ^^)

./18 : Je ne comprends pas trop tes réponses, j'ai l'impression qu'à chaque fois tu supposes que je connais ton framework et ça n'est pas le cas :/ J'ai bien compris qu'il était merveilleux et qu'il pouvait tout faire, mais concrêtement de quelle façon il me permet de sortir de l'une des trois solutions du premier post ? Si les paramètres arg1 .. n sont passés à un "plugin" et que ce plugin doit "faire les tests nécessaires", de ce que je comprends c'est exactement la solution 3 (à la sauce de ton framework, certes, mais ça ne change pas grand chose ?).

./19 : Pas mal smile (je ne comprends pas bien pourquoi tu as tout englobé dans un seul paramètre "$args" en revanche ; c'est juste parceque c'est ta façon habituelle de structurer le code, ou il y a une raison liée au problème ?)

Il y a juste un cas qui n'est pas couvert : si la page appelle plusieurs fragments qui utilisent le même paramètre (ou bien si la page utilise un paramètre également utilisé par l'un de ses fragments). Dans l'exemple, la page affiche "$forum_id", donc on est obligé de l'avoir validé avant (en vérifiant que c'est un entier, en faisant le select kivabien dans la base, etc). Le fragment qui affiche la liste des topics utilise également ce $forum_id, et sans information complémentaire on est obligé d'effectuer à nouveau la vérification. En gros là où tu as marqué "(aucun pour cet exemple)", ce n'est pas tout à fait exact, il faut quand même vérifier $args['forum_id'], et cette vérification va être à nouveau effectuée lors de l'appel à rendre_topics_liste sad
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

21

iwannabeamaki (./20) :
./15 & ./16 : "signés"... dans l'URL tu veux dire ? au lieu d'avoir "forum=156" tu as "forum=sq6ZDs545dsf", et pareil pour chaque paramètre ? Pourquoi pas, mais c'est une sacré incitation à essayer de casser le code, et ça expose le mécanisme de sécurité à l'extérieur. Quand c'est possible d'éviter, j'aime autant smile.gif (et puis ça rend assez difficile de générer des requêtes côté client aussi, je sais que c'est censé ne jamais arriver, mais des fois ça rend bien des services ^^)
Je ne chiffre pas les adresses, je ne fais qu'ajouter une signature. Donc "truc.php?forum=156&taillepolice=2" devient "truc.php?sforum=156&ntaillepolice=2&s=sq6ZDS545dsf". Ici, le paramètre forum est signé alors que la taille de la police ne l'est pas. Le module qui utilise forum le cherchera dans la liste des paramètres signés alors que pour la taille de la police, ce sera dans la liste des paramètres non signés. Avant le chargement des modules, le moteur vérifie la signature avec les paramètres présents et refuse de charger la page en cas d'erreur. Comme ça, on est sûr de la validité des paramètres et qu'ils sont tous présents.
iwannabeamaki (./20) :
(je ne comprends pas bien pourquoi tu as tout englobé dans un seul paramètre "$args" en revanche ; c'est juste parceque c'est ta façon habituelle de structurer le code, ou il y a une raison liée au problème ?)
C'est pour ajouter un niveau d'abstraction. Ton module topics peut ne pas connaître la liste des paramètres utilisés par le module topics_liste. Ça permet de placer plusieurs modules sur une même page et de pouvoir les faire évoluer sans toucher au module de la page. Pour créer un lien avec certains paramètres, il faut les ajouter dans $args et générer l'adresse qui en reprend toutes les valeurs, ainsi les liens de tous les modules font recharger la même page lorsqu'on clique dessus (sauf les modifications du module lui-même), c'est pratique lorsque le JavaScript est désactivé. Lorsqu'un module termine une action unique (supprimer un message par exemple), il retire les paramètres de cette action de $args pour que les liens n'amènent pas à reproduire l'action.

Avec ce système, les modules ont une grande indépendance, mais en contrepartie, il faudra vérifier certains paramètres plusieurs fois (les modules ont chacun la responsabilité de leurs paramètres...). Ce qui pourrait être envisagé, ce serait de placer les paramètres dans une autre liste une fois qu'ils ont été vérifiés, les modules chercheraient dans cette liste avant de regarder dans la liste habituelle et d'en faire la vérification...

avatar

22

Oui, c'est le système de cache que j'évoquais pour limiter les problèmes de la solution n°3, mais on retombe globalement dans les mêmes problèmes. Même si ça me semble être la moins mauvaise des solutions, je suis sûr qu'il y a moyen de faire un peu mieux...

(par contre le coup de la signature je suis vraiment pas fan du tout, c'est vraiment un truc que je ne rendrai jamais visible à l'utilisateur grin)
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

23

En fait, si deux modules ont besoin des mêmes informations, c'est peut-être qu'il manque quelque chose ^^
Si tu ajoutes une sorte de gestionnaire de forum, un singleton qui est donc initialisé par le premier module qui en a besoin, le second pourra lui demander la liste des topics sans aucune vérification supplémentaire.
avatar

24

Les deux fragments n'ont pas besoin des mêmes informations, par contre ils utilisent les mêmes paramètres : tous deux ont besoin de $forum_id, l'un pour récupérer des informations sur ce forum, l'autre pour récupérer des topics qui appartiennent à ce forum. Je peux bien avoir un singleton responsable de tout ça, mais chaque fragment va quand même devoir vérifier dans son coin la validité des paramètres (ne serait-ce que pour vérifier que ce sont bien des entiers, par exemple, puisqu'ils viennent de la query string). Le seul gain que peut m'apporter le singleton, c'est qu'il retiendra l'instance de forum correspondant à "$forum_id" en base donc je n'aurai pas besoin de faire deux requêtes, mais toutes les vérifications préalables seront quand même dupliquées :/
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

25

en fait ce que j'essaie d'expliquer est dans le même esprit que la solution de RHJPP

juste le truc, c'est qu'avant de coder tout ca, je me suis mis à la place du graphiste, et ai essayer d'arrêter de penser comme un développeur 2 minutes, et donc j'ai dégagé complètement (c'est optionnel) le php des "templates" bref, ca t'apporte juste des solutions alternative, plus claires et simples pour appeler tes plugins/modules

après, le php du plugins n'est chargé qu'une seule fois, au premier appel du plugin
le fichier php du plugin contient une fonction principale qui va être appelé directement au second appel, donc pour ton problème, rien ne t'empêche de sortir les test de la fonction principale, et qu'ensuite cette fonction ai accès aux résultats de tes tests

concrètement, prenons l'exemple d'un panier, en dehors de la fonction principale du module panier, je teste celui ci, et suivant les cas, le créé ou le charge

dans l'header collectif à toute mes pages, j'insère un appel au plugin en lui disant 'draw_petit', argument que la fonction principale du plugin panier va comprendre par affiche moi mon panier en petit pour en haut, bref juste le nombre d'élément à l'intérieur, sans fioriture
<div id="content-panier">§panier|draw_petit§</div>
e un <?php echo callPlugin('panier',array('draw_petit')); ?>rien n'empêchais de passer par du php classique et fairmais va expliquer ca au graphiste smile

ensuite, si l'on est pas sur la page du panier, le plugin à simplement fini son job, il ne sera jamais rappelé

maintenant dans le cas ou c'est la page votre-panier qui est demandé, le contenu de cette page contiendra
<div id="panierWrap">§panier|listing§</div>

ici je rappelle le plugin en lui disant 'listing' et cette fois ci, avec cet argument, la fonction principale va m'afficher mon panier complet
le php du plugin avais déjà été chargé pour l'affichage mini de l'header, il ne sera pas réinclu et donc le code de chargement/test du panier non réévalué, simplement la fonction principale est appelé.

je te met le code de callPlugin, c'est du php 100% standard, juste ca matte si le plugin est chargé, si oui il appelle juste la fonction, si non, il charge et appelle, c'est la seule différence avec ta solution 3

function needPlugin($plg)
{	$fn = 'fn_'.$plg;
	if(function_exists($fn)) return true;
	$path = module_path.'/'.$plg.'/'.$plg.'.php';
	if(is_file($path)) { include($path); return true; }
	return false;
}

function callPlugin($plg,$args)
{	if(false === needPlugin($plg)) return false;
	$fn = 'fn_'.$plg;
	ob_start(); $fn($args); $out = ob_get_contents(); ob_end_clean();
	return $out;
}
et la le mec il le pécho par le bras et il lui dit '

26

Heu oui ok, merci pour l'explication, mais en quoi cette histoire de plugin a le moindre rapport avec le problème que j'évoque au-dessus ? C'est très bien, et ça peut s'intégrer sans problème avec les trois solutions que je proposais au premier post, mais pour moi ça a toujours rien à voir avec la question posée ?
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

27

ici un plugin/module est un fichier php avec une fonction dedans c'est tout

tu veut extraire ton code de topic et le réutiliser, c'est ce que j'appelle une fonction

tu veut faire des test une seule fois et lancer ton code n fois, c'est ce que j'appelle charger un fichier php qui va effectuer les test puis tu va lancer la fonction n fois.

sinon 24 répondais pas a 23, c'est un cross happy

mais il peu tout de même y répondre, vu que c'est la même chose que l'exemple du panier, tu teste dans le fichier php que tu inclus, en dehors de toute fonction tes entrées, tu fait tout ton business, puis tu exécute ton code de traitement/blit qui lui est inclus dans la fonction

en gros :

include 'topic.php' // ici ca teste les entrée et déclare function_topic
function_topic('traitement1');
...
function_topic('traitementN');


si tu à correctement fait tes test, la validité des entrées ne va pas changer entre l'inclusion et les appels, donc tu n'as aucun test à dupliquer

ou alors j'ai rien compris à ton blème
et la le mec il le pécho par le bras et il lui dit '

28

iwannabeamaki (./24) :
Les deux fragments n'ont pas besoin des mêmes informations, par contre ils utilisent les mêmes paramètres : tous deux ont besoin de $forum_id, l'un pour récupérer des informations sur ce forum, l'autre pour récupérer des topics qui appartiennent à ce forum. Je peux bien avoir un singleton responsable de tout ça, mais chaque fragment va quand même devoir vérifier dans son coin la validité des paramètres (ne serait-ce que pour vérifier que ce sont bien des entiers, par exemple, puisqu'ils viennent de la query string). Le seul gain que peut m'apporter le singleton, c'est qu'il retiendra l'instance de forum correspondant à "$forum_id" en base donc je n'aurai pas besoin de faire deux requêtes, mais toutes les vérifications préalables seront quand même dupliquées :/
Si on considère qu'une page ne peut contenir des informations que d'un seul forum à la fois, alors tu n'as pas à faire les tests de validité plusieurs fois pour $forum_id.

Le premier module à avoir besoin du gestionnaire de forum va l'instancier en donnant $forum_id en paramètre à son constructeur (c'est lui qui va faire les vérifications). Le second module arrive et là le singleton existe déjà et il n'y aura donc pas de vérification de $forum_id. La liste des topics retournée par le gestionnaire sera bien celle de $forum_id puisque c'est ce forum qui a été donné en paramètre au constructeur.
avatar

29

./27 : j'ai ptet compris pourquoi on ne parle pas du tout de la même chose, je ne cherche pas à exécuter une même fonction plusieurs fois smile mais je ne comprends toujours rien à ce que tu proposes... si qqun d'autre voit de quoi tu parle, je suis preneur d'un décodage parceque j'ai l'impression que tu m'expliques comment résoudre un problème que je n'ai pas grin

./28 : pourquoi cette limitation d'ID commun entre page et fragment ? la page "voir un topic" appelle plusieurs fois le fragment pour voir un post, mais avec des IDs différents, donc pour le coup la vérification devrait théoriquement être faite à chaque fois. En pratique, la vérification ne sert à rien si c'est la page qui a généré les IDs qu'elle transmet aux fragments (elle ne va pas générer des IDs invalides), elle est utile uniquement si on accède directement à ce fragment pour afficher un seul post. C'est pour ça que la solution de Sally pourrait être bonne, à savoir considérer un contexte "sûr" quand on est appelé depuis une fonction interne, et "à vérifier" quand on est appelé par une URL. Reste à voir comment implémenter ça de façon discrète.
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

30

Bob > en fait, je crois que je n'ai pas bien compris en quoi la solution 3 impose d'écrire deux fonctions à chaque fois ?
avatar
<<< Kernel Extremis©®™ >>> et Inventeur de la différence administratif/judiciaire ! (©Yoshi Noir)

<Vertyos> un poil plus mais elle suce bien quand même la mienne ^^
<Sabrina`> tinkiete flan c juste qu'ils sont jaloux que je te trouve aussi appétissant