IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage PHP Discussion :

Patron MVC : bien séparer la vue.


Sujet :

Langage PHP

  1. #1
    Membre du Club Avatar de CompuTux
    Homme Profil pro
    Développeur Python et Django
    Inscrit en
    Juillet 2004
    Messages
    82
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur Python et Django
    Secteur : Conseil

    Informations forums :
    Inscription : Juillet 2004
    Messages : 82
    Points : 68
    Points
    68
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 !

  2. #2
    Membre averti

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Décembre 2006
    Messages
    242
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Décembre 2006
    Messages : 242
    Points : 354
    Points
    354
    Par défaut
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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.

  3. #3
    Membre du Club Avatar de CompuTux
    Homme Profil pro
    Développeur Python et Django
    Inscrit en
    Juillet 2004
    Messages
    82
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur Python et Django
    Secteur : Conseil

    Informations forums :
    Inscription : Juillet 2004
    Messages : 82
    Points : 68
    Points
    68
    Par défaut
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    $tableau_membres = new Table($nb_membres, $nb_champs);
    Puis l'afficher avec un echo...

    Qu'en penses tu ? Faisable ?

  4. #4
    Membre expert Avatar de RunCodePhp
    Profil pro
    Inscrit en
    Janvier 2010
    Messages
    2 962
    Détails du profil
    Informations personnelles :
    Localisation : Réunion

    Informations forums :
    Inscription : Janvier 2010
    Messages : 2 962
    Points : 3 947
    Points
    3 947
    Par défaut
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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]

  5. #5
    Membre du Club Avatar de CompuTux
    Homme Profil pro
    Développeur Python et Django
    Inscrit en
    Juillet 2004
    Messages
    82
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur Python et Django
    Secteur : Conseil

    Informations forums :
    Inscription : Juillet 2004
    Messages : 82
    Points : 68
    Points
    68
    Par défaut
    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 ?

  6. #6
    Membre expert Avatar de RunCodePhp
    Profil pro
    Inscrit en
    Janvier 2010
    Messages
    2 962
    Détails du profil
    Informations personnelles :
    Localisation : Réunion

    Informations forums :
    Inscription : Janvier 2010
    Messages : 2 962
    Points : 3 947
    Points
    3 947
    Par défaut
    La classe View
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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]

  7. #7
    Membre du Club Avatar de CompuTux
    Homme Profil pro
    Développeur Python et Django
    Inscrit en
    Juillet 2004
    Messages
    82
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur Python et Django
    Secteur : Conseil

    Informations forums :
    Inscription : Juillet 2004
    Messages : 82
    Points : 68
    Points
    68
    Par défaut
    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 ...

  8. #8
    Membre expert Avatar de RunCodePhp
    Profil pro
    Inscrit en
    Janvier 2010
    Messages
    2 962
    Détails du profil
    Informations personnelles :
    Localisation : Réunion

    Informations forums :
    Inscription : Janvier 2010
    Messages : 2 962
    Points : 3 947
    Points
    3 947
    Par défaut
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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]

  9. #9
    Membre du Club Avatar de CompuTux
    Homme Profil pro
    Développeur Python et Django
    Inscrit en
    Juillet 2004
    Messages
    82
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur Python et Django
    Secteur : Conseil

    Informations forums :
    Inscription : Juillet 2004
    Messages : 82
    Points : 68
    Points
    68
    Par défaut
    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 ?

  10. #10
    Membre du Club Avatar de CompuTux
    Homme Profil pro
    Développeur Python et Django
    Inscrit en
    Juillet 2004
    Messages
    82
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur Python et Django
    Secteur : Conseil

    Informations forums :
    Inscription : Juillet 2004
    Messages : 82
    Points : 68
    Points
    68
    Par défaut
    Re,


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

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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.

  11. #11
    Membre expert Avatar de RunCodePhp
    Profil pro
    Inscrit en
    Janvier 2010
    Messages
    2 962
    Détails du profil
    Informations personnelles :
    Localisation : Réunion

    Informations forums :
    Inscription : Janvier 2010
    Messages : 2 962
    Points : 3 947
    Points
    3 947
    Par défaut
    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]

Discussions similaires

  1. Réponses: 8
    Dernier message: 21/05/2014, 09h47
  2. Réponses: 4
    Dernier message: 21/02/2008, 17h05
  3. [MVC/Organisation] Séparer le Modèle/Métier de la Vue/Controller
    Par Wookai dans le forum Développement Web en Java
    Réponses: 5
    Dernier message: 25/06/2007, 11h03
  4. [Spring MVC] Renvoyer vers une vue depuis un intercepteur
    Par ChtiGeeX dans le forum Spring Web
    Réponses: 2
    Dernier message: 01/04/2007, 10h26
  5. [SWT][MVC] Comment séparer métier et présentation
    Par pyorg dans le forum SWT/JFace
    Réponses: 3
    Dernier message: 27/08/2004, 18h21

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo