Catégorie : modules apache

Tout ce qui concerne les modules du serveur Apache, que ce soit du point de vue développement ou du point de vue de l’utilisation.

Librairie Apr : tutoriels 3 et 4

Pour information : regardez le tutoriel au complet, en Anglais, ici.
Vous trouverez tout ce tutoriel séparé en plusieurs pages ici.

3. memory pool (apr_pool_t)

La plupart des fonctions de libapr sont dépendants de pools mémoire. Grâce aux pools mémoire, vous pouvez facilement gérer des groupes de portions de mémoire.

Imaginez un cas sans le système de pool mémoire, où vous devez allouer chaque portion de mémoire. Vous devez libérer chacun d’eux. Si vous avez fait dix allocations, vous devez faire dix libérations, sinon vous allez avoir des fuites mémoire. Le principe de pool mémoire résout ce genre de problème. Après avoir crée un pool mémoire, vous pouvez allouer autant de morceaux de mémoire que vous voulez sur ce pool. Pour tout libérer, la seule chose que vous ayez à faire est de détruire le pool mémoire. Le pool s’occupe de libérer le reste.

Il y a deux gros avantages :

  1. Comme expliqué juste avant, cela évite les fuites mémoire ;
  2. Ensuite, le coût machine d’allocation est réduit.

D’une certaine façon, vous devez tout de même programmer comme si vous étiez dans une session. Un pool mémoire est un peu comme un contexte de session, c’est à dire que tous les objets qui y sont liés ont la même durée de vie. Vous pouvez contrôler un groupe d’objets dans un contexte de session. Au début d’une session, vous créez un pool mémoire. Ensuite, vous créez des objets sur ce pool mémoire pendant cette session. Notez que vous n’avez pas à vous préoccuper de leur durée de vie. A la fin de la session, la seule chose que vous ayez à faire est de libérer le pool mémoire.

REMARQUE : En général, le contrôle de la durée de vie des objets est la chose la plus difficile en programmation. C’est pour cela qu’il y a plein d’autres techniques pour cela, telles que les « pointeurs intelligents, le ramasse-miettes (GC – garbage collector) etc. Il est difficile d’utiliser de telles techniques en même temps. Comme le pool mémoire est l’une de ces techniques, il vous faire attention aux mélanges !

Il y a trois fonctions principales de l’API, concernant les pools mémoire :

/* extrait de apr_pools.h */
APR_DECLARE(apr_status_t) apr_pool_create(apr_pool_t **newpool, apr_pool_t *parent);
APR_DECLARE(void *) apr_palloc(apr_pool_t *p, apr_size_t size);
APR_DECLARE(void) apr_pool_destroy(apr_pool_t *p);

On crée un pool mémoire via apr_pool_create(). Ce pool existe tant qu’on ne l’a pas libéré via apr_pool_destroy(). Le premier argument de apr_pool_create() est un argument-resultat. Un nouvel objet « pool mémoire », apr_pool_t, est renvoyé par cet appel à l’API. On appelle apr_palloc() pour allouer une portion de mémoire en précisant la taille. Jetez un coup d’oeil à mp-sample.c pour voir un exemple simple.

/* extrait de mp-sample.c */
apr_pool_t *mp;
/* créer un pool mémoire. */
apr_pool_create(&mp, NULL);
/* allouer une portion mémoire sur le pool */
char *buf1;
buf1 = apr_palloc(mp, MEM_ALLOC_SIZE);

En résumé, on peut utiliser apr_palloc() comme malloc(3). On peut aussi appeler apr_pcalloc(). Comme vous l’imaginez, apr_pcalloc() ressemble fortement à calloc(3). apr_pcalloc() renvoie une portion mémoire intégralement initialisée avec des zéro. Si vous vous servez de malloc(3)/calloc(3), vous aurez besoin d’appeler free(3) pour chaque portion allouée. A l’inverse, vous n’avez pas besoin de faire ça pour chaque portion allouée sur un pool mémoire. Vous appelez simplement apr_pool_destroy() avec le pool concerné et tout ce qui a été alloué est libéré.

REMARQUE : Il n’y a pas de limitation sur la taille que vous pouvez allouer avec apr_palloc(). Néanmoins, ce n’est pas une bonne idée d’allouer de gros morceaux de mémoire avec une gestion de mémoire orienté « pool ». C’est simplement parce que la gestion via un pool mémoire a été conçue pour gérer efficacement des petites allocations. En réalité, la taille initiale d’un pool mémoire est de de 8 kb. Si vous avez besoin d’allouer quelque chose de plus large, p.ex. plusieurs méga bytes, vous devriez éviter la gestion via un pool mémoire.

REMARQUE : Par défaut, un manager de pool mémoire ne libère jamais la mémoire allouée au système tant qu’il n’est pas détruit. Si un programme tourne pendant longtemps, il aura des problèmes. Je vous conseille de préciser dès le début une taille limite maximale comme suit :

/* exemple qui applique une limite haute pour forcer le
 * manager du pool à libérer de la mémoire au système
 * à partir d'un certain seuil
 */
#define YOUR_POOL_MAX_FREE_SIZE 32 /* taille max. du pool */
apr_pool_t *mp;
apr_pool_create(&mp, NULL);
apr_allocator_t* pa = apr_pool_allocator_get(mp);
if (pa) {
    apr_allocator_max_free_set(pa, YOUR_POOL_MAX_FREE_SIZE);
}

