Les points d'entrées

Chacun de ces "points d'entrées" de votre application a besoin d'un contexte de travail particulier, par exemple :

  • en mode web, l'uri de la requête et les paramètres GET/POST... sont nécessaire à l'application
  • en mode batch, les paramètres saisies en ligne de commande sont nécessaire à l'application
  • en mode tests unitaires, les classes du frameworks PHPUnit ainsi que les classes de tests doivent être disponible
  • en mode tests fonctionnels, les classes de Fixtures doivent être disponible
  • ...

Bien que chacun de ces "contextes" a des spécificités particulières, l'ensemble de ces contextes ont besoin de :

  • l'ensemble des classes développées pour la logique de l'application
  • de la connexion à la base de données
  • des paramétrage éventuels dans le fichier de configuration
  • d'écrire dans un fichier de log
  • ...

L'objectif a atteindre est donc de faire en sorte que le maximum de code développé pour l'application soit fonctionnel et ce peut importe le point d'entrée (i.e. le contexte) par lequel l'application a été lancée.

Les fichiers contextes

Si nous résumons, nous avons besoin de :

  • un fichier spécifique à chacun des contextes
  • un fichier commun inclus par chacun des contextes

Voici donc un exemple de fichiers contextes :

  • Fichier contexts/http.php :
<?php

$context = 'http';

require_once dirname(__FILE__).'/@common.php';

// traitement spécifique: traite la requête HTTP, exemple avec Zend Framework :

$controller = Zend_Controller_Front::getInstance();

// ...

$controller->dispatch();

  • Fichier contexts/phpunit.php :
<?php

$context = 'phpunit';

require_once dirname(__FILE__).'/@common.php';

// ligne suivante pas nécessaire si PHPUnit est dans l'include path et que l'autoload est actif
require_once 'PHPUnit/Framework/TestCase.php';

// traitement spécifique: paramètre l'include path pour trouver les classes de tests, injecte les données de tests

set_include_path(get_include_path().PATH_SEPARATOR.dirname(__FILE__).'/../tests/phpunit');

// injecte les données de tests situées dans config/db/phpunit/create-schema.sql et create-data.sql
// ...

  • Fichier contexts/greenpepper.php :
<?php

$context = 'greenpepper';

require_once dirname(__FILE__).'/@common.php';

// traitement spécifique: paramètre l'include path pour trouver les fixtures, injecte les données de références

set_include_path(get_include_path().PATH_SEPARATOR.dirname(__FILE__).'/../tests/greenpepper');

// injecte les données de références situées dans config/db/greenpepper/create-schema.sql et create-data.sql
// ...

// ajoute la fonction adhoc de logging dans le fichier greenpepper.log

function gpLog($txt)
{
    $fp = fopen(dirname(__FILE__).'/../logs/greenpepper.log','a+');
    $msg = '['.date('Y-m-D H:i:s').'] '.$txt.PHP_EOL;
    fputs($fp,$msg);
    fclose($fp);
}

  • Fichier contexts/batch.php :
<?php

$context = 'batch';

require_once dirname(__FILE__).'/@common.php';

// traitement spécifique: aucun, ils sont réalisés dans les scripts qui inclus le contexte batch

  • Fichier contexts/@common.php :
<?php

// sur quel environnement (prod, preprod, ...) sommes nous ?
$env     = getenv('PHP_ENV');

// sets the include path : library/ ...

// load configuration file
// ...

// load database connection
// ...

// load file logger
// ...

// activate autloader (pour l'include automatique des
// fichiers contenant la définition des classes instanciées)
// ...

L'utilisation des fichiers contextes

Maintenant que nous avons nos différents contextes prêt à l'emploi il faut faire le branchement pour qu'ils soient appeller au bon moment.

Contexte HTTP

Le fichier contexte http doit être le premier fichier appellé dans une application de type MVC(2), voici comment faire :

  • Fichier public/index.php :
<?php

require dirname(__FILE__).'/../contexts/http.php';
  • Fichier public/.htaccess (compatible Apache 2 uniquement) :
RewriteEngine On

RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_URI} !-f
RewriteRule ^.*$ /index.php [L}

Contexte Batch

  • Fichier bin/mon-batch1-qui-fait-quelque-chose-de-precis.php :
<?php

require_once dirname(__FILE__).'/../contexts/batch.php';

// écrire ici la logique spécifique du batch 1
// ...

  • Fichier bin/mon-batch2-qui-fait-quelque-chose-de-precis.php :
<?php

require_once dirname(__FILE__).'/../contexts/batch.php';

// écrire ici la logique spécifique du batch 2
// ...

Contexte Tests Unitaires (PHPUnit)

  • Fichier tests/phpunit/MonPackage/MonServiceTest.php :
<?php

require_once dirname(__FILE__).'/../../../contexts/phpunit.php';

class MonPackage_MonServiceTest extends PHPUnit_Framework_TestCase
{
    public function setUp()
    {
        $this->f = new MonPackage_MonService();
    }

    public function testSomethingForNormalCaseReturnTrue()
    {
        $this->assertTrue($this->f->someting());
    }

    // ...
}

Contexte Tests Fonctionnels (GreenPepper)

  • Fichier tests/greenpepper/MonPackage/MonServiceFixture.php :
<?php

require_once dirname(__FILE__).'/../../../contexts/greenpepper.php';

class MonPackage_MonServiceFixture
{
    public function something()
    {
        $r = null;
        try {
            $s = new MonPackage_MonService();
            $r = $s->something();
        } catch (Exception $e) {
            $r = 'erreur: '.$e->getMessage();
            return $r;
        }

        // for debug only
        gpLog(__METHOD__.' => '.var_export($r,true));

        return $r;
    }

    // ...
}

Ajouter un nouveau contexte d'appel

Si vous avez besoin d'ajouter un nouveau contexte d'appel à votre application, par exemple PHP-GTK (si votre application doit tourner en mode desktop client lourd PHP-GTK), vous pouvez créer le fichier suivant par exemple :

  • Fichier contexts/php-gtk.php :
<?php

$context = 'gtk';

require_once dirname(__FILE__).'/@common.php';

// traitement spécifique à GTK : ajout du répertoire des classes graphiques à l'include path, initialisation des évènements, ...

set_include_path(get_include_path().PATH_SEPARATOR.dirname(__FILE__).'/../library-gtk');

// ...

Il vous suffit maintenant de faire en sorte que vos script d'appel GTK utilise votre nouveau fichier de contexte :

  • Fichier bin/gtk-main.php :
<?php

require_once dirname(__FILE__).'/../contexts/php-gtk.php';

// lancement de l'application avec les spécificités du script GTK...
// ...

Conclusion

Il est important de distinguer le code commun et le code spécifique aux contextes d'appel de votre application, cela vous permettra entre autre :

  • de factoriser votre code
  • de capitaliser sur un coeur applicatif pouvant être réutiliser dans d'autres mode de lancement (ligne de commande, web, graphique...)
  • de ne pas charger l'ensemble de toutes les configurations nécessaires pour tous les contextes à chaque fois (ajout des répertoires de tests à l'include path...)
  • ...

Et vous quels sont vos pratiques pour rendre une application multi-contextuelle ?