Archives de catégorie : Developpement PHP

Photos en base de données

Type de champs à utiliser

On devra disposer au minimum du type de photo : JPEG, GIF, PNG, BMP ... et du champ contenant les données binaires de la photo.

Dans une base MySql, pour les données binaire, on dispose des types suivants :

  • tinyblob : 255 octets
  • blob : 64 Ko
  • mediumblob : 16 Mo
  • longblob : 4 Go

Création de la table

CREATE TABLE `photos` (
    `id` int NOT NULL,
    `phototype` varchar(25) NOT NULL DEFAULT 'JPEG',
    `photodata` blob NOT NULL
);
ALTER TABLE `photos`
  ADD PRIMARY KEY (`id`);

Afin de lier la photo à une fiche d'un individu, d'un objet ou d'un paysage ... contenue dans une table datas, on aura intérêt à déclarer une foreign key

ALTER TABLE `datas`
  ADD CONSTRAINT `photos_ibfk_1` FOREIGN KEY (`id`) REFERENCES `datas` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;

Formulaire d'envoi d'une photo

Le formulaire doit être celui d'un envoi de fichier :

<form name="formphoto" enctype="multipart/form-data" method="post">
    <input name="id" type="hidden" value="">
    <label>Choisissez le fichier image (JPEG, GIF ou PNG)
    <input name="filephoto" type="file"></label>
    <input name="submit" type="submit" value="Envoyer la photo">
</form>

Réception coté serveur

Ne pas oublier que les variables se récupèreront dans la variable globale $__POST sauf le fichier photo qui sera dans la variable globale $__FILES.

