Précédent   Forum des professionnels en informatique > PHP > Langage
Langage Forum sur le langage PHP, la POO, les conventions, la sécurité, etc. Avant de poster : FAQ Langage, toutes les FAQ PHP, cours langage et sources PHP
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Proposer ce sujet en actualité
 
Outils de la discussion
Publicité
'
Vieux 30/01/2012, 18h28   #1
Nouveau Membre du Club
 
Homme Cédric Bohnert
En auto-formation
Inscription : juillet 2004
Messages : 71
Détails du profil
Informations personnelles :
Nom : Homme Cédric Bohnert
Âge : 33
Localisation : France, Bas Rhin (Alsace)

Informations professionnelles :
Activité : En auto-formation
Secteur : Conseil

Informations forums :
Inscription : juillet 2004
Messages : 71
Points : 28
Points : 28
Par défaut Patron MVC : bien séparer la vue.

Bonjour à tous,

J'ai commencé à développer un espace membre générique pour une utilisation future en essayant d'appliquer au mieux les principes du patron MVC.

Tout fonctionne bien, j'ai développé les fonctionnalités suivantes :
  • inscription d'un membre,
  • connexion d'un membre,
  • afficher son profil,
  • modifier son profil.

J'utilise MySQL pour stocker les informations des membres.

Je me suis attaqué à l'affichage de la liste des profils des membres et cela fonctionne bien mais je bute sur un problème :

Je trouve que le code de ma Vue (qui affiche la liste des membres) contient "trop de code php", alors qu'elle devrait contenir, en principe, que de l'HTML...

Voici mes sources :

1) la fonction du modèle "membres.php" :
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function selectioner_liste_membres() {
 
	$pdo = PDO2::getInstance();
	$requete = $pdo->prepare("SELECT id,nom_utilisateur, adresse_email, avatar, date_inscription
		FROM membres
		");
 
	$requete->execute();
	if ($result = $requete->fetchAll(PDO::FETCH_ASSOC)) {
 
		$requete->closeCursor();
		return $result;
	}
	return false;
 
}
2) le contrôleur "afficher_liste_membres.php"
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
if (!utilisateur_est_connecte()) {
 
	// On affiche la page d'erreur comme quoi l'utilisateur doit être connecté pour voir la page
	include CHEMIN_VUE_GLOBALE.'erreur_non_connecte.php';
 
} else {
 
 
	// on selection la liste des membres inscrits
	$liste_membres=selectioner_liste_membres();
 
	if (!empty($liste_membres)) {
 
 
 
			include CHEMIN_VUE.'liste_membres_utilisateurs.php';
 
 
	}
	else {
 
		include CHEMIN_VUE.'liste_membres_inexistante.php';
	}
 
 
}
3) la Vue "liste_membres_utilisateurs.php" :

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php 
$html_liste='';
foreach($liste_membres as $num_membre=>$infos_membre) {
 
	list(,$nom_utilisateur,$adresse_email,$avatar,$date_inscription) = array_values($infos_membre);
 
	$html_liste.='<h2>Profil de '.htmlspecialchars($nom_utilisateur).'</h2>';
	$html_liste.='<table border="1"><tr><img  src="'.$avatar.'" title="Avatar de '.htmlspecialchars($nom_utilisateur).'" BORDER="2" WIDTH="'.AVATAR_LARGEUR_MAXI.'" HEIGHT="'.AVATAR_HAUTEUR_MAXI.'" /></td></tr></table>';
 
 
 
	$html_liste.='<p>';
 
	$html_liste.='<span class="label_profil">Adresse email</span> : '.htmlspecialchars($adresse_email).'<br />';
	$html_liste.='<span class="label_profil">Date d inscription</span> : '.dateFR($date_inscription);
 
}
 
 
echo $html_liste;

Ma question est : Est-il possible de recoder le contrôleur et la vue de manière à respecter la contrainte précédente que je me suis imposée?

Merci infiniment pour toute suggestion qui pourrait m'aider à résoudre ce problème !
frenchem67 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 30/01/2012, 18h46   #2
Membre confirmé
 
Homme Clément
Développeur informatique
Inscription : décembre 2006
Messages : 213
Détails du profil
Informations personnelles :
Nom : Homme Clément
Localisation : France

