Lancer un script PHP dans un article

All contributions published for previous SPIP versions

Pouvoir lancer un script personnel dans un article spécifique et créer ainsi des articles à contenu « dynamique ».

Introduction :

La philosophie de SPIP est de pouvoir publier des articles.
Donc on ne peut pas faire un <?php include( "script.php"); ?> dans un article, ca n’est pas fait pour ca.

Pourtant dans certains cas précis, vous pouvez avoir besoin d’afficher dans un article des informations que vous avez calculé et surtout qui peuvent évoluer.

Exemple

Je dois gérer sur mon site une liste des membres d’un club.

Cette liste est modifiée régulièrement, et je l’ai donc mise dans une table de ma base de données.

J’aurai pu créer une page spécifique pour afficher cette liste, et pourtant j’aurai aimé qu’elle s’intègre parfaitement dans l’environnement SPIP, déjà niveau design, mais aussi qu’on puisse faire une recherche SPIP sur la partie fixe de l’article (autour de la liste), qu’elle apparaisse dans les rubriques, etc.

J’aurai également pu utiliser la fonction de redirection d’article, mais elle ne me plait pas non plus parce que j’ai envie d’utiliser une fonctionnalité importante de SPIP qui est le cache des pages !

Je vais donc vous montrer comment j’ai fait !

Avantages :

Par rapport au développement d’un partie spéciale d’un site, cette solution permet :
-  de garder une arborescence homogène avec les rubriques de SPIP, et compatible avec le moteur de recherche.
-  de pouvoir habiller un script dans un article et donc de le modifier facilement.
-  de bénéficier du moteur de cache de SPIP.

Sécurité :

Pour une question de sécurité, chaque script proposé dans les articles DOIT être inscrit dans une liste de scripts autorisés, pour ne pas que chaque rédacteur puisse faire n’importe quoi avec n’importe quel script !

Concrètement :

Ce que nous allons pouvoir faire c’est inclure à n’importe quel endroit de l’article la commande suivante :

#INCLURE (mon_script.php3)

Ceci plusieurs fois, et avec plusieurs scripts différents.

Le filtre qui va bien

J’ai créé un filtre appelé “autoriser_php” à appliquer sur le champs voulu (en particulier #TEXTE)

Voici ce filtre, à ajouter au fichier «mes_fonctions.php3»

/*****************************************************************************/
/**  Cette fonction permet de lancer un script PHP dans un article
/**
/**  La syntaxe est la suivante :
/**    #INCLURE ( nom_fichier )
/**
/**  Auteur  : F. Quointeau
/**  Version : 0.0.2
/**  Date    : 16 Mai 2004
/*****************************************************************************/
function autoriser_php( $texte) {
 
    // Dossier où sont stockés les scripts
    $dossier_inclus = './';
 
    // Liste des scripts autorisés
    $scripts_autorises = Array( 
        'mon_premier_script.php3',
        'mon_second_script.php3'
    );
 
 
    // Les fichiers inclus autorises sont : *.php, *.php3, *.php4, *.phtml, *.htm, *.html, *.inc
    $chaine_recherche = '/#INCLU[R|D]E( *)\(( *)([^\)]*\.(php|php3|php4|phtml|htm|html|inc))( *)\)/i';
 
    // Recherche la chaine #INCLUDE(script)
    while( preg_match( $chaine_recherche, $texte, $resultats)) {
        // Verifie le droit d'inclure ce fichier script !
        $autorise = false;
        reset( $scripts_autorises);
        while( $script = each( $scripts_autorises)) {
            if( strcasecmp( $script[value], $resultats[3]) == 0) $autorise = true;
        }
        if( $autorise == true) {
            // Verifie que le fichier existe
            if( file_exists ( $dossier_inclus . $resultats[3])) {
                // Vide le buffer de sortie
                $affichage_php = '';
                // Et lance le fichier inclu !!!
                include( $dossier_inclus . $resultats[3]);
            } else {
                $affichage_php = "<b>#INCLURE: Le fichier de script n'existe pas !</b>";
            }
        } else {
            $affichage_php = "<b>#INCLURE: Script NON autoris&eacute; !</b>";
        }
        // Attention, n'effectue qu'un seul remplacement à la fois !
        $texte = preg_replace( $chaine_recherche, $affichage_php, $texte, 1);
    }
 
    return( $texte);
}

Attention :
Au début de la fonction, il y a un tableau php qui décrit la liste des scripts qui ont le droit d’être lancé à partir d’un article. Pensez à le mettre à jour avec vos scripts.

Dossier des scripts

Si vous souhaitez mettre vos scripts dans un dossier spécial, mettez également le chemin des scripts dans la variable
$dossier_inclus au début de la fonction filtre. Par exemple :

    $dossier_inclus = 'mes_scripts/';

Notez le ’/’ obligatoire à la fin du dossier.

Mais vous pouvez aussi «cacher» vos scripts (non disponibles par une requête HTTP dessus) en les placant en amont du dossier racine du site web :

    $dossier_inclus = '../mes_scripts_caches/';

Attention, en fonction de votre hébergeur, vous n’aurez pas forcément la possibilité de le faire !

Le script des familles :

Voici le script que vous voulez promouvoir :

<?php
    echo( "Hello World !");
?>

Puissant, mais vous allez devoir l’écrire sous cette forme :

<?php
    $affichage_php = "Hello World !";
?>

Vous suivez ?

Vous ne pouvez pas vous permettre d’afficher sauvagement vos informations directement par un echo( ), celles-ci ne seraient pas prises en compte dans le cache de SPIP, et souvenez-vous que votre script travaille à l’intérieur d’un filtre, et qu’il y a peut être d’autres filtres qui attendent derrière :-)

