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 :

Programmation orientée composant avec PHP (sans framework)


Sujet :

Langage PHP

  1. #1
    Nouveau membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2015
    Messages
    8
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mars 2015
    Messages : 8
    Par défaut Programmation orientée composant avec PHP (sans framework)
    Bonjour, je viens de lire sur Wikipedia l'article présentant la POC.
    D'après cet article: Il est possible de créer des composants avec la grande majorité des langages. Toutefois, dans certains cas, notamment pour les langages interprétés ou semi-compilés il n'est pas possible de créer des composants "classiques".

    J'aimerais donc savoir si quelqu'un avait déjà mis en pratique cette approche nativement avec PHP? (sans aucun framework)
    Et surtout comment vous avez fait ?

    Le but étant de changer l'implémentation d'une classe dans une application uniquement en remplacant le fichier composant.php
    Et donc sans modifier le code de l'application qui utilise le composant en appelant ses méthodes.
    Un peut comme un Plugin mais à l'envers:
    Les plugins peuvent être ajoutés et utilisent l'API de l'application.
    Alors q'un composant (dépendance) est utilisé par l'application via sont interface peu importe sont implémentation.

    Donc si je créer 2 classes qui font la même chose mais différement et qui implémentent la même interface, comment faire pour choisir la classe qui sera utilisée par l'appli sans modifier le code ?
    Ce qui est le principe de la POC si j'ai bien tout compris...

    Merci pour vos réponses

  2. #2
    Membre confirmé Avatar de alejandro
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Septembre 2004
    Messages
    167
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Tarn (Midi Pyrénées)

    Informations professionnelles :
    Activité : Chef de projet NTIC
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Septembre 2004
    Messages : 167
    Par défaut
    Il me semble que le patron de conception stratégie adresse ton problème.

    Renseigne toi sur le polymorphisme en php, par exemple en lisant cet article sur le polymorphisme en PHP ou des cours et tutoriels PHP.
    En tant qu'exemple, chacun à sa façon de programmer, ci-dessous quelques pistes à explorer sur une ébauche de programme (PHP 5.4 mini) :

    Arborescence :

    |index.php
    |strategy
    --|main.php
    --|rest.php
    |pattern
    --|filterinterface.php
    --|interceptingfilters.php
    --|singleton.php
    --|strategy.php
    --|strategyinterface.php
    |http
    --|request

    index.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
     
    /**
     * This is the application bootstrap
     */
     
    /**
     * Setup some PHP engine options
     */
    ini_set('display_errors','1');
     
    /**
     * Starts the SPL autoloading service
     */
    spl_autoload_register();
     
    /**
     * Run the main application strategy
     */
    Strategy\Main::getInstance()->execute();
    main.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
    <?php namespace Strategy;
     
    use \Pattern\InterceptingFilters;
    use \Pattern\StrategyInterface;
     
    /**
     * The main application strategy consists of a front controller extending
     * the intercepting filters pattern and defines default application behaviours.
     */
     
    class Main extends InterceptingFilters
    {
        use \Pattern\Singleton;
     
        public function init(StrategyInterface $strategy = NULL)
        {
            /**
             * If no strategy is provided during instanciation, we set the default
             * strategy as REST and include the corresponding strategy
             */
            if(!$strategy instanceof StrategyInterface) {
                $this->strategy = Rest::getInstance();
            } else $this->strategy = $strategy;
        }
    }
    rest.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
    <?php namespace Strategy;
     
    use \Http;
    use \Pattern;
     
    /**
     * The REpresentational State Transfer strategy as defined by Fielding
     */
     
    final class Rest extends Pattern\Strategy
    {
        use Pattern\Singleton;
     
        /**
         * Implements the REST strategy
         */
        public function execute()
        {
            header('content-type: text/plain');
            echo Http\Request::fromEnv();
        }
    }
    filterinterface.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
    <?php namespace Pattern;
     
    /**
     * The Pattern\Filter defines a unique class interface that allows to implements
     * and define intercepting filters.
     */
     
    interface FilterInterface
    {
        /**
         * Every filter MUST implement both preProcess and postProcess methods.
         * The former is called before processing whereas the later is called once
         * processing has been carried out.
         */
        public function preProcess();
        public function postProcess();
    }
    interceptingfilters.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
    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
    <?php namespace Pattern;
     
    /**
     * The controller pattern implements the intercepting filters design pattern
     * to improve scalability and maintenability. By the way, it extends the
     * strategy pattern to match the whole application design.
     */
     
    abstract class InterceptingFilters extends Strategy implements FilterInterface
    {
        /**
         * Pattern\Filter objects set
         *
         * @var array
         */
        protected $filters = array();
     
        /**
         * Add a Pattern\Filter to the filters collection
         *
         * @arg Pattern\Filter $filter The filter to add to the controller
         * @return Pattern\Controller
         */
        public function addFilter(FilterInterface $filter)
        {
            array_push($this->filters, $filter);
            return $this;
        }
     
        /**
         * Remove a Pattern\Filter from the filters collection
         *
         * @arg Pattern\Filter $filter The filter to remove
         * @return Pattern\Controller
         */
        public function remFilter(FilterInterface $filter)
        {
            foreach($this->filters as $key => $value)
            {
                if ($filter === $value) {
                    unset($this->filters[$key]);
                    break;
                }
            }
            return $this;
        }
     
        /**
         * Run each Pattern\Filter preFilter method one after another.
         */
        public function preProcess()
        {
            foreach($this->filters as $filter) { $filter->preProcess(); }
        }
     
        /**
         * Run each Pattern\Filter postFilter method one after another but in
         * reverse order.
         */
        public function postProcess()
        {
            $reversed_filters = array_reverse($this->filters);
            foreach( $reversed_filters as $filter) { $filter->postProcess(); }
        }
     
        /**
         * We slightly modify the parent execute behaviour to include intercepting
         * filters to perform their job before and after strategy execution.
         */
        public function execute()
        {
            $this->preProcess();
            parent::execute();
            $this->postProcess();
        }
    }
    singleton.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
    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
    <?php namespace Pattern;
     
    /**
     * The singleton design pattern allows only one single instance of a class
     * to be cast via a static getInstance() method.
     */
     
    trait Singleton
    {
        /**
         * Protected constructor ensure the singleton to be cast only via the
         * getInstance method, and therefore to keep only one instance of child
         * class.
         */
        protected function __construct() {}
     
        /**
         * Modified version of the classical getInstance to take advantage of PHP
         * late state binding and allow child classes to inherit this abstract
         * singleton.
         *
         * Simulate constructor by checking for an init() method and calling it
         * with optional arguments provided within getInstance call.
         *
         * @args mixed
         */
        public static function getInstance()
        {
             /**
             * The singleton unique instance
             *
             * @var object Pattern\Singleton
             */
            static $_instance;
     
            /**
             * Creates and initialize a singleton instance
             */
            if(!$_instance instanceof static)
            {
                $_instance = new static; // This is the trick !!!
     
                /**
                 * Thanks to getInstance() we can simulate constructor behaviour
                 * by calling an optional init method provided by the child class.
                 */
                if(method_exists($_instance, 'init'))
                {
                    $args = func_get_args();
                    call_user_func_array(array($_instance, 'init'), $args);
                }
            }
     
            return $_instance;
        }
     
        /**
         * Main principle of the singleton pattern is that a singleton has only
         * one instance of itself throughout the whole application. It cannot
         * therefore be cloned.
         */
        protected function __clone() {}
    }
    strategy.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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    <?php namespace Pattern;
     
    /**
     * The strategy behavioural pattern
     */
     
    abstract class Strategy implements StrategyInterface
    {
        /**
         * The strategy object to execute
         *
         * @var Pattern\Strategy\Facade
         */
        protected $strategy;
     
        /**
         * Return current object strategy
         *
         * @return Pattern\Strategy\Facade
         */
        public function getStrategy()
        {
            return $this->strategy;
        }
     
        /**
         * Set current object strategy
         *
         *
         * @return Pattern\Strategy
         */
        public function setStrategy(StrategyInterface $strategy)
        {
            $this->strategy = $strategy;
            return $this;
        }
     
        /**
         * Run current object strategy
         *
         *
         * @return Pattern\Strategy\Facade
         */
        public function execute()
        {
            if ( $this->strategy instanceof StrategyInterface )
            {
                return $this->strategy->execute();
            }
        }
    }
    strategyinterface.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
    <?php namespace Pattern;
     
    /**
     * The strategy behavioural pattern facade
     */
     
    interface StrategyInterface
    {
        /**
         * This is the core strategy method and each class implementing
         * the Pattern\Strategy\Facade must implement it.
         */
        public function execute();
    }
    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
    <?php namespace Http;
     
    /**
     * We define a RFC2616 compliant HTTP Request Message
     */
     
    class Request
    {
        /**
         * Initialize HTTP Request properties
         */
        public $method, $uri, $version, $headers, $body;
     
        /**
         * Creates a new HTTP Request
         *
         * @arg string $m HTTP Request Method
         * @arg string $u HTTP Request URI
         * @arg array  $h HTTP Request Headers [ 'name' => 'value', ...]
         * @arg string $b HTTP Request Body
         * @arg string $v HTTP Request Version
         */
        public function __construct( $m, $u, array $h, $b = '', $v = 'HTTP/1.1' )
        {
            $this->method = $m;
            $this->uri = $u;
            $this->headers = $h;
            $this->body = $b;
            $this->version = $v;
        }
     
        /**
         * Return current HTTP Request
         *
         * @return Http\Request
         */
        public static function fromEnv()
        {
            static $_request = null;
            if(!$_request instanceof static)
            {
                $_request = new static(
                    $_SERVER['REQUEST_METHOD'],
                    $_SERVER['REQUEST_URI'],
                    apache_request_headers()
                );
            }
            return $_request;
        }
     
        /**
         * HTTP Request is basically a text string
         *
         * @return string
         */
        public function __toString()
        {
            $buffer = "{$this->method} {$this->uri} {$this->version}\r\n";
            foreach($this->headers as $name => $value)
            {
                $buffer .= "$name: $value\r\n";
            }
            $buffer .= "\r\n{$this->body}";
            return $buffer;
        }
    }
    Voilà, tout se passe dans index.php, c'est une sorte de bootstrap pour l'application.
    Le programme gagne en flexibilité grâce à l'utilisation des patrons strategy et intercepting filters dont il n'y a pas d'exemple d'utilisation.

    En espérant que cela constituera un bon point de départ pour faire ce que tu veux, si tu as des questions ...

    Bonne journée.

  3. #3
    Membre Expert

    Profil pro
    Inscrit en
    Mai 2008
    Messages
    1 576
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 1 576
    Par défaut
    À moins que je ne me méprenne sur ce que tu demandes, il est tout à fait possible d'utiliser ça sur PHP, et le sera encore plus grâce aux normes PSR. Ex, toutes les bibliothèques qui respecteront PSR-6 (cache) ou PSR-7 (HTTP) constituent des composants interchangeables.
    Ton application cliente utilisera alors simplement un objet CacheItemInterface à travers ses méthodes get, set, exists etc..., tandis que le composant en question peut être aussi bien un FileCache, un RedisCache, un MySQLCache ou un TestCache. Tu peux aussi remplacer ton composant par un composants identique mais écrits par un autre développeur, tout ça sans modifier une seule ligne dans ton application, à part la ligne qui initialise le composant.

  4. #4
    Nouveau membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2015
    Messages
    8
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mars 2015
    Messages : 8
    Par défaut
    Voici un exemple plus concret avec du code pour lequel j'ai essayé d'utiliser une approche POC :

    Lorsque qu'on execute main.php (en ligne de commande) il nous demande d'écrire une ligne: tapez n'importe quoi, puis entrée.
    Ensuite relancez main, il vous affichera toutes les lignes que vous avez entrées et proposera d'en ajouter encore, etc...

    main.php Example en PHP CLI
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?php
    spl_autoload_register(function ($class) {
    	require $class . '.php';
    });
    require 'config.php';
    $text_storage = new ComponentTextStorage($options);
    if (!$text_storage instanceof TextStorage){ exit; }
    echo $text_storage->read(), PHP_EOL;
    $text_storage->write(readline('Write: '));
    TextStorage.php Interface pour les composants
    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
    <?php
    /**
     * Interface du composant.
     */
    interface TextStorage{
    	/**
    	 * Constructeur du composant.
    	 * @param array $options Le composant qui implémente l'interface peut être configuré avec ce tableau
    	 */
    	public function __construct($options = array());
     
    	/**
    	 * Enregistre une nouvelle ligne.
    	 * @param string $line
    	 */
    	function write($line);
     
    	/**
    	 * Lit toutes les lignes enregistrées.
    	 * @return string
    	 */
    	function read();
    }
    TextStorageFile.php Composant qui stocke dans un fichier
    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
    <?php
    /**
     * Composant qui stocke les lignes dans un fichier.
     */
    class TextStorageFile implements TextStorage{
    	const FILENAME = 0;
    	private $filename;
    	public function __construct($options = array()){
    		if (isset($options[self::FILENAME])){ $this->filename = $options[self::FILENAME]; }
    		else{ $this->filename = 'Default.txt'; }  // Paramètre par défaut
    	}
    	public function read(){
    		if (is_file($this->filename)){ return file_get_contents($this->filename); }
    		return '';
    	}
    	public function write($line){
    		if ($line != ''){ file_put_contents($this->filename, $line . PHP_EOL, FILE_APPEND); }
    	}
    }
    TextStorageDatabase.php Composant qui stocke dans une base
    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
    <?php
    /**
     * Composant qui stocke les lignes dans une base de données.
     */
    class TextStorageDatabase implements TextStorage{
    	const DSN = 0;
    	const USERNAME = 1;
    	const PASSWORD = 2;
    	private $pdo;
    	public function __construct($options = array()){
    		// Paramètres par défaut
    		$dsn = 'sqlite::memory:';
    		$username = '';
    		$password = '';
    		if (isset($options[self::DSN])){ $dsn = $options[self::DSN]; }
    		if (isset($options[self::USERNAME])){ $username = $options[self::USERNAME]; }
    		if (isset($options[self::PASSWORD])){ $password = $options[self::PASSWORD]; }
    		$this->pdo = new PDO($dsn, $username, $password);
    	}
    	public function read(){
    		$statement = $this->pdo->query('select Line from Lines');
    		if ($statement == false){
    			$this->createTable();
    			return '';
    		}
    		$lines = $statement->fetchAll(PDO::FETCH_COLUMN, 0);
    		return implode(PHP_EOL, $lines);
    	}
    	public function write($line){
    		if ($line != '') $this->pdo->exec("insert into Lines (Line) values ('$line')"); }
    	}
    	private function createTable(){
    		$this->pdo->exec('create table Lines (Line varchar)');
    	}
    }
    config.php Fichier de configuration pour l'utilisateur
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?php
    class_alias('TextStorageFile', 'ComponentTextStorage');
    $options = array(ComponentTextStorage::FILENAME => 'Test.txt');
    /*
    class_alias('TextStorageDatabase', 'ComponentTextStorage');
    $options = array(
    	TextStorageDatabase::DSN => 'sqlite:Test.db',
    	TextStorageDatabase::USERNAME => 'Vince',
    	TextStorageDatabase::PASSWORD => 'Mot2pass');
    */
    Si vous regardez dans config.php on peut voir qu'il utilise TextStorageFile pour sauvegarder les lignes dans un fichier.
    Le but est de pouvoir utiliser TextStorageDatabase juste en modifiant ce fichier de configuration. (j'aurais aussi pu faire un vrai config.ini )
    Et donc sans rien toucher au code de l'application principale !

    Il suffit donc de changer class_alias() et $options pour sauvegarder les lignes dans une base.
    J'aimerais savoir ce que vous en pensez car vos 2 solutions semblent impliquer forcément de modifier le code pour utiliser un nouveau composant.
    Ce qui est contraire au principe de la POC (si j'ai bien tout compris) qui préconise une approche modulaire pour permettre à un non-developpeur de "composer" sont aplication avec les modules de son choix ?

  5. #5
    Membre Expert

    Profil pro
    Inscrit en
    Mai 2008
    Messages
    1 576
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 1 576
    Par défaut
    Bah, comme je l'ai écrit:
    tout ça sans modifier une seule ligne dans ton application, à part la ligne qui initialise le composant
    Dans ton cas, la ligne qui initialise le composant est juste déportée dans config.php (le class_alias)

  6. #6
    Nouveau membre du Club
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2015
    Messages
    8
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Mars 2015
    Messages : 8
    Par défaut
    Exact! mais sans le class_alias ça n'est plus un composant interchangeable (par l'utilisateur).
    C'est juste une classe qui implémente une interface pour que l'objet soit facilement interchangeable pour le developper.
    Le dev doit juste faire attention lorsque qu'il change de composant aux nouveaux arguments que prend le constructeur.

    http://fr.wikipedia.org/wiki/Program...%A9e_composant
    La POC n'est pas sans similitudes avec la POO, puisqu'elle revient à utiliser une approche objet, non pas au sein du code, mais au niveau de l'architecture générale du logiciel.
    Et c'est là que je pense que l'on ne parle pas des mêmes composants (niveau de dépendance avec l'application hôte).

    Pour moi un "vrai" composant doit être générique et donc respecter les 4 points suivant :
    - Implémenter une interface qui défini toutes les methodes public
    - Constructeur avec 1 seul tableau comme argument pour les options du composant (si il y en a)
    - Autoload des class
    - Initialisation de l'objet avec un alias fixe grace à class_alias()

    Si un de ces points n'est pas appliqué il faudra modifier le code chaque fois que l'on utilisera un autre composant.

    Pour revenir à PSR: est-ce que l'interface de PSR-6 ou PSR-7 est prête? car j'ai trouvé des interfaces mais je ne suis pas sur que ce soient les bonnes...

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 160
    Dernier message: 18/07/2012, 21h39
  2. [AOP] programmation orientée Aspect en PHP
    Par anaon dans le forum Bibliothèques et frameworks
    Réponses: 2
    Dernier message: 23/06/2012, 14h08
  3. Programmation orienté objet avec vb
    Par anisj1m dans le forum VBScript
    Réponses: 10
    Dernier message: 07/05/2008, 17h19
  4. Réponses: 2
    Dernier message: 19/04/2006, 13h43

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