Informations professionnelles :
Activité : Développeur informatique

Informations forums :
Inscription : décembre 2006
Messages : 213
Points : 277
Points : 277
Ton code respecte déjà plus bien le MVC (contrairement à ce que l'on peut voir un peu partout) !

Pour l'améliorer, je vois pas grand chose : le code de ta vue, ne fait rien d'autre qu'initialiser une chaine de caractères, pour ensuite faire un echo dessus.
Une solution un peu plus propre (et aussi plus efficace car pas de concaténation de chaine), est d'écrire le HTML directement comme template, et d'inclure les <?php ?> là où il faut. Ton code sera pas beaucoup plus court, mais tu gagnes en lisibilité avec un bon environnement de dev qui te fera la coloration syntaxique. Ca te permet aussi de bien indenter ton code, lorsque celui-ci sera plus complexe (plein de balises imbriquées).
Exemple :
Code :
1
2
 
<h2>Profil de <?php echo(htmlspecialchars($nom_utilisateur)) ?></h2>
Pour réduire la taille du code, tu peux éventuellement faire des fonctions de ce genre :
Code :
1
2
3
function h($var) {
  echo(htmlspecialchars($var));
}
Et sinon, mais là c'est sujet à de grands débats, tu peux utiliser un moteur de template, comme Smarty.

EDIT : il te manque une balise td ouvrante dans ta table.
Climoo est déconnecté   Envoyer un message privé Réponse avec citation 10
Vieux 30/01/2012, 19h08   #3
Nouveau Membre du Club
 
Homme Cédric Bohnert
En auto-formation
Inscription : juillet 2004
Messages : 71
Détails du profil
Informations personnelles :
Nom : Homme Cédric Bohnert
Âge : 33
Localisation : France, Bas Rhin (Alsace)

Informations professionnelles :
Activité : En auto-formation
Secteur : Conseil

Informations forums :
Inscription : juillet 2004
Messages : 71
Points : 28
Points : 28
Merci Climoo pour ta réponse rapide.

Hélas, je n'ai pas encore étudié la possibilité d'utiliser un moteur de templates. Pour le moment, je développe pour m'entrainer à comprendre le patron MVC dans le but futur de pouvoir bien utiliser un framework comme Zend.

En ce qui concerne mon code, merci, j'ai corrigé les oublis, et je ne vois pas non plus comment faire pour bien séparer la vue.

En effet, la liste des membres est sujette à évolution : leur nombre n'est pas constant et fixé, ce serait trop simple !

Aussi je ne vois pas d'autre moyen, pour l'instant, que de construire un tableau HTML par concaténation d'une chaine ....

A moins que !

Je pense maintenant à construire une classe de génération d'une table HTML avec comme attributs le nombre de lignes et colonnes.

Genre :

Code :
$tableau_membres = new Table($nb_membres, $nb_champs);
Puis l'afficher avec un echo...

Qu'en penses tu ? Faisable ?
frenchem67 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 30/01/2012, 19h15   #4
Expert Confirmé
 
Avatar de RunCodePhp
 
Inscription : janvier 2010
Messages : 2 727
Détails du profil
Informations personnelles :
Localisation : Réunion

Informations forums :
Inscription : janvier 2010
Messages : 2 727
Points : 3 294
Points : 3 294
Salut

Je te donne un avis personnel (donc à voir).
Qu'il y ait du code Php dans la vue n'est la plus grosse contrainte, à partir du moment où on ne mélange pas avec le modèle ou le controller ou autre.

Ce qui m'interpelle le plus dans la manière où tu procède, c'est dans la récupération de la vue qui à mon sens manque ou manquera tôt ou tard de souplesse.

Je te donne une technique qui est assez utilisée dans les FrameWork.
L'idée est simple, c'est de pouvoir récupérer le contenu des vues à n'importe quel moment au niveau du controller.

Un code/exemple vos mieux qu'un grand discourt.
Juste le principe de base (donc code très basique, avec une fonction) :
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 
function captureVue($vue_filename, array $vue_data = NULL) {
    if (empty($vue_filename)) {
        return FALSE;
    }
    else {
        if (!empty($vue_data)) extract($vue_data, EXTR_SKIP);
        // 
        ob_start();
        // Charge la vue dans le scope courant
        try {
            include($vue_filename);
        }
        catch (Exception $e) {
            // Cette fonction vide le tampon de sortie sans l'envoyer au navigateur.
            ob_clean();
            trigger_error($e->getMessage());
        }
        // Retourne la capture de sortie et vide le buffer
        return ob_get_clean();
    }
}
Donc l'idée principale est de stocker/récupérer chaque vue au niveau du controller, et du faite que les vues ne sont pas générées mais stockées, ça permet de les appeler dans le désordre (le footer en 1er, puis le menu de gauche, ensuite le header, etc ...).
En somme, on est totalement indépendant du déroulement du Html, par conséquent on fait les chose selon le déroulement du Php, et ça change tout.

Ensuite, le controller à la fin génère l'ensemble des vues une fois avoir recoller les morceaux dans le bon ordre (un système de template pourrait faire l'affaire par exemple).

Tu remarqueras que la fonction attend un tableau, ce tableau peu contenir tout ce qu'on veut (comme d'autres tableau et même des Objets.
Le tableau est extrait, et chaque élément sera une variable, elle pourra alors être exploité dans la vue.


Comme je l'ai dit, c'est un code basique, plus pour voir si cela t’intéresse.
Normalement il faudrait créer au minimum une classe View qui se chargerait de retourner la vue.



Pour ma part, il est quasi impossible de créer des vues 100% statique (100% en Html), on a toujours besoin de données dynamiques.
On peu exploiter des truc comme Smarty ou Twig, mais ceci me semble plutôt utile lorsque des non développeurs doivent intervenir dans ces fichiers.
Ceci dit, ces outils là demandent d'apprendre un nouveau langage pour ainsi dire.
Faut voir.
__________________
Win XP | WampServer 2.2d | Apache 2.2.21 | Php 5.3.10 | MySQL 5.5.20
Si debugger, c'est supprimer des bugs, alors programmer ne peut être que les ajouter [Edsger Dijkstra]
RunCodePhp est déconnecté   Envoyer un message privé Réponse avec citation 10
Vieux 30/01/2012, 19h38   #5
Nouveau Membre du Club
 
Homme Cédric Bohnert
En auto-formation
Inscription : juillet 2004
Messages : 71
Détails du profil
Informations personnelles :
Nom : Homme Cédric Bohnert
Âge : 33
Localisation : France, Bas Rhin (Alsace)

Informations professionnelles :
Activité : En auto-formation
Secteur : Conseil

Informations forums :
Inscription : juillet 2004
Messages : 71
Points : 28
Points : 28
Merci à toi aussi RunCodePhp pour ton commentaire très constructif !

Effectivement, je commence aussi à penser que la vue ne peut être à 100% statique pour une application web évoluée.

Très intéressante ta technique de récupération de la vue au niveau du contrôleur. Et c'est déjà un pas de plus dans le sens du développement d'un framework.

Je n'ai pas cette prétention, mais si je pouvais me construire un ensemble de classes perso utiles et réutilisables, ce ne serait que bon pour mes projets !

Aussi pourrais tu, stp, me donner d'autres conseils voire des articles pour construire une telle classe View ?
frenchem67 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 30/01/2012, 19h54   #6
Expert Confirmé
 
Avatar de RunCodePhp
 
Inscription : janvier 2010
Messages : 2 727
Détails du profil
Informations personnelles :
Localisation : Réunion

Informations forums :
Inscription : janvier 2010
Messages : 2 727
Points : 3 294
Points : 3 294
La classe View
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
<?php defined('ENVIRON') or exit('Aucun accès directe.');
/** 
 * view.php
 * 
 */
 
/**
 * Classe Runfw_View
 *
 */
class Runfw_View {
    /**
     * Nom de la vue
     * @var string Vue
     */
    protected $file;
 
    /**
     * Gloable data
     * @var array Data
     */
    protected $data = array();
 
    /**
     * Returns a new View object. If you do not define the "file" parameter,
     * you must call [View::set_filename].
     *
     *     $view = View::factory($file);
     *
     * @param   string  view filename
     * @param   array   array of values
     * @return  View
     */
    public static function factory($file = NULL, array $data = NULL) {
        return new View($file, $data);
    }
 
 
    public function __construct($file = NULL, array $data = NULL) {
        if (!empty($file)) $this->file = Controller::getFilenameVue($file);
        if ($data !== NULL) {
            // Add the values to the current data
            $this->_data = $data + $this->_data;
        }
    }
 
    public static function capture($vue_filename, array $vue_data = NULL) {
        if (empty($vue_filename)) {
            //echo '$vue_filename : '.$vue_filename.'<br />';
            //$array = debug_backtrace();
            //Sp::pr($array);
            //exit();
            return FALSE;
        }
        else {
            // Import variables de la vue avec leur nom
            if (!empty($vue_data)) extract($vue_data, EXTR_SKIP);
            //
            ob_start();
            // Charge la vue dans le scope courant
            try {
                include($vue_filename);
            }
            catch (Exception $e) {
                // Cette fonction vide le tampon de sortie sans l'envoyer au navigateur.
                ob_clean();
                Runfw::exceptionHandler($e);
            }
            // Retourne la capture de sortie et vide le buffer
            return ob_get_clean();
        }
    }
 
    public static function inclure($vue_filename) {
        if (empty($vue_filename)) {
            //echo '$vue_filename : '.$vue_filename.'<br />';
            //$array = debug_backtrace();
            //Sp::pr($array);
            trigger_error('['.__METHOD__.'] Vue inexistante, inclusion impossible.', E_USER_WARNING);
        }
        else {
            // Charge la vue dans le scope courant
            include($vue_filename);
        }
    }
 
    public function & __get($key) {
        if (isset($this->data[$key])) {
            return $this->data[$key];
        }
    }
 
    public function __set($key, $value) {
        $this->set($key, $value);
    }
 
    public function set($key, $value = NULL) {
        if (is_array($key)) {
            foreach ($key as $name => $value) {
                $this->data[$name] = $value;
            }
        }
        else {
            $this->data[$key] = $value;
        }
        //
        return $this;
    }
 
    //--------------------------------------------------------------------------
    /**
     * Magic method, returns the output of [View::render].
     * @return string
     * @uses View::render
     */
    public function __toString() {
        try {
            return $this->render();
        }
        catch (Exception $e) {
            // Display the exception message
            Runfw::exceptionHandler($e);
            return '';
        }
    }
 
    /**
     * Renders the view object to a string
     * @param string $file
     * @return string nom du fichier
     * @throws Kohana_View_Exception
     */
    public function render($file = NULL) {
        if (is_null($file) == FALSE) {
            $this->file = Controller::getFilenameVue($file);
        }
        //
        if (empty($this->file)) {
            //throw new Kohana_View_Exception('You must set the file to use within your view before rendering');
            //Runfw_Exception
            throw new Exception('['.__METHOD__.'] Aucun nom pour la vue vue.', E_USER_WARNING);
        }
        else {
            // Inclus la vue avec des données au besoin et renvoi la sortie
            return View::capture($this->file, $this->data);
        }
    }
}
Ce code est un tout petit peu personnalisé, il reste quand même assez fidèle du FrameWork Kohana.
Ce FrameWork est excellent quand on débute dans ce domaine, car il est assez facile à prendre en main, le code n'est pas énorme/volumineux, on peu le parcourir petit à petit et comprendre comment tout ça se déroule.

Son gros point faible, c'est la doc quasi inexistante, et le peu qu'il y a est souvent obsolète, car non compatible entre les anciennes et nouvelles versions.


Ca reste une solution, sans nulle doute qu'il y en a d'autres.
__________________
Win XP | WampServer 2.2d | Apache 2.2.21 | Php 5.3.10 | MySQL 5.5.20
Si debugger, c'est supprimer des bugs, alors programmer ne peut être que les ajouter [Edsger Dijkstra]
RunCodePhp est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 30/01/2012, 20h39   #7
Nouveau Membre du Club
 
Homme Cédric Bohnert
En auto-formation
Inscription : juillet 2004
Messages : 71
Détails du profil
Informations personnelles :
Nom : Homme Cédric Bohnert
Âge : 33
Localisation : France, Bas Rhin (Alsace)

Informations professionnelles :
Activité : En auto-formation
Secteur : Conseil