Il y a deux autres fonctions de l’API que vous devez connaitre. L’une d’elles est apr_pool_clear(), et l’autre est apr_pool_cleanup_register(). apr_pool_clear() ressemble à apr_pool_destroy(), mais le pool mémoire est toujours réutilisable. Le code typique est le suivant :

/* exemple montrant comment fonctionne apr_pool_clear() */
apr_pool_t *mp;
apr_pool_create(&mp, NULL);
for (i = 0; i < n; ++i) {
    do_operation(..., mp);
    apr_pool_clear(mp);
}
apr_pool_destroy(mp);

Le pool mémoire est utilisé dans do_operation(), qui, imaginons, alloue plein de morceaux mémoire. Si vous n’avez pas besoin de tous les morceaux alloués hors de do_operation(), vous pouvez appeler apr_pool_clear(). Ainsi vous limiterez l’utilisation mémoire. Si vous connaissez bien le principe du fonctionnement du système de la pile locale, vous pouvez imaginer que la gestion de pool mémoire est une « pile mémoire locale ». L’appel de apr_palloc() est similaire au fait de déplacer SP(stack pointer), et appeler apr_pool_clear() est similaire à faire reculer SP. Les deux opérations sont très légères en termes de calcul CPU.
La fonction apr_pool_cleanup_register() offre la possibilité d’appeler automatiquement des fonctions de libération lorsque le pool est nettoyé via apr_pool_clear() ou détruit vie apr_pool_destroy(). Dans les fonctions de callback, vous pouvez implémenter un code de libération de vos objets, ou de finalisation, liés au nettoyage/destruction du pool mémoire.
La dernière chose concernant les pools mémoire est le sous-pool. Chaque pool a la possibilité d’avoir un parent. Pour être plus précise, les pools mémoire sont organisés dans un arbre. Le deuxième argument de  apr_pool_create() est le parent du pool mémoire à créer. Si vous passez NULL en tant que parent, le nouveau pool est positionné à la racine de l’arbre. Vous pouvez créer des « sous-pools » mémoire en les liant à ce dernier. Quand vous appelez apr_pool_destroy() pour un pool mémoire dans l’arbre, tous ses descendants sont aussi détruits. Quand vous appelez apr_pool_clear(), le pool mémoire reste en vie, il est simplement nettoyé, mais les enfants sont détruits. Quand un enfant est détruit, les fonctions callback liées au nettoyage/destruction dont on a parlé précédemment sont appelées.

REMARQUE : Le bogue suivant est très courant : vous passez NULL comme paramètre pool à la fonction callback de nettoyage. Il faut plutôt utiliser apr_pool_cleanup_null, comme le montre l’exemple :

/* pseudo code sur le bogue habituel de pool mémoire */
/* apr_pool_cleanup_register(mp, CONTEXTE_DE_VOTRE_CODE, CALLBACK_DE_VOTRE_CODE, NULL); C'EST UN BOGUE */
/* Version corrigée : */
apr_pool_cleanup_register(mp, CONTEXTE_DE_VOTRE_CODE, CALLBACK_DE_VOTRE_CODE, apr_pool_cleanup_null);

4. Error status (apr_status_t)

La plupart des fonctions de la librairie libapr renvoient une valeur de type apr_status_t. La valeur apr_status_t est très souvent, soit APR_SUCCESS soit autre chose. APR_SUCCESS indique que tout s’est correctement déroulé. Le code typique ressemble à cela :

/* pseudo code sur la vérification du retour apr_status_t */
apr_status_t rv;
rv = apr_pool_create(&mp, NULL);
if (rv != APR_SUCCESS) {
    /* Gestion de l'erreur */;
}

libapr définit quelques statuts d’erreur tels que APR_EINVAL, et des macros pour vérifier des types d’erreurs telles que APR_STATUS_IS_ENOMEM((). Quelques unes d’entre elles sont vraiment très utiles, surtout pour la gestion des problèmes de portabilité. Un exemple typique est la macro APR_STATUS_IS_EAGAIN(). Historiquement, il y a deux erreurs qui ont le même chiffre mais pas la même signification, EAGAIN et EWOULDBLOCK. La macro APR_STATUS_IS_EAGAIN() gère ce problème.
Néanmoins, c’est presque impossible de gérer toutes les erreurs sans prendre en compte le système sous-jacent. La librairie libapr ne réinvente pas la roue. Ce qu’elle fait est très simple :

  • En cas de succès, retour = APR_SUCCESS
  • En cas d’erreur concernant uniquement la librarie elle-même, retour = APR_XXX
  • En cas d’erreur système commune à tous les systèmes d’exploitation, retour = APR_XXX
  • En cas d’erreur spécifique à un système d’exploitation, retour = numéro d’erreur du système plus un offset

Je vous conseille de suivre ces règles simple :

  1. Comparer la valeur de retour avec APR_SUCCESS
  2. Si vous avez besoin de plus de détails sur l’erreur, comparer avec d’autres valeurs d’erreur définies dans la librairie

Un fonction extrêmement utile est apr_strerror(). Vous pouvez afficher la chaine décrivant l’erreur de cette façon :

/* pseudo code expliquant apr_strerror() */
apr_status_t rv;
rv = apr_xxx_xxx();
if (rv != APR_SUCCESS) {
    char errbuf[256];
    apr_strerror(rv, buf, sizeof(buf));
    /* afficher la description de l'erreur */
    puts(errbuf);
}

Librairie Apr : tutoriels 1 et 2

1. Disponibilité du tutoriel au complet

Regardez le tutoriel au complet, en Anglais, ici.
Vous trouverez tout ce tutoriel séparé en plusieurs pages ici.

2. Un squelette de code pour libapr

Je pense que c’est une bonne idée d’écrire un « squelette de code » au début, lorsque vous commencez à apprendre une nouvelle librairie ou un nouveau framework. Un ‘squelette de code’ est le plus petit morceau de code possible que vous pouvez compiler et exécuter (malgré le fait qu’il ne fasse rien de vraiment utile).
Heureusement, le squelette de code que l’on va faire pour la libapr est beaucoup plus simple que la plupart des frameworks modernes. Voyons apr-skeleton.c. On appelle apr_initialize() pour ouvrir la librairie, et apr_terminate() pour la fermer. C’est tout. Comme vous pouvez l’imaginer, le code ne fait rien du tout.
libapr n’est pas une librairie de type framework. Pour être plus précis, libapr ne vous aide pas du tout à concevoir la structure de votre code programme. Il y a des avantages et des inconvénients. L’avantage le plus important est que l’utilisation de libapr est très facile à utiliser et à intégrer dans du code existant. L’inconvénient principal est que vous devez concevoir la structure complète du code lorsque vous utilisez libapr.
Ici, nous avons tout de même quelques règles et style concernant libapr :

  • la règle de nommage est très simple et claire ;
  • les types de données opaques sont souvent utilisés (p.ex. les types incomplets) ;
  • la plupart des retours de fonctions sont apr_status_t et si besoin est, certaines variables en paramètre sont aussi des arguments résultats ;
  • règles de pool mémoire.

On peut voir les styles et les règles directement par cet exemple :

/* extrait de mp-sample.c */
apr_status_t rv;
apr_pool_t *mp;
rv = apr_pool_create(&mp, NULL);

Voici l’explication du code : ici ne regardez que la notation. Vous voyez le préfixe apr_. Ce dernier indique que le symbole fait partie de l’espace de nommage de la librairie libapr. Le suffixe _t précise que le nom concerné est un type.
apr_pool_t est un type opaque. Cela signifie que la structure de ce type n’est pas publique. En utilisant une terminologie de style OO (Object Oriented), toutes les variables membres sont privées. Vous ne pouvez pas y accéder directement. Vous ne pouvez pas les voir dans les fichiers « .h » non plus. La seule chose que vous puissiez faire pour ce type est d’appeler une API qui l’utilise, telle que apr_foo_bar() par exemple. Plus important encore, vous ne pouvez créer une instance de ce type dynamiquement. Vous êtes obligé de passer par une fonction d’allocation de la librairie. Seule libapr sait comment construire et détruire ce genre d’objets.
Comme vous pouvez le voir, le type de retour de la fonction apr_pool_create() est apr_status_t. apr_status_t est soit un code indiquant un status soir un code d’erreur. apr_status_t is est utilisé couramment en tant que type de valeur de retour par la plupart des fonctions de l’APIs. De la même façon, on peut recevoir des résultats de fonction via des arguments. De tels arguments sont appelés arguments-résultat. Il y a beaucoup d’arguments-résultat dans le monde de la libapr.
En général, si vous voyez le type apr_foo_t, vous verrez des fonctions apr_foo_bar(), qui ont un lien avec le type apr_foo_t. Le code qui suit est un pseudo code courant :

/* pseudo code of libapr. Vérif. des erreur omise */

apr_status_t rv;
apr_foo_t *foo;

/* créer un objet @foo selon @args : */
rv = apr_foo_create(&foo, args...);

/* faire quelque chose avec @foo : */
rv = apr_foo_do_something(foo, args...); /*  */

/* détruire l'objet @foo. */
apr_foo_destroy(foo);

/* NB : Parfois la destruction d'un objet est
   faite implicitement lorsque le pool mémoire
   associé est détruit. */

Dix règles d'or pour les filtres de sortie.

Le contenu en Anglais non traduit se trouve ici.

Voilà les règles que doivent suivre à la lettre les filtres de sortie :

  1. Les filtres de sortie ne devraient pas passer de brigades vides le long de la chaine de sortie, mais devraient être tolérantes sur l’arrivée de brigades vides.
  2. Les filtres de sortie doivent passer tous les seaux de métadonnées (metadata buckets) le long de la chaine de sortie ; les seaux de vidages (FLUSH buckets) devraient être respectés en passant tous les seaux en attentes le long de la chaine de sortie.
  3. Les filtres de sortie devraient ignorer tous les seaux qui suivent un seaux de fin de fichier (EOS buckets).
  4. Les filtres de sortie doivent traiter une quantité fixe de données, à la fois, afin de s’assurer que la consommation mémoire n’est pas proportionelle à la taille du contenu qui est filtré.
  5. Les filtres de sortie devraient ignorer les types de seaux, et doivent être capables de traiter des seaux de type inconnu.
  6. Après avoir appelé ap_pass_brigade pour faire suivre une brigade le long de la chaine de filtres, les filtres de sortie devraient appeler apr_brigade_cleanup pour s’assurer que la brigade est vide avant de s’en resservir ; les filtres ne devraient jamais utiliser apr_brigade_destroy pour « détruire » des brigades.
  7. Les filtres de sortie doivent mettre de côté les seaux qui sont destinés à être gardés plus longtemps que la durée du filtrage.
  8. Les filtres de sortie ne doivent pas ignorer la valeur de retour de ap_pass_brigade, et doivent renvoyer les erreurs appropriées en retour, à la chaine de filtres.
  9. Les filtres de sortie doivent seulement créer un nombre fixe de brigades de seaux pour chaque réponse, plutôt que une par invocation.
  10. Les filtres de sortie devraient en tout premier lieu essayer des lectures non bloquantes sur chaque seau, et envoyer un buffer de vidage (FLUSH bucket) le long de la chaine de filtres si la lecture doit être faite à nouveau, et de manière bloquante, avant de recommencer une lecture bloquante.

Sachant que la règle 9 n’est pas très claire pour moi, je vous la relivre en Anglais :
Output filters must only create a fixed number of bucket brigades for each response, rather than one per invocation.

Apache : notes pour les filtres

Voilà ce que j’ai mis longtemps à comprendre :

Lorsqu’un client envoie quelque chose à httpd, le serveur Web commence à créer des seaux qu’il remplit au fur et à mesure : et quand un seau est plein, il en remplit un autre etc jusqu’à ce qu’il ait un nombre de seaux suffisants. Ces seaux sont rangés dans une brigade. Une brigade de seaux. Une bucket brigade, en Anglais. Et là, il les fait passer au premier filtre qu’il a sur sa liste. Ce filtre peut à son tour, ajouter d’autres seaux, en enlever, ou même, tout supprimer et créer toute une série d’autres seaux. Toujours est-il que quand on développe un filtre, il faut toujours avoir en tête plusieurs choses :

  • Il va toujours y avoir une série de seaux qu’il va falloir parcourir, ce ne sera jamais un seul seau simple et seul. La boucle de parcours sera toujours de la forme :
    /* on demande à lire une série de seaux : */
    rv = ap_get_brigade(f->next, bb, AP_MODE_READBYTES, APR_BLOCK_READ, readbytes);
    /* si erreur, la gérer, sinon boucle pour lire seau après seau : */
    for (b = APR_BRIGADE_FIRST(bb);
         b != APR_BRIGADE_SENTINEL(bb);
         b = nextb) {
        nextb = APR_BUCKET_NEXT(b);
        ...
        traitement de ce qu'on a reçu
        ...
    }
  • Le serveur Apache gère une chaine de filtres, qui se suivent dans un ordre qu’il est impossible à connaitre du point de vue d’un filtre. En résumé, on ne sait jamais, quand on programme un filtre, quels sont les filtres qui ont été appelés avant le nôtre, ou quels sont les filtres le seront après. Bon ok. Il y a quand même une possibilité de dire grossièrement où on veut que le filtre soit, dans la chaine. En réalité il est possible de préciser l’un cinq niveaux grossiers parmi :
    1. APR_HOOK_REALLY_FIRST
    2. APR_HOOK_FIRST
    3. APR_HOOK_MIDDLE
    4. APR_HOOK_LAST
    5. APR_HOOK_REALLY_LAST

    Revenons à notre brigade, pleine de seaux, mais dont on ne sait pas exactement ce qu’elle contient, mais on le suppose. Lorsque celle-ci arrive et que notre filtre est appelé, on ne sait que grossièrement où est placé le filtre dans la chaine, donc il se peut que la brigade arrive avec des données en plus qui proviennent d’autres filtres mais qui ne nous concernent pas (entre autres des données personnelles qui vont d’un filtre à l’autre, des méta-données, en Anglais METADATA). C’est pour cela qu’il faut toujours s’assurer que ce ne sont pas des métadonnées qui ne nous concernent pas : c’est la macro APR_BUCKET_IS_METADATA :
    /* on demande à lire une série de seaux : */
    rv = ap_get_brigade(f->next, bb, AP_MODE_READBYTES, APR_BLOCK_READ, readbytes);
    /* si erreur, la gérer, sinon boucle pour lire seau après seau : */
    for (b = APR_BRIGADE_FIRST(bb);
         b != APR_BRIGADE_SENTINEL(bb);
         b = nextb) {
        nextb = APR_BUCKET_NEXT(b);
        if ( ! APR_BUCKET_IS_METADATA(b) ) {
            ...
            traitement de ce qu'on a reçu
            ...
        }
    }

Zeemoz : créer un nouveau module C pour Apache

Aller dans le répertoire des sources, et taper :

/opt/httpd-2.2.4/bin/apxs -g -n modulenouveau

Il va créer un répertoire ./modulenouveau et à l’intérieur les fichiers Makefile et mod_modulenouveau.c, qui contient le squelette du code tel qu’attendu par Apache.
Ensuite il ne reste qu’à remplir.
Pour le script d’installation, il n’y a qu’à copier /usr/bin/modsearch_install.sh en modmodulenouveau_install.sh et modifier uniquement les variables $SOURCES et $MOD.

Les filtres Apache

Il existe plusieurs filtres, qui sont passés dans l’ordre lorsqu’ils arrivent au serveur, puis sont passés exactement dans l’ordre inverse lorsqu’ils repartent vers le client (un seul m’intéresse, le premier : à l’arrivé ce sont les filtre d’entrée, et lorsque cela repart ce sont les filtres de sortie) :

  1. AP_FTYPE_RESOURCE
    Pour les filtres de contenu : cela englobe tous les types de filtres qui peuvent modifier le contenu, cela va de la décomposition de pages XML qui arrivent, à la transformation d’images ou encore à l’assemblage de contenu qui arrive en plusieurs fois. Ce type de filtre peut changer complètement la nature du contenu de la requête. Par exemple, un filtre XSLT peut transformer le contenu d’une page XML en une page HTML ou en un fichier PDF ;
  2. AP_FTYPE_CONTENT_SET
    C’est la seconde étape du filtrage du contenu, qui est principalement destinée à empaqueter le contenu, par exemple le module mod_deflate (qui applique une compression de type gzip) ;
  3. AP_FTYPE_PROTOCOL
    Le troisième filtre, : le filtre habituel a pour fonction d’insérer les entêtes HTTP au début des données qui ressortent des filtres de contenu. C’est réalisé par un filtre intégré nativement à Apache : le filtre HTTP_HEADER(la fonction ap_http_header_filter exactement), donc les applications, en général, l’ignorent.
  4. AP_FTYPE_TRANSCODE
    On s’en tape, ça ne sert pratiquement jamais.
  5. AP_FTYPE_CONNECTION
    On s’en tape, ça ne sert pratiquement jamais.
  6. AP_FTYPE_NETWORK
    On s’en tape, ça ne sert pratiquement jamais.

Le redirection interne

Il est possible dans Apache, lors de la gestion de la requête, de faire une redirection interne, c’est à dire de faire croire qu’il y a eu une redirection, mais au lieu de la renvoyer au client, et que le client demande la nouvelle URL, elle est faite en interne. Pour pouvoir faire cela, deux possibilités :

  1. Location: http://olivier.pons.free.fr/apache/
  2. Location: /apache/

La première est basée sur la nature double de l’entête Location d’un script CGI : elle force à envoyer une redirection au client, alors que la seconde fait une redirection en interne, sans envoyer quelque chose au client.

Seule la seconde possibilité nous intéresse. Il est possible de le faire de deux façons dans un module :

  1. void ap_internal_redirect(
      const char *new_uri, request_rec *r)
    .

    Cette forme de redirection interne est le mécanisme à utiliser dans tous les cas à de rares exceptions près. Cette fonction crée un nouvel objet requête pour l’URL passée en paramètre, et lance la nouvelle requête comme si new_uri avait été demandée en premier lieu.

  2. void ap_internal_fast_redirect(
      request_rec *new_req, request_rec *r)

    Cette fonction clone une structure existante new_req dans la requête en cours afin de pouvoir mettre en place une nouvelle requête et simuler le fait qu’on lui passe le contrôle. Ce mécanisme est parfois utilisé pour promouvoir une sous-requête en requête principale. Ce qui est important de noter c’est qu’on ne repart pas de zéro, donc on ne refait pas complètement la requête.

Dix façons de devenir un meilleur développeur PHP

Les 5 premiers conseils viennent d’ici

Les 5 conseils suivants venaient de ce lien d’http://www.hackingajax.com/2008/02/13/5-more-ways-to-be-a-better-php-developer/ mais il est mort…

Souvent, un développeur PHP inexperimenté va sauter sur IRC et poser une question sur Freenode##php. Et si la question est facile, il aura une réponse rapidement. Au début. Parce que par la suite, il va être bombardé de réponses du style « LOL », « ROFL », « Va apprendre PHP et reviens », « Vas te documenter un minimum », « On n’est pas ton prof personnel », etc. Donc, comment devenir un meilleur développeur PHP ? Dans ce post, je vais mettre en avant cinq façons d’être un meilleur développeur, d’être plus productif, d’écrire moins de code et de faire ainsi plus de choses avec vos applications web. Il y a toujours quelque chose de nouveau à apprendre quand on parle de développement PHP. Nouvelles fonctions internes, nouveaux frameworks, nouveaux design patterns, nouveau styles de documentation. Ci-suivent quelques-unes des façons les plus efficaces pour vous aider à être un meilleur développeur PHP.

  1. Lisez le manuel
    Je ne le dirai jamais assez : c’est là qu’il faut regarder en premier et il y a des tonnes de choses à apprendre simplement en lisant le manuel. Les problèmes les plus courants sont déjà résolus si vous lisez ce qui concerne les chaines de caractères (string), et les tableaux (array). Il y a plein d’exemples qui fonctionnent directement accessibles, et souvent, rien qu’en parcourant le manuel vous allez vous rendre compte que vous êtes en train de ré-inventer la roue, parce qu’une fonction que vous voulez faire existe déjà et, mieux, elle est directement d’origine dans PHP ! Le manuel PHP est votre ami. Note d’Olivier : une petite astuce supplémentaire : si vous voulez chercher quelque chose, il vous suffit de taper : php.net/[mot à chercher] et même si PHP ne le connait pas il vous donnera une liste de mots approchants ! Génial non ?
  2. Balladez vous dans le code d’autres personnes
    PHP a plein de code open source. Pourquoi ne pas apprendre grâce à ça ? Récupérez une application PHP open source et jetez un coup d’oeil dans le code. Les plus gros projets sont sûrement meilleurs, car, même s’ils auront des structures plus complexes, ils auront des systèmes fonctionnels mais derrière tout ça une documentation détaillée qui explique tout. Visitez SourceForge.net si vous n’arrivez pas à trouver un point de départ.
  3. Apprenez un nouveau framework
    Il y a plus de frameworks PHP que vous n’avez eu de diners galants qui se sont bien terminés (!) ; la plupart sont open source et disponibles en ligne si vous savez où regarder. Essayez les principale, par exemple phpframeworks.com a une excellente liste. Votre framework ne peut jamais être entièrement complet, votre prochain travail, ou projet, aura besoin de telle ou telle fonctionnalité, ou s’appuiera sur un framework différent et vous trouverez certainement que des idées des autres sont très utiles pour l’un de vos projets.
  4. Recherchez
    Vous avez probablement entendu plein de choses et de mots dans le contexte d’un développement Internet en PHP. En allant de OOP à MVC, en passant par KISS, DRY, YAML, INI, REST, XML-RPC, etc, il y a des centaines de termes techniques et de concepts ici qui pourraient être directement reliés à vos travaux. Peut-être avez-vous déjà une compréhension basique, ou grossière d’eux, mais est-ce que vous savez vraiment en profondeur ce qu’ils signifient pour vous ? Passez un peu de temps à faire de la vraie recherche, apprenez, formez vous un peu dessus. Wikipedia est un bon endroit pour commencer. Vous allez inévitablement apprendre des choses que vous ne connaissez pas, et ça ne peut que vous être profitable.
  5. Apprenez la programmation orientée objet
    C’est un peu lié au point précédent, mais la programmation orientée objet (POO, ou en Anglais, Object Oriented Programming, OOP), est beaucoup plus importante que vous ne pouvez l’imaginer : est-ce que vous connaissez vraiment la POO sur PHP 5 ? Par exemple, est-ce que vous manipulez facilement les classes abstraites, les interfaces, le mot clé implements, les méthodes et propriétés statiques, et les propriétés protected ? Beaucoup de développeurs PHP expérimentés ne sont pas compétents dans ce domaine, et pourtant, si vous utilisez ce genre de caractéristiques propres à la POO, vous construirez certainement des architecture beaucoup plus souples, évolutives, et vous gagnerez par là même beaucoup de temps de développement.
  6. Commencez un projet que d’autres personnes (développeurs et utilisateurs finaux) vont utiliser.
    Vous ne gagnerez jamais autant de respect (ou de dédain) plus rapidement qu’en écrivant un programme pour des étrangers qui doivent se servir de votre code. Que ce soit une application web pour des utilisateurs finaux, ou une extension PEAR pour les développeurs, ou un plugin WordPress pour le bloggeurs, le fait de mettre un logiciel, ou du code entre les mains d’autres personnes de manière à ce qu’elles puissent y réagir est la chose la plus efficace que vous puissiez faire pour devenir un meilleur développeur. Être mis dans la position de devoir répondre aux demandes des uns et des autres est aussi important et valorisant que le lien entre un journaliste et le journal publié. Tous ceux qui l’ont fait vous le diront, ce n’est pas facile mais une fois que vous arrivez à gérer ça, vous saurez écrire en étant sous pression, avec des délais à tenir, et éventuellement sous le feu d’une opinion publique qui peut ne pas être facile. D’ailleurs, écrire un programme en entier de bout en bout n’est pas si différent. Faites votre travail, donnez-le et attendez le retour. C’est cette boucle d’échanges qui fait les bons programmes et les bons développeurs. En fin de compte, c’est peut-être ce fameux feedback qui a principalement de la valeur.
  7. Apprenez un autre langage.
    Si vous commencez à peine avec PHP, ce n’est peut-être pas le moment de vous plonger dans quelque chose de nouveau, mais si vous connaissez le langage et que vous voulez amélorier vos compétences, vous verrez qu’il y a des avantages à ne pas être spécialisé uniquement dans un seul langage. Par exemple : Java vous oblige à comprendre la programmation orientée objet (P.O.O., en Anglais : O.O.P.), Ruby on Rails vous oblige à apprendre le principe Modèle – Vue – Conteneur (M.V.C.), C# vous oblige à apprendre ce qu’est la dépendance à l’autocomplétion (je blague, C# ne sert à rien, apprenez Java c’est déjà une excellente chose). Le fait d’élargir vos horizons vous offre la possibilité de comprendre comment d’autres langages ont résolu certains problèmes qui ne le sont pas encore, ou pas correctement, en PHP, ou bien qui ne sont pas évidents à résoudre, tout simplement. Par exemple, je vous certifie que je suis devenu un développeur PHP nettement plus compétent depuis que j’ai écrit un peu plus de 7,500 lignes de code pour Ruby on Rails. Apparemment, je ne suis pas le seul.
  8. Apprenez un autre langage à quelqu’un.
    Rien ne vous fait mieux apprendre que d’apprendre à quelqu’un d’autre. N’importe quelle personne qui est vraiment intéressée par ce que vous apprenez va inévitablement vous posez des questions sérieuses, des questions précises, des colles que vous aurez du mal à surmonter au début, mais qui vont vous amener à un degré de connaissance du langage insoupçonnable. Et puis au moins vous aurez un autre angle de vision sur ce qu’est la patience, lorsque vous aurez à répondre pour la vingtième fois à « Au fait, vous pourriez ré-expliquer ce qu’est une variable ? ». J’ai appris le langage Python à mon fils, récemment, après n’y avoir pas touché pendant 5 ans. C’était une expérience très intéressante pas uniquement dans le sens où j’ai réalisé à quel point j’avais tout oublié de Python mais surtout dans la manière de programmer qui est différente. Quand vous réussissez à apprendre à des jeunes de 13 ans pourquoi vous devez transformer des entiers avant de pouvoir les concaténer à des chaines de caractères en Python et pourquoi ce n’est pas nécessaire en PHP — et qu’il le comprennent — vous aurez d’ici là appris énormément de choses et vous serez forcément devenu un meilleur développeur.
  9. Demandez des suggestions et des solutions
    Si jamais vous n’arrivez pas à vous décoller d’IRC, assurez vous que vous avez vraiment recherché dans tous les sens la résolution d’un problème avant de demander de l’aide. Et lorsque vous demandez, montrez votre code. Si vous montrez que vous avez essayé quelque chose et que vous avez fait déjà un bon paquet d’efforts, croyez moi, vous aurez beaucoup plus facilement des réponses à vos questions.
    Vos question devraient ressembler plutôt à « je pense qu’ici je fais quelque chose qu’il ne faut pas, pouvez-vous m’aider » plutôt que « pourriez vous faire le travail pour moi. »
  10. Utilisez ce que vous lisez.
    Que dire de plus que cette loi de programmation simple, cardinale et fondamentale :
    C’est toujours plus dur de lire du code que de l’écrire. (Joel Spolsky) ?
    Après ces deux conseils de Lisez le manuel et Balladez vous dans le code d’autres personnes, vous allez apprendre beaucoup de choses, mais il ne suffit pas que de lire. Il faut aussi écrire. Vous ne pouvez pas lire 100 livres faits par des gens talentueux et pondre juste après Le Seigneur des Anneaux en une nuit. Ne faites pas que parcourir le code, utilisez le aussi dans le votre. Eventrez CakePHP et regardez comment ils gèrent la validation des formulaires. Ecartelez HTML_QuickForm2 et utilisez sa création d’éléments. Comprenez comment WordPress gère les transactions de sa base de données, et comparez avec Drupal. Ce sont toutes les choses que votre application, à un moment ou à un autre, devra pouvoir réaliser, donc vous devriez vraiment jeter un coup d’oeil et essayer de voir comment les autres ont résolu les problèmes auxquels vous êtes confronté. Et quand c’est adéquat, et légal, utilisez leur code. Vous apprendrez incroyablement plus si vous devez prendre un morceau de code, et le modifier afin qu’il fonctionne dans votre application que si vous ne faite que lire les branches SVN du projet.

Directive directory

<Directory [répertoire]> et </Directory> sont utilisées pour encadrer un groupe d’instructions qui vont être appliquées sur le répertoire et les sous-répertoires de ce répertoire.

On peut mettre n’importe quelle instruction tant que c’est une instruction « pour répertoire ». Dans tous les cas, [répertoire] est :

  • soit le nom complet du répertoire concerné (sans le slash final) ;
  • soit une expression régulière simple (que des ?, * ou []) (je n’entrerai pas en détail ici).

Exemple :

<Directory "/var/www/cgi-bin">
  AllowOverride None
  Options None
  Order allow,deny
  Allow from all
</Directory>

AllowOverride : All|None|directive-type [directive-type] ..

On précise quelles sont les directives autorisées dans le fichier .htaccess.

Voici en fonction de ce que l’on met, le résultat :

  • None : Les fichiers .htaccess sont complètement ignorés
  • All : Toutes les directives possibles dans un contexte .htaccess sont prises en compte
  • AuthConfig : Autorise les directives d’autorisation (!) à des utilisateurs (AuthDBMGroupFile, AuthDBMUserFile, AuthGroupFile, AuthName, AuthType, AuthUserFile, Require, etc.).
  • FileInfo : Autorise les directives qui controllent les types des documents (DefaultType, ErrorDocument, ForceType, LanguagePriority, SetHandler, SetInputFilter, SetOutputFilter, et les directives du mod_mime Add* et Remove*, etc.), données méta de document (Header, RequestHeader, SetEnvIf, SetEnvIfNoCase, BrowserMatch, CookieExpires, CookieDomain, CookieStyle, CookieTracking, CookieName), directives mod_rewrite RewriteEngine, RewriteOptions, RewriteBase, RewriteCond, RewriteRule) et Action du module mod_actions.
  • Indexes : Autorise les directives qui controllent l’indexation de répertoire (AddDescription, AddIcon, AddIconByEncoding, AddIconByType, DefaultIcon, DirectoryIndex, FancyIndexing, HeaderName, IndexIgnore, IndexOptions, ReadmeName, etc.).
  • Limit : Autorise les directives qui controllent les accès « hôtes » (Allow, Deny et Order).
  • Options[=Option,...] : Autorise les directives qui controllent des particularités des répertoires (Options et XBitHack).

Order : Allow,Deny / Deny,Allow

La directive Order, lorsqu’elle est accompagnée des directives Allow et Deny, sert pour un système de contrôle en 3 passes.

  1. On passe au travers d’une des deux directives (Allow ou Deny)
  2. On passe ensuite l’autre directive (Deny ou Allow)
  3. Enfin on applique la 3ème passe pour toutes les requêtes qui n’ont correspondu ni à la première ni à la seconde

Notez bien que toutes les directives Allow et Deny sont appliquées, à l’inverse d’un pare-feu où, dès qu’il y a correspondance, on s’arrête. La troisième passe aussi est appliquée. En conséquence, l’ordre dans lequel les lignes de configuration apparaissent importe peu : toutes les directives Allow sont mises en un seul groupe, et toutes les directives Deny dans un autre. Les ordres possibles sont :

  1. Allow,Deny
    1. Toutes les directives Allow sont appliquées. Au moins l’une doit correspondre sinon la requête est rejetée.
    2. Ensuite, toutes les directives Deny sont appliquées. Si l’une d’elles correspond, la requête est rejetée.
    3. Enfin, toutes les requêtes qui ne sont validées ni par Allow ni par Deny sont considérées comme Deny
  2. Deny,Allow
    1. Toutes les directives Deny sont appliquées. Si l’une d’elles correspond, la requête est rejetée à moins qu’à la passe suivante, il y ait correspondance.
    2. Toutes les directives Allow sont appliquées.
    3. Toutes les requêtes qui ne sont validées ni par Allow ni par Deny sont considérées comme Allow
