Héritage multiple d'applications
-
^Construire des logiciels modulaires et évolutifs
Pourquoi hériter les projets entre eux ?
Aujourd’hui les framework PHP proposent de travailler de plusieurs façons, lorsque vous créez un nouveau projet utilisant des projets déjà existants :
- Soit vous réalisez un “fork” du projet que vous souhaitez installer voire faire un évoluer de votre côté. Les inconvénients sont multiples :
- vous ne pouvez forker qu’un seul projet à la fois : si vous avez besoin d’utiliser d’autres projets tiers qui ne sont pas fournis sous forme de modules réutilisables, vous devrez vous livrer vous-même à un assemblage fastidieux voire impossible,
- pour tenir à jour votre projet des évolutions du projet d’origine, vous devrez faire des “merge” réguliers des nouveaux commits. Si vous avez fait évoluer le logiciel de votre côté, il va falloir résoudre à chaque fois les éventuels conflits engendrés.
- Soit vous vous intéressez à un projet qui a été conçu de façon modulaire, c’est à dire par assemblage de “vendors” que vous pouvez charger facilement dans un projet tiers. C’est l’architecture que propose notamment Symfony, le deuxième framework PHP le plus utilisé au monde. Vous pouvez ainsi assembler différents composants. Les inconvenients sont moins nombreux :
- si vous souhaitez composer un logiciel à partir de plusieurs logiciels existants, ils doivent tous avoir été divisés en bundles utilisables sous forme de vendors : vous ne pourrez pas récupérer de logiciels full-stacks qui n’auraient pas été écrit dans cet esprit modulaire,
- si vous avez besoin de faire évoluer des fonctionnalités de ces vendors, vous allez devoir créer un “fork” de chacun d’entre eux, gérer les merge réguliers, etc.
it.rocks propose une approche différente : de la même manière que vous pouvez hériter vos classes les unes des autres en programmation objets, vous allez pouvoir hériter votre projet de plusieurs projets existants. En imposant une architecture héritable dès le départ, vous pourrez donc toujours assembler des projets déjà existants réalisés avec it.rocks.
Les intérêts sont multiples :- votre projet pourra toujours être réutilisé dans un projet tiers,
- l’assemblage de projets est toujours possible,
- certains éléments de projets peuvent être surchargés dans les projets hérités, ce qui peut vous éviter d’avoir à modifier le code source des projets tiers assemblés,
- en utilisant l’injection de dépendances tels que les mécanismes d’AOP dans des plugins, le remplacement d’objets en utilisant Builder, vous pouvez modifier le comportement de n’importe quel composant de vos projets tiers sans avoir à toucher à leur code source. Un inconvénient subsiste toutefois ici : lorsque vous mettrez à jour ces projets tiers pour en récupérer la dernière version, vous devrez vérifier que la compatibilité de vos injections continuent de fonctionner.
- un projet réutilisable, pouvant être installé plusieurs fois, ou servir de composition à des projets plus complexes,
- un projet final, qui correspond à la configuration d’un ou plusieurs projets réutilisables.
A moins de réaliser un développement dédié à une utilisation unique, dont vous vous interdirez la réutilisation de tout ou partie des composants, auquel cas on peut les mettre directement dans le projet final, vos développements devraient toujours être déposés dans un dépôt de projet réutilisable.
Principe
Tout projet it.rocks peut se présenter comme une composition de projets déjà existants et de nouveaux modules.
Le cas le plus simple est un projet final, qui se compose au moins du framework it.rocks, voire du logiciel intermédiaire dont il représente l’instance.
Par exemple le présent wiki se compose de plusieurs projets :
- itrocks/framework : le framework
- itrocks/wiki : le logiciel de wiki, qui utilise lui-même le framework
- Dans la racine : le projet final, instance du logiciel de wiki pour le site internet itrocks.org.
On sépare le logiciel “wiki” de son instance finale “itrocks-wiki”, car le wiki représente le logiciel réutilisable soit directement pour réaliser un wiki ailleurs, soit comme composant d’un logiciel plus gros qui “hériterait” de plusieurs logiciels.
Si il contient du code dédié (qui ne sera pas réutilisable), le projet final peut comporter également un dossier author/project avec sa classe Application et ces autres classes dédiées. S’il n’y a aucun code dédié, c’est inutile.
De la même façon le fichier de configuration config.php : si le projet final n’est la configuration que d’un seul projet réutilisable on peut se contenter d’un fichier de redirection dans la racine, comme c’est le cas de ce wiki dont le dépôt de sources dur projet itrocks-wiki ne comporte que :
- Le fichier composer.json décrivant les dépendances :
{ "authors": [{ "email": "baptiste dot pillot at bappli dot com", "name": "Baptiste Pillot" }], "description": "https://itrocks.org/wiki", "extra": { "installer-paths": { "{$vendor}/{$name}/": ["type:itrocks"] }, "installer-types": ["itrocks"] }, "license": "MIT", "minimum-stability": "dev", "name": "itrocks/itrocks-wiki", "prefer-stable": true, "repositories": [{ "type": "composer", "url": "https://packages.bappli.com" }], "require": { "itrocks/wiki": "dev-master" }, "type": "itrocks-final" }
- Un script de redirection pour le fichier de configuration principal, en l’absence de dossier itrocks/itrocks-wiki :
<?php require __DIR__ . '/itrocks/wiki/config.php';
Si la configuration nécessitait une personnalisation, ce script de configuration simplifié peut appliquer ces personnalisations, sans qu’il soit besoin d’un fichier de configuration complet dans author/projects/config.php
Projet réutilisable simple
On considère un projet réutilisable comme pouvant donner lieu à plusieurs installations / adaptations, à l’inverse d’un projet final. Son dépôt git démarre sous le dossier author/project de votre projet final, ce dernier disposant de son propre dépôt git à la racine.
Un projet simple nécessite au minimum dans la racine du dépôt git (donc sous author/project du projet final) :
- Un fichier composer.json pour décrire ses dépendances, au minimum le framework it.rocks, par exemple :
{ "authors": [{ "email": "my.name@email.com", "name": "My Name" }], "description": "Description of your cool project", "license": "MIT", "name": "author/project", "repositories": [{ "type": "composer", "url": "https://packages.bappli.com" }], "require": { "itrocks/framework": "dev-master" }, "type": "itrocks" }
- Une classe Author\Project\Application qui hérite de SAF\Framework\Application :
<?php namespace Author\Project; use ITRocks\Framework; /** * My project Application class */ class Application extends Framework\Application { }
- Le plus souvent les projets peuvent embarquer un fichier de configuration config.php, lorsque leur configuration par défaut diffère de la configuration du framework. Un exemple ici où est activé par défaut dans votre logiciel le contrôle d’accès pour que seul des utilisateurs authentifiés aient le droit d’écrire des données dans votre application :
<?php namespace Author\Project; use ITRocks\Framework\Configuration; use ITRocks\Framework\Plugin\Priority; use ITRocks\Framework\User\Write_Access_Control; global $loc; require __DIR__ . '/../../loc.php'; require __DIR__ . '/../../itrocks/framework/config.php'; $config['Author/Project'] = [ Configuration::APP => Application::class, Configuration::ENVIRONMENT => $loc[Configuration::ENVIRONMENT], Configuration::EXTENDS_APP => 'ITRocks/Framework', Priority::NORMAL => [ Write_Access_Control::class ] ];
Avec ces fichiers de configuration, tout projet final qui utilise votre projet aura donc accès aux fonctionnalités du framework et de votre projet.
Projet héritant d’un projet déjà existant
Vous pouvez également créer un nouveau projet réutilisable qui utilise les fonctionnalités d’un autre projet réutilisable existant. On hérite alors de ce projet comme on hériterait du framework. Exemple ici pour un logiciel basé sur itrocks/wiki que vous souhaitez étendre.
- Le fichier composer.json annonce la dépendance à votre projet :
{ "authors": [{ "email": "my.name@email.com", "name": "My Name" }], "description": "Description of your cool WIKI project", "license": "MIT", "name": "author/project", "repositories": [{ "type": "composer", "url": "https://packages.bappli.com" }], "require": { "itrocks/wiki": "dev-master" }, "type": "itrocks" }
- La classe Author\Project\Application hérite cette fois de ITRocks\Wiki\Application :
<?php namespace Author\Project; use ITRocks\Wiki; /** * My own WIKI project Application class */ class Application extends Wiki\Application { }
- Pour le fichier de configuration, soit la configuration de votre projet final héritera du fichier de configuration de itrocks/wiki, soit de votre propre fichier config.php :
<?php namespace Author\Project; use ITRocks\Framework\Configuration; use ITRocks\Framework\Plugin\Priority; use ITRocks\Framework\User\Write_Access_Control; global $loc; require __DIR__ . '/../../loc.php'; require __DIR__ . '/../../itrocks/wiki/config.php'; $config['Author/Project'] = [ Configuration::APP => Application::class, Configuration::ENVIRONMENT => $loc[Configuration::ENVIRONMENT], Configuration::EXTENDS_APP => 'ITRocks/Wiki', Priority::NORMAL => [ Write_Access_Control::class ] ];
Projet héritant de plusieurs projets déjà existant
Enfin vous pouvez composer votre projet réutilisable ou votre projet final à partir de plusieurs projets déjà existants. PHP ne permettant pas l’héritage multiple, on distinguera le projet principal dont on hérite via la clause
extends
des projets secondaires dont on hérite via l’annotation @extends. Exemple ici pour un logiciel réutilisable qui hérite à la fois d’une gestion de projets, d’un wiki et d’un CRM fictif, et qui sera donc l’assemblage de ces logiciels.- Le fichier composer.json cumule donc les dépendances de ces trois projets :
{ "authors": [{ "email": "my.name@email.com", "name": "My Name" }], "description": "Description of your cool project", "license": "MIT", "name": "author/project", "repositories": [{ "type": "composer", "url": "https://packages.bappli.com" }], "require": { "itrocks/crm": "dev-master", "itrocks/projects": "dev-master", "itrocks/wiki": "dev-master" }, "type": "itrocks" }
- La classe Author\Project\Application hérite à la fois des trois projets d’origine :
<?php namespace Author\Project; use /** @noinspection PhpUnusedAliasInspection @extends */ ITRocks\CRM; use /** @noinspection PhpUnusedAliasInspection @extends */ ITRocks\Projects; use ITRocks\Wiki; /** * My own WIKI project Application class * * @extends CRM\Application * @extends Projects\Application */ class Application extends Wiki\Application { }
- A l’heure de la rédaction de cet article, le fichier de configuration config.php ne gère par contre pas encore l’héritage multiple : vous hériterez votre configuration du projet principal (ici ITRocks\Wiki), à votre charge de récupérer les réglages de vos projets secondaires dont vous auriez besoin pour votre nouveau logiciel. C’est un point qui sera amélioré à l’avenir. Ici notre logiciel de gestion de projets de propose pas de configuration particulière, on peut donc simplement étendre la configuration du wiki qu’on a pris comme projet principal :
namespace Author\Project; use ITRocks\Framework\Configuration; use ITRocks\Framework\Plugin\Priority; use ITRocks\Framework\User\Write_Access_Control; global $loc; require __DIR__ . '/../../loc.php'; require __DIR__ . '/../../itrocks/wiki/config.php'; $config['Author/Project'] = [ Configuration::APP => Application::class, Configuration::ENVIRONMENT => $loc[Configuration::ENVIRONMENT], Configuration::EXTENDS_APP => 'ITRocks/Wiki', Priority::NORMAL => [ Write_Access_Control::class ] ];
- Soit vous réalisez un “fork” du projet que vous souhaitez installer voire faire un évoluer de votre côté. Les inconvénients sont multiples :