Informations forums :
Inscription : juillet 2004
Messages : 71
Points : 28
Points : 28
Je vais retenir l'idée et parcourir le code de Kahona plus tard, car j'ai peur que mes connaissances actuelles ne puissent pas encore m'aider à le comprendre.

Il me faut encore un peu plus de théorie sur le MVC et les constituants d'un framework avant d'en aborder un !

Je vais continuer à lire des articles et tutos sur le sujet. Quand le moment sera venu je mettrai mon nez dans le code ...
frenchem67 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/01/2012, 07h08   #8
Expert Confirmé
 
Avatar de RunCodePhp
 
Inscription : janvier 2010
Messages : 2 727
Détails du profil
Informations personnelles :
Localisation : Réunion

Informations forums :
Inscription : janvier 2010
Messages : 2 727
Points : 3 294
Points : 3 294
Retiens tout de même cette technique de chargement de vues indépendant, ça t'apportera un gros plus, ça ne fait aucun doute, même en le faisant avec une simple fonction comme dans l'exemple.

Un exemple pour utiliser cette fonction :
Code :
1
2
3
4
5
6
7
8
9
// Dans le controller
// Une liste de membres venant de la Bdd (requête SQL)
$membres = array(
    '1' => array('nom' => NOM1, 'prenom' => 'Prénom1'), 
    '2' => array('nom' => 'NOM2', 'prenom' => 'Prénom2')
);
 
// Chargement de la vue
$vue_membres = captureVue('chemin/vers/la/vue.php', array('membres' => $membres, 'titre' => 'Un titre'));
Dans la vue
Code :
1
2
3
4
5
6
7
8
9
 
<h1><?php echo $titre; ?></h1>
<?php
if (isset($membres) && !empty($membres)) {
    foreach ($membres as $membre) {
        ... etc ...
    }
}
?>
Tu as dû remarqué aussi dans ce système de chargement de vue qu'il y a un ob_start/ob_get_clean().
Ceci permet de mettre directement du HTML ou des echo dans les vues, donc d'éviter comme tu le fais de tout stocker dans une variable.
Mais surtout, ça évite tout conflit avec des noms de variables qui pourraient être communs à plusieurs vues.
En somme, comme on passe par une fonction et qu'en plus tout est vidé après exécution, on ne rencontrera pas de problème de portée (ou visibilité) sur les variables.



Ensuite, il me semble que le controller ne soit pas Objet, mais procédural.
Si c'est le cas, il faudrait améliorer ce point là, faire une classe genre FrontController, une classe Parente à tous les controller.
Puis créer une classe Controller par page.
Aussi, faudrait voir comment toutes ces pages sont structurées/organisées.
Il est bon de s'imposer une structure et aussi certaines règles de nommage des fichiers se qui permettra d'automatiser un peu plus les choses.

Par exemple, stocker tous les controller dans un répertoire "controller", les modèles dans un répertoire "model", et les vues dans "view".
Ensuite nommer chaque classes comme :
Model_listeMembres (nom du fichier : Model_listeMembres.php)
Controller_listeMembres (nom du fichier : Controller_listeMembres.php)
View_listeMembres (nom du fichier : View_listeMembres.php)

L'idée est de nommer les classes selon l'arborescence adoptée, se qui par exemple permettra de faire un auto-chargement de classes (un autoload).
En somme, chaque underscore (_) correspond à un slash (/) du système de fichier.
(Kohana, ZendFrameWork entre autre adoptent se principe là).
__________________
Win XP | WampServer 2.2d | Apache 2.2.21 | Php 5.3.10 | MySQL 5.5.20
Si debugger, c'est supprimer des bugs, alors programmer ne peut être que les ajouter [Edsger Dijkstra]
RunCodePhp est déconnecté   Envoyer un message privé Réponse avec citation 10
Vieux 31/01/2012, 21h31   #9
Nouveau Membre du Club
 
Homme Cédric Bohnert
En auto-formation
Inscription : juillet 2004
Messages : 71
Détails du profil
Informations personnelles :
Nom : Homme Cédric Bohnert
Âge : 33
Localisation : France, Bas Rhin (Alsace)

Informations professionnelles :
Activité : En auto-formation
Secteur : Conseil

