Software is like sex... It's better when it's free.

Blog - Tag 'php'

Si vous voulez pousser le générique assez loin avec PHP et que vous en arrivez à vouloir accéder à des variables PHP sans connaitre leur nom, sachez que c'est possible assez simplement sans passer par des fonctions eval() ou autre code illisible.

En effet, la flexibilité de php permet de "générer" des noms de variables grâce aux caractères {} ; un petit exemple, supposons une classe représentant un modèle (ici une news), volontairement simplifiée.

<?php
class News
{
  private $news_title;
  private $news_description;

  public function __construct($_news_title,$news_description)
  {
    $this->news_title = $_news_title;
    $this->news_description = $news_description;
  }

  public function __get($name)
  {
    return $this->{$name};
  }
}
?>

Grâce au getter générique __get(), nous pouvons accéder à des variables de classe sans connaître leur nom. Imaginons, dans un autre fichier qu'une variable contienne la string décrivant la variable de classe, nous pouvons en récupérer la valeur d'une manière abstraite comme ceci :

<?php
include("News.class.php");
$news = new News("titre","contenu");
$generic = "news_title";
var_dump($news->__get($generic));
// Ce qui affichera "String(7) contenu";
?>
Tags:  abstractgenericphp.
Posté le Mardi 02 juin 2009 18:01:21 - 0 commentaire

Si vous avez essayé mainte fois de produire un export excel de données issues d'une base en UTF-8 par PHP, vous êtes sans doute arrivés à des résultats surprenants.

Notre cher ami Microsoft utilise en fait l'encodage UTF-16le.