Validation : Résultat sur Allow,Deny Résultat sur Deny,Allow
Seul Allow valide Requête autorisée Requête autorisée
Seul Deny valide Requête refusée Requête refusée
Allow et Deny pas valides Seconde directive appliquée = refusée Seconde directive appliquée = autorisée
A la fois Allow & Deny valides Dernier contrôle valide appliqué = refusée Dernier contrôle valide appliqué = autorisée

Allow from all|host|env=env-variable [host|env=env-variable] ...

La directive Allow spécifie qui peut accéder à une zone du serveur.

L’accès peut être contrôlé par nom d’hôte, adresse IP, plage d’adresses IP ou encore d’autres caractéristiques de la requête du client qui ont été capturées dans des variables d’environnement.

Le premier argument est toujours from. Les arguments suivants peuvent prendre 3 formes différentes :

  • Si Allow from all est spécifié, alors tout le monde est autorisé, dans le cadre de la directive Allow, c’est à dire qu’il faudra prendre en compte la directive Deny et l’ordre spécifié par Order, mais ceci est expliqué plus haut.
  • Pour autoriser l’accès au serveur à des hôtes ou à des groupes d’hôtes on peut le faire en utilisant différents formats qui sont décrits en détail (en Anglais) ici.
  • Le dernier format possible est celui qui se base sur les variables d’environnement. C’est aussi expliqué en détail (en Anglais) ici.