Informations forums :
Inscription : juillet 2004
Messages : 71
Points : 28
Points : 28
Plus je relis tes messages, et plus je comprends leur contenu ! C'est formidable !

J'aime de plus en plus cette manière (le MVC) de structurer une application, qui est bien entendu néanmoins difficile à aborder.

Pour résumer :

1) Je vais restructurer mon arborescence et renommer mes fichiers selon la convention de nomenclature que tu m'as conseillée.

J'ai déjà fait une bonne partie du travail dès le départ, mais je n'ai pas utilisé les préfixes "Controler_", "View_" et "Model_".

C'est la première chose à faire, je pense, et il faudra penser à recoder tous les appels aux fichiers utilisés dans le module. Juste ?

Donc, ensuite :

2) Je vais coder une classe autoload générique pour mes appels aux trois différentes parties : les controlers, les view et les models.

3) Je vais lire les articles http://julien-pauli.developpez.com/t...vc-controleur/ et http://tahe.developpez.com/web/php/mvc/.

Ils vont me permettre de mieux comprendre les classes de FrontControler et les Controler et de View.

Je vais tenter finalement de recoder mon module d'espace membres avec les mêmes fonctionnalités mais en "objectifiant" les blocs redondants.

Voilà, tu m'as donné les infos, je te remercie encore, car j'en ai saisi une bonne partie. Par contre je sais pas si j'ai bon dans ma manière d'ordonner un peu mes futures taches.

Ai-je bon ?
frenchem67 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 01/02/2012, 02h32   #10
Nouveau Membre du Club
 
Homme Cédric Bohnert
En auto-formation
Inscription : juillet 2004
Messages : 71
Détails du profil
Informations personnelles :
Nom : Homme Cédric Bohnert
Âge : 33
Localisation : France, Bas Rhin (Alsace)

Informations professionnelles :
Activité : En auto-formation
Secteur : Conseil

Informations forums :
Inscription : juillet 2004
Messages : 71
Points : 28
Points : 28
Re,


J'ai trouvé sur ce blog une classe Autoloader universelle dont voici la source :

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
<?php
class ExtensionFilterIteratorDecorator extends FilterIterator {
   private $_ext;
   public function accept (){
      if (substr ($this->current (), -1 * strlen ($this->_ext)) === $this->_ext){
         return is_readable ($this->current ());
      }
      return false;
   }
   public function setExtension ($pExt){
      $this->_ext = $pExt;
   }
}
 
interface IClassHunter {
   public function find ($pFileName);
}
 
class ClassHunterForPHP5_3 implements IClassHunter {
   public function find ($pFileName){
      $toReturn = array ();
      $tokens = token_get_all (file_get_contents ($pFileName, false));
 
      $currentNamespace = '';
      $namespaceHunt = false;
      $validatedNamespaceHunt = false;
      $classHunt = false;
      $whitespaceCount = 0;
      foreach ($tokens as $token){
         if (is_array ($token)){
            if ($token[0] === T_INTERFACE || $token[0] === T_CLASS){
               $classHunt = true;
               continue;
            }elseif ($token[0] === T_NAMESPACE){
               $namespaceHunt = true;
               continue;
            }
 
            if ($classHunt && $token[0] === T_STRING){
               $toReturn[(strlen ($currentNamespace) > 0 ? $currentNamespace.'\\' : '').$token[1]] = $pFileName;
               $classHunt = false;
            }elseif ($namespaceHunt && $validatedNamespaceHunt && ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR)){
               $currentNamespace .= $token[1];
            }elseif ($namespaceHunt && !$validatedNamespaceHunt && $token[0] === T_WHITESPACE){
               $currentNamespace = '';
               $validatedNamespaceHunt = true;
            }elseif ($namespaceHunt && !$validatedNamespaceHunt && $token[0] !== T_WHITESPACE){
               $namespaceHunt = false;
            }
         }else{
            if ($token === ';' || $token === '{'){
               //le seul cas ou cela permet de valider un namespace est la déclaration d'un namespace par défaut namespace{}
               if ($namespaceHunt && !$validatedNamespaceHunt && $token === '{'){
                  $currentNamespace = '';
               }
               $classHunt = false;
               $namespaceHunt = false;
               $validatedNamespaceHunt = false;
            }
         }
      }
      return $toReturn;
   }
}
 