Et maintenant, comment relier le tout ?

Créez un article comme celui-ci par exemple :

Bonjour,
voici le résultat de ma fonction :
#INCLURE (mon_premier_script.php3)

Hop

Créez ensuite un fichier mon_premier_script.php3 qui contient le script énoncé plus haut; mettez le dans le dossier de SPIP si vous n’avez pas spécifié de dossier dans la variable $dossier_inclus (par défaut elle contient le dossier courant).

Dans votre squelette, vous devez avoir quelque chose du genre :

<BOUCLE_1 (ARTICLES) {id_article}>
  [(#TEXTE|autoriser_php)]
</BOUCLE_1>

J’ai appliqué le filtre “autoriser_php” à l’affichage de #TEXTE.

Et votre article affiche désormais :

Bonjour,
voici le résultat de ma fonction :
Hello World !

Hop

Pour aller un peu plus loin...

Imaginons que vous ayez besoin de récupérer une variable de l’url dans votre script. Les variables globales de SPIP sont accessibles depuis votre script, puisqu’il fonctionne à l’intérieur d’un filtre SPIP.

Regardez le script suivant

<?php
  // Ma variable
  $id_machin = $GLOBALS['id_machin'];
 
  $affichage_php = "Hello machin n°$id_machin";
?>

Je récupère la variable globale $id_machin et je l’affiche dans mon script. Cette variable globale est tout simplement une des variables passées en paramètres.

Si j’essaye l’url :
article.php3?id_article=x{{&id_machin=11}}

J’aurai alors l’affichage :

Bonjour,
voici le résultat de ma fonction :
Hello machin n°11

Hop

C’est d’autant plus pratique que SPIP va générer un cache pour TOUTES les url différentes.

Fort heureusement, sinon l’affichage de
article.php3?id_article=x&id_machin=1
serait le même que l’affichage de
article.php3?id_article=x&id_machin=2

puisque la page article.php3?id_article=x est à priori déjà dans le cache.

Attention toutefois à ne pas remplir votre cache de millions de fichiers si votre script peut comporter des millions de combinaisons de paramètres dans l’url !

Récupérer des variables globales de SPIP

En plus de pouvoir récupérer des variables de l’url, vous pouvez récupérer des variables de l’environnement SPIP, comme celle-ci :

$GLOBALS['spip_lang']

qui vous permet de récupérer la langue courante (si vous utilisez SPIP multilingue).
Ca peut en effet être très pratique si vous avez besoin de générer un affichage «localisé» avec votre script. [1]

Mise en garde avec MySQL

SPIP utilise par défaut la connexion courante à la base de données MySQL. Autrement dit, si dans votre script, vous ouvrez une base MySQL autre que celle utilisée par SPIP, celui-ci n’aura plus accès à sa base une fois le filtre exécuté.

Pour remédier à ca, il y a deux solutions :

-  Utiliser la base de SPIP. Pour avoir la base toujours ouverte au début de votre script, utilisez
include_ecrire( “inc_connect.php3”);
En effet ce sript ouvre la base seulement si elle n’est pas déjà ouverte, et l’avantage c’est que vous n’avez pas besoin de gérer vous même les paramètres d’ouverture.

-  Demander à SPIP de faire des accès complets à la base (avec sélection de la base à chaque requête). Pour ca, renseignez la variable
$mysql_rappel_connexion = true;
dans mes_options.php3 [2].

Exemple d’utilisation :

<?php
include_ecrire( "inc_connect.php3"); 
 
$ma_requete = mysql_query( "SELECT id, nom, prenom, DATE_FORMAT(naissance, '%d/%m/%Y') as naissance, FROM utilisateur ORDER BY nom,prenom");
if( $ma_requete) {
    if( mysql_num_rows($ma_requete) > 0) {
        $affichage_php .= "<table cellpadding=\"2\" align=\"center\" border=\"0\" class=\"spip\">\n";
        $affichage_php .= "<tr align=\"center\" class=\"row_first\"> <td><b>Nom</b></td> <td><b>Date de naissance</b></td> </tr>\n";
        while ($mon_resultat = mysql_fetch_array($ma_requete)) {
            $affichage_php .= "<tr class=\"row_" . ($ncr_numero_ligne++ % 2 ? 'odd' : 'even') . "\">";
            $affichage_php .= "<td>" . $mon_resultat["nom"] . " " . $mon_resultat["prenom"] . "</a></td> <td align=\"right\">" . $mon_resultat["naissance"] . "</td>";
            $affichage_php .= "</tr>\n";
        }
        $affichage_php .= "</table>\n";
    }
}
?>

Conclusion

Attention, ceci ne fonctionne que dans la partie publique évidemment !

J’utilise actuellement ce script sur mon site, et ca fonctionne plutôt bien ! Et ce qui est génial, c’est que le cache est géré correctement par SPIP.

Notez: la page est regénérée quand elle a expirée, et pas quand le résultat de votre script a changé !

Footnotes

[1Merci à Paolo pour l’info

[2Je l’indique pour information puisque je ne l’ai pas testé

updated on 30 November 2012

Discussion

46 discussions

  • 3

    Salut à tous,

    J’ai fait un test avec cette petite fonction. Mais je pige pas un truc.

    Voici ce que j’ai réalisé.
    -  J’ai crée un fichier avec mon script php <? phpinfo(); ?> (tout simplement).
    -  J’autorise mon fichier a s’executer dans la fonction ’autoriser_php’
    -  Je code dans mon squelette (#TEXTE
    -  Et enfin, je note dans le champ de mon article #INCLURE (MONFICHIER.php)

    Le soucis, c’est que le fichier appelé dans le squelette s’affiche en haut du fichier et non dans le corps du squelette. Ca semble logique, étant donné que le fichier ’mes_fonctions.php3’ affiche toujours les résultats au début des squelettes. !

    Quelqu’un a un solus pour que le contenu de mon script php s’affiche dans le corps de mon squelette ?

    Cyp

    • C’est normal !!!

      La fonction phpinfo() écrit directement vers la sortie, alors qu’il est bien précisé dans l’article que l’affichage ne doit _pas_ se faire directement par un echo(), mais dans une variable qui sera récupérée à la fin de ton script par SPIP (pour éventuellement être retraitée) !

    • Cuvelier Etienne

      Je ne comprends pas comment récupérer(dans SPIP) le contenu de la variable et comment la retraiter (càd ici afficher le contenu de la variable) ???

    • quelle variable ?
      phpinfo() ?

      Tu ne peux pas récupérer le contenu. La fonction balance les infos automatiquement...

    Reply to this message

  • Bonjour,

    Merci pour cette contrib

    J’essaie de placer du code entre les guillemet de $affichage_php = “ici”. Et ca marche pas. Quand je place du texte ça marche mais quand je place le code d’un formulaire javascript + balise

    c’est pas possible. On doit pouvoir placer autre chose que du texte sinon je n’ai pas compris l’intérêt. Pouvez-vous me donner un coup de main ?

    Merci

    Elsey

    Reply to this message

  • 1

    Bonjour,
    J’ai utilisé cette contrib sur mon site pour afficher des listes issues d’une base de donnée MySQL. Après avoir rencontré à peu près tous les problèmes évoqués dans les messages liés à la contrib, j’en ai découvert un nouveau, qui se reproduit avec tous mes scripts php affichant des listes de valeurs dans des formulaires.

    Exemple : Pour l’interrogation de l’annuaire du laboratoire, je vais chercher toutes équipes de recherche dans la base MySQL pour l’afficher dans un formulaire sous forme de liste cliquable.

    J’utilise pour cela une boucle while que je n’arrive pas à copier ici parce que la moitié des caractères est interprétée et disparait.

    A première vue, ça fait penser à une erreur php. Mais cette boucle fonctionnait bien dans un programme php indépendant (avant spip). Mais maintenant, dans la page affichée, seule la dernière équipe s’affiche (il y en a 12).

    Quelqu’un peut-il me donner une piste ? Merci d’avance.
    Véronique

    • Bonjour,
      Je réponds moi-même à mon message concernant le problème d’affichage d’une liste de valeurs dans un formulaire dans un article spip. Cela pourra peut-être intéresser d’autres spipeurs.

      Rappel : seule la dernière valeur collectée dans la base MySQL s’affichait dans ma liste cliquable.

      En fait, le #INCLUDE ne s’exécutant qu’une fois, il faut effectuer également l’affichage en une fois et non à chaque instance de la boucle while.

      Donc il suffit, à l’intérieur de la boucle while, de stocker ce qu’on veut afficher dans une variable au fur et à mesure que le script récupère les données.

      Après la boucle while, en une seule fois, on affiche le contenu de la variable par le $affichage_php.

      Bonne journée à tout le monde.
      Véronique.

    Reply to this message

  • 2

    Bon est avec la version spip 1.9 ; cela est t’il toujours d’actualité ?

    parceque le fichier article du repertoire dist je ne trouve pas de #TEXTE

    hormis ça : mais la j’ai comme un doute ...

    #DEBUT_SURLIGNE
                            [<div class="chapo">(#CHAPO)</div>]
                            <br class="nettoyeur" />
                            [<p class="lien"><:voir_en_ligne:> : <a href="(#URL_SITE)" class="spip_out">[(#NOM_SITE|sinon{[(#URL_SITE|couper{80})]})]</a></p>]
                            [<div class="texte">({{#TEXTE|}}image_reduire{520,0}{{|autoriser_php}})</div>]
                            #FIN_SURLIGNE

    merci du coup de main

    • <div class="texte">(#TEXTE|autoriser_php|image_reduire{520,0})</div>]

      dans le fichier article.html, ligne 68.

    • Bonjour,

      A l’intérieur d’un script lancé depuis un article, je voudrais récupérer le nom (et le chemin) du script en question. Je n’arrive pas à trouver d’info. Evidemment $_SERVER[SCRIPT_NAME] contient spip.php.

      Merci d’avance pour toute suggestion.

      Michel

    Reply to this message

  • 2

    L’article proposé par GoUaRfIg fonctionne avec SPIP 1.8pr3.

    Néanmoins, pour les débutants dont je fais parti, j’ajoute quelques infos qui peuvent être utiles:

    Le fichier “mes_fonctions.php3” doit être à la racine de votre site (là où se trouvent notamment par défaut, les fichiers avec l’extension .css).

    Si vous faites un copier/coller de l’exemple donné par GoUaRfIg du fichier “mes_fonctions.php3”, alors votre script (dans le cas présent “mon_premier_script.php3”) doit être lui aussi à la racine de votre site.

    Le fichier “article.html” où il faut modifier la balise #TEXTE, est situé dans le répertoire “dist” sur votre site.
    Dans ce fichier, recherchez le bloc de texte suivant:

    #DEBUT_SURLIGNE[
    <div class="chapo">(#CHAPO)</div>
    ][<div class="texte">(#TEXTE)</div>
    ][<div class="ps">(#PS)</div>][
    <div class="notes">(#NOTES)</div>
    ]#FIN_SURLIGNE

    et remplacez-le par :

    #DEBUT_SURLIGNE[
    <div class="chapo">(#CHAPO)</div>
    ][<div class="texte">(#TEXTE|autoriser_php)</div>
    ][<div class="ps">(#PS)</div>][
    <div class="notes">(#NOTES)</div>
    ]#FIN_SURLIGNE

    Veillez à bien suivre les indications de GoUaRfIg et ça doit marcher du 1er coup.

    En vous souhaitant une bonne insertion de votre code ;-)

    • merci de ces précisions pour débutants :)

      j’ajouterais cependant que de manière génbérale il n’est pas conseillé de travailler sur les fichiers du répertoire /dist, mais sur des copies, placées à la racine du site spip (ou dans un dossier nommé squelettes, spip 1.8)

    • Bonjour,
      J’ai beau faire, ça ne veut pas marcher. J’utilise SPIP 1.8.2g avec les squelettes de la distribution.
      Je suis tout neuf sur SPIP, donc les boucles, j’ai du mal à maîtriser.
      Après avoir fait tant bien que mal les modifs décrites, j’obtiens un résultat: celui d’avoir systématiquement tout le code du script de la fonction autoriser_php affichée en fond d’écran...

    Reply to this message

  • Magnifique, exactement ce que je cherchais !

    Est-il possible de faire fonctionner cet outil dans une rubrique également?

    Merci d’avance, Florence

    Reply to this message

  • Bonjour,

    J’arrive à inclure des scripts php sans problème. Par contre, je n’arrive pas à inclure des morceaux de javascript dans ces scripts php.

    J’ai mis la fonction suivante :

    function supprimer_la_securite($texte)
    return ereg_replace (“<script “,”<script”,$texte);
    ;

    dans “mes_fonctions.php3”.
    Mais rien n’y fait, j’ai plein d’erreurs. help please !!
    Y’a-t-il un autre moyen ??

    Merci d’avance

    Reply to this message

  • 2

    Pour info :

    Testé aujourd’hui avec une 1.8pr1, ca ne pose aucun problème.

    Fred.

    • GoUaRfIg!

      Bon ...

      Pour ceux que ca intéresse, c’est compatible 1.8, 1.8.1, et ca ne devrait probablement pas changer de si tôt !

      Bon spip à tous

    • emilie

      effectivement ça fonctionne...

      je voudrais faire passer des arguments au script php via l’article.
      Comment ça pourrait être possible d’après vous ?

      merci d’avance
      emilie

    Reply to this message

  • 1

    [spip-dev] une astuce pour inclure des scripts externes + discussion interdire_scripts()
    De : Fil <fil@rezo.net>
    À : spip-dev <spip-dev@rezo.net>

    Coucou,

    pour un besoin particulier (récupérer des pages dynamiques existantes) j’ai
    commencé à développer la procédure habituelle : un système compliqué de
    mots-clés, scripts associés, tout en veillant à la sécurité, etc..., avant
    de penser à une astuce si simple et puissante que je ne peux pas m’empêcher
    de la faire partager :-)

    En effet, le filtre |file_get_contents fonctionne très bien à partir de php
    version >= 4.3.0 :

    Illustration :

    /}>
    (#PS*

    En français : « Si le post-scriptum commence par “http://”, intégrer la page
    en question. »

    Le * de [(#PS*)] est là pour éviter que le moteur typographique entre en jeu
    sur l’URL de la page.

    Le “résultat” du filtre (c’est-à-dire le code produit) est dans le cache de
    spip : ça évite de bastonner le script à chaque visite.

    Bonus sécurité : dans le script externe, on peut vérifier qu’on est bien
    appelé depuis la même machine, et qu’on peut donc avoir “confiance”.

    Petit bug : les scripts (javascript et php) sont interdits ; pour le php
    c’est presque absolument nécessaire (sauf cas exceptionnel), mais pour le
    javascript ça peut être ennuyeux (si l’on sait ce qu’on fait).

    Ce qui m’amène à poser la question d’une mise à niveau de la fonction
    interdire_scripts(), qu’il faudrait décomposer en interdire_php() et
    interdire_javascript(). Avec une possibilité de désactiver la seconde,
    peut-être avec la syntaxe (#PS** — d’ailleurs pourquoi pas les deux,
    pour les malades et inconscients, avec la syntaxe (#PS*** ?

    (Ajoutons éventuellement un code spécifique de désactivation, qu’il faudra
    installer dans mes_options.php3, et qui sera disponible seulement si on lit
    toute la documentation ?)

    — Fil

    • oui, Fil, ça devient nécessaire lorsque par exemple on veut simplement afficher dans un article le résultat du distrib.php3 d’un autre site avec la commande appropriée :

      <script type"text/javascript src="http://serveur.distant/distrib.php3"></script>

      nota : l’aide de spip indique que le raccourci <html>...</html> permet de ne pas traiter la typo de code php ou javascript, ce qui est contraire à la fonction interdire_script ... est-ce une erreur ou une mauvaise compréhension ?

    Reply to this message

  • 1

    2 Questions :
    -  Est-ce que ta contrib fonctionne avec spip 1-8b2 ?
    -  Est-ce qu’il serait possible d’inclure un script externe (se trouvant sur un autre site) ?

    • Oui, cette contrib doit fonctionner avec la 1.8, en tout cas avec la 1.8a, pour la 1.8b je n’ai pas testé, mais il n’y a aucune contre-indication

      En ce qui concerne un script sur un site externe, non ce n’est pas du tout prévu.

    Reply to this message

Comment on this article

Who are you?
  • [Log in]

To show your avatar with your message, register it first on gravatar.com (free et painless) and don’t forget to indicate your Email addresse here.

Enter your comment here

This form accepts SPIP shortcuts {{bold}} {italic} -*list [text->url] <quote> <code> and HTML code <q> <del> <ins>. To create paragraphs, just leave empty lines.

Add a document

Follow the comments: RSS 2.0 | Atom