Ainsi, pour former un document excel (ici une liste d'utilisateurs), il vous "suffit" de faire :

<?php
header("Content-Type: application/vnd.ms-excel; charset=UTF-16LE");
header("Content-Disposition: attachment; filename=\"Export Excel\"");
mysql_connect("localhost","root","");
mysql_select_db("fake");
$sql = "SELECT * FROM users ORDER BY user_name";
$req = mysql_query($sql);
?>
<html><head></head><body>
  <table>
    <tr>
      <td>Nom</td>
      <td>Prénom</td>
      <td>Email</td>
    </tr>
<?
while ($row = @mysql_fetch_assoc($req))
{
?>
    <tr>
      <td><?=substr(chr(255).chr(254).mb_convert_encoding($row['user_sname'], "UTF-16LE", "UTF-8"),2)?></td>
      <td><?=substr(chr(255).chr(254).mb_convert_encoding($row['user_fname'], "UTF-16LE", "UTF-8"),2)?></td>
      <td><?=substr(chr(255).chr(254).mb_convert_encoding($row['user_email'], "UTF-16LE", "UTF-8"),2)?></td>
    </tr>
<?
}
?>
  </table>
</body></html>
Tags:  charsetmicrosoftmysqlphp.
Posté le Mardi 28 avril 2009 18:23:05 - 4 commentaires

Si vous souhaitez en MySQL (ou en PHP) récupérer les commentaires de tables et/ou les commentaires des colonnes d'une table, sachez qu'il est très facile d'accéder à ces informations directement à partir d'une requête.

Récupérer les commentaires d'une table :

# 'sillysmart' est le nom de la base de données dont vous voulez extraire des informations
mysql> SHOW TABLE STATUS FROM sillysmart 
mysql> 

produira les informations suivantes:

Name Engine Version Rows Comment
Country MyISAM 10 240 Countries Listing
News MyISAM 10 30 News Listing

où la colonne 'Comment' vous donnera l'information voulue, à savoir le commentaire de la table. Sachez que beaucoup d'autres informations sont accessibles grâce à cette requête.

Récupérer les commentaires des colonnes d'une table :

# 'Country' est le nom de la table voulue
mysql> SHOW FULL COLUMNS FROM Country 
mysql> 

produira les informations suivantes:

Field Type Collation Null Key Extra Privileges Comment
country_id bigint(20) NULL NO PRI auto_increment select,insert,update Id
country_name varchar(255) latin1_swedish_ci NO UNI   select,insert,update Country Name
country_tld varchar(2) latin1_swedish_ci YES     select,insert,update Country TLD

où la colonne 'Comment' vous donnera l'information voulue, à savoir le commentaire de la colonne de la table.

Libre à vous d'exploiter ces informations en PHP. Personnellement nous utilisons ces colonnes de manière transparente sur SillySmart pour créer des back-office génériques qui reste néanmoins parlant pour un non-informaticien. La seule contrainte est que la longueur du commentaire est bridée à 60 caractères, donc soyez succinct !

Tags:  mysqlphp.
Posté le Lundi 08 juin 2009 15:45:08 - 3 commentaires

Si vous débutez en XSL, au delà des features basiques (select, for-each, choose, if, variable), vous avez dû être surpris de ne pas trouver de simple boucle for, alors que le for-each était présent ; or le for-each ne permet pas toujours de tout faire. Ainsi, si vous souhaitez faire une petite boucle for, plusieurs solutions s'offrent à vous. Pour les illustrer, nous allons prendre un exemple simple : une note/5 d'article (en float) en partant du fait que vous avez cette note dans votre flux xml, et que vous souhaitez afficher le nombre d'étoiles correspondant à cette note.

1ère solution, vous utilisez PHP5 et à l'aide de la fonction registerPHPFunctions() de la classe XSLTProcessor(), vous pouvez simplement utiliser l'une de vos fonctions PHP pour réaliser le traitement.

<?php
$xml = new DOMDocument()
$xml->load('flux.xml');
$xsl = new DOMDocument()
$xsl->load('flux.xsl');
$proc = new XSLTProcessor();
$proc->registerPHPFunctions();
$proc->importStyleSheet($xsl); 
echo $proc->transformToXML($xml);

function displayStars($note)
{
  $html = "";
  for($i=0 ; $i<floor($note) ; $i++)
    $html .= '<img src="Public/Styles/Img/star.gif" title="'.floor($note).'/5" />';
  echo $html;
}
?>

Ainsi, en XSL, vous n'avez qu'à appeler directement votre fonction PHP grâce au xmlns php, en ommettant pas l'attribut disable-output-escaping afin d'interpréter le HTML retourné pas votre fonction.

<!--
- Test passant par PHP
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl" version="1.0">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
  <xsl:template match="root">
    <html>
      <head></head>
      <body>
        <xsl:value-of select="php:function('displayStars',//article/note)" disable-output-escaping="yes" />
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

La 2eme solution se base uniquement sur XSL et consiste en l'utilisation de templates récursifs afin de simuler une boucle for.

<!--
- Template récursif
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl"" version="1.0">
  <xsl:template name="displayStars">
    <xsl:param name="i" />
    <xsl:param name="length" />

    <!-- Traitement du for -->
    <img src="Public/Styles/Img/star.gif" alt="{concat($length,'/5')}" title="{concat($length,'/5')}" />
    <!-- /Traitement du for -->

    <!-- Tant que length n'est pas atteint, l'on rappele le template pour continuer la boucle -->
    <xsl:if test="$i < $length">
      <xsl:call-template name="displayStars">
        <xsl:with-param name="i" select="$i + 1" />
        <xsl:with-param name="length" select="$length" />
      </xsl:call-template>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

Que l'on appellera comme ceci :

<!--
- Test d'appel du template récursif
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl" version="1.0">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
  <xsl:template match="root">
    <html>
      <head></head>
      <body>
        <xsl:call-template name="displayStars">
          <xsl:with-param name="i" select="'1'" />
          <xsl:with-param name="length" select="round(//article/note)" />
        </xsl:call-template>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

A noter que même si la boucle for reste exploitable en XSL, il devient vite fastidieux de rédevelopper des templates spécifiques à chaque traitement de boucles.

Une 3e solution, pour les gros projets, si l'on conserve le même exemple de la note, est de s'arranger pour modifier légèrement la structure de son flux XML formé par PHP pour pouvoir réaliser son traitement avec seulement le for-each.

Ici, au lieu de former :

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <article>
    <note><![CDATA[3.4]]></note>
  </article>
</root>

On aurait pu former :

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <article>
    <note>
      <item><![CDATA[]]></item>
      <item><![CDATA[]]></item>
      <item><![CDATA[]]></item>
    </note>
  </article>
</root>

où le nombre d'item correspondant au floor() php afin de pouvoir "foreacher" tranquillement sur notre note et simuler une boucle for :

<!--
- For-each détourné
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl" version="1.0">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
  <xsl:template match="root">
    <html>
      <head></head>
      <body>
        <xsl:variable name="note" select="count(//article/note/item)" />

        <xsl:for-each select="//article/note/item">
          <img src="Public/Styles/Img/star.gif" alt="{concat($note,'/5')}" title="{concat($note,'/5')}" />
        </xsl:for-each>

      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

Vous pouvez voir que plusieurs solutions vous permettent de réaliser votre boucle, libre à vous de choisir celle qui vous convient le mieux, en rapport avec votre envirronnement technique et vos choix en matière d'architecture.

Tags:  phpxmlxsl.
Posté le Mercredi 06 mai 2009 19:32:22 - 4 commentaires

Une des grandes nouveautés de PHP5 est que la classe de parsing XML/XSL XSLTProcessor permet nativement d'utiliser PHP pour effectuer des transformations lourdes et fastidieuses auparavant. Dorénavant, plus besoin de faire de template pour effectuer un ucwords(), il vous suffit d'un tout petit peu de paramétrage et on est bon.

Au niveau PHP :

<?php
$xml = new DOMDocument();
$xml->load('flux.xml');
$xsl = new DOMDocument();
$xsl->load('flux.xsl');
$proc = new XSLTProcessor();

// On précise au parseur que l'on veut utiliser des fonctions PHP en XSL
$proc->registerPHPFunctions();

$proc->importStyleSheet($xsl); 
echo $proc->transformToXML($xml);
?>

Et au niveau XSL :

<!--
- Simple test pour appeler une fonction php
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl"
 version="1.0">
<!-- On ajoute surtout le xmlns PHP -->
  <xsl:output method="xml" encoding="UTF-8" indent="yes" />
  <xsl:template match="root">
    <html>

      <!-- On utilise le préfixe php pour appeler la fonction ucwords() -->
      <xsl:value-of select="php:function('ucwords','php peut désormais être utilisé en xsl')" />
      <!-- Output: 'Php Peut Désormais Etre Utilisé En Xsl' -->

    </html>
  </xsl:template>
</xsl:stylesheet>

On peut même utiliser des fonctions statiques de classe, au niveau PHP :

<?php
class XSL
{
  /**
  * Traite une chaine pour la passer dans l'url
  *
  * @param $string la chaine a modifier
  * @param $delimiter le caractère d'implode
  * @return $string la chaine modifie
  * @see self::fullTrim
  * @see self::removeSpecialCaracteres
  * @see self::removeAccents
  */
  static function stringToUrl($string,$delimiter=" ")
  {
    return self::fullTrim(self::removeSpecialCaracteres(strtolower(self::removeAccents($string)))
    ,$delimiter);		
  }

  /**
  * Enleve les caractères accentuées d'une chaine
  *
  * @param $string la chaine a modifier
  * @return $string la chaine modifie	 
  */
  static function removeAccents($string)
  {
    static $search = array ("à", "á", "â", "ã", "ä", "ç", "è", "é", "ê", "ë", "ì", "í", "î", "ï",
                            "ñ", "ò" ,"ó", "ô", "õ", "ö", "ù", "ú", "û", "ü", "ý", "ÿ", "À", "Á",
                            "Â", "Ã", "Ä", "Ç", "È", "É", "Ê", "Ë", "Ì", "Í", "Î", "Ï", "Ñ", "Ò",
                            "Ó", "Ô", "Õ", "Ö", "Ù", "Ú", "Û", "Ü", "Ý");
    static $replac = array ("a", "a", "a", "a", "a", "c", "e", "e", "e", "e", "i", "i", "i", "i",
                            "n", "o", "o", "o", "o", "o", "u", "u", "u", "u", "y", "y", "A", "A",
                            "A", "A", "A", "C", "E", "E", "E", "E", "I", "I", "I", "I", "N", "O",
                            "O", "O", "O", "O", "U", "U", "U", "U", "Y");		
    return str_replace($search,$replac,$string);			
  }

  /**
  * Enleve les caractères spéciaux d'une chaine
  *
  * @param $string la chaine a modifier
  * @return $string la chaine modifie	 
  */
  static function removeSpecialCaracteres($string)
  {
    static $search = array ("²", "&", "~", "#", "{", "(", "[", "-", "|", "`", "\\", "^", "'", ")",
                            "]", "}", "^", "¨", "£", "¤", "%", "*", ",", "?", ";", ".", ":",
                            "/", "!", "§", "<", ">", "»", "«", "\n", "\r", "  ");
    static $replac = " ";
    return str_replace($search,$replac,$string);
  }

  /**
  * Enlève tous les espaces d'une chaine de caractères
  *
  * @param string $element la chaine dont on veut hoter les espaces
  * @param $delimiter le caractère d'implode
  * @return string la chaine sans espaces	 
  */
  static function fullTrim($element,$delimiter="")
  {		
    return implode($delimiter,self::processFunction(explode(' ',$element),'trim'));
  }

}
?>

Et au niveau XSL :

<!--
- Test plus complexe pour appeler des fonctions de classe
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl"
 version="1.0">
  <xsl:output method="html" version="XHTML 1.0" encoding="UTF-8" indent="yes" />
  <xsl:template match="root">
    <html>

      <!-- On utilise le préfixe php pour appeler la fonction de classe stringToUrl() -->
      <xsl:value-of select="php:function('XSL::stringToUrl','une_superstring-àÔ|modifier')" />
      <!-- Output: 'une_superstring ao modifier' -->

    </html>
  </xsl:template>
  </xsl:stylesheet>

On peut donc aisément appliquer des transformations PHP à même nos gabarits XSL.

Tags:  phpxmlxsl.
Posté le Lundi 04 mai 2009 17:49:31 - 2 commentaires