skip to main | skip to sidebar
Code 18
Manuel du savoir-faire à l'usage des geeks et des curieux
RSS
  • Accueil
  • Le web au Québec
  • Liens
  • Twitter
  • Facebook
  • À propos

dimanche 31 mai 2009

Obtenir le Page Rank Google en PHP ou Perl

Publié par Infinite Loop, à 10 h 27 1 commentaire

Pour nos services SEO (Search Engine Optimization), je cherchais un moyen de lister les sites dont nous avons la responsabilité du référencement en faisant afficher à côté de chacun le Page Rank Google (échelle allant jusqu'à un maximum de 10). Google Toolbar a beau être pratique, mais il faut visiter les sites un à un pour le récupérer.

En voulant automatiser ce processus, j'ai trouvé une classe PHP qui me permettrait d'y arriver sans effort. Sur Google Code, on peut télécharger la classe google_pagerank.class.php à partir du projet Popstats.

À noter la dépendance avec le fichier cacher.class.php. On doit donc définir une constante pour indiquer le répertoire utilisé pour l'engin de cache.

require_once("popstats/google_pagerank.class.php");

// par exemple :
define('CACHE_DIR', dirname(__FILE__));

$gpr = new GooglePageRank("http://code18.blogspot.com");

// duh... juste 2
echo $gpr->pagerank;
Pour mes besoins, il ne me reste plus qu'à créer une liste, effectuer les requêtes en boucle et stocker l'information.

Sinon, je sais que l'équipe réseau préfère de loin les scripts Perl pour ce type de tâche. Si la version PHP n'est pas retenue, je me tournerai vers le module Perl de CPAN WWW-Google-PageRank.

use WWW::Google::PageRank;

my $pr = WWW::Google::PageRank->new;

print "Le page rank du site est : " . scalar($pr->get($ARGV[0]));

Où l'appel pourra être fait à l'aide d'un terminal :

perl page-rank.pl http://code18.blogspot.com

Pour les gens moins techniques qui souhaiteraient connaître le Page Rank de leur site, jetez un oeil à un des nombreux interfaces web-based gratuits comme Google PageRank Checker.

Avec tout ça, je réalise que le page rank de mon blogue n'est pas très fort (cordonnier mal chaussé?). Le site est encore jeune et je dois dire que je n'ai pas vraiment fait d'efforts pour le référencer Mais le plus étonnant, c'est qu'il soit consulté par plus de 2000 personnes mensuellement et la tendance est visiblement à la hausse (300 visites de plus que le mois dernier).


Tags: Perl, PHP

Citation no. 32 sur Windows

Publié par Infinite Loop, à 08 h 33 0 commentaire

Le comble de Windows, c'est que pour l'arrêter, il faut cliquer sur démarrer.

- La critique


Tags: Citations

samedi 30 mai 2009

Google Talk chatback badge

Publié par Infinite Loop, à 17 h 07 2 commentaires

Hier soir, j'ai ajouté un badge Google Talk chatback à mon blogue pour pouvoir discuter avec les lecteurs. Si je suis en ligne, vous verrez mon statut changer automatiquement et d'un simple clic, vous pourrez ouvrir une session pour parler avec moi sans avoir besoin d'avoir Google Talk d'installé ou d'ouvrir un compte. Évidemment, je ne serai pas toujours disponible alors si vous avez des questions, n'hésitez pas à m'écrire un courriel ou à laisser un commentaire.



À votre tour, si vous souhaitez ajouter une fonctionnalité semblable à votre site, votre blogue ou votre service à la clientèle, vous pouvez le faire simplement en suivant les instructions pour ajouter un badge Google Talk chatback sur le site de Google.

En gros, il suffit :

  • d'avoir un compte GMail
  • installer Google Talk
  • générer le script à l'aide du wizard
  • copier/coller le code (iframe) dans votre site
  • lorsque vous serez connecté, les visiteurs pourront cliquer sur l'icône pour démarrer une session de chat
  • de votre côté, vous recevrez un lien par Google Talk pour ouvrir la fenêtre de chat web-based. Vous devrez être authentifié dans GMail pour que ça fonctionne
J'en profite aussi pour vous inviter à suivre mes commentaires impertinents en vous inscrivant sur Twitter ou Identi.ca (semblable mais open source).


Tags: Intégration

vendredi 29 mai 2009

Images volantes en JavaScript

Publié par Infinite Loop, à 16 h 42 0 commentaire

Voici un vieux truc en JavaScript qui permet de faire "voler" les images d'une page avec un superbe effet visuel.

  1. Rendez-vous sur la page d'accueil de Google, sur Google Image Search ou encore sur le site d'Amazon
  2. Dans la barre d'adresse de votre fureteur (testé fonctionnel dans IE, Firefox et Chrome), copiez et entrez le code JavaScript suivant à la place de l'URL:

    javascript:R=0; x1=.1; y1=.05; x2=.25; y2=.24; x3=1.6; y3=.24; x4=300; y4=200; x5=300; y5=200; DI=document.images; DIL=DI.length; function A(){for(i=0; i-DIL; i++){DIS=DI[ i ].style; DIS.position='absolute'; DIS.left=Math.sin(R*x1+i*x2+x3)*x4+x5; DIS.top=Math.cos(R*y1+i*y2+y3)*y4+y5}R++ } setInterval('A()',25); void(0);

  3. Faites "Enter" puis admirez le spectacle


Que se passe-t-il exactement dans ce code pour rendre l'effet possible? Regardons la de plus près:
// déclaration et initialisation des variables
R=0;
x1=.1;
y1=.05;
x2=.25;
y2=.24;
x3=1.6;
y3=.24;
x4=300;
y4=200;
x5=300;
y5=200;

// obtenir un array des images contenues dans le document (balises IMG)
DI=document.images;

// combien sont trouvées ?
DIL=DI.length;

// déclaration d'une fonction
function A(){

// pour chaque image
for(i=0; i-DIL; i++){
// changer les propriétés dynamiquement
DIS=DI[ i ].style;
DIS.position='absolute';
DIS.left=Math.sin(R*x1+i*x2+x3)*x4+x5;
DIS.top=Math.cos(R*y1+i*y2+y3)*y4+y5
}
R++
}

// rappeler la fonction à chaque 25 millisecondes
// pour créer l'effet de déplacement
setInterval('A()',25); void(0);


Tags: Curiosités, JavaScript

jeudi 28 mai 2009

ASP vs PHP

Publié par Infinite Loop, à 18 h 52 0 commentaire

Y a-t-il des nostalgiques du langage ASP parmi les lecteurs ? En fait, c'est surtout parce que je me pose la question à savoir comment le langage ASP du méchant Microsoft a bien pu inspirer le bien-aimé PHP au point d'y laisser des traces (au grand dam des développeurs les plus puristes).

D'abord, le non moins controversé ASP Tags consent à accepter les tags <% %> en plus du standard PHP <?php ?>. Simple caprice de style ou réel avantage ? Certains argueront que ça leur fait économiser la saisie de trois caractères à chaque fois alors que d'autres se contenteront d'affirmer que le raccourci d'impression <%= "Hello World!" %> est très pratique en comparaison avec la notation classique <?php echo "Hello World!"; ?>.

Pour les curieux qui souhaiteraient en faire usage, vous pouvez l'initialiser de deux façons:

  • dans le fichier php.ini, modifiez la valeur asp_tags pour qu'elle soit à "on"
  • si vous n'y avez pas accès, vous pouvez vous rabattre sur le fichier .htaccess en y ajoutant la ligne suivante : php_flag asp_tags on
  • Désolé, c'est impossible de le faire par ini_set().
L'autre vestige qui peut faire penser à l'ASP est la syntaxe alternative des structures de contrôles, où la première accolade est remplacée par un : et où un mot clé termine l'énoncé. Ainsi:
$list = array(1,2,3);

foreach($list as $item) :
echo $item;
endforeach;
est équivalent à :
foreach($list as $item){
echo $item;
}
Disponible en plusieurs saveurs : endif, endwhile, endfor, endforeach et endswitch. À vrai dire, c'est faux de croire qu'ASP a eu une influence majeure ici car on devrait plutôt en attribuer la paternité au langage C++. Mais pour ceux qui ont fait un peu d'ASP, avouez que ça vous rappelle des souvenirs : If/End If, While/Wend ou encore ForEach/Next ou Function/End Function. Et on ne s'en ennuie pas.

La preuve est qu'un programme de conversion de code ASP vers PHP a été créé pour mettre fin à vos souffrances. Asp2Php peut donc faire une conversion "rough" de vos scripts préférés mais au risque d'obtenir du code PHP pas toujours très compréhensible et plutôt difficile à maintenir. À classer au rayon des curiosités à expérimenter une fois dans votre vie.


Tags: PHP

mercredi 27 mai 2009

Intervalles de dates sous PostgreSQL et SQL Server

Publié par Infinite Loop, à 19 h 09 0 commentaire

Quand j'ai commencé à programmer avec PostgreSQL, je sortais d'un environnement SQL Server et bien qu'il y ait des standards qui ne changent pas, je m'étonnais souvent de l'approche différente qu'avaient les deux SGBD. On le sait, Postgre a une tendance à suivre les traces d'Oracle alors que MSSQL le fait... à sa manière.

Dans SQL Server, si on veut ajouter un intervalle à une date, on doit utiliser la fonction DateAdd (inspirée de VBScript) en utilisant la forme : DateAdd(interval, number, date).

-- Ajouter 7 jours à la date
SELECT DateAdd("d", 7, "2009-05-27")
Sous PostgreSQL, c'est un peu plus subtil et on peut y arriver en utilisant plusieurs notations:
SELECT '2009-05-27'::date + 7
SELECT '2009-05-27'::date + interval '7 days'
SELECT '2009-05-27'::date + '7 days'::interval
Remarquez que l'opérateur "::" est utilisé pour la conversion de type, au même type que sa syntaxe équivalente CAST('2009-05-27' as date):
SELECT CAST('2009-05-27' as date) + 7
Ainsi, on peut convertir une chaîne de caractères en intervalle et l'utiliser comme s'il s'agissait du premier argument de DateAdd.
-- retourne 00:00:07
SELECT '7 seconds'::interval

-- retourne 00:10:00
SELECT '10 minutes'::interval

-- retourne 14:00:00
SELECT '14 hours'::interval
Maintenant qu'on comprend mieux les intervalles, plaçons en un dans un contexte pratique. Si on imagine un système de calendrier où chaque événement entré doit compter une date de fin calculée à 1 semaine plus tard, on pourrait faire quelque chose comme ceci:
SELECT date_event as date_start,
date_event + interval '1 week' as date_end
FROM calendar
Par contre, si la durée de l'intervalle se compte en jours et n'est pas fixe, on pourra la paramétrer, à l'aide d'une variable ou d'un champ de la table, qui composera l'intervalle:
UPDATE calendar
SET date_event = dt_event + (duration || ' days')::interval
Enfin, tant qu'à manipuler des intervalles, aussi bien en profiter pour montrer comment extraire une partie de la date (sous forme de timestamp - datetime en MSSQL):
-- 2009
SELECT EXTRACT(year FROM '2009-05-27 18:50:48.609-04'::timestamp)

-- 27
SELECT EXTRACT(day FROM '2009-05-27 18:50:48.609-04'::timestamp)

-- 18
SELECT EXTRACT(hour FROM '2009-05-27 18:50:48.609-04'::timestamp)
Vous l'aurez deviné, c'est l'équivalent PostgreSQL pour DatePart.


Tags: PostgreSQL, SQL Server

mardi 26 mai 2009

Ordre de chargement avec Prototype

Publié par Infinite Loop, à 19 h 31 0 commentaire

Cet après-midi, j'ai rencontré un bogue un peu spécial avec la librairie Prototype.js. Pour une raison particulière, plusieurs fonctions étaient reliées au même événement, celui du chargement de la page (load).

Comme d'habitude, tout fonctionnait à merveille dans Firefox mais une fois testé dans Internet Explorer, un message d'erreur JavaScript était lancé. Avec un collègue de travail, nous avons tenté un certain temps de chercher la réponse jusqu'à ce qu'on s'impatiente et qu'on décide de suivre la trace des appels à l'aide de Firebug (Firefox) et Firebug Lite (IE).

En considérant le snippet de code suivant (simplifié par rapport à l'exemple réel) :

/*
les scripts suivants sont inclus dans la page

/js/firebug-lite-min.js
/js/prototype-1.6.0.3-min.js

*/

document.observe('dom:loaded', function(){ console.log('dom:loaded # 1'); });
document.observe('dom:loaded', function(){ console.log('dom:loaded # 2'); });

Event.observe(window, 'load', function(){ console.log('load'); } );
On remarque que la séquence des appels dans Firefox (Firebug) indique :

"dom:loaded # 1"
"dom:loaded # 2"
"load"

Tandis qu'Internet Explorer 7 affiche (Firebug Lite) :

"load"
"dom:loaded # 2"
"dom:loaded # 1"

C'est à dire que le stack des appels aux fonctions est complètement inversé! Firefox semble les exécuter dans l'ordre rencontré FIFO (First In, First Out) tandis qu'IE agit en FILO (First In, Last Out) Comme nous avions une fonction qui initialisait dynamiquement un composant d'interface et que la deuxième prenait le relais en prenant pour acquis que les changements avaient été appliqués précédemment, la séquence du code rencontré n'était pas respectée et IE se plaigna tout simplement que l'objet n'était pas trouvé dans la page.

Comme on ne fait pas de miracle, on a tout rapatrié dans la même fonction. Si quelqu'un croit avoir la solution, n'hésitez pas à me la partager.


Tags: JavaScript

lundi 25 mai 2009

Pouvez-vous lire ceci ?

Publié par Infinite Loop, à 17 h 35 0 commentaire

Fait intéressant :

Sleon une édtue de l'Uvinertisé de Cmabrigde, l'odrre des ltteers dnas un mtos n'a pas d'ipmrotncae, la suele coshe ipmrotnate est que la pmeirère et la drenèire soit à la bnnoe pclae. Le rsete peut êrte dnas un dsérorde ttoal et vuos puoevz tujoruos lrie snas porlblème. C'est prace que le creaveu hmauin ne lit pas chuaqe ltetre elle-mmêe, mias le mot cmome un tuot.


Tags: Saviez-vous que

dimanche 24 mai 2009

Service ReCaptcha en français

Publié par Infinite Loop, à 13 h 09 6 commentaires

Sur la plupart des sites web, les formulaires, que ce soient ceux dans les sections "Contactez-nous", les forums de discussions ou autres, sont vulnérables au SPAM et à l'envoi abusif de contenu par des bots (des programmes automatiques qui simulent des actions qui devraient normalement être effectuées par des humains).

Il n'existe pas de moyens parfaitement fiables pour contrer ce fléau mais il y a quand même une technique qui permet d'en réduire considérablement le volume : le Captcha. Ce nom symbolise l'acronyme de "Completely Automated Public Turing test to tell Computers and Human Apart" et consiste à faire afficher un test qui permet de mesurer selon les probabilités si la réponse donnée par le visiteur est le fruit d'un cerveau humain (jusqu'à preuve du contraire, nous sommes supposés être plus intelligents que la machine).

Le modèle le plus fréquent est l'image contenant des lettres et des chiffres flous ou déformés qui doivent être saisis au clavier par le visiteur. Malheureusement, à mesure que les innovations des captchas avancent, les hackers développement eux aussi des mécanismes de plus en plus sophistiqués pour les déjouer. Placer un captcha dans une zone stratégique a au moins le mérite de minimiser les dégâts.

L'université Carnegie Mellon a développé un service gratuit nommé reCaptcha qui peut être intégré dans votre site assez facilement. D'ailleurs, des plugins sont disponibles pour PHP, .NET, Perl, Python, Java, WordPress, etc. Le principe de reCaptcha est qu'il utilise les vieux documents et manuscrits que l'OCR ne peut reconnaître correctement et soumet les termes en tant que captcha que le visiteur "humain" tente de déchiffrer, ce qui leur permet d'obtenir des résultats plus précis que ce qu'offre la reconnaissance optique des caractères.

Pour utiliser le service

  • vous devez vous inscrire (gratuit) et fournir un nom d'usager, mot de passe et votre courriel. Vous devrez aussi faire vous-même le test du captcha pour prouver que vous êtes humain!
  • Notez la clé publique et la clé privée qui vous seront assignées.
  • Téléchargez la librairie PHP pour reCaptcha et déposez la dans votre serveur.
Exemple de code

Dans votre formulaire, ajoutez l'appel qui génère le captcha :
<form id="frm" method="post">
<!-- input fields... -->

<?php
echo recaptcha_get_html($public_key);
?>
<input type="submit" value="Enregistrer" />
</form>
Validation dans le code PHP :
require_once('ReCaptcha/recaptchalib.php');

$public_key = "VOTRE CLÉ PUBLIQUE";
$private_key = "VOTRE CLÉ PRIVÉE";

if($_SERVER['REQUEST_METHOD'] == 'POST') {
$response = recaptcha_check_answer(
$private_key,
$_SERVER["REMOTE_ADDR"],
$_POST["recaptcha_challenge_field"],
$_POST["recaptcha_response_field"]
);

if (!$response->is_valid) {
echo 'Échec de la validation';
}
else {
// le captcha est valide
// effectuer le traitement normal
}
}
Les clés "recaptcha_challenge_field" et "recaptcha_response_field" du $_POST sont générées lors de l'appel de recaptcha_get_html().

En français svp!

Vous remarquerez ici que le service est uniquement en anglais. Heureusement, on peut changer la langue par défaut pour traduire les textes et le captcha audio en modifiant la configuration. Ceci doit être fait par JavaScript en ajoutant dans l'entête le script suivant :
<script type= "text/javascript">
var RecaptchaOptions = {
lang : 'fr',
theme : 'white',
custom_translations : { instructions_visual : "Entrez les mots ici :"}
};
</script>
Le thème rouge par défaut peut aussi être modifié pour se fondre plus facilement avec le design de votre site. Choisissez parmi red, white, blackglass, clean ou custom. Le dictionnaire custom_translations permet d'écraser les textes proposés mais celui-ci est optionnel.

Contrairement à d'autres captcha, celui-ci possède certains avantages:
  • si les mots affichés ne sont pas clairs, on peut rafraîchir l'image pour en générer une nouvelle
  • une personne non-voyante ne sera pas freinée par le déchiffrage des mots de l'image car une fonction auditive est disponible mais on doit porter attention à la prononciation. En anglais, ce sont des mots tandis qu'en français, il s'agit d'une séquence de huit chiffres à entrer.
D'autres implémentations de captcha

En plus du texte brouillé à transcrire, d'autres bonnes idées à considérer sont le calcul simple d'une formule mathématique simple où le visiteur doit donner la réponse et l'identification d'une image parmi une liste affichée. Aussi, l'objet Zend_Captcha du Zend Framework possède d'autres types à explorer, dont le figlet (bannières textes ASCII).


Tags: PHP, Sécurité

Citation no. 31 sur la lune

Publié par Infinite Loop, à 09 h 34 0 commentaire

Quand le sage pointe la lune, le fou regarde le doigt.

- Confucius


Tags: Citations

samedi 23 mai 2009

Google killers

Publié par Infinite Loop, à 20 h 27 0 commentaire

Voici un article intéressant qui vaut la peine d'être partagé. Qui se souvient des moteurs de recherche Wisenut, Wikia Search, Exalead, Quaero ou Cuil ? Peu de gens, car ces services, annoncés comme des Google Killers, n'ont pas su s'imposer sur le marché.

À lire sur le site l'Expansion.com : 10 "Google Killers"... et ce qu'il en reste.


Tags: Saviez-vous que

Configurer Zend_Tool en CLI

Publié par Infinite Loop, à 11 h 52 0 commentaire

Dans le développement d'applications web PHP MVC (Model-View-Controller), si vous utilisez Zend Studio for Eclipse, l'éditeur offre la possibilité de créer un projet en définissant automatiquement l'arborescence des répertoires et fichiers (application, controllers, models, views, etc) en utilisant l'option du menu File / New / Zend Framework Project. Ensuite, on peut ajouter les items séparément.

Mais savez-vous qu'on peut aussi utiliser Zend_Tool dans l'interface command line ? Avant de s'en servir, on doit d'abord le configurer.

1. PHP doit être installé sur votre poste de travail

Assurez-vous que PHP est présent sur votre poste. Il sera requis par Zend_Tool lors de l'appel en mode invite de commande. Comme je développe à partir de Windows XP et EasyPHP, l'exécutable php.exe se trouve par défaut ici : C:\Program Files\EasyPHP 3.0\php\

2. Variable d'environnement "path"

Pour que PHP puisse être invoqué de n'importe où, ajoutez le chemin de l'exécutable à la variable d'environnement "path". Sinon, vous devrez spécifier le chemin ou accéder à l'emplacement où il est situé pour faire l'appel.

  • Cliquez avec le bouton de droit sur l'icône My Computer, choisissez Properties, l'onglet Advanced et cliquez sur le bouton "Environment Variables" dans l'encadré du bas "Startup and Recovery". Le raccourci Touche Windows + Break (pause) fonctionne aussi pour ouvrir cet écran.
  • Dans la liste des System variables, recherchez "Path". Sélectionnez-le et cliquez Edit.
  • À la fin de "variable value", ajoutez le chemin qui pointe vers PHP. Attention, le point-virgule est important car il agit comme séparateurs pour les différents chemins listés :

    ;C:\Program Files\EasyPHP 3.0\php

  • Vous devrez redémarrer votre PC pour que la configuration prenne effet au prochain démarrage. Pour l'instant, faites le plus tard car vous devrez inclure un autre chemin à l'étape suivante.
  • Suite au démarrage, ouvrez un terminal, invoquez php -v pour obtenir la version. Si tout fonctionne, vous devriez voir la version apparaître :

    PHP 5.2.8 (cli) (built: Dec 8 2008 19:31:23)
    Copyright (c) 1997-2008 The PHP Group
    Zend Engine v2.2.0, Copyright (c) 1998-2008 Zend Technologies
3. Configurer Zend_Tool CLI

Tout le nécessaire se trouve dans l'archive Zend Framework :
  • Téléchargez la version la plus récente (zip ou tar.gz).
  • Décompressez l'archive.
  • Copiez les répertoires "bin" et "library" dans un nouveau répertoire sur votre poste, par exemple : C:\Program Files\Zend\Tool\Cli\ (je possède déjà un répertoire Zend parce que j'avais installé Zend Guard. Donc il me semble tout à fait logique de le placer ici).
  • Ajoutez le chemin C:\Program Files\Zend\Tool\Cli\bin à la variable d'environnement "path" (comme à l'étape précédente) en prenant soin d'ajouter le ; entre les chemins.
  • Redémarrez.
4. Test et utilisation
  • Ouvrez un terminal et testez l'installation de Zend_Tool. Entrez zf show version. Si le texte "Zend Framework Version: 1.8.0" s'affiche, tout est en ordre.
  • Pour démarrer un nouveau projet à l'aide du command line interface, naviguez jusqu'à votre répertoire de travail où vous voulez le créer et entrez :

    zf create project firstproject

    L'arborescence sera automatiquement créée ainsi que la configuration de base (.htaccess, fichier .ini)
  • À la racine du projet, vous pourrez ensuite créer les nouveaux items (model, view, controller) avec une simple commande :

    zf create action add index
    zf create action edit index
    zf create action delete index

    zf create controller ...
    zf create view ...

  • Et finalement zf ? peut être utilisé en tout temps pour obtenir de l'aide, comme dans zf ? action


Tags: PHP, Programmation, Zend Framework

vendredi 22 mai 2009

Quelques logos intéressants

Publié par Infinite Loop, à 13 h 16 0 commentaire

Si vous programmez des sites web, vous avez certainement dû faire quelques créations pour des entreprises locales. Combien de fois est-ce qu'on a vu des PME (et même certaines grosses entreprises) arborer un logo imaginé et dessiné par le patron de la boîte, dont les talents artistiques ne sont pas, de toute évidence, ses meilleurs atouts ?

Et je n'ai pas la prétention de pouvoir faire mieux! Il y a quelques années, j'avais payé un infographiste la somme de 50$ pour concevoir le logo d'un de mes projets web. En moins d'une heure, j'avais quelque chose de sobre, de professionnel et de bon goût. La situation était peut-être exceptionnelle ou bien je n'étais pas exigeant sur les détails, mais j'avais accepté la première proposition qu'on m'avait fait.

Le meilleur exemple de succès sur la simplicité est certainement le "Swoosh" de Nike. Créé en 1971 par une étudiante en arts graphiques, sa conception n'avait coûté que 35$ à l'époque. À ce stade-ci, je voudrais rectifier et mettre un bémol à mes propos : je ne sous-entends pas qu'il faut chercher à exploiter les étudiants en graphisme, au contraire ! Leur travail est beaucoup plus complexe qu'on peut l'imaginer. Ce que je dis, c'est qu'à certaines occasions, on peut être chanceux et s'en tirer à bon compte.

Quand je regarde les logos d'entreprises, il y en a deux qui me fascinent. Ils sont simple, évocateurs et je ne sais pas ce que j'aurais donné pour être celui qui les a imaginés.

Amazon

  • La flèche du logo actuel symbolise l'objectif de pouvoir proposer tous les produits de A à Z
  • Le sourire fait référence à la satisfaction du consommateur
  • C'est une boutique que j'adore, j'ai définitivement un parti pris (j'ai été un "early adopter" il y a plus de 10 ans et apprendre crée une dépendance...)
  • J'en profite pour dire que c'est certainement la meilleure source pour se procurer des livres de programmation. Si vous achetez vos livres informatiques en français, le temps de les traduires, le contenu est déjà désuet et vous les payez beaucoup trop chers
Voyez l'historique et l'évolution du logo d'Amazon.

FedEx


Une seule raison :
  • Le choix de la police est astucieux. En collant les lettres E et X, l'espace laissé en blanc forme une flèche subtile pointant vers la droite. Une compagnie de transport qui va dans la bonne direction.
Avant de me faire la remarque, je sais que deux de mes logos préférés mettent en scène des flèches. N'y voyez pas de corrélation, ce n'est que le fruit du hasard!


Tags: Le coin du geek

jeudi 21 mai 2009

Créer un WSDL facilement avec Zend Framework

Publié par Infinite Loop, à 22 h 35 18 commentaires

Par le passé, je dois avouer avoir fait une tentative, une seule, pour créer à la main un fichier WSDL (Web Services Description Language) et je me suis promis que plus jamais je ne voulais revivre cette expérience. Certains outils existent pour automatiser cette pénible tâche et ceux que j'avais testé jusqu'à maintenant généraient un fichier .wsdl statique.

Pour expliquer simplement, un fichier WSDL comprend une structure XML qui définit les fonctions accessibles par le service, ses paramètres, les types et ce qui est retourné. Lorsque le comportement d'une des fonctions du service web change, on est obligé soit de modifier à la main la description, soit de regénérer le fichier WSDL et le redéposer sur le serveur.

Jusqu'à ce qu'on découvre l'objet Zend_Soap_AutoDiscover du Zend Framework. Sa force : être en mesure d'inspecter une classe au runtime et de générer à la volée le fichier WSDL correspondant.

Voici un petit tutoriel pour créer un premier service web, réduit à sa plus simple expression. J'offrirai le service de retourner le pourcentage de taxe associé au nom envoyé par le client.

1. Créer une classe qui offrira les services

Ici, ça peut être n'importe quelle classe, pas nécessairement une construite spécifiquement pour le service web. Une seule condition doit être respectée : suivre le format de documentation des docblocks car AutoDiscover se base sur commentaires pour construire la description WSDL.

Chaque fonction de la classe doit être précédée d'un docblock qui doit suivre la syntaxe suivante :

  • @param type param_name
  • @return type
Comme dans l'exemple ci-dessous :
// Ex: fichier "tax.class.php"

class Tax {

/**
* Return tax value
* @param string $type
* @return float
*/
public function getTaxValue($type = 'TPS'){
// récupérer les taxes d'une quelconque façon,
// par exemple d'une base de données

// Taxe sur les Produits et Services
if($type == 'TPS'){
return 5.0;
}
// Taxe de Vente du Québec
elseif ($type == 'TVQ'){
return 7.5;
}
else {
return 0;
}

}
}
Si vous rencontrez l'erreur "Fatal error: Uncaught SoapFault exception: [WSDL] SOAP-ERROR: Parsing WSDL: Missing <message>...", c'est probablement que votre docblock est mal structuré. Le commentaire doit absolument débuter par /**, avec deux astérisques (dans Eclipse, vous devriez voir une différence dans la coloration syntaxique).

2. Manipuler les requêtes du côté serveur

Comme c'est vous qui offrez un service, vous devrez préparer une page qui effectuera les manipulations des requêtes clients. Ce fichier aura deux utilités :
  • retourner la description du WSDL lorsqu'un paramètre sera passé
  • traiter la demande
// Ex: fichier "service.php"

require_once('Zend/Soap/AutoDiscover.php');
require_once('Zend/Soap/Server.php');
require_once('tax.class.php');

if(isset($_GET['wsdl'])){
// inspecter la classe Tax et retourner la description
$wsdl = new Zend_Soap_AutoDiscover();
$wsdl->setClass('Tax');
$wsdl->handle();
}
else{
// traitement
$server = new Zend_Soap_Server('http://localhost/WSDL/service.php?wsdl');
$server->setClass('Tax');
$server->handle();
}
Si l'erreur suivante se produit "Fatal error: Uncaught exception 'Zend_Soap_Server_Exception' with message 'SOAP extension is not loaded.'", c'est que l'objet Zend_Soap_Server utilise l'extension php_soap.so (ou .dll). Activez la dans php.ini.

Vous êtes maintenant prêts à fournir les taux de taxes.

3. Créer une requête en tant que client

Probablement qu'en temps normal, ce ne sera pas vous qui utiliserez votre service web mais mieux vaut le tester avant de l'offrir à vos clients. Ceux-ci pourront utiliser le langage de leur choix pour faire appel à votre service, mais nous, comme nous utilisons déjà le Zend Framework, nous allons nous servir d'un autre objet au nom évocateur de Zend_Soap_Client. Cet objet peut aussi être utilisé lorsque vous voudrez vous-même utiliser un service offert par un autre fournisseur. Rappelez-vous que du point de vue du client, il ne voit pas que c'est programmé en PHP, ils ne voient que la définition WSDL pour pouvoir communiquer avec lui.

Une fois la connection établie, le client peut appeler les fonctions comme un objet. Ici, il aura accès à la fonction getTaxValue.
// Ex: fichier "appel.php"

require_once('Zend/Soap/Client.php');

try {
$client = new Zend_Soap_Client('http://localhost/WSDL/service.php?wsdl');

echo 'La TPS est de : ' . $client->getTaxValue('TPS') . ' %';
echo 'La TVQ est de : ' . $client->getTaxValue('TVQ') . ' %';
}
catch(Zend_Exception $e){
echo $e->getMessage();
}
Ce qui devrait afficher ceci :
La TPS est de : 5 %
La TVQ est de : 7.5 %


Tags: PHP, Zend Framework

mercredi 20 mai 2009

XSS sur Wikio... et Archambault Musique

Publié par Infinite Loop, à 18 h 55 3 commentaires

Est-ce que vous connaissez le XSS (Cross-Site Scripting) ? Logiquement, ça aurait dû s'appeler CSS mais l'acronyme était déjà utilisé pour identifier les feuilles de styles (Cascading Style Sheets).

Une attaque XSS représente une vulnérabilité d'un site à pouvoir injecter du code, souvent malicieux, à l'intention des visiteurs. Souvent, ça se caractérise par l'ajout de code JavaScript qui s'exécute ou une librairie JS qui est intégrée dynamiquement au contenu de la page. Comme on peut inclure un fichier hébergé sur un autre serveur, le hacker peut contrôler à distance les actions voulues.

Par exemple, j'ai remarqué que la faille était présente dans la boîte de recherche des onglets Blogs et Top Blogs de Wikio.fr. Qu'est-ce que ça permet de faire ? Au lieu de chercher des mots clés, entrez le code JavaScript suivant, incluant les balises :

<script>alert('bonjour');</script>
Vous remarquerez qu'une fois la recherche lancée, le terme inscrit fait parti de l'URL pour devenir :

http://www.wikio.fr/blogs/search/%3Cscript%3Ealert(%27bonjour%27)%3B%3C%2Fscript%3E

Et immédiatement en dessous, le breadcrumb affiche :

Wikio > Blogs > <script>alert('bonjour');</script>

Le problème, c'est que le code JavaScript est exécuté et une boîte d'alerte affiche "bonjour". Ceci est rendu possible parce que l'élément recherché est récupéré tel quel et que le terme est ensuite affiché dans la page pour montrer ce qui a été inscrit par le visiteur.

Comment le régler ?

Sachant que la programmation est en PHP, il semblerait que la configuration magic_quotes_gpc de php.ini soit à "off". Dans le cas contraire, ça aurait limité les possibilités puisque les caractères auraient au moins été échappés (alert('bonjour') serait transformé en alert(\'bonjour\')). Aussi, la fonction PHP htmlentities() aurait pu contribuer à régler le problème en convertissant les balises SCRIPT en caractères HTML pour l'affichage.

Les possibilités...

Comme les paramètres font partis de l'URL et qu'ils sont interprétés, on peut se permettre d'appeler n'importe quelle fonction JavaScript, par exemple en provoquant une redirection :
<script>window.location="http://www.google.com";</script>
Imaginez que je sois malintentionné et que je souhaite piéger des gens. Je n'ai qu'à construire un lien comprenant le code, l'obsfusquer avec un service comme TinyURL ou PetitURL et le diffuser sur différents forums de discussion.

Juste pour rire...

Cliquez sur le lien ci-dessous pour voir un exemple de redirection XSS: http://tinyurl.com/o3pjqn (c'est juré, rien de compromettant ou de dangereux!). Vous pouvez désactiver le JavaScript de votre fureteur pour voir ce qui se passe.

Dans votre programmation, ayez toujours en tête que ce genre d'attaque existe. Tous les endroits où l'utilisateur peut entrer quelque chose sont à risques. Ne permettez pas aux hackers de faire les bouffi-bouffons (référence aux Goonies)!

Mise à jour 2009-05-21

Suite à mon article d'hier, l'équipe de Wikio.fr a été très rapide à corriger la vulnérabilité XSS sur son site. Bravo! Ce n'est pas tous les jours que les développeurs fixent les failles aussi aisément.

Donc j'ai besoin d'un nouvel exemple pour illustrer ce qu'est le XSS. Des volontaires ? Heureusement (ou pas), ce ne sont pas les exemples qui manquent. Pour le trouver, je reviens chez nous au Québec, avec le site d'Archambault Musique. Le même code peut s'exécuter, la seule différence est que le site est programmé en JSP.

Voyez la prévisualisation de nouveau TinyURL Bouffi-Bouffon ou la redirection XSS instantannée.

Mise à jour 2009-06-19
Archambault a aussi corrigé le problème.
Qu'est-ce qu'ils ne trouvent pas drôle : la vulnérabilité ou la photo ?
:-D


Tags: JavaScript, PHP, Programmation, Sécurité

mardi 19 mai 2009

Encryption de données en PHP

Publié par Infinite Loop, à 19 h 37 0 commentaire

Si vous êtes dans une situation où vous devez protéger des données de manière sécuritaire, ça peut définitivement être une bonne idée de considérer à les encrypter. Vous ne voulez certainement pas que l'information reliée à la carte de crédit de votre client se retrouve entre de mauvaises mains! La librairie Mcrypt de PHP pemet d'effectuer de l'encryption de haut niveau, sans avoir besoin d'avoir des connaissances poussées dans ce domaine.

Petit crash course sur l'encryption :

  1. D'abord, il faut savoir qu'il existe de nombreux algorithmes (ciphers) qui offrent différents niveaux de sécurité : DES, Blowfish, Rijndael, Serpent, ThreeWay, TripleDES, Gost, Enigma, etc.
  2. Juste pour Rijndael, il est offert en mode 128, 192 et 256 bits.
  3. Ensuite, il y a toujours une clé, parfois appelée "key", "salt" ou "passphrase" qui permet d'être utilisée comme code secret à même l'algorithme d'encryption.
  4. Finalement, le mode d'encryption peut changer selon ce qu'on veut encoder : texte (ECB - Electronic Codebook), un fichier (CBC - Cipher Block Chaining), un byte stream (CFB), etc.
  5. Une fois configuré, on est en mesure de fournir le texte à encrypter.
Illustrons tout cela par un exemple :
// la donnée à protéger
$creditCard = "4500 xxxx xxxx 1234";

// clé secrète d'encryption (ça peut être n'importe quoi)
$key = "f0158bd5ea6dde2e0ba56103340b4393";

// algorithme à utiliser. Mode ECB = Electronic CodeBook
$cipher = mcrypt_module_open('rijndael-256', '', 'ecb', '');

// créer le vecteur d'initialisation (iv)
// MCRYPT_RAND est utilisé pour en créer un de source aléatoire (random)
$iv_size = mcrypt_enc_get_iv_size($cipher);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);

// initialisation des buffers
mcrypt_generic_init($cipher, $key, $iv);

// encryption à l'aide de la fonction générique
$crypted = mcrypt_generic($cipher, $creditCard);

// crypter pour lire à l'écran :-)
$crypted = base64_encode($crypted);

// affichera : Y/EmDRkEW6UusTWf58HYS90qK9JT5Pv21ojy4g/0exQ=
echo $crypted;

// ménage
mcrypt_generic_deinit($cipher);
Ensuite, on peut faire l'opération inverse pour déchiffrer le texte :
// réinitialiser les buffers (obligatoire)
mcrypt_generic_init($cipher, $key, $iv);

// remettre dans l'état crypté normal
$crypted = base64_decode($crypted);

$decrypted = trim(mdecrypt_generic($cipher, $crypted));

// affichera : 4500 xxxx xxxx 1234
echo $decrypted;

// ménage final
mcrypt_generic_deinit($cipher);
mcrypt_module_close($cipher);
Il ne reste plus qu'à dissimuler le tout dans des fonctions ou dans une classe et le tour est joué. Je vous laisse avec un peu de lecture complémentaire pour approfondir le sujet : AES - Advanced Encryption Standard.


Tags: PHP, Sécurité

lundi 18 mai 2009

Virtualiser les fureteurs avec Xenocode

Publié par Infinite Loop, à 09 h 45 1 commentaire

Tout bon développeur web connaît cette vérité universelle : tester le rendu et les fonctionnalités d'un projet dans différents fureteurs peut résulter en une prescription d'anti-dépresseurs. On s'efforce de créer du code compatible mais comme chacun des browsers possède ses propres particularités, on doit constamment en tenir compte. On doit faire des choix et parfois limiter la compatibilité à certaines versions (les plus populaires étant les dernières versions d'Internet Explorer, Firefox, Safari, Opera et Chrome).

Et quand on croit avoir réussit à faire quelque chose d'acceptable, il y a toujours un client rabat-joie qui se fait un plaisir de nous rappeler que dans SON application à lui, il y a quelque chose qui cloche. Le programmeur s'étonne alors :

Programmeur : "Internet Explorer 6 ? Il y a encore des gens qui l'utilisent ?"

Chargé de projet : "Eh oui, 1 internaute sur 5 environ..."

Programmeur : "Je voudrais bien le tester mais j'ai la version 8 sur mon poste, mais bon, ce n'est pas que je l'utilise, Windows Update l'a ajouté automatiquement (je suis en entreprise, c'est une version légale)! Mais je tiens à spécifier que je préfère Firefox qui est bien plus cool!!! (point bonus si tu as un t-shirt ou un fond d'écran)"

Chargé de projet : "Appelle la réceptionniste, je crois qu'elle l'a encore sur son PC. Sinon, on l'installera sur un vieux PC de test"
Vous vous reconnaissez dans cette situation ? Alors vous apprécierez Xenocode, un plugin qui permet de virtualiser les fureteurs pour les exécuter sur votre poste, sans même avoir à les installer (par le fait même, d'éviter de polluer votre environnement de travail).

Pour l'utiliser, il suffit d'installer le plugin gratuit présent sur le site. Sur Firefox, il faut installer un fichier d'extension .xpi tandis que sur IE, ce sera un exécutable (j'ai testé et ça fonctionne aussi sur environnement Chrome). Vous devrez donc en assumer la sécurité. Une fois présent sur votre poste, vous pourrez démarrer n'importe quel fureteur supporté en cliquant sur l'icône de démarrage vert. Xenocode s'occupera de télécharger les fichiers nécessaires pour démarrer une version standalone. Mieux, aucune configuration particulière n'est à faire.

Finalement, vous trouverez en page d'accueil de Xenocode d'autres applications de types différents qui peuvent être virtualisés : Quicktime, Songbird, WinAmp, iTunes, Winrar, 7-Zip, des clients Twitter et P2P, Silverlight, WorldWide Telescope, etc.


Tags: Coffre à outils, Virtualisation

dimanche 17 mai 2009

Traductions pour Uploadify

Publié par Infinite Loop, à 11 h 00 12 commentaires

Il y a environ 1 mois, j'ai présenté un plugin jQuery du nom de Uploadify qui permet de faire l'upload de fichiers multiples avec affichage d'une barre de progression en temps réel. Les développeurs, Ronnie Garcia et Travis Nickels, ont fait du beau travail mais moi, en tant que développeur québécois francophone, j'étais déçu de ne pas trouver de configuration pour pouvoir le faire afficher en français. La traduction dans différentes langues était tout simplement absente.

C'est pourquoi je me suis mis au travail et j'ai ajouté la fonctionnalité à la plus récente version 1.6.2. J'ai posté sur leur forum ma contribution en espérant qu'elle sera utile à plusieurs d'entre vous.

Comme je disais sur leur forum, la langue anglaise est encore celle par défaut. Donc il n'y a aucun changement dans l'implémentation si vous comptez utiliser l'anglais dans votre projet, à moins que vous ayez l'intention de remplacer un ou plusieurs termes ou que vous souhaitiez traduire les étiquettes.

À l'intérieur de l'objet original, j'ai créé un objet de traduction qui regroupe les différents textes de la librairie. Celui-ci utilisera ceux définis par défaut et si le programmeur décide d'en remplacer, il le fera à l'initialisation de l'objet où il pourra écraser le ou les champs spécifiés.

Dans la librairie jquery.uploadify.js, j'ai principalement effectué les changements suivants :

1. l'objet de traduction

J'ai créé une nouvelle propriété objet définie lors de l'initialisation. Les champs par défaut seront remplacés par ceux fournis par le programmeur à la dernière ligne (options.translations).

translations: $.extend({
browseButton: 'Browse',
error: 'An error occured',
completed: 'Completed',
replaceFile: 'Do you want to replace the file',
unitKb: 'KB',
unitMb: 'MB'
}, options.translations )
2. les références pour i18n

À l'interne, chaque texte appelle l'objet de la façon suivante : settings.translations.browseButton.

3. Comment implémenter

Les points 1 et 2 ne sont que de la mécanique interne et le programmeur n'a pas à s'en soucier (je l'explique pour les curieux). Lors de l'implémentation, tout se fait comme à l'ordinaire, sauf s'il souhaite faire la traduction (l'objet translations est optionnel, de même que chaque étiquette).
$(document).ready(function() {

$('#fileInput').fileUpload ({
'uploader' : 'uploader.swf',
'script' : 'upload.php',
'cancelImg' : 'cancel.png',
'auto' : true,
'folder' : '/uploads',

// nouveau!
translations: {
browseButton: 'Parcourir',
error: 'Une erreur s\'est produite',
completed: 'Terminé',
replaceFile: 'Voulez-vous remplacer le fichier',
unitKb: 'Ko',
unitMb: 'Mo'
}
});
});
J'en profite pour faire remarquer que la propriété buttonText a été remplacée par la traduction browseButton. Les seuls éléments manquants que je n'ai pas pu configurer pour la traduction sont les types d'erreurs que retourne le composant Flash (par exemple errorObj.type : "File Size"). Pour le moment, ce n'est qu'un détail.

J'ai soumis sur le forum d'Uploadify ma version complète du composant que j'ai étiquetté version 1.7 pour illustrer l'ajout de la nouvelle fonctionnalité. Cependant, je laisserai le soin aux auteurs et responsables du projet de choisir s'il veulent supporter officiellement les changements que j'ai suggéré.

Rendez-vous sur le forum pour obtenir le code source de jquery-uploadify.js qui contient les traductions.


Tags: JavaScript

Citation no. 30 sur la force

Publié par Infinite Loop, à 08 h 31 0 commentaire

Qui vole un boeuf est vachement musclé.

- Yvan Francis Le Louarn, dit Chaval


Tags: Citations

samedi 16 mai 2009

ASCII Art avec GIMP

Publié par Infinite Loop, à 12 h 46 0 commentaire

Saviez-vous que sur Linux et Mac OS X, vous pouvez utiliser GIMP pour créer des ASCII Art ?

  • Ouvrez une image
  • Rendez-vous au menu File
  • Choisissez Save a copy
  • Dans Select file type (by extension), choisissez ASCII Art
  • Cliquez sur Save
  • Choisissez le type de texte (contexte d'enregistrement, dans mon cas Pure HTML)
  • Ouvrez le fichier HTML et admirez
Idéalement, faites afficher le résultat dans une console à l'aide de la commande Linux "cat" suivie du nom du fichier. Le mieux est aussi de le faire apparaître sur un fond noir, le résultat en ressort toujours mieux. Comme je l'ai enregistré en HTML, j'ai modifié la source pour ajouter dans la balise HEAD le fond noir et le texte en blanc :
<style type="text/css">
body{
color:white;
background-color:black;
}
</style>
L'image originale :



Le résultat (cliquez le screenshot pour voir le format agrandit) :



Si vous souhaitez plutôt en créer facilement à partir d'une interface web, vous pouvez simplement envoyer votre photo à un des services suivants et la conversion se fera on-the-fly :
  • Glass Giant ASCII Art Generator
  • Text-Image.com (un autre genre, en couleur!)


Tags: Curiosités, GIMP

vendredi 15 mai 2009

Fonction JavaScript à paramètres variables

Publié par Infinite Loop, à 12 h 04 0 commentaire

En JavaScript, il n'y a pas réellement de moyen précis de faire une surcharge de méthode, c'est à dire d'appeler une fonction qui accepte un nombre arbitraire de paramètres.

En fait, si on définit deux fonctions qui portent le même nom mais qui possèdent une nombre différent de paramètres, seule la dernière déclarée sera appelée.

// ne sera jamais exécutée
function test(p1){
console.log('Version à 1 paramètre');
}

// celle-ci fonctionnera et affichera un message dans Firebug
function test(p1, p2){
console.log('Version à 2 paramètres');
}

test('a');
test('a', 'b');
Sinon, une alternative est de définir une seule fonction, de ne pas spécifier de liste d'arguments dans sa signature et de boucler sur la liste des arguments reçus. Cependant, ça peut être difficile à gérer car les paramètres ne sont pas nommés, peuvent être dans certains cas optionnels et doivent suivre l'ordre reçu.
function test() {
for(var i=0; i<arguments.length; i++) {
console.log("Paramètre " + i + ": " + arguments[i]);
}
}
Ma solution préférée est d'utiliser un framework JavaScript comme jQuery ou Prototype et de passer une objet JSON en paramètre. Ainsi, la signature de la fonction ne change pas (toujours le même nombre d'arguments, dont un qui en contient plusieurs sous la forme JSON), les paramètres sont nommés du fait que la valeur est une propriété de l'objet et je peux les passer dans l'ordre que je veux.

Pour donner un exemple concret, imaginez une fonction qui reçoit différentes propriétés reliées à une pièce musicale numérique. À l'intérieur de la fonction, je liste les propriétés attendues et je les initialise avec des valeurs par défaut qui seront écrasées seulement si je passe une nouvelle valeur en paramètre. L'exemple ci-dessous se base sur jQuery, mais peut facilement être modifié pour l'utiliser avec Prototype.
function myFunction(params) {

// Pour Prototype, remplacer $.extend par Object.extend
var properties = $.extend(
{
'Artist' : '',
'Album' : '',
'Track' : '',
'Title' : '',
'Year' : '',
'Genre' : 'Unknown',
'Format' : 'Mp3',
'Kbps' : '320',
'Disk' : ''
}, params || {} );

// afficher toutes les propriétés et leurs valeurs
for(var p in properties){
console.log(p + ' = ' + properties[p]);
}

// validations
if (properties.Artist == ''){
console.log('Oups, il faut au moins mettre le nom de l\'artiste');
}
}
Finalement, il ne reste plus que l'appel à la fonction. Pour faciliter la lecture, je crée d'abord la liste d'arguments et je passe l'objet à ma fonction.
var args =
{
'Artist' : 'Jean Leloup',
'Album' : 'Mille excuses Milady',
'Track' : '8',
'Title' : 'Recommencer',
'Year' : '2009',
'Genre' : 'Francophone',
'Kbps' : '320',
// ajouter dynamiquement une propriété
// qui n'existe pas dans ceux attendus
'Rating' : '5'
};

myFunction(args);
J'espère que ça vous sera utile. Je vous souhaite un bon weekend!


Tags: JavaScript

jeudi 14 mai 2009

The Mythical Man-Month - Loi de Brooks

Publié par Infinite Loop, à 23 h 57 0 commentaire

Dans l'entreprise où je travaille, il y a un projet qui traîne sur les tablettes depuis longtemps et que pour des raisons de planification, nous avons été dans l'impossibilité de le mettre en production avant cette semaine. Pire, pour être en règle avec l'échéancier, nous avons dit au client que tout le travail avait été commencé depuis plusieurs semaines et que nous devrions être en mesure de lui montrer un démo bientôt... ce qui est faux.

Pour remédier au problème, un chargé de projet a eu l'idée d'ajouter deux ressources supplémentaires au projet, et ce dans le but de pouvoir effectuer une livraison plus rapide. Celui-ci aurait eu intérêt à lire l'ouvrage classique de Frederick P. Brooks, The Mythical Man-Month (1975).

Vous vous rendez compte, je n'étais pas encore né et les principes et prédictions sur la productivité des projets informatiques sont toujours d'actualité. Dans mon cas, j'ai lu la réédition de 1995 (20th anniversary) qui incluait 4 nouveaux chapitres ainsi que des commentaires sur les pronostics de l'auteur, vu avec un certain recul. Un des points marquants du livre est sans aucun doute la loi de Brooks qui statue qu'ajouter des ressources humaines à un projet en retard sur les prévisions ne fait qu'accentuer ce retard. Si vous êtes chargé de projet et que vous n'avez pas l'intention de le lire, sachez que c'est le seul principe que vous devez retenir. C'est universellement prouvé, ça n'a jamais fonctionné.

Ça semble être logique non ? Le fait d'introduire des nouveaux programmeurs au projet se traduira par la formation de ceux-ci, l'augmentation du temps attribué à la communication, aux réunions et à l'échange d'information entre les membres de l'équipe. Et comme le dit le proverbe : Neuf femmes ne font pas un enfant en un mois.

De façon générale, j'ai noté la même chose au niveau de l'entreprise : il y a quelques années, nous étions moitié moins d'employés et nous étions autant productifs que nous le sommes aujourd'hui, parfois même plus. Un projet web qui était évalué à 200 heures doit aujourd'hui en compter entre 300 et 400... C'est bon signe que l'entreprise prenne de l'expansion, mais certaines façons de faire doivent être remises en question.

Outre la loi de Brooks, plusieurs autres principes intéressants sont soulevés dans ce livre :

  • l'effet du deuxième système (dépasser les spécifications exigées)
  • le prototype (ne pas livrer le prototype!)
  • la planification (selon la complexité du projet)
  • le contrôle des versions
  • chirurgien en chef (un décideur)
  • la balle en argent (les techniques miracles - bonus de l'édition 1995)
Même si le livre a été écrit au temps du IBM 360, les principes méritent qu'on leur prêtent attention. L'informatique a évolué mais la gestion de projet n'a pas vraiment changé puisque le facteur humain y joue pour beaucoup.


Tags: Livres

mercredi 13 mai 2009

Fichier en attachement avec Zend_Mail

Publié par Infinite Loop, à 17 h 59 0 commentaire

Avec PHP, envoyer un courriel avec un fichier en attachement est loin d'être un jeu d'enfant. On doit normalement construire le message ligne par ligne, ajouter le mime type, configurer le charset, l'encodage, etc... Et ça, c'est quand ça fonctionne correctement.

L'équipe du Zend Framework nous a encore une fois facilité la tâche en créant Zend_Mail. On crée l'instance en y spécifiant l'encodage désiré. Ensuite, on configure les différentes propriétés et on y ajoute l'attachement avec la fonction createAttachment(). Comme cette fonction est appelée de l'instance, le fichier est automatiquement lié à l'objet $mail référencé et fera parti de l'envoi lors de l'appel de send().

$mail = new Zend_Mail('UTF-8');

// propriétés
$mail->setFrom('email');
$mail->addTo('email');
$mail->setSubject('Le sujet');
$mail->setBodyHtml('Bla bla bla...');

// créer l'attachement
$file = $mail->createAttachment(file_get_contents($_FILES['file_to_upload']['tmp_name']));
$file->type = $_FILES['file_to_upload']['type'];
$file->disposition = Zend_Mime::DISPOSITION_ATTACHMENT;
$file->filename = $_FILES['file_to_upload']['name'];

$mail->send();
Dans l'exemple ci-dessus, le fichier est transféré à partir d'un formulaire web, d'où l'utilisation de l'array PHP $_FILES mais il pourrait tout aussi bien provenir de l'espace de stockage sur le serveur. Quand vous transférez un fichier, n'oubliez pas de spécifier le enctype multipart/form-data comme dans le formulaire suivant:
<form method="post" enctype="multipart/form-data">
<label for="file_to_upload">Fichier en attachement</label>
<input type="file" name="file_to_upload" id="file_to_upload" value="" />
<input type="submit" name="btn" id="btn" value="Envoyer" />
</form>
Avez-vous une solution plus claire à proposer ?


Tags: PHP, Zend Framework

mardi 12 mai 2009

Konami Code sur jQuery et Facebook

Publié par Infinite Loop, à 18 h 07 0 commentaire

Connaissez-vous le Konami Code ? Piqué par la curiosité sur le statut d'un ami sur Facebook, j'ai essayé cette suite de touches, inspirée d'un cheat classique présents dans plusieurs jeux de la marque. Certains sites web ont repris l'idée et y ont caché un oeuf de pâque informatique (easter egg).

Sur Facebook, on peut en tout temps (même en n'étant pas connecté) entrer sur son clavier la suite ci-dessous pour voir apparaître un effet graphique caché.

HAUT HAUT BAS BAS
GAUCHE DROITE GAUCHE DROITE
B A ENTER

Du côté de la programmation, le site de jQuery compte parmi ceux qui ont la fonction cachée. En entrant le code, vous serez redirigé vers la page JavaScript Rock Star pour y jouer Nirvana - Smells Like Teen Spirit - à une version simplifiée de Guitar Hero.

J'ai fouillé dans la source et j'ai trouvé le code nécessaire pour implémenter le Konami Code :
// Hehe.
if ( window.addEventListener ) {
var kkeys = [], konami = "38,38,40,40,37,39,37,39,66,65";
window.addEventListener("keydown", function(e){
kkeys.push( e.keyCode );
if ( kkeys.toString().indexOf( konami ) >= 0 )
window.location = "http://ejohn.org/apps/hero/";
}, true);
}
Enfin, vous pouvez poursuivre votre visite en vous rendant sur un site qui répertorie les endroits compatibles avec le code Konami. Mémoriez bien le code, vous en aurez besoin pour entrer!


Tags: Easter Eggs, JavaScript, Programmation

lundi 11 mai 2009

Star Trek

Publié par Infinite Loop, à 20 h 16 0 commentaire

Vendredi dernier sortait le tout dernier film de Star Trek (que je n'ai pas vu et que je n'ai pas vraiment l'intention de voir). Alors pourquoi ne pas remettre de vieilles publicités informatique au goût du jour, mettant en vedette William Shatner (Captain Kirk) et le Commodore Vic 20.


(cliquez sur les images pour les agrandir)

Et je cite : "The only computer you'll need for years to come". S'il l'a dit, j'imagine que ça doit être vrai. Beam me up in the 80's!


Tags: Le coin du geek, Vieilles publicités

dimanche 10 mai 2009

3 façons de lire du XML en PHP

Publié par Infinite Loop, à 10 h 18 4 commentaires

Ce matin, j'ai fait des recherches sur les façons de lire des fichiers XML en PHP et comme il y a plusieurs possibilités, je voulais en connaître les différences. Mon but était de lire et de traiter un fichier XML volumineux, soit le fichier iTunes de ma collection mp3, sauf que le XML n'était pas tout à fait comme je m'y attendais (c'est un PLIST; ouvrez-le vous allez comprendre pourquoi). J'ai fini par utiliser la classe iTunes XML parser pour faire le travail (elle utilise DomDocument à l'intérieur).

Pour en revenir aux tests que j'avais fait, j'ai quand même vu comment on pouvait les utiliser. J'ai quand même décidé de mettre en ligne mon exemple du même code, dans chacune de ses 3 variantes. Pour simplifier le problème que j'ai rencontré avec iTunes, j'ai cherché et téléchargé une liste quelconque, par exemple un fichier XML contenant plus de 2400 User-Agents (environ 700 kb). Peu importe si l'information est valide ou exacte, c'était suffisant pour tester les capacités des différents modèles.

La structure du fichier ressemble à ceci et mon but est d'extraire les nodes String qui contiennent le user-agent et d'en créer une liste :

<?xml version="1.0"?>
<user-agents>
<user-agent>
<ID>id_moz_150408_3</ID>
<String>Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9b5) Gecko/2008032620 Firefox/3.0b5</String>
<Description>Mozilla Firefox 3.0 beta (Gran Paradiso) for Win</Description>
<Type>B</Type>
<Comment></Comment>
<Link1>http://developer.mozilla.org/en/docs/Firefox_3_for_developers</Link1>
<Link2></Link2>
</user-agent>
...
XMLReader
XMLReader est peut-être le plus compliqué à comprendre. On lit chaque node en boucle. Chacun possède un type (élément, attribut, texte, etc) qui doit être comparé avec les constantes prédéfinies. Ici, je recherche les éléments dont le nom est String.
$reader = new XMLReader();
$reader->open('allagents.xml');

while ($reader->read()) {
if ($reader->nodeType == XMLREADER::ELEMENT){
if ($reader->name == "String"){
$reader->read();
echo $reader->value . "\n";
}
}
}

$reader->close();
DOMDocument
Celui-ci est un peu plus simple, principalement parce qu'on peut utiliser un sélecteur pour obtenir d'un seul coup tous les nodes du document qui portent le nom user-agent. Ensuite, il suffit de boucler sur l'array et d'extraire le contenu du node qui porte le nom String. La fonction getElementsByTagName() retourne dans un array tous les éléments du node user-agent qui portent ce nom. Il n'en contient qu'un seul, c'est pourquoi on doit y faire référence par item(0).
$doc = new DOMDocument();
$doc->load( 'allagents.xml' );

$nodes = $doc->getElementsByTagName( "user-agent" );

foreach($nodes as $ua) {
$string = $ua->getElementsByTagName( "String" );
echo $string->item(0)->nodeValue . "\n";
}
SimpleXML
Encore mieux, SimpleXML rend l'opération plus facile. On peut utiliser xpath pour obtenir les nodes (au lieu de getElementsByTagName) en utilisant une syntaxe correspondant à la structure du fichier. Ensuite, on peut accéder aux propriétés de chaque node de la même façon qu'on le fait avec un objet (flèche).
$xml = new SimpleXMLElement('allagents.xml', Null, True);

// yeah! xpath!
$nodes = $xml->xpath('//user-agents/user-agent');

foreach($nodes as $ua) {
// attention, l'appel à String est case sensitive !
echo $ua->String . "\n";
}
Ceci représente l'essentiel de la lecture XML. Le traitement peut être plus complexe selon le cas mais ça donne quand même un aperçu des alternatives. J'espère que j'ai pu vous aider à démêler tout ça et à choisir la forme la plus appropriée pour vos besoins.


Tags: PHP

Citation no. 29 sur l'infini

Publié par Infinite Loop, à 08 h 31 0 commentaire

Deux choses sont infinies: l'univers et la bêtise humaine; Et en ce qui concerne l'univers, je n'en ai pas acquis la certitude absolue.

- Albert Einstein


Tags: Citations

samedi 9 mai 2009

Déboguer avec FirePHP

Publié par Infinite Loop, à 10 h 22 0 commentaire

En développement web, on déplore souvent le manque d'outils de débogage. Combien de fois avez-vous utilisé une suite de "echo", "print_r" ou "var_dump" pour afficher une trace de vos variables et essayer de comprendre ce qui se passait ? Peut-être avez-vous même programmé une fonction pour faire un log de vos erreurs ? Si c'est le cas, vous apprécirez FirePHP.

Comme c'est le cas avec Zend, certains produits commerciaux sont offerts pour nous aider à traquer et trouver les problèmes, mais on doit y mettre le prix. Avant de sauter aux conclusions et acheter un produit coûteux, jetez un oeil à FirePHP, un outil gratuit qui peut souvent s'avérer suffisant.

Pour en tirer avantage, vous aurez besoin de trois choses :

  • installer l'extension Firebug pour Firefox
  • installer l'extension FirePHP
  • installer la librairie FirePHPCore (aussi disponible par PEAR). Si vous utilisez le Zend Framework, assurez-vous de jeter un oeil à la classe Zend_Log_Writer_Firebug pour une meilleure intégration.
FirePHP est intrinsèquement lié à Firebug et une icône d'insecte bleu s'ajoutera à côté de l'icône originale, mais les deux extensions partageront la même console pour y inscrire les messages d'erreurs.

Pour pouvoir faire afficher du contenu dans la console Firebug à partir de PHP, vous devrez inclure les classes de FirePHPCore à votre projet. Ensuite, il y a deux façons de l'utiliser : un mode procédural ou par l'API orienté objet. Dans mon cas, je préférerai la seconde version.

FirePHP est activé par défaut et un interrupteur permet de le mettre à "off" pour cacher l'information sensible lorsque le projet est en production. Cette configuration peut être conservée et vérifiée dans le bootstrap du projet, ce qui nous permet de garder toutes les lignes de code qui effectuent des traces sans avoir à les retirer, les commenter une à une ou ajouter des conditions d'exécution.
// procédural
// require_once('FirePHP/FirePHPCore/fb.php');

// orienté objet
require_once('FirePHP/FirePHPCore/FirePHP.class.php');

$firephp = FirePHP::getInstance(true);

// important de mettre à false en production!
$firephp->setEnabled(true);
Plusieurs types de messages peuvent être affichés, chacun avec une icône différente pour facilement les repérer dans la console.
$firephp->log('Début du programme');
$firephp->info('La condition est fausse...');
$firephp->warn('Possiblement une erreur');
$firephp->error('Exception : ça a planté!');
Ou mieux, utiliser la trace :
$firephp->trace('You are here!');
Celle-ci a l'avantage de pointer à quel endroit exact (fichier et ligne de code PHP) le message a été déclenché.

On peut aussi grouper les appels sous une étiquette :
$firephp->group('Transaction 1');
$firephp->log('Faire un lock');
$firephp->log('Fonction quelconque');
$firephp->log('Retirer le lock');
$firephp->groupEnd();
Et faire afficher le contenu des variables en utilisant dump() :
$variable = 'Contenu de la variable';
$array = array('x' => 1, 'y' => 2);

$firephp->dump('Variable 1', $variable);
$firephp->dump('Variable 2', $array);
Contrairement aux autres fonctions, celle-ci affichera les valeurs dans l'onglet Net (réseau), à l'intérieur de la requête HTTP, sous l'onglet Server.


Mais rien n'empêche de le faire avec log() pour le placer dans la console :
$firephp->log($variable);
$firephp->log($array);
Si vous comptez faire du développement professionnel, vous vous devez d'avoir au moins un outil de débogage et de l'utiliser efficacement. N'oubliez jamais que vous êtes responsables de votre propre qualité, même si votre entreprise compte des ressources en assurance qualité. D'ailleurs, je ne connais aucun programmeur qui aime se faire remettre sur le nez un bogue qui lui serait passé sous le nez.

À titre d'information, une version 1.0 complètement réécrite est en cours. Ça promet!


Tags: Extensions Firefox, PHP

vendredi 8 mai 2009

99 bouteilles de bière

Publié par Infinite Loop, à 17 h 29 0 commentaire

Depuis 1994, le site 99 bottles of beer recense une collection de programmes informatiques écrits en différents langages qui génèrent les paroles de la chanson traditionnelle du même nom. À l'heure actuelle, 1270 variations sont disponibles.

Comme les paroles sont répétitives, il s'agit essentiellement d'une itération débutant à 99 jusqu'à zéro.

99 bottles of beer on the wall, 99 bottles of beer.
Take one down and pass it around, 98 bottles of beer on the wall.

98 bottles of beer on the wall, 98 bottles of beer.
Take one down and pass it around, 97 bottles of beer on the wall.

..

1 bottle of beer on the wall, 1 bottle of beer.
Take one down and pass it around, no more bottles of beer on the wall.

No more bottles of beer on the wall, no more bottles of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.

Si un langage est manquant ou si vous avez une solution alternative originale, apportez-y votre contribution. C'est sensiblement la même chose que la collection de programmes Hello World, sauf avec un sujet apprécié de tous (il y a quand même 2 fois plus de variations pour celui sur la bière et les exemples sont parfois très drôles).

Bon weekend et buvez avec modération.

Liens connexes :
  • Fiche sur Wikipédia
  • Un screensaver pour Windows
  • La collection de programmes Hello World


Tags: Liens, Programmation

jeudi 7 mai 2009

Étude de cas Zend Solutions

Publié par Infinite Loop, à 19 h 26 0 commentaire

Pour annoncer la sortie du Zend Framework 1.8, j'ai reçu un courriel d'information concernant les nouveautés Zend (liste de diffusion) et j'ai été surpris de lire une étude de cas sur les technologies Zend implantées chez nos amis de Cyberpresse.

Vous pourrez lire l'intégrale de l'article ici (PDF). On y apprend entre autre que leur équipe est composée de 10 développeurs PHP et que l'histoire des frères Kostitsyn (joueurs du Canadiens de Montréal, jusqu'à preuve du contraire), qui auraient des liens avec le crime organisé, a été suivie par 150 000 lecteurs sur une base régulière, d'où la nécessité d'avoir un site performant.

Cyberpresse.ca Site More Productive with Zend Solutions
Media conglomerate Gesca owns daily newspapers in Quebec and Ontario and has over 80 million page views per month on their Montreal Cyberpresse.ca site alone. Cyberpresse uses a 3rd party content management system (CMS) running PHP on Apache as a backend (on Windows Server) that produces rendered pages, as well as another backend system on Windows Server, IIS, and Microsoft SQL Server for text mining, along with a Linux/Apache/MySQL/PHP (LAMP)-based lightweight frontend where they manage sessions and cookie data for all user accounts.

"We have significantly optimized our systems using Zend's web application servers", says Matthieu Delorme, chief technology officer at Gesca Digital. "Root cause analysis ... alone saved us many hours of coding. Code was debugged and much higher quality in half or less the time it used to take." Delorme continues, "Zend's production solution allows us to debug 10x faster compared to anything we had before."


Tags: PHP

mercredi 6 mai 2009

Zend_Service_Twitter

Publié par Infinite Loop, à 19 h 00 0 commentaire

J'ai démontré il y a quelques jours comment on pouvait utiliser CUrl pour poster son statut sur Twitter. Plus tard, lorsque j'ai abordé le sujet du service Amazon implémenté par le Zend Framework, je me suis rendu compte que parmi les services offerts, il y en avait un disponible pour Twitter.

La prochaine fois, plutôt que de faire quelque chose de compliqué (mais fonctionnel) comme je l'ai fait, prenez quelques minutes et voyez comment c'est simple à réaliser avec ZF.

require_once('Zend/Service/Twitter.php');

$twitter = new Zend_Service_Twitter('code18', '** pwd **');
$response = $twitter->account->verifyCredentials();

if ( $response->isSuccess() ) {
// succès
$twitter->status->update(utf8_encode(''));
}
else {
// échec
echo $response->error;
}

$twitter->account->endSession();
Et n'oubliez pas d'appeler la fonction utf8_encode() si vous souhaitez utiliser les accents! Mon prochain essai dans le monde du micro-blogging sera de voir de quelle façon je peux faire la même chose sur Identi.ca (plateforme open source basée sur Laconica).


Tags: PHP, Zend Framework

mardi 5 mai 2009

Devinette mathématique classique

Publié par Infinite Loop, à 18 h 35 3 commentaires

Ce weekend, j'étais au party d'anniversaire d'une amie quand un des invités lui a proposé une suite de devinettes à résoudre pour obtenir le droit d'ouvrir son cadeau. Un des problèmes était d'ordre mathématique et a donné du fil à retordre non seulement à la fêtée mais aussi à tous les convives.

Tout le monde s'est creusé les méninges pendant de longues minutes et personne n'est arrivé à la solution... sauf moi (je crois que j'avais déjà entendu un problème semblable auparavant, alors disons que ça ne comptait pas). Pourtant, ce n'était pas un problème compliqué, juste une question de logique. Après la lecture de la question, j'ai presque su instantanément la réponse, que j'ai validée en secret avec l'organisateur et nous avons laissé les autres chercher (et j'ajouterais - en rigolant un peu). Il semble que ce n'était pas évident pour tout le monde.

Et vous, saurez-vous la résoudre ?

3 personnes se rendent dans un hôtel et louent une chambre au prix de 30$. Chaque personne remet 10$ au commis pour régler la facture. Un peu plus tard, le commis réalise que le prix de la chambre était en fait à 25$.

Il appelle le valet et lui demande d'aller remettre les 5$ excédentaires aux locataires. En route, le valet se demande de quelle façon il partagera les 5$ en trois.

Il décide de rembourser 1$ à chaque personne et il gardera 2$ pour lui. Donc, au lieu de 10$ par personne, chacun aura payé 9$ pour la chambre, pour un total de 27$.

Ajoutons à ces 27$ les 2$ gardés par le valet, cela fait 29$.
Où est le dollar manquant ?
Quelles sont vos explications ?
Avez-vous réussi à trouver la solution sans trop de problème ?


Tags: Le coin du geek

lundi 4 mai 2009

Obtenir l'extension d'un fichier

Publié par Infinite Loop, à 21 h 08 1 commentaire

Dans de nombreux exemples de code, j'ai vu des développeurs PHP (incluant moi-même, pas plus tard que dans mon exemple d'hier) utiliser un petit raccourci classique pour obtenir l'extension d'un fichier :

$file = 'infinite-loop.jpg';
$extension = end(explode('.', $file));
echo $extension; // jpg
La combinaison des appels de end() et explode() permet d'utiliser le point comme séparateur de chaîne et de diviser le nom du fichier en sous-ensemble ("infinite-loop" et "jpg") retourné en tant qu'array qui sera passé en entrée à end() qui à son tour retournera le dernier élément de l'array, soit "jpg".

C'est pratique car on peut utiliser le même explode pour récupérer la première partie, soit "infinite-loop" en utilisant la fonction current() qui retourne le premier élément de l'array. Ça fonctionne bien quand on a seulement le nom du fichier mais imaginez à la place qu'on a l'exemple suivant :
$path = '/home/code18/infinite-loop.jpg';
$filename = current(explode('.', $path));
Ce que contiendrait $filename serait "home/code18/infinite-loop", incluant le chemin complet du fichier. Pour contourner ceci, on doit faire appel à basename() :
$path = '/home/code18/infinite-loop.jpg';
$file = basename($path); // infinite-loop.jpg
$filename = current(explode('.', $file)); // infinite-loop
$ext = end(explode('.', $file)); // jpg
On obtient le nom du fichier, le nom sans extension et son extension mais on n'a pas encore isolé et extrait le répertoire. Un tour de magie :
# /home/code18/
echo str_replace($file, '', $path);

# ou sans se compliquer la vie :

# /home/code18 (sans le / de la fin)
echo dirname($path);
La solution

Pourtant, il y a un moyen très simple de faire tout cela d'un seul coup, avec la fonction built-in de PHP pathinfo() :
$path = 'http://www.google.ca/intl/en_ca/images/logo.gif';
$info = pathinfo($path);

echo $info['dirname']; // http://www.google.ca/intl/en_ca/images
echo $info['basename']; // logo.gif
echo $info['extension']; // gif
echo $info['filename']; // gif


Tags: PHP

dimanche 3 mai 2009

Récupérer une image sur Amazon avec Zend Framework

Publié par Infinite Loop, à 11 h 51 0 commentaire

Récemment, j'avais jeté un coup d'oeil à l'API d'Amazon pour voir de quelle façon je pouvais reproduire un comportement semblable à la fonctionnalité de programmingbooks.org, qui récupère les informations d'un produit recherché et qui copie localement l'image affichée sur Amazon.

Ceci peut être possible grâce à Amazon Web Services qui offre un outil pour faire des requêtes en tout genre sur le contenu. Pour des raisons légales, comme le service nous fournit l'information, on doit en retour faire un lien vers la page du produit.

Pour pouvoir utiliser l'API, on doit d'abord se créer un compte pour obtenir une clé d'accès. Il suffit de s'inscrire, d'accepter les termes et conditions (courtes, lecture recommandée) et d'activer son compte à l'aide du lien d'activation reçu par courriel. Une fois identifié, on obtiendra la clé à utiliser dans notre code dans l'encadré "Your Access Key ID".

Une fois en possession de la clé d'accès, on peut utiliser des librairies pour manipuler les requêtes à Amazon. Ici, Zend Framework nous simplifie beaucoup le travail avec son composant Zend_Service_Amazon.

Après avoir mis à jour ZF pour la plus récente version 1.8.0 (qui contient plusieurs ajouts intéressants), j'ai créé un petit script PHP. On doit inclure le service Amazon et instancier un objet en utilisant la clé d'accès obtenue.

require_once('Zend/Service/Amazon.php');

$service = new Zend_Service_Amazon(ENTREZ_VOTRE_CLE_ICI);
Appelez la méthode itemSearch() et indiquez dans quelle section vous voulez lancer la recherche (Music, Books, etc) et spécifiez les mots clés. Le ResponseGroup indique quels éléments doivent être retournés par Amazon. Pour mes besoins, j'ai mis le strict minimum, soit l'information générique sur le produit. L'important est d'inclure dans la réponse "Images".
$results = $service->itemSearch(
array(
'SearchIndex' => 'Music',
'Keywords' => 'Metallica Master of puppets',
'ResponseGroup' => 'Small,ItemAttributes,Images'
)
);
La variable $results n'est pas un array, c'est un objet Zend_Service_Amazon_ResultSet. Donc pour voir si quelque chose nous est retourné, on doit vérifier totalResults() plutôt que faire un count(). L'objet permet d'être traversé et comme je n'ai besoin que du premier (j'assume que l'image proposée est la bonne), j'appelle current() qui me positionne au premier élément du ResultSet. Les propriétés d'un item seront accédées avec la flèche : DetailPageURL, Title, SmallImage, MediumImage, LargeImage, Subjects, etc.
if( $results->totalResults() > 0 ){
$item = $results->current();

echo $item->Title;
echo '<img src="' . $item->LargeImage->Url . '" />';

// suite plus loin
}
Comme on obtient l'URL de l'image stockée sur les serveurs d'Amazon, on peut utiliser le chemin pour effectuer une capture de l'image avec CURL. Son contenu sera intercepté et stocké dans la variable $output. La propriété LargeImage de l'objet $item contient en réalité un autre objet de type Zend_Service_Amazon_Image, qui lui contient la propriété Url.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $item->LargeImage->Url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
Comme l'URL contient le nom du fichier, je fais un petit tour de passe-passe populaire pour obtenir l'extension, bien qu'il y ait des solutions plus fiables (j'en parlerai plus tard cette semaine). J'enregistrerai le tout dans le répertoire files et j'utiliserai un nom générique pour les fins de ma démonstration.
// save file
$ext = end(explode('.', $item->LargeImage->Url));
$saveAs = 'files/image.' . $ext;
Enfin, j'utilise le contenu récupéré par CUrl pour créer un nouveau fichier sur mon serveur (permissions requises).
$fh = fopen($saveAs, 'w') or die("Impossible d'ouvrir le fichier");
fwrite($fh, $output);
fclose($fh);
En regardant la liste des services pris en charge par le Zend Framework, on voit qu'il offre d'autres classes qui facilitent l'utilisation des API de différents sites, comme Delicious, Flickr, Nirvanix, ReCaptcha, Simpy, SlideShade, StrikeIron, Technorati, Twitter et Yahoo.

Twitter ? Hum, ça semble être une solution beaucoup plus simple et transparente que celle que j'ai présenté il y a 1 mois...


Tags: PHP, Zend Framework

Citation no. 28 sur un jour de travail productif

Publié par Infinite Loop, à 08 h 33 0 commentaire

One of my most productive days was throwing away 1000 lines of code.

- Ken Thompson


Tags: Citations

samedi 2 mai 2009

Modifier l'entête Server d'Apache

Publié par Infinite Loop, à 14 h 58 0 commentaire

Pour des raisons de sécurité, plusieurs administrateurs réseau préfèrent cacher le nom du type de serveur qui héberge les sites afin de ne pas donner d'indice pouvant faciliter une attaque, par exemple sur une version précise d'Apache qui contiendrait une faille de sécurité.

Ainsi, un hacker pourrait simplement regarder les entêtes HTTP pour découvrir le type et la version du serveur. Ceci se retrouve par défaut dans l'entête Server. À titre d'exemple, Cyberpresse retourne ceci (à voir avec l'extension Firefox Live HTTP Headers) :

Server: Apache/2.2.3 (CentOS)

Pour cacher cette information, on peut ajouter dans httpd.conf les deux lignes suivantes :

ServerSignature Off
ServerTokens Prod

Où ServerSignature sera désactivé pour ne pas afficher l'information du serveur sur les pages 404 et les index de répertoires alors que "Prod" est une abréviation de ProductOnly. Le résultat devient le suivant :

Server: Apache

Optionnellement, si on est paranoïaque ou qu'on veut s'amuser à changer le terme "Apache" par autre chose, on peut modifier le code source d'Apache en modifiant les lignes suivantes (version 2) et recompiler. Ces lignes se retrouvent dans include/ap_release.h.

#define AP_SERVER_BASEVENDOR "Apache Software Foundation"
#define AP_SERVER_BASEPROJECT "Apache HTTP Server"
#define AP_SERVER_BASEPRODUCT "Code 18"

#define AP_SERVER_MAJORVERSION_NUMBER 2
#define AP_SERVER_MINORVERSION_NUMBER 2
#define AP_SERVER_PATCHLEVEL_NUMBER 11
#define AP_SERVER_DEVBUILD_BOOLEAN 0

Une alternative connue mais que je n'ai pas testé (en tout cas pas encore) est d'utiliser mod_security et de modifier l'entête Server en utilisant la directive SecServerSignature :

SecServerSignature "Code18 v1.0"

Dans ce cas-ci, ServerTokens devra être initialisé à Full :

ServerTokens Full

Mise à jour 2009-05-03
J'ai pris le temps de tester SecServerSignature ce matin et tout fonctionne tel que décrit.


Tags: Apache

vendredi 1 mai 2009

Contrôler le téléchargement d'un fichier

Publié par Infinite Loop, à 23 h 59 0 commentaire

Dans plusieurs cas, ça peut être intéressant de contrôler le téléchargement d'une ressource hébergée sur le serveur à l'aide d'un fichier PHP, plus particulièrement s'il est stocké en dehors de la zone publique accessible par le serveur web. Par exemple, si une boutique en ligne doit attendre la réception d'un paiement pour rendre disponible l'accès limité à un fichier en téléchargement, on pourra le faire en utilisant le streaming.

Imaginez qu'on reçoive par $_GET un identificateur unique représentant le fichier, qu'une requête SQL vérifie si l'utilisateur connecté (par la session) peut télécharger le document, tout ce qu'il restera à faire sera de diffuser son contenu.

Le fichier pourrait être hébergé dans un répertoire non accessible par le serveur. Seul le code PHP connaîtrait l'emplacement où le récupérer. Le nom sur le disque pourra aussi différer du nom qu'on voudra présenter au visiteur.

Par exemple, vous vendez vos créations musicales en format mp3 :

// download.php
set_time_limit(0);

$filePath = '/chemin/sur/le/serveur/Track-01.mp3';
$newName = 'Artiste-Album-01-Titre.mp3';

header('Content-Type: audio/mpeg');
header('Content-Disposition: attachment; filename="' . $newName . '";');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($filePath) );

@readfile($filePath) or die('Echec...');
  • Le content-disposition "attachment" indique au fureteur qu'il faudra faire afficher la fenêtre "Enregistrer sous" et proposera le nom indiqué dans la variable $newName.
  • La fonction filesize() s'assurera d'initialiser l'entête content-length à la taille réelle du fichier.
  • @readfile or die : on pourrait mieux controler la lecture du contenu du fichier, en vérifiant d'abord s'il existe avec la fonction file_exists().
  • En indiquant set_time_limit(0), on spécifie au serveur qu'il devra diffuser sans interruption. Ceci n'est pas recommandé, il est préférable d'indiquer un temps plus approprié.
Sur la page principale, un lien pourrait pointer vers le script de téléchargement mais il n'y aurait pas de redirection vers download.php car l'entête indique "attachment" pour son contenu.
<a href="download.php?track=1">Télécharger la première pièce</a>
Une autre façon d'utiliser ce script est dans la source d'une balise IMG (évidemment, à condition que le fichier diffusé soit un type compatible). Le fichier download.php récupérerait une image et la diffuserait en tant que source, comme si on avait spécifié <img src="image.png" />. Dans ce cas-ci, on aura qu'à remplacer le content-disposition "attachment" par "inline".
<img src="download.php?file-id=100" />


Tags: PHP

Messages plus récents Messages plus anciens Accueil
S'abonner à : Messages (Atom)
    Suivre @code18 sur Twitter

    Catégories

    • Apache (21)
    • Citations (167)
    • Club Vidéo (24)
    • Coffre à outils (55)
    • CSS (8)
    • Curiosités (117)
    • Design Pattern (2)
    • Drupal (8)
    • Easter Eggs (22)
    • Extensions Firefox (20)
    • GIMP (7)
    • Histoire (21)
    • HTML (32)
    • Humour (57)
    • Intégration (34)
    • iPod (12)
    • JavaScript (110)
    • Jeu de combat (6)
    • Le coin du geek (128)
    • Liens (12)
    • Linux (56)
    • Livres (78)
    • Lois et principes (46)
    • Marché des saveurs (26)
    • Mathématique (18)
    • Mobile (5)
    • Montréal (32)
    • Musique (112)
    • Pancartes et écriteaux (16)
    • Perl (8)
    • Pérou (1)
    • PHP (130)
    • PostgreSQL (44)
    • Programmation (105)
    • Saviez-vous que (55)
    • Sécurité (22)
    • SEO (5)
    • SQL Server (22)
    • Vieilles publicités (6)
    • Virtualisation (8)
    • Voyages (1)
    • Zend Framework (26)

    Divers

    Archives

    • ►  2015 (6)
      • ►  août 2015 (1)
      • ►  juillet 2015 (1)
      • ►  février 2015 (3)
      • ►  janvier 2015 (1)
    • ►  2014 (8)
      • ►  décembre 2014 (1)
      • ►  novembre 2014 (1)
      • ►  octobre 2014 (1)
      • ►  août 2014 (2)
      • ►  juillet 2014 (2)
      • ►  janvier 2014 (1)
    • ►  2013 (53)
      • ►  décembre 2013 (2)
      • ►  novembre 2013 (1)
      • ►  octobre 2013 (3)
      • ►  septembre 2013 (2)
      • ►  août 2013 (5)
      • ►  juillet 2013 (3)
      • ►  juin 2013 (5)
      • ►  mai 2013 (3)
      • ►  avril 2013 (7)
      • ►  mars 2013 (7)
      • ►  février 2013 (11)
      • ►  janvier 2013 (4)
    • ►  2012 (105)
      • ►  décembre 2012 (8)
      • ►  novembre 2012 (5)
      • ►  octobre 2012 (4)
      • ►  septembre 2012 (1)
      • ►  août 2012 (8)
      • ►  juillet 2012 (7)
      • ►  juin 2012 (7)
      • ►  mai 2012 (10)
      • ►  avril 2012 (13)
      • ►  mars 2012 (15)
      • ►  février 2012 (15)
      • ►  janvier 2012 (12)
    • ►  2011 (146)
      • ►  décembre 2011 (14)
      • ►  novembre 2011 (11)
      • ►  octobre 2011 (12)
      • ►  septembre 2011 (13)
      • ►  août 2011 (15)
      • ►  juillet 2011 (17)
      • ►  juin 2011 (18)
      • ►  mai 2011 (15)
      • ►  avril 2011 (9)
      • ►  mars 2011 (7)
      • ►  février 2011 (3)
      • ►  janvier 2011 (12)
    • ►  2010 (398)
      • ►  décembre 2010 (29)
      • ►  novembre 2010 (28)
      • ►  octobre 2010 (32)
      • ►  septembre 2010 (34)
      • ►  août 2010 (22)
      • ►  juillet 2010 (35)
      • ►  juin 2010 (42)
      • ►  mai 2010 (36)
      • ►  avril 2010 (37)
      • ►  mars 2010 (34)
      • ►  février 2010 (32)
      • ►  janvier 2010 (37)
    • ▼  2009 (429)
      • ►  décembre 2009 (32)
      • ►  novembre 2009 (34)
      • ►  octobre 2009 (33)
      • ►  septembre 2009 (37)
      • ►  août 2009 (37)
      • ►  juillet 2009 (39)
      • ►  juin 2009 (38)
      • ▼  mai 2009 (37)
        • Obtenir le Page Rank Google en PHP ou Perl
        • Citation no. 32 sur Windows
        • Google Talk chatback badge
        • Images volantes en JavaScript
        • ASP vs PHP
        • Intervalles de dates sous PostgreSQL et SQL Server
        • Ordre de chargement avec Prototype
        • Pouvez-vous lire ceci ?
        • Service ReCaptcha en français
        • Citation no. 31 sur la lune
        • Google killers
        • Configurer Zend_Tool en CLI
        • Quelques logos intéressants
        • Créer un WSDL facilement avec Zend Framework
        • XSS sur Wikio... et Archambault Musique
        • Encryption de données en PHP
        • Virtualiser les fureteurs avec Xenocode
        • Traductions pour Uploadify
        • Citation no. 30 sur la force
        • ASCII Art avec GIMP
        • Fonction JavaScript à paramètres variables
        • The Mythical Man-Month - Loi de Brooks
        • Fichier en attachement avec Zend_Mail
        • Konami Code sur jQuery et Facebook
        • Star Trek
        • 3 façons de lire du XML en PHP
        • Citation no. 29 sur l'infini
        • Déboguer avec FirePHP
        • 99 bouteilles de bière
        • Étude de cas Zend Solutions
        • Zend_Service_Twitter
        • Devinette mathématique classique
        • Obtenir l'extension d'un fichier
        • Récupérer une image sur Amazon avec Zend Framework
        • Citation no. 28 sur un jour de travail productif
        • Modifier l'entête Server d'Apache
        • Contrôler le téléchargement d'un fichier
      • ►  avril 2009 (35)
      • ►  mars 2009 (36)
      • ►  février 2009 (32)
      • ►  janvier 2009 (39)
    • ►  2008 (84)
      • ►  décembre 2008 (34)
      • ►  novembre 2008 (39)
      • ►  octobre 2008 (11)

    Abonnés

Copyright © All Rights Reserved. Code 18 | Converted into Blogger Templates by Theme Craft