class ClassHunterForPHP5_2 implements IClassHunter {
   public function find ($pFileName){
      $toReturn = array ();
      $tokens = token_get_all (file_get_contents ($pFileName, false));
      $tokens = array_filter ($tokens, 'is_array');
 
      $classHunt = false;
      foreach ($tokens as $token){
         if ($token[0] === T_INTERFACE || $token[0] === T_CLASS){
            $classHunt = true;
            continue;
         }
 
         if ($classHunt && $token[0] === T_STRING){
            $toReturn[$token[1]] = $pFileName;
            $classHunt = false;
         }
      }
      return $toReturn;
   }
}
 
class DirectoriesAutoloaderException extends Exception {}
 
class DirectoriesAutoloader {
   private $_classHunterStrategy;
 
   //--- Singleton
   private function __construct (){}
   private static $_instance = false;
   public static function instance ($pTmpPath){
      if(self::$_instance === false){
         self::$_instance = new DirectoriesAutoloader();
         self::$_instance->setCachePath ($pTmpPath);
         self::$_instance->_classHunterStrategy = ClassHunterFactory::create (PHP_VERSION);
      }
      return self::$_instance;
   }
   //--- /Singleton
 
   public function register (){
      spl_autoload_register (array ($this, 'autoload'));
   }
 
   //--- Cache
   private $_cachePath;
   public function setCachePath ($pTmp){
      if (!is_writable ($pTmp)){
         throw new DirectoriesAutoloaderException('Cannot write in given CachePath ['.$pTmp.']');
      }
      $this->_cachePath = $pTmp;
   }
   //--- /Cache
 
   //--- Autoload
   public function autoload ($pClassName){
      //On regarde si on connais la classe
      if ($this->_loadClass ($pClassName)){
         return true;
      }
 
      //Si on a le droit de tenter la regénération du fichier d'autoload, on retente l'histoire
      if ($this->_canRegenerate){
         $this->_canRegenerate = false;//pour éviter que l'on
         $this->_includesAll ();
         $this->_saveInCache ();
         return $this->autoload ($pClassName);
      }
      //on a vraiment rien trouvé.
      return false;
   }
   private $_canRegenerate = true;
   //--- /Autoload
 
   /**
    * Recherche de toutes les classes dans les répertoires donnés
    */
   private function _includesAll (){
      //Inclusion de toute les classes connues
      foreach ($this->_directories as $directory=>$recursive){
         $directories = new AppendIterator ();
 
         //On ajoute tous les chemins à parcourir
         if ($recursive){
            $directories->append (new RecursiveIteratorIterator (new RecursiveDirectoryIterator ($directory)));
         }else{
            $directories->append (new DirectoryIterator ($directory));
         }
 
         //On va filtrer les fichiers php depuis les répertoires trouvés.
         $files = new ExtensionFilterIteratorDecorator ($directories);
         $files->setExtension ('.php');
 
         foreach ($files as $fileName){
            $classes = $this->_classHunterStrategy->find ((string) $fileName);
            foreach ($classes as $className=>$fileName){
               $this->_classes[strtolower ($className)] = $fileName;
            }
         }
      }
   }
 
   private $_classes = array ();
   private function _saveIncache (){
      $toSave = '<?php $classes = '.var_export ($this->_classes, true).'; ?>';
      if (file_put_contents ($this->_cachePath.'directoriesautoloader.cache.php', $toSave) === false){
         throw new DirectoriesAutoloaderException ('Cannot write cache file '.$this->_cachePath.'directoriesautoloader.cache.php');
      }
   }
 
   /**
    * Tente de charger une classe
    */
   private function _loadClass ($pClassName){
      $className = strtolower ($pClassName);
      if (count ($this->_classes) === 0){
         if (is_readable ($this->_cachePath.'directoriesautoloader.cache.php')){
            require ($this->_cachePath.'directoriesautoloader.cache.php');
            $this->_classes = $classes;
         }
      }
      if (isset ($this->_classes[$className])){
         require_once ($this->_classes[$className]);
         return true;
      }
      return false;
   }
 
   /**
    * Ajoute un répertoire a la liste de ceux à autoloader
    */
   public function addDirectory ($pDirectory, $pRecursive = true){
      if (! is_readable ($pDirectory)){
         throw new DirectoriesAutoloaderException('Cannot read from ['.$pDirectory.']');
      }
      $this->_directories[$pDirectory] = $pRecursive ? true : false;
      return $this;
   }
   private $_directories = array ();
}
 
class ClassHunterFactory {
   public static function create ($version){
      if (($result = version_compare ($version, '5.3.0')) >= 0){
         return new ClassHunterForPHP5_3 ();
      }
      return new ClassHunterForPHP5_2 ();
   }
}
Elle fonctionne nickel, je vais donc l'utiliser dans mon projet !

Mon arborescence est la suivante :
  • global : tous les fichiers globaux du projet (config.php, init.php, etc).
  • images : tous les fichiers images.
    • avatars : tous les avatars des membres.
  • libs : toutes les classes externes et génériques.
  • modules : c'est là que vont être mes différents modules du projet (membres, livreor, articles, etc.).
    • membres : le module de gestion espace membres.
      • controlers
      • views
      • models
  • style : les fichiers css.

Maintenant je vais bosser sur mon module membres proprement dit, à savoir, les controlers, viewers, et models.
frenchem67 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 01/02/2012, 07h30   #11
Expert Confirmé
 
Avatar de RunCodePhp
 
Inscription : janvier 2010
Messages : 2 727
Détails du profil
Informations personnelles :
Localisation : Réunion

Informations forums :
Inscription : janvier 2010
Messages : 2 727
Points : 3 294
Points : 3 294
Là, il y a des choses qui me dépassent

Pourquoi fais tu des codes selon l'environnement de Php (php5.2/php5.3) ?

Est-ce par ce qu'il y aura plusieurs personnes/codeurs qui utilisera ton code dans des environnement Php ? (comme tout projet Open Source finalement).

C'est quoi au juste "un espace membre générique" ?


Je ne sais ce que les autres pense sur le sujet, mais offrir une compatibilité Php5.2 et Php5.3 comme les namespace ça me semble rudement compliqué voir impossible, c'est pas loin d'être comme Php4 et Php5.

Dans tous les projets Open Source que je vois, ils optent pour 2 versions différentes, du moins ça me semble le cas pour php5.2 et php5.3 à cause des grosses différences qu'il y a entre ces 2 versions.


Est-ce que selon ton besoin la création de nouvelles pages (nouveaux controller entre autre) doit être modulaire ?

De mon coté je perçois plutôt qu'un module c'est une fonctionnalité en plus du "moteur" (ou core) de base.
Par exemple : une connexion à une Bdd, envoyer un mail, créer un PDF, parser un XML, etc ...

Juste pour exemple, les structures s'approchent de ceci :
.root/www/ (fichiers publique [images, css, js, etc ...])

.root/system/ (le core du projet [commun à 1 voire plusieurs applications/site Web])

.root/application1/model/
.root/application1/view/
.root/application1/controller/

.root/modules/mailing/ (bibliotèque pour envoyer des mails)
.root/modules/pdf/ (bibliotèque pour créer des PDF)
... etc ... autres modules ...

Ici, on peu donc utiliser un "core" commun à plusieurs applications (sites Web)
Les modules sont aussi indépendant (comme le core), toutes les applications peuvent les exploiter.
(Si on crée un autre site sur le même serveur, on créera alors un autre virtual host (genre www2), et un autre répertoire pour l'application (genre application2).

Ceci dit, rien empêche de créer dans chaque application un répertoire "genre lib" pour créer leur propre librairies spécifiques.
Ca peut être de nouvelles classes, classes étendues des modules communs, classes étendue du core, etc ...


C'est juste un exemple de structure évidemment, tout dépend des besoins.
__________________
Win XP | WampServer 2.2d | Apache 2.2.21 | Php 5.3.10 | MySQL 5.5.20
Si debugger, c'est supprimer des bugs, alors programmer ne peut être que les ajouter [Edsger Dijkstra]
RunCodePhp est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Proposer ce sujet en actualité
Outils de la discussion



Fuseau horaire GMT +2. Il est actuellement 07h01.


 
 
 
 
Partenaires

Hébergement Web