$ret =is_uploaded_file($__FILES['filephoto']['tmp_name'];
if (!$ret) {
    throw new Exception('Problème de transfert.');
}
$filesize = $__FILES['filephoto']['size'];
if ($filesize > SIZE_MAX) {
    throw new Exception('Fichier photo trop gros');
}
$phototype = $__FILES['filephoto']['type'];
$photodata = file_get_contents($__FILES['filephoto']['tmp_name']);
$id = $__POST['id'];
// si on travaille en https, il faut ajouter le context dans file_get_contents()
$photodata = addslashes($photodata); // indispensable pour enregistrer dans MySql
// procédure classique d'enregistrement des données $id, $phototype, $photodata

Affichage de la photo dans une page

Comme il n'y a pas de fichier, on doit envoyer les données binaires de la photo, encodées base64. On pourra utiliser la fonction suivante :

function base64_encode_image($imgdata, $imgtype) {
    return 'data:image/' . $imgtype .';base64,' . stripslashes($imgdata);
}

et on affectera ce résultat à la propriété src de la balise <img> comme ceci :

<img src="<?= base64_encode_image($photodata, $phototype);?>">

SSL certificate problem: unable to get local issuer certificate

Erreur fréquemment obtenue lorsqu'on utilise CURL pour interroger un service par son API depuis son serveur local de développement.

Solution 1 : modifier le paramétrage du fichier php.ini

Dans le fichier php.ini, il suffit d'indiquer ce qui suit

[curl]
 ; A default value for the CURLOPT_CAINFO option. This is required to be an
 ; absolute path.
 curl.cainfo = "C:\Users\admin\cer\cacert.pem"

et de placer dans le dossier un fichier cacert.pem à l'emplacement indiqué. Le chemin doit être absolu. Voici un fichier cacert compressé au format zip.

Solution 2 : indiquer à curl l'emplacement du fichier cacert.pem

Lors de l'utilisation, au moment du paramétrage de CURL, indiquer la ligne :

curl_setopt($ch, CURLOPT_CAINFO,  getcwd().'/cert/cacert.pem');

Attention, le chemin doit être absolu et le fichier doit être présent.

La mauvaise solution

Eviter de désactiver le contrôle des certificats. L'option  CURLOPT_SSL_VERIFYPEER permet d'arrêter la vérification mais provoque une faille de sécurité lorsque le code est installé sur un serveur de production.

Eclipse ne répond pas ?

Je travaille sous Windows 7. Suite au chargement d'un fichier trop gros et à un traitement (recherche dans les fichiers), Eclipse s'est planté.
En réalité, il semble que le problème vienne de Java SE (message: Java Platform SE binary ne répond pas).
La solution consiste à aller dans le panneau de configuration, Java, onglet Général, bouton Paramètres puis bouton Supprimer les fichiers.

Création d’un état avec niveau de regroupement

Dans un rapport Microsoft Access, il est possible de créer des niveaux de regroupement sur des champs d'une table ou d'une requête. Comment réaliser ces mêmes états sous ZF2 / MySql avec TcPdf ?

Exemple concret

Soit 2 tables, `eleves` et `tarifs` définies de la manière suivante :

Table `eleves` :

CREATE TABLE IF NOT EXISTS `t_eleves` (
 `eleveId` int(11) NOT NULL AUTO_INCREMENT,
 `tarifId` int(11) NOT NULL DEFAULT "0"',
 `nom` varchar(30) COLLATE utf8_unicode_ci NOT NULL,
 `prenom` varchar(30) COLLATE utf8_unicode_ci NOT NULL,
 `adresseL1` varchar(38) COLLATE utf8_unicode_ci NOT NULL,
 `adresseL2` varchar(38) COLLATE utf8_unicode_ci NOT NULL,
 `codePostal` varchar(5) COLLATE utf8_unicode_ci NOT NULL,
 `commune` varchar(32) COLLATE utf8_unicode_ci NOT NULL,
 `dateN` date NOT NULL,
 `dateCreation` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
 `dateModification` datetime NOT NULL DEFAULT '1900-01-01 00:00:00',
 `dateInscription` datetime NOT NULL DEFAULT '1900-01-01 00:00:00',
 `inscrit` tinyint(1) unsigned NOT NULL DEFAULT '1',
 `selection` tinyint(1) unsigned NOT NULL DEFAULT '0',
 PRIMARY KEY (`eleveId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

Table `tarifs` :

CREATE TABLE IF NOT EXISTS `t_tarifs` (
 `tarifId` int(11) NOT NULL AUTO_INCREMENT,
 `montant` decimal(10,2) NOT NULL DEFAULT '0.00',
 `nom` varchar(48) COLLATE utf8_unicode_ci NOT NULL,
 `rythme` int(4) NOT NULL DEFAULT '1',
 `grille` int(4) NOT NULL DEFAULT '1',
 PRIMARY KEY (`tarifId`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

On souhaite créer un état qui affiche la liste des élèves par tarif, avec un sous-total à la fin de chaque groupe de tarifs, et le total général à la fin de l'état.

 Création de la requête

On a 3 parties dans la requête :

  • le détail de chaque groupe contenant les élèves
  • le pied de chaque groupe contenant le nombre d'élèves du groupe et le montant total du groupe
  • la marge du bas contenant le nombre total d'élèves et le total général

On codera 'd'  pour 'détail', 'p' pour 'pied', 'g' pour 'groupe', 'm' pour 'marge'. On remarquera que le codage respecte l'ordre d'affichage :

  • 'g' avant 'm'
  • 'd' avant 'p'

On obtient un résultat convenable par :

SELECT nom, prenom, montant
FROM (
 SELECT 'g' c1, t.tarifId c2, 'd' c3, e.nom, e.prenom, t.montant 
 FROM t_eleves e INNER JOIN t_tarifs t ON e.tarifId=t.tarifId
UNION
 SELECT 'g' c1, t.tarifId c2, 'p' c3, concat('Pour ', t.nom), count(eleveId), sum(t.montant) 
 FROM t_eleves e INNER JOIN t_tarifs t ON e.tarifId=t.tarifId GROUP BY t.tarifId
UNION
 SELECT 'm' c1, '' c2, '' c3, 'Total général', count(eleveId), sum(t.montant) 
 FROM t_eleves e INNER JOIN t_tarifs t ON e.tarifId=t.tarifId
) u 
ORDER BY c1,c2,c3,1,2

Pour la mise en page, on aura sans doute intérêt à afficher les colonnes c1, c2 et c3.

etat-niveau-regroup1

avec PhpMyAdmin

 

ZF2 et utilisation de Tcpdf

Pour utiliser Tcpdf sous ZF2, j'ai créé un module basé sur le principe suivant :

  • La méthode qui souhaitera créer un pdf lancera un événement contenant tous les paramètres et toutes les données permettant de créer le pdf.
  • un écouteur (listener) sera chargé de surveillé la présence de ces événements et déclenchera la création

Voici le schéma d'organisation de mon module :

DafapTcpdf

Schéma d'organisation du module

La configuration de Tcpdf est basée sur des constantes. Elle se fera dans le fichier config/autoload/tcpdf-config.global.php de l'application.

Voici ci-dessous un exemple d'utilisation de mon module dans un controller. Il s'agit de créer un listing à partir d'une sélection dans une table de tarifs. La sélection est définie dans l'action tarifListeAction() à partir d'un formulaire criteres_form et ses paramètres sont conservés en session.

/**
* envoie un evenement contenant les paramètres de création d'un document pdf
* (le listener DafapTcpdf\Listener\PdfListener lancera la création du pdf)
* Il n'y a pas de vue associée à cette action puisque la response html est créée par \TCPDF
*/
public function tarifPdfAction()
{
  $currentPage = $this->params('page', 1);
  $criteres_form = new CriteresForm('tarifs');
  $criteres_obj = new ObjectDataCriteres($criteres_form->getElementNames());
  $session = new SessionContainer(str_replace('pdf', 'liste', $this->getSessionNamespace()));
  if (isset($session->criteres)) {
      $criteres_obj->exchangeArray($session->criteres);
  }
  $tableTarifs = $this->getServiceLocator()->get('Sbm\Db\Table\Tarifs');
  $data = array();
  foreach ($tableTarifs->fetchAll($criteres_obj->getWhere(), 'nom') as $row) {
    $data[] = array(
        $row->nom,
        $row->montant,
        $row->rythme,
        $row->grille,
        $row->mode
    );
  }
  $call_pdf = $this->getServiceLocator()->get('RenderPdfService');
  $call_pdf->setData($data)
  ->setHead(array('Nom', 'Montant', 'Rytme', 'Grille', 'Mode'))
  ->setPdfConfig( array(
      'title' => 'Liste des tarifs',
      'header' => array(
      'title' => 'Liste des tarifs',
      'string' => 'éditée par School Bus Manager le ' . date('d/m/Y à H:i')
  )))
  ->setTableConfig(array(
      'tbody' => array(
          'cell' => array(
              'txt_precision' => array(0, 2, 0, 0, 0)
      )),
      'column_widths' => array(64, 30, 30, 20, 36)
  ))
  ->renderPdf();
  $this->flashMessenger()->addSuccessMessage("Création d'un pdf.");
}

DafapTcpdf-2

 

Cliquez sur cette image pour voir le résultat.

 

 

Ce module est disponible sur github/dafap.

ZF2 et installation de Tcpdf

Pour installer Tcpdf il faut l'ajouter dans composer.json. Or cette bibliothèque n'est pas intégrée dans Packagist. Comment doit-on faire ?

D'après les différents posts trouvés ici et là, il faut ajouter :

"require" : {
   ... ,
   "tecnick.com/tcpdf": "*"
}
"repositories" : [
      ... ,
   {
      "type":"package",
      "package": {
          "name": "tecnick.com/tcpdf",
          "version":"master",
          "source": {
              "url": "http://git.code.sf.net/p/tcpdf/code",
              "type": "git",
              "reference":"master"
         }
      }
   }
],
"autoload": {
   "classmap": [
     "vendor/tecnick.com/tcpdf"
   ]
 }

Or lors de l'exécution sous windows 7, l'installation échouait en permanence :

D:\monProjet>php composer.phar update
Loading composer repositories with package information
Updating dependencies (including require-dev)
 - Installing tecnick.com/tcpdf (6.0.080)
   Cloning fcd0098a2a8b0fb88e6ea291393dc8c1ecb2f38b

 [Symfony\Component\Process\Exception\ProcessTimedOutException]
 The process "git clone --no-checkout "git://git.code.sf.net/p/tcpdf/code" 
 "D:\monProje t\vendor\tecnick.com/tcpdf" && cd /D 
 "D:\monProjet\vendor\tecnick.com/tcpdf" && git remote add composer 
 "git://git.code.sf.net/p/tcpdf/code" && git fetch composer" 
 exceeded the timeout of 300 seconds.

update [--prefer-source] [--prefer-dist] [--dry-run] [--dev] [--no-dev] [--lock]
[--no-plugins] [--no-custom-installers] [--no-scripts] [--no-progress] 
[--withdependencies] [-v|vv|vvv|--verbose] [-o|--optimize-autoloader] [packages1] 
...[packagesN]

Il faut donc augmenter le temps d'exécution pour composer.phar. Pour cela, dans la console d'exécution de windows 7 :

D:\monProjet>set COMPOSER_PROCESS_TIMEOUT=5000

D:\monProjet>php composer.phar update
Loading composer repositories with package information
Updating dependencies (including require-dev)
 - Installing tecnick.com/tcpdf (6.0.080)
 Cloning fcd0098a2a8b0fb88e6ea291393dc8c1ecb2f38b

 - Updating zf-commons/zfc-user dev-master (70eb1db => b96419e)
 Checking out b96419e91bdc7ec09b49038c18634f7d0b503597

 - Updating zendframework/zend-developer-tools dev-master (26af0e6 => 8907edf)
 Checking out 8907edf3a03ae0ac761b43e33b3629ac1b6c3cf3

Writing lock file
Generating autoload files

Eurêka ! Ça a marché !

Par la suite, en recherchant dans la documentation, on peut indiquer ce délai dans la clé config de composer.json :

 "config" : {
    "process-timeout" : 5000,
    "use-include-path": false,
    "github-protocols": ["git", "https", "http"],
    "vendor-dir": "vendor"
 },

Voir le détail sur cette page.

Aide de vue dans les layouts de Zendframework 2 : Exemple

Présentation de l'exemple

Une aide de vue, ou ViewHelper, sert à factoriser des bouts de code pour nos views ou layouts.

L'appel à notre aide de vue se fera très simplement. Prenons par exemple le cas où on a besoin de déclarer des feuilles de style dont les noms sont contenus dans un tableau :

$base_path = $this->basePath(); 
echo $this->getHeadLinks($base_path, $favicon_file = null, $this->config['css']);

Dans cet exemple, on utilise deux aides de vue, basePath() qui est fournie dans le package Zendframework 2 et getHeadLinks() que je vais développer ci-dessous.

Pour mon aide de vue je passe trois paramètres :

  • $base_path : la racine du service fournie par l'aide de vue de ZendFramework
  • $favicon_file : le favicon à utiliser pour cette page
  • $config['css'] : le tableau provenant du config/module.config.php

Dans config/module.config.php, je décris les feuilles de style à charger de la façon suivante :

return array(
    ...
    'css' => array(
        'css/style.css',
        'css/nav.css',
        array(
            'href' => 'css/ie7.css',
            'media' => 'screen',
            'conditionalStylesheet' => 'lt ie7',
            'extras' => array('id' => 'dafap',) 
        ),
    ),
    ...

cela devra donner dans le code de la page :

<!--[if lt ie7]> <link href="/css/ie7.css" media="screen" rel="stylesheet" type="text/css" id="dafap" /><![endif]-->
<link href="/css/nav.css" media="screen" rel="stylesheet" type="text/css">
<link href="/css/style.css" media="screen" rel="stylesheet" type="text/css">
<link href="/img/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon">

Implémentation de notre aide de vue

Une aide de vue, ou ViewHelper, est une classe dérivée de Zend\View\Helper\AbstractHelper. Pour mon cas, je l'appelerai HeadLinks et elle sera placée dans le src du module, dossier View/Helper :

module
  └ MonModule
          └ src
               └ MonModule
                        └ View
                             └ Helper

Sa méthode _invoke() retournera le résultat en invoquant son nom déclaré dans module.php dans la méthode getViewHelperConfig().

public function getViewHelperConfig()
{
    return array(
        'invokables' => array(
            'getHeadLinks' => __NAMESPACE__ .'\View\Helper\HeadLinks',
        ),
    );
}

Dans mon aide de vue, j'ai besoin d'utiliser l'aide de vue headLink de Zendframework. Or, je ne peux pas utiliser l'alias headLink qui n'est pas invokable. Je vais donc utiliser la classe directement.

namespace MonModule\View\Helper;
use Zend\View\Helper\AbstractHelper;
use Zend\View\Helper\HeadLink;

class HeadLinks extends AbstractHelper
{
   public function __invoke($base_path, $favicon_file = null, $cssFilesArray = array())
   {
     $headLink = new HeadLink();
     if (! is_null($favicon_file) && is_string($favicon_file)) {
         $headLink = $headLink(array(
             'rel' => 'shortcut icon',
             'type' => 'image/vnd.microsoft.icon',
             'href' => $this->concatPath($base_path, $favicon_file)
         ));
     } else {
         $headLink = $headLink();
     }
     foreach ($cssFilesArray as $css_file) {
         if (is_string($css_file)) {
             $headLink = $headLink->prependStylesheet($this->concatPath($base_path, $css_file));
         } elseif (is_array($css_file)) {
             $href = $this->getParam('href', $css_file);
             $media = $this->getParam('media', $css_file);
             $conditionalStylesheet = $this->getParam('conditionalStylesheet', $css_file);
             $extras = $this->getParam('extras', $css_file);
             if (!is_null($href)) {
                 $headLink = $headLink->prependStylesheet($this->concatPath($base_path, $href), $media, $conditionalStylesheet, $extras);
             }
         } else {
             throw new \Exception('Erreur de structure pour la définition des fichiers css.');
         }
     }
     return $headLink;
   }
   private function getParam($nom, $tableau)
   {
       if (array_key_exists($nom, $tableau)) {
           return $tableau[$nom];
       } else {
           return null;
       }
   }
   private function concatPath($base_path, $file = null)
   {
       if (! is_null($file)) {
           $file = '/' . ltrim($file, '/');
       }
       $base_path = rtrim(str_replace('\\', '/', $base_path), '/');
       return $base_path . $file;
   }
}

Utilisation de mon aide de vue dans un layout

Pour utiliser mon aide de vue dans un layout, je dois disposer d'une variable $this->config qui contient notamment la clé 'css' donnant la structure à monter (en fait, je passe également dans ce tableau tous les éléments constitutifs du layout comme les composants du header, du footer, etc.).

Comment passer une variable à un layout ?

Première façon : depuis un contrôleur

Dans un contrôleur, on a accès au layout par la méthode layout(). On crée donc les variables directement :

$this->layout()->ma_variable = $valeur;

En particulier pour mon cas, je trouverai la valeur de ma variable de configuration de la façon suivante :

$sm = $this->getServiceLocator();
$config = $sm->get('config');
$this->layout()->config = $config['layout'];

Deuxième façon : depuis la classe Module

Etant donné que la configuration est disponible dans la classe Module, on peut créer la variable depuis la méthode onBootstrap() de cette classe :

 public function onBootstrap(MvcEvent $e)
 {
     $eventManager = $e->getApplication()->getEventManager();
     $config = $this->getApplication()->getServiceManager()->get('config');
     $config_layout = $config['layout'];
     $eventManager->attach(MvcEvent::EVENT_RENDER, function($e) use ($config_layout) {
         $e->getViewModel()->setVariable('config', $config_layout);
   });
   $moduleRouteListener = new ModuleRouteListener();
   $moduleRouteListener->attach($eventManager);
 }

Troisième façon : en dérivant la classe Module de ZfcBase\Module\AbstractModule

On devra alors restructurer le fichier module.config.php de la façon suivante :

return array(
    'MonModule' => array(
        'layout' => array(
            'css' => array(
                'css/style.css',
                'css/nav.css',
                array(
                    'href' => 'css/ie7.css',
                    'media' => 'screen',
                    'conditionalStylesheet' => 'lt ie7',
                    'extras' => array('id' => 'dafap',)
                ),
            ),
            ...
        ),
    ),
    ...
);

Ainsi, on pourra utiliser dans le module la méthode getOptions() :

namespace MonModule;
use ZfcBase\Module\AbstractModule;
class Module extends AbstractModule
{
 public function onBootstrap(MvcEvent $e)
 {
     $eventManager = $e->getApplication()->getEventManager();
     $config_layout = $this->getOptions('layout');
     $eventManager->attach(MvcEvent::EVENT_RENDER, function($e) use ($config_layout) {
         $e->getViewModel()->setVariable('config', $config_layout);
   });
   $moduleRouteListener = new ModuleRouteListener();
   $moduleRouteListener->attach($eventManager);
 }
 public function getDir()
 {
     return __DIR__;
 }
 public function getNamespace()
 {
     return __NAMESPACE__;
 }
}

Cela va permettre d'utiliser une configuration particulière pour chaque module de l'application. Mais ça, ce sera l'objet d'un prochain article.

Voir aussi un article complet sur la création d'un ViewHelper dans ZF2.

Utilisation de composer.phar

Le fichier composer.phar doit être présent à la racine du projet qu'il va gérer.

S'il n'est pas présent, on peut l'installer par la commande :

shell\mon_projet>curl -s http://getcomposer.org/installer | php

S'il est présent, on peut lancer une recherche pour obtenir sa dernière version stable :

shell\mon_projet>php composer.phar self-update

Ensuite, il faut créer un fichier composer.json qui va décrire le projet. On peut utiliser la commande :

shell\mon_projet>php composer.phar init

Cette commande va demander d'indiquer les renseignements décrivant le projet :

  • package name : le nom se donne sous la forme <vendor>/<name><vendor> est le nom de votre organisation et <name> est le nom de votre projet
  • description : description de votre projet
  • author : votre nom et adresse email
  • minimum stability : les valeurs possibles sont stable, RC, beta, alpha, dev
  • license : nom de la licence indiquant les droits sur ce logiciel
  • dependencies : indiquer ici les noms et versions des bibliothèques nécessaires au projet en production comme par exemple zendframework 2.3.* (require)
  • dev dependencies : indiquer ici les noms et versions des bibliothèques nécessaires uniquement au développement comme par exemple zendframework/zftool ou bjyoungblood/bjy-profiler, etc. (require-dev)

Pour trouver plus facilement les bibliothèques (dependencies et dev dependencies), répondez yes à la question : Would you like to define your dependencies (require) interactively [yes]?

Ensuite, voici un exemple pour ajouter bjyoungblood/bjy-profiler

Search for a package []: bjyoungblood
Found 9 packages matching bjyoungblood
 [0] bjyoungblood/BjyProfiler
 [1] bjyoungblood/bjy-authorize
 [2] bjyoungblood/bjy-profiler
 [3] saeven/circlical-acl-admin
 [4] bjyoungblood/bjy-cache-storage
 [5] bjyoungblood/bjy-modulus
 [6] shashikant/circlical-acl-admin
 [7] shashikant/circlical-acl-admin-1
 [8] bjyoungblood/rpc-tester
Enter package # to add, or the complete package name if it is not listed []: 2
Enter the version constraint to require []:dev-master

Puis on lance le téléchargement des bibliothèques indiquées par

shell\mon_projet>php composer.phar install

Par la suite, on peut rajouter une bibliothèque en lançant la commande :

shell\mon_projet>php composer.phar require  [paquet1] ... [paquetN]
ou
shell\mon_projet>php composer.phar require  --dev [paquet1] ... [paquetN]

[paquet1] ... [paquetN] sont les noms des bibliothèques à installer séparés par un espace
et --dev indique qu'il faut ajouter la bibliothèque à la liste des dev dependencies

La mise à jour des bibliothèque se fait en lançant la commande :

shell\mon_projet>php composer.phar update

WampServer et ZendFramework

Je développe plusieurs projets sous Zendframework 1 et 2. Par commodité j'ai choisi de mettre en place un virtualhost par projet afin ce coller au mieux à la configuration de production propre à chacun de mes clients.

Jusqu'à maintenant, j'utilisais pour le développement un Zend Server Community Edition 5.6 (licence gratuite) mais je n'ai pas trouvé comment faire évoluer gratuitement les versions de Php. Aussi je viens d'installer WampServer 2.4 comme serveur de développement, sous Windows 7. Il est composé de :

  • Apache 2.4.4
  • MySql 5.6.12
  • Php 5.4.12
  • PhpMyAdmin 4.0.4
  • SqlBuddy 1.3.3
  • XDebug 2.2.3

Si les mises à jour de WampServer semblent plus faciles à gérer, ce service semble légèrement plus lent (pas grave en développement) et sa configuration diffère sensiblement de celle de Zend Server.

Cet article détaille les configurations que j'ai faites.

Configuration de MySql

Avant d'installer WampServer, il faut s'assurer que le service MySql n'est pas démarré, sinon l'installation risque à bloquer au moment de la mise en place de MySql.

Un dump des bases de données est fait avant l'arrêt du service MySql précédent.

Une fois l'installation de WampServer terminée, je constate que le service MySql fonctionne :

 

shell>mysql -uroot
Welcome to the MySQL monitor. Commands end with ; or g.
Your MySQL connection id is 6
Server version: 5.6.12-log MySQL Community Server (GPL)

Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its affiliates. 
Other names may be trademarks of their respective owners.
Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.

shell>

Reste quelques opérations à faire :

  • créer un mot de passe pour root. J'utilise la console pour plus de facilité :
    shell> mysqladmin -u root password [votre mot de passe]
  • s'assurer que le path de l'ordinateur pointe bien sur le répertoire bin de la bonne version de MySql (chez moi, il pointait toujours sur la version précédente ce qui fait que mysqladmin et mysqldump ne correspondaient pas à la bonne version).
    variables-d-environnement
    (On en profite aussi pour rajouter le chemin de php.exe qui sera utile par la suite)
  • recharger en premier le dump de la base mysql et ensuite ceux des autres bases.

 Configuration d'apache

Comme toujours, la configuration d'apache se trouve dans son répertoire conf, dans le fichier httpd.conf. On aura besoin de charger le module rewrite et de faciliter la création des virtualhosts. Pour cela, j'apporte les modifications suivantes :

  • décommenter la ligne suivante :
    LoadModule rewrite_module modules/mod_rewrite.so
  • ajouter la ligne suivante :
    # Virtual hosts
    #Include conf/extra/httpd-vhosts.conf
    Include "C:/Program Files/wamp/vhosts/vhost_*.conf"

    Ainsi, chaque virtualhost sera configuré dans un fichier indépendant du répertoire vhosts de wamp et il n'y aura plus à retoucher ce fichier httpd.conf. Le nom du fichier doit commencer par vhost_ et l'extension est .conf.

  • créer un virtualhost pour localhost.
    En effet, lorsqu'on crée un virtualhost, l'alias localhost ne fonctionne plus. Pour régler ce problème, je crée le fichier vhosts/vhost_localhost.conf suivant :

    # Virtual Hosts
    #
    # Required modules: mod_log_config
    
    
        ServerAdmin webmaster@localhost
        DocumentRoot "C:/Program Files/wamp/www"
        ServerName localhost
        ServerAlias 127.0.0.1
        ErrorLog "C:/Program Files/wamp/logs/apache_error.log"
        CustomLog "C:/Program Files/wamp/logs/access.log" common
    

    La définition des droits de ce répertoire est contenue dans httpd.conf donc on n'a pas besoin d'y retoucher.

  • exemple d'un virtualhost pour un projet jobeet utilisant Zendframework 2 :
    # Virtual Hosts
    #
    # Required modules: mod_log_config
    
    
    ServerAdmin webmaster@jobeet.dev
    DocumentRoot "D:dafapDeveloppements Eclipseexemple_jobeetpublic"
    ServerName www.jobeet.dev
    ServerAlias jobeet.dev
    ErrorLog "C:/Program Files/wamp/logs/jobeet_error.log"
    CustomLog "C:/Program Files/wamp/logs/jobeet_access.log" common
    SetEnv APPLICATION_ENV "development"
    
    DirectoryIndex index.php
    AllowOverride All
    Require all granted
    
    

importantPassant de la version 2.2 à la version 2.4 d'apache, on notera ici la nécessité de se référer à la documentation d'Apache pour remplacer les directives Order, Allow et Deny par la directive Require.

Il peut être intéressant pour les projets qui n'utilisent pas Zendframework de rajouter dans le bloc <Directory> la ligne :

Options Indexes Multiviews

afin de pouvoir lister le contenu des répertoires (nous sommes sur un serveur de développement). Cela n'a pas d'utilité avec Zendframework puisque tout démarre de public/index.php.

Configuration de PHP

La spécificité de WampServer  est d'utiliser deux fichiers php.ini, l'un  pour la console (pour moi dans le dossier phpphp5.4.12) et l'autre pour le serveur web (pour moi dans apacheApache2.4.4bin). Or l'activation d'une extension par l'outil d'administration de wamp n'intervient que sur le fichier php.ini d'apache ... et encore, de façon imparfaite.

Pour activer une extension, il faut décommenter la ligne de cette extension dans les deux fichiers php.ini.
importantPour l'extension intl, il faut en outre copier les fichiers icu*.dll du dossier phpphp5.4.12 dans le dossier apacheApache2.4.4bin. Sans cela, apache ne charge pas cette extension dans php.

Ne pas oublier de redémarrer les services après toute modification de php.ini.

Installation de PEAR

Bien évidemment, on aura besoin de pear, ne serait-ce que pour effectuer des tests unitaires avec PhpUnit... et pear n'est pas dans le paquet d'installation de WampServer.

Voici la procédure à suivre :

  • télécharger go-pear.phar et l'enregistrer dans le répertoire de la version de php (celui qui contient php.exe - ici, j'ai le dossier php5.4.12 au moment où je rédige cet article).
  • ouvrir une console (démarrer / cmd) et se placer dans ce répertoire et lancer la commande :
    php go-pear.phar

    console-dossier-php Répondre local et valider.

  • Il y a maintenant 12 paramètres à fournir pour l'installation. Valider tous les répertoires proposés par défaut (chez moi de 1 à 11). Compléter éventuellement (pour le 12e paramètre - Path to CLI php.exe - j'indique le chemin C:Program Fileswampbinphpphp5.4.12).
  • Autoriser la modification de php.ini pour prendre en compte le chemin des extensions de pear (ici, C:Program Fileswampbinphpphp5.4.12pear) dans la variable include_path.
  • Il semble qu'il y ait 2 fichiers php.ini, l'un dans le dossier de la version de php (ici php5.4.12) et l'autre dans le dossier d'Apache. Le gestionnaire de WampServeur propose de modifier le fichier php.ini. Il s'agit de celui contenu dans le dossier d'Apache, alors que la procédure d'installation de pear modifie celui du dossier de la version de php. On va donc rapporter la modification réalisée dans le php.ini d'Apache :
    ;***** Added by go-pear
    include_path=".;C:Program Fileswampbinphpphp5.4.12pear"
    ;*****
  • Il reste à surcharger les variables d'environnement de l'ancienne installation de pear avec Zend Server :
    • PHP_PEAR_BIN = C:Program Fileswampbinphpphp5.4.12
    • PHP_PEAR_DAT = C:Program Fileswampbinphpphp5.4.12data
    • PHP_PEAR_DOC = C:Program Fileswampbinphpphp5.4.12docs
    • PHP_PEAR_INSTALL_DIR = C:Program Fileswampbinphpphp5.4.12pear
    • PHP_PEAR_PHP_BIN = C:Program Fileswampbinphpphp5.4.12php.exe
    • PHP_PEAR_SYSCONF_DIR = C:Program Fileswampbinphpphp5.4.12
    • PHP_PEAR_TEST_DIR = C:Program Fileswampbinphpphp5.4.12tests
  • Maintenant on vérifie que pear fonctionne. console-pear-list

 Installation de PhpUnit

  • Ajout des channels nécessaires :
    pear channel-discover pear.phpunit.de
    pear channel-discover components.ez.no
    pear channel-discover pear.symfony.com
  • Installer PhpUnit :
    pear install --alldeps phpunit/PHPUnit
  • Pour le bon fonctionnement de PhpUnit, notamment phpunit --self-update, on doit pouvoir charger l'extension openssl. L'outil d'administration qui permet de charger les extension ne suffit pas parcequ'il n'intervient que sur le fichier php.ini d'Apache alors que phpunit sera lancé en console. Il faut donc modifier manuellement le fichier php.ini du répertoire de la version de php (ici wamp/bin/php/php5.4.12/php.ini).
    Dans ce fichier, décommenter la ligne :

    extension=php_openssl.dll

    puis redémarrer les services de wamp. On vérifiera à partir d'une console (cmd) que l'extension openssl est chargée en lançant la commande :

    php -m

console-phpunit-channels

 Installation de Zendframework 1

Etant donné que ces projets évolueront peu, j'ai choisi de placer la librairie de Zendframework 1 dans un seul répertoire qui sera disponible pour tous les projets. J'y ai rajouté la documentation.

ZF1
  └ZendFramework-1.12.6
    ├bin
    ├demos
    ├documentation
    ├externals
    ├extras
    ├library
    ├puppet
    ├resources
    └tests

 

Cela nécessitera de référencer ce dossier library dans l'include_path de php. Toutefois, afin de pouvoir facilement faire évoluer les versions de Zendframework et de rester indépendant de le configuration, j'ai décidé de placer cette référence dans le .htaccess du projet et de modifier le fichier public/index.php des projets.

Voici le .htaccess :

SetEnv ZEND_PATH "C:Program Files (x86)ZendZF1ZendFramework-1.12.6library"
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]

A noter qu'il sera sans doute nécessaire de supprimer de l'affichage les alertes de compatibilité que PHP 5.4 envoie. Pour cela, il faut rajouter dans .htaccess la ligne :

php_value error_reporting 30711

Voici l'entête de mes fichiers index.php :

Installation de Zendframework 2

Pour ZF2, j'utilise composer.phar qui installe une librairie ZF2 dans chaque projet et met à jour les fichiers autoloader. Je n'ai donc pas trouvé utile d'installer la librairie du framework.

Soit on part du projet Zend Skeleton, soit on installe composer.phar dans le dossier racine du projet par la commande :

shell>curl -s http://getcomposer.org/installer | php

à condition que curl soit installé, bien évidemment.

PHPUnit sous Windows 7 et la couleur sur la console

Lors des tests unitaires avec PHPUnit, un code couleur (code ANSI) apparaît en fin de message mais n'est pas interprété par la console windows.
D:\www\projet\tests>phpunit
..........
Time: 98 ms, Memory: 9.75Mb
←[30;42m←[2KOK (11 tests, 18 assertions)
←[0m←[2K
D:\www\projet\tests>

Pour obtenir la couleur, on peut utiliser le programme ansicon.

Décompresser ce zip et copier le contenu du répertoire x64 ou x86 (respectivement pour systèmes 64 bits ou 32 bits) dans un dossier référencé dans le path.

A chaque ouverture d'une console, exécuter ansicon avant de lancer les phpunit.
C:\Users\moi>ansicon
Microsoft Windows [version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. Tous droits réservés.
C:\Users\moi>d:
D:\>cd www\projet\tests
D:\www\projet\tests>phpunit
..........
Time: 98 ms, Memory: 9.75Mb
OK (11 tests, 18 assertions)D:\www\projet\tests>