Deny from all|host|env=env-variable [host|env=env-variable] ...

La directive Deny spécifie qui peut accéder à une zone du serveur.

L’accès peut être contrôlé par nom d’hôte, adresse IP, plage d’adresses IP ou encore d’autres caractéristiques de la requête du client qui ont été capturées dans des variables d’environnement.

Les arguments sont identiques à ceux décrit pour la directive Allow.

Pour plus de détails, vous pouvez le lire en Anglais ici.

Options [+|-]option [[+|-]option] ...

La directive Option décrit quelles sont les possibilités offertes au serveur pour un répertoire donné.

Option peut être mis à :

  • None : dans ce cas, aucune des possibilités supplémentaires n’est activée
  • All : toutes les possibilités supplémentaires sont actives sauf MultiViews. C’est la configuration par défaut.
  • ExecCGI : L’éxécution des scripts CGI (en utilisant le module mod_cgi) est autorisée. Le serveur suivra les liens symboliques de ce répertoire. Malgré le fait que le serveur suivra les liens symboliques de ce répertoire, cela ne change pas le nom du répertoire utilisé pour appliquer les directives dans les sections <Directory>.
    Notez que cette option est ignorée si elle est dans une section <Location> (mais c’est un peu hors sujet ici).
  • Includes : les inclusions côté serveur (en utilisant le module mod_include) sont autorisées.
  • IncludesNOEXEC : les inclusions côté serveur (en utilisant le module mod_include) sont autorisées, mais les commandes #exec et #exec cgi sont interdites.
    Il est toujours possible d’inclure en utilisant #include, des scripts virtuels CGI à partir de répertoires ScriptAliased (mais c’est un peu hors sujet ici).
  • Indexes : Si l’URL de la requête correspond à un répertoire, et qu’il n’y a aucun DirectoryIndex (par exemple ni de index.html, ni de index.php) dans ce répertoire, alors le module mod_autoindex renverra un listing formatté du répertoire.
  • MultiViews : Les contenus negotiés "MultiViews" sont autorisés en utilisant le module mod_negotiation.
  • SymLinksIfOwnerMatch : Le serveur ne suivra les liens symboliques que si l’id de l’utilisateur qui possède le répertoire – ou fichier – destination est le même que celui du lien.
    Notez que cette option est ignorée si elle est dans une section <Location> (mais c’est un peu hors sujet ici).

