Comme annoncé la semaine dernière et les vacances se terminant, il est maintenant temps de remonter (un peu) le niveau de ce blog qui sombrait doucement mais sûrement dans le grand n'importe quoi.
L'astuce du jour n'est guère facile à expliquer en quelques lignes, un exemple sera à mon avis beaucoup plus clair.
Comment utiliser un tableau comme un pur objet ? ( à la sauce Java, si je puis m'exprimer ainsi )
// Ecriture PHP classique
$array = array(); // Ligne facultative
$array['key1'] = 'value 1';
$array['key2'] = 'value 2';
$array['key3']['key31'] = 'value 31';
...
Voici une écriture qui ne fonctionne qu'en PHP 5 :
// Ecriture PHP5 objet
$array = new ClassArray(); // Ligne obligatoire !
$array['key1'] = 'value 1';
$array['key2'] = 'value 2';
...
En soit, il n'y a absolument rien de révolutionnaire, mais le new
crée l'instance de l'objet ClassArray()
et vous pouvez constater que l'écriture reste un tableau et non un objet
$array->key1 = 'value 1';
Une question se pose sûrement dans votre cerveau qui bouillonne d'impatience : Qui a-t-il dans cette ClassArray()
? Une simple implémentation de ArrayAccess
( PHP5 only ), ce qui nous donne :
class ClassArray implements ArrayAccess {
protected $_array = array();
public function __construct() {
$this->_array = func_get_args();
}
// En cas d'appel de la fonction isset()
public function offsetExists($offset) {
return isset($this->_array[$offset]);
}
// En cas d'appel de la valeur
public function offsetGet($offset) {
return $this->_array[$offset];
}
// En cas d'affectation de la valeur
public function offsetSet($offset, $value) {
return $this->_array[$offset] = $value;
}
// En cas d'appel de la fonction unset()
public function offsetUnset($offset) {
unset($this->_array[$offset]);
}
}
Ensuite, libre à vous de modifier cette classe pour lui ajouter de nouvelles fonctionnalités...
Remarque importante : Si vous essayez de créer un tableau sur plusieurs dimensions, un message d'erreur se produira avec l'écriture suivante :
$array = new ClassArray();
$array['key3']['key31'] = 'value 31';
// Problème : Vous n'avez pas définit $array['key3'] ! l'écriture POO est beaucoup plus stricte ;)
// Solution :
$array = new ClassArray();
$array['key3'] = new ClassArray();
$array['key3']['key31'] = 'value 31';
Documentation officielle :
http://www.php.net/~helly/php/ext/spl/interfaceArrayAccess.html
Hélas, je ne vais pas vous sortir un Quake en Javascript ( encore que... avec les nouvelles fonctions de transformation de Webkit, je suis sûr qu'on pourrait presque y arriver ). Pour le moment, on va rester dans le simple mais dont certains restent tout de même très impressionnant.
• Escapa : vous devez sauver un carré rouge de méchants rectangles bleuuuuussss
• Bezumie : un peu de tout...
• Chess : Jeu d'échec
• Othello : Qui ne connait pas Othello ?
• Yast : ni plus ni moins qu'un Tetris
• Lemmings : et pour finir en beauté, les Lemmings ! Oui Madame !
Toujours dans notre série "détente" du moins d'août, je vous présente le grand magicien Presto et son lapin blanc, Alec...
Presto est un court métrage des Studios Pixar, sorti en même temps que WALL•E.
Soit dit en passant, ce petit Wall•E est une pur merveille !
Les vacances sont là,
Les plages sont pleines de monde,
Les glaces fondent trop vite,
Les bières sont chaudes... ( vous sentez le stress monter ? )
Et y faut faire les cahiers de vacances de vos chers petits, ( encore et toujours )
Mais vous, avez-vous pensez aux vôtres ?
Heureusement, votre humble serviteur, Oui ! ( en bon tortionnaire que je suis ;-)
Voici une liste de liens avec des petits Quiz sur vos langages de programmation préférées.
Cette fonction function array2object()
n'est pas nouvelle en soit, mais voici une écriture récursive en PHP5 plus rigolote ( et surtout plus restrictive ) à utiliser.
La fonction ci-dessous ne fonctionne qu'en PHP 5 de part le typage du paramètre (array $array)
ainsi on évite le contrôle is_array()
. Toutefois, si vous envoyez une valeur qui n'est pas un tableau, une erreur de compilation se produira.
function array2object(array $array) {
$object = new stdClass();
foreach($array as $key => $value) {
if(is_array($value)) {
$object->$key = array2object($value);
} else {
$object->$key = $value;
}
}
return $object;
}
Exemples d'utilisation :
$tableau[0] = 'Valeur 0';
$tableau[1] = 'Valeur 1';
$tableau['deux'] = 'Valeur 2';
$tableau['trois'] = 'Valeur 3';
$objet = array2object($tableau);
echo $objet->0; // Ecriture incorrecte
echo $objet->{'1'}; // Ecriture correcte
echo $objet->deux; // Ecriture classique
echo $objet->trois; // etc...
De bon matin, je me lève, j'arrive sur une de mes applications en ligne, et là horreur ! cette dernière ne fonctionne plus sous Mozilla Firefox 3... Après analyse des petits messages d'erreurs, je me rends à l'évidence le formulaire <input type="file" />
ne se comporte plus de la même façon...
Les développeurs de Firefox 3 ont corrigé une petite faille de sécurité : Lorsque vous cliquez sur le bouton Parcourir le value
de votre zone input type="file"
contenait le nom du fichier ainsi que son chemin sur le disque dur, et bien aujourd'hui cela n'est plus, vous n'aurez plus que le nom du fichier.
D'ailleurs, Opera Browser se comporte aussi de la sorte, pour le moment Microsoft Internet Explorer et Apple Safari retournent toujours le chemin complet du fichier.
Une très bonne nouvelle s'accompagne de ce petit changement, vous pouvez à présent accéder aux données du fichier sélectionné par l'utilisateur en Javascript sans poster le formulaire grâce à l'objet FileList
.
Un exemple vaut mieux qu'un long discours, voici une page qui vous permettra d'essayer cette nouvelle fonctionnalité (qui je le rappelle, ne fonctionne que sur Mozilla Firefox 3) :
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>input type=file & Firefox 3</title>
</head>
<body>
<h1>input type=file & Firefox 3</h1>
<script type="text/javascript">
// <![CDATA[
function inputFileOnChange() {
var v_console = '';
v_console += 'value: ' + document.getElementById('fichier').value;
v_console += '<br \/>';
if(document.getElementById('fichier').files) {
// Support: nsIDOMFile, nsIDOMFileList
v_console += 'files.length: ' + document.getElementById('fichier').files.length;
v_console += '<br \/>';
v_console += 'fileName: ' + document.getElementById('fichier').files.item(0).fileName;
v_console += '<br \/>';
v_console += 'fileSize: ' + document.getElementById('fichier').files.item(0).fileSize;
v_console += '<br \/>';
v_console += 'data: ' + document.getElementById('fichier').files.item(0).getAsDataURL();
// v_console += 'data: ' + document.getElementById('fichier').files.item(0).getAsBinary();
// v_console += 'data: ' + document.getElementById('fichier').files.item(0).getAsText();
v_console += '<br \/>';
};
document.getElementById('console').innerHTML = v_console;
};
// ]]>
</script>
<div>
<input type="file" name="fichier" id="fichier" onchange="inputFileOnChange();" />
<br /><br />
<code id="console">...console...</code>
</div>
</body>
</html>
Documentation officielle :
• http://developer.mozilla.org/en/docs/nsIDOMFile
• http://developer.mozilla.org/en/docs/nsIDOMFileList
• http://www.w3.org/TR/file-upload/
Astuce du jour : Comment retourner plusieurs variables depuis une fonction PHP ?
Vous pouvez utiliser les tableaux array()
ou les objets new stdClass()
dans une variable et retourner la dite variable, toutefois cette écriture n'est pas forcément limpide lors d'une relecture de code.
Je vous propose une écriture assez souple et surtout beaucoup plus claire qui vous donnera une réelle impression de return
avec X valeurs.
<?php
function retourner_plusieurs_valeurs() {
$variable_n_1 = 'One';
$variable_n_2 = 'Two';
$variable_n_3 = '333';
return array($variable_n_1, $variable_n_2, $variable_n_3);
}
list($variable_n_1, $variable_n_2, $variable_n_3) = retourner_plusieurs_valeurs();
?>
Cette astuce n'a absolument rien d'extraordinaire, mais peut être vous fera-t-elle découvrir les différentes manières de formater un nombre au format monétaire.
<?php
$number = 1234.567;
// Première méthode ( peu pratique )
echo sprintf('%.2f €', $number); // 1234.57 €
// Seconde méthode ( plus simple mais non automatique )
echo number_format($number, 2, ',', ' ') . ' €'; // 1 234,57 €
// Troisième méthode ( qui peut être automatisée )
setlocale(LC_MONETARY, 'en_US');
echo money_format('%n', $number); // $1,234.57
setlocale(LC_MONETARY, 'fr_FR');
echo money_format('%n', $number); // 1 234,57 Eu
echo money_format('%!n €', $number); // 1 234,57 €
?>
Article inspiré du livre O'Reilly PHP Cookbook, Second Edition.
Problème du jour :
Vous avez une table en latin1_general_ci
(ou un interclassement assez proche), et vous souhaitez effectuer une recherche sur le terme "cinéma" avec un LIKE
.
Un problème va se poser si l'utilisateur saisit "cinema" et non "cinéma"... votre requête ne retournera aucun résultat...
SELECT * FROM `table` WHERE `texte` LIKE '%cinema%' ;
Une partie de la solution se trouve dans l'interclassement, si vous effectuez cette même requête LIKE
dans une table en utf8_general_ci
, le résultat retournera tous les "cinéma" !
Certains diront sûrement qu'il suffit de changer l'interclassement et le problème sera réglé mais parfois vous n'avez pas cette possibilité pour diverses raisons...
Voici une solution qui je l'espère vous plaira, il "suffit" de traiter le texte reçu en latin1
ISO-8859-1
dans la requête avec un CONVERT(_utf8 '%...%' USING utf8) COLLATE utf8_general_ci
:
SELECT * FROM `table`
WHERE `texte` LIKE CONVERT(_utf8 '%cinema%' USING utf8) COLLATE utf8_general_ci ;
En promo : Comment réinitialiser le numéro identifiant AUTO_INCREMENT
dans une table ?
ALTER TABLE `table` AUTO_INCREMENT = 1 ;
Votre site comporte peut être des formulaires avec des zones <input type="text" />
, mais le navigateur soucieux d'assister l'utilisateur, pollue vos formulaires avec de la saisie semi-automatique...
Comment remédier à ce problème et surtout comment désactiver cette saisie semi-automatique ? Il "suffit" d'utiliser l'attribut autocomplete
.
<input type="text" name="zone_texte" value="" autocomplete="off" />
J'ai pu testé avec succès cet attribut sur les différents navigateurs du moment : Microsoft Internet Explorer, Mozilla Firefox, et Apple Safari (webkit). Toutefois, cet attribut autocomplete
écrit de manière brute dans le code source de votre page n'est ni valide en HTML, ni valide en XHTML. Pour fixer ce petit soucis, vous pouvez initialiser l'attribut avec du Javascript au moyen de la méthode setAttribute
.
Voici un exemple de code Javascript qui désactivera totalement la saisie semi-automatique dans vos formulaires HTML :
<script type="text/javascript">
window.onload = function() {
for(var i = 0, l = document.getElementsByTagName('input').length; i < l; i++) {
if(document.getElementsByTagName('input').item(i).type == 'text') {
document.getElementsByTagName('input').item(i).setAttribute('autocomplete', 'off');
};
};
};
<script>
Une fois n'est pas coutume, il n'y aura pas d'astuce cette semaine... mais une actualité totalement inutile, et comme toute chose inutile, elle se trouve être indispensable !
Vous pouvez à présent Poster des commentaires sur ce site ! Les saisies sont très primaires comme tout blog qui se respecte, toutefois le splash est plutôt sympatoche.
Voici un petit javascript qui vous permettra de fixer la transparence des images au format PNG transparent 24 bits sous Microsoft Internet Explorer 6.
function fixPNG() {
if(navigator.appName == 'Microsoft Internet Explorer') {
var png = /\.png$/i;
var imgs = document.getElementsByTagName('img');
for(var i = 0, l = imgs.length; i < l; i++) {
if(png.test(imgs.item(i).src)) {
imgs.item(i).style.width = imgs.item(i).offsetWidth;
imgs.item(i).style.height = imgs.item(i).offsetHeight;
imgs.item(i).style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\'' + imgs.item(i).src + '\',sizingMethod=\'image\')';
imgs.item(i).src = 'empty.gif';
}
}
}
}
Fonctionnement de la function
:
Le PNG transparent est remplacé par un fichier GIF totalement transparent de 1px², puis le filter
contenant le PNG est appliqué sur l'image GIF.
Pour activer cette fonction, il suffit de l'appeler dans un événement window.onload = function(){}
.
Rappel des différentes écritures du filter
en CSS :
img.image1 { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='image1.png', sizingMethod='image'); }
img.image2 { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='image2.png', sizingMethod='scale'); }
img.image3 { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='image3.png', sizingMethod='crop'); }
Voici une petite commande PHP qui vous permettra de désactiver ou plutôt de nettoyer les magic_quotes quand ces dernières sont activées ( et accessoirement de préparer votre code si la fonction get_magic_quotes_gpc()
venait à disparaître dans PHP 6 ).
if(!function_exists('get_magic_quotes_gpc')) {
// En prevision de PHP 6
function get_magic_quotes_gpc() { return 0; }
}
if(get_magic_quotes_gpc()) {
function undo_magic_quotes(&$array) {
foreach($array as $key => $value) {
if(is_array($array[$key])) {
undo_magic_quotes($array[$key]);
} else {
$array[$key] = stripslashes($value);
}
}
}
undo_magic_quotes($_GET);
undo_magic_quotes($_POST);
undo_magic_quotes($_COOKIE);
undo_magic_quotes($_REQUEST);
undo_magic_quotes($_FILES);
}
A l'heure où j'écris ces quelques lignes, il n'existe pas réellement de vraie solution pour effectuer une rotation sur une image (ou sur un élément) en CSS.
Sous Microsoft Internet Explorer, on peut effectuer une telle opération avec filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2);
. Toutefois, ceci ne permet pas d'effectuer une rotation au degré prêt.
Sous Apple Safari (Webkit), l'équipe de développement a proposé l'écriture suivante -webkit-transform: rotate(90deg);
. Cette solution présente un très grand avantage, elle peut être appliquée sur tout type de balises et non simplement sur une image, mais cette propriété reste propre à Safari version 3.x...
Dans cette histoire, il reste toujours un navigateur sans réelle solution, notre cher Mozilla Firefox... on aurait pu espérer une commande du type -moz-rotate: 90;
, mais il n'en est rien.
Voici donc la solution, utiliser la balise <canvas></canvas>
.
La balise <canvas>
est une extension non standard à la norme HTML qui permet d'effectuer des rendus dynamiques d'images bitmap via des scripts.
Cette balise est supportée par les derniers navigateurs Mozilla Firefox, Apple Safari, et Opera Browser. A noter que Microsoft Internet Explorer ne supporte pas nativement la balise <canvas>
, pour ce faire, vous devez utiliser une sous couche excanvas ( disponible sur http://excanvas.sourceforge.net )
Exemple de script de rotation d'image compatible avec la plupart des navigateurs récents :
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>rotate()</title>
<style type="text/css" media="screen">
img, canvas { border: 1px solid black; }
</style>
<script type="text/javascript">
function rotate(p_deg) {
if(document.getElementById('canvas')) {
/*
Ok!: Firefox 2, Safari 3, Opera 9.5b2
Non: Opera 9.27
*/
var image = document.getElementById('image');
var canvas = document.getElementById('canvas');
var canvasContext = canvas.getContext('2d');
switch(p_deg) {
default :
case 0 :
canvas.setAttribute('width', image.width);
canvas.setAttribute('height', image.height);
canvasContext.rotate(p_deg * Math.PI / 180);
canvasContext.drawImage(image, 0, 0);
break;
case 90 :
canvas.setAttribute('width', image.height);
canvas.setAttribute('height', image.width);
canvasContext.rotate(p_deg * Math.PI / 180);
canvasContext.drawImage(image, 0, -image.height);
break;
case 180 :
canvas.setAttribute('width', image.width);
canvas.setAttribute('height', image.height);
canvasContext.rotate(p_deg * Math.PI / 180);
canvasContext.drawImage(image, -image.width, -image.height);
break;
case 270 :
case -90 :
canvas.setAttribute('width', image.height);
canvas.setAttribute('height', image.width);
canvasContext.rotate(p_deg * Math.PI / 180);
canvasContext.drawImage(image, -image.width, 0);
break;
};
} else {
/*
Ok!: MSIE 6 et 7
*/
var image = document.getElementById('image');
switch(p_deg) {
default :
case 0 :
image.style.filter = 'progid:DXImageTransform.Microsoft.BasicImage(rotation=0)';
break;
case 90 :
image.style.filter = 'progid:DXImageTransform.Microsoft.BasicImage(rotation=1)';
break;
case 180 :
image.style.filter = 'progid:DXImageTransform.Microsoft.BasicImage(rotation=2)';
break;
case 270 :
case -90 :
image.style.filter = 'progid:DXImageTransform.Microsoft.BasicImage(rotation=3)';
break;
};
};
};
window.onload = function() {
var image = document.getElementById('image');
var canvas = document.getElementById('canvas');
if(canvas.getContext) {
image.style.visibility = 'hidden';
image.style.position = 'absolute';
} else {
canvas.parentNode.removeChild(canvas);
};
rotate(0);
};
</script>
</head>
<body>
<p>
rotate:
<input type="button" value="0°" onclick="rotate(0);" />
<input type="button" value="90°" onclick="rotate(90);" />
<input type="button" value="180°" onclick="rotate(180);" />
<input type="button" value="-90°" onclick="rotate(-90);" />
</p>
<p>
<img id="image" src="http://www.google.com/intl/en_ALL/images/logo.gif" alt="" />
<canvas id="canvas"></canvas>
</p>
</body>
</html>