Normalement, si on applique plusieurs Options dans un répertoire, c’est la directive qui est la plus spécifique au répertoire qui est appliquée. Les autres Options sont ignorées. Les autres Options ne sont pas fusionnées.

Néanmoins, si, dans une directive Options, toutes les options qui suivent sont précédées par un signe + ou -, les deux Options sont fusionnées. Dans ce cas, n’importe quelle option précédée par un + est ajoutée aux options actives, et n’importe quelle option précédée par un – est retirée des options actives.

Regardez l’exemple (en Anglais) ici.

Notez bien encore une fois que si on ne précise pas la directive Options, la valeur All est appliquée.

Authname : à quoi ça sert ?

Domaine d’autorisation à utiliser lors de l’authentification HTTP. Cette directive applique le domaine d’autorisation à utiliser pour un répertoire donné.

Le domaine d’autorisation est affiché au client pour qu’il sache quel identifiant et quel mot de passe envoyer. AuthName prend un seul argument.

Si le domaine contient des espaces il doit être entouré par des guillemets.

Pour fonctionner, il doit être obligatoirement accompagné par les directives AuthType et Require, et éventuellement d’autres directives telles que AuthUserFile et AuthGroupFile.

Par exemple:

AuthName "Top Secret"

La chaine donnée par AuthName est ce qui va apparaître dans la fenêtre demandant le mot de passe de la plupart des browsers Internet.

Attention !

Si jamais vous ne mettez pas d’espaces dans la directive AuthName, alors il ne faut pas mettre de guillemets.

Par exemple:

AuthName "Top Secret"

Et :

AuthName Secret

sont des exemples corrects.

Mais ceci ne fonctionnera pas :

AuthName "Secret"

… cela peut, comme cela me l’a fait, faire perdre des heures entières…