juin 26 2008

PHP5 et type hinting

Tag: phpMickaël Desfrênes @ 5:11

PHP permet le “type hinting” mais uniquement pour les objets et les tableaux. Voici une bouine trouvée sur php.net pour ajouter à cette fonctionnalité les types scalaires, en attendant une hypothétique modification de PHP en ce sens :

< ?php

define('TYPEHINT_PCRE','/^Argument (\d)+ passed to (?:(\w+)::)?(\w+)\(\) must be an instance of (\w+), (\w+) given/');

class Typehint
{
    private static $Typehints = array(
        'boolean'   => ‘is_bool’,
        ‘integer’   => ‘is_int’,
        ‘float’     => ‘is_float’,
        ’string’    => ‘is_string’,
        ‘resource’  => ‘is_resource’
    );

    public static function initializeHandler()
    {

        set_error_handler(’Typehint::handleTypehint’);

        return TRUE;
    }

    private static function getTypehintedArgument($ThBackTrace, $ThFunction, $ThArgIndex, &$ThArgValue)
    {

        foreach ($ThBackTrace as $ThTrace)
        {
            if (isset($ThTrace[’function’]) && $ThTrace[’function’] == $ThFunction)
            {

                $ThArgValue = $ThTrace[’args’][$ThArgIndex - 1];

                return TRUE;
            }
        }

        return FALSE;
    }

    public static function handleTypehint($ErrLevel, $ErrMessage)
    {
        if ($ErrLevel == E_RECOVERABLE_ERROR)
        {
            if (preg_match(TYPEHINT_PCRE, $ErrMessage, $ErrMatches))
            {
                list($ErrMatch, $ThArgIndex, $ThClass, $ThFunction, $ThHint, $ThType) = $ErrMatches;
                if (isset(self::$Typehints[$ThHint]))
                {

                    $ThBacktrace = debug_backtrace();
                    $ThArgValue  = NULL;

                    if (self::getTypehintedArgument($ThBacktrace, $ThFunction, $ThArgIndex, $ThArgValue))
                    {
                        if (call_user_func(self::$Typehints[$ThHint], $ThArgValue))
                        {
                            return TRUE;
                        }
                    }
                }
            }
        }

        return FALSE;
    }
}

Typehint::initializeHandler();

function testInt(integer $int)
{
	return $int;
}

function testString(string $string)
{
	return $string;
}

testInt(5454);

testString(”);

juin 26 2008

Consommez bien, on s’occupe de tout.

Tag: UncategorizedMickaël Desfrênes @ 12:42

Je suis content de savoir que le gouvernement veille sur nous. Je suis vraiment rassuré. Il s’agit bien sur de “protéger les utilisateurs d’Internet”, alors forcément ça ne peut pas être mal.

Je pense qu’il est temps de revoir certains classiques


mai 28 2008

FirePHP

Tag: UncategorizedMickaël Desfrênes @ 10:00

Une extension Firefox dénichée par l’ami Nico, FirePHP permet d’afficher des debugs directement dans la console Firebug, depuis un script PHP, tel qu’on le ferait en Javascript avec console.log(), console.dir(), etc.

La partie serveur trouvera naturellement sa place dans /usr/share/php.

Utilisation:

< ?php
ob_start();
require('FirePHPCore/fb.php');

fb('Log message'  ,FirePHP::LOG);
fb('Info message' ,FirePHP::INFO);
fb('Warn message' ,FirePHP::WARN);
fb('Error message',FirePHP::ERROR);

fb('Message with label','Label',FirePHP::LOG);

fb(array('key1'=>'val1',
'key2'=>array(array('v1','v2'),'v3')),
'TestArray',FirePHP::LOG);

function test($Arg1) {
throw new Exception('Test Exception');
}
try {
test(array('Hello'=>'World'));
} catch(Exception $e) {
/* FirePHP peut aussi afficher une exception avec la stack trace */
fb($e);
}

/* Will show only in "Server" tab for the request */
fb(apache_request_headers(),
'RequestHeaders',FirePHP::DUMP);

echo('pouet');

Résultat:


mai 24 2008

Partenariat Zend et Dojo

Tag: ajax, javascript, php, zendMickaël Desfrênes @ 5:13

Je l’imagine comme une conséquence du partenariat entre Zend et IBM: le Zend Framework sera livré avec Dojo et mettra l’accent sur l’échange de données en JSON.
J’aurai préféré de loin JQuery (visiblement je ne suis pas le seul !) mais il faut croire que Dojo se prête mieux à l’écriture d’helpers générant du code javascript cradingue.

Bon… je m’emporte. Ce sera peut-être pas si mal finalement. Wait and see.


mai 12 2008

Changement de dédiboite

Tag: dediboxMickaël Desfrênes @ 8:54

En service depuis 2006, je viens de changer ma dedibox v1 contre une dedibox v2 flambant neuve. Et toujours sous Debian Etch bien sur. :-)


mai 08 2008

Ajax sous Django : Python et Taconite

Tag: ajax, django, jquery, pythonMickaël Desfrênes @ 4:07

Parce que je vais en avoir besoin pour faire de l’Ajax sous Django (en association avec http_response), j’ai récris en Python ma classe PHP servant à construire les “command documents”, soit les fichiers XML contenant les modification du DOM à apporter par le plugin Taconite de JQuery, en me basant sur minidom que l’on pourrait présenter comme étant l’équivalent de simpleXML en PHP.

Un peu d’indulgence, pour l’instant je suis encore un touriste dans le monde de Python (conseils bienvenus toutefois):

# usage:
#
# t = Taconite()
#
# t.append("#toto","“)
# t.remove(”#tutu”)
# t.js(’alert(”hello world”);’)
# t.toggleClass(’blue’,'body’)
# t.css(”body”,”background-color”,”white”)
# […]
# print t.toprettyxml()

import xml.dom.minidom as dom

class Taconite(dom.Document):
  def __init__(self):
    dom.Document.__init__(self)
    taconite = self.createElement(”taconite”)
    self.appendChild(taconite)

  def __str__(self):
    return self.toxml(encoding=”utf-8″)

  def camelizeCssProperty(self,property):
    words = property.split(”-”)
    camelized = words[0].lower()
    for word in words[1:] :
      camelized = camelized + word[0].upper() + word[1:]
    return camelized

  def js(self,script):
    command = self.createElement(”eval”)
    js = self.createTextNode(script)
    command.appendChild(js)
    self.childNodes[0].appendChild(command)

  def changeContentCommand(self,method,selector,content):
    html_dom = dom.parseString(content)
    command = self.createElement(method)
    command.setAttribute(”select”,selector)
    command.appendChild(html_dom.childNodes[0])
    self.childNodes[0].appendChild(command)

  def changeStateCommand(self,action,selector):
    command = self.createElement(action)
    command.setAttribute(”select”,selector)
    self.childNodes[0].appendChild(command)

  def CssCommand(self,action,css_class,selector):
    command1 = self.createElement(action)
    command1.setAttribute(”select”,selector)
    command1.setAttribute(”arg1″,css_class)
    command2 = self.createElement(action)
    command2.setAttribute(”select”,selector)
    command2.setAttribute(”value”,css_class)
    self.childNodes[0].appendChild(command1)
    self.childNodes[0].appendChild(command2)

  def addClass(self,css_class,selector):
    self.CssCommand(”addClass”,css_class,selector)

  def removeClass(self,css_class,selector):
    self.CssCommand(”remove”,css_class,selector)

  def toggleClass(self,css_class,selector):
    self.CssCommand(”toggleClass”,css_class,selector)

  def append(self,selector,content):
    self.changeContentCommand(”append”,selector,content)

  def prepend(self,selector,content):
    self.changeContentCommand(”prepend”,selector,content)

  def before(self,selector,content):
    self.changeContentCommand(”before”,selector,content)

  def after(self,selector,content):
    self.changeContentCommand(”after”,selector,content)

  def wrap(self,selector,content):
    self.changeContentCommand(”wrap”,selector,content)

  def replace(self,selector,content):
    self.changeContentCommand(”replace”,selector,content)

  def replaceContent(self,selector,content):
    self.changeContentCommand(”replaceContent”,selector,content)

  def remove(self,selector):
    self.changeStateCommand(”remove”,selector)

  def show(self,selector):
    self.changeStateCommand(”show”,selector)

  def hide(self,selector):
    self.changeStateCommand(”hide”,selector)

  def removeContent(self,selector):
    self.changeStateCommand(”empty”,selector)

  def css(self,selector,property,value):
    command = self.createElement(”css”)
    command.setAttribute(”select”,selector)
    command.setAttribute(”name”,self.camelizeCssProperty(property))
    command.setAttribute(”value”,value)
    self.childNodes[0].appendChild(command)

Quand j’aurai un peu de temps je m’occuperai des commentaires au format pydoc. En attendant, JQuery + Django… ça devrait faire des étincelles.


mai 06 2008

Python toujours…

Tag: django, pythonMickaël Desfrênes @ 12:40

Plus je creuse le sujet, plus je trouve Python et son écosystème très intéressant.

Le langage d’abord, souvent décrit comme concis et élégant est effectivement agréable, lisible et plein d’astuce sans jamais être obscure. Un programme Python est toujours bien présenté… d’ailleurs un programme Python mal indenté est un programme qui ne fonctionne pas !
Les possibilités du langage, objet de pied en cape, sont aussi énormes.
Ensuite, un peu à l’image de PHP, Python est fourni en standard avec un nombre impressionnant de modules si bien qu’il est inutile de courir après telle ou telle bibliothèque pour commencer à l’utiliser pleinement sur des cas réels, que ce soit en environnement web ou desktop.

L’écosystème ensuite, qui visiblement se porte bien: les frameworks de haut niveau pour le web ne manquent pas et c’est Python qui a été retenu par Google pour sa plateforme d’hébergement d’applications webs (comme à beaucoup d’autres endroits d’ailleurs). La communauté de développeurs, si elle n’est pas aussi importante que d’autres langages, semble toutefois très active, plus compétente, passionnée et très pédagogue !

Évidemment mon intérêt pour Python tient pour beaucoup à Django mais il est vrai aussi que sur le papier Rails offre les mêmes avantages, et pourtant je n’avais pas accroché… la faute à Ruby sans doute, ou peut-être à la documentation de l’époque.

Bref, je vais mettre de coté un moment Java-le-pas-très-fun (mais j’y reviendrai, soif de savoir et Jython oblige) pour me consacrer beaucoup plus à Python (et Django). Pour m’aider à intégrer ce langage dans une démarche méthodologique globale, j’ai trouvé un très bon livre chez Dunod, qui viendra compléter mon achat précédent :

Python, petit guide à l'usage du développeur agile

Écrit par Tarek Ziadé, évidemment spécialiste de la question, ce petit livre au ton parfois décalé passe très vite sur la syntaxe de Python pour faire la part belle aux méthodologies de développement agile avec ce langage : design patterns, tests unitaires, tests fonctionnels, développement dirigé par la documentation, gestion de projet, etc…

Ce livre traite de l’agilité appliquée à Python. Il est le fruit de dix ans de pratique.

De fait, on jurerait que Python a été taillé pour l’agilité :-)


mar 28 2008

Livre de chevet (ter) : Snakes

Tag: django, pythonMickaël Desfrênes @ 9:15

Python, guide de survie

Et un livre de plus… je suis tombé sur ce petit condensé et c’est vraiment un bonheur quand, comme moi, on débute en Python. On y trouve des exemples de codes pour réaliser des tâches courantes, le tout est complètement orienté sur la pratique.

Pour ce qui est de Python, si vous ne connaissez pas, sachez qu’il s’agit d’un langage généraliste interprété totalement objet, à la fois très utilisé et relativement méconnu. On peut réaliser tout type d’application en Python, que ce soit desktop, console ou web. Justement, en ce qui concerne le web, je reviendrai bientôt vous parler de Django, un excellent framework écrit en Python qui permet de développer vraiment très vite, ce dernier étant souvent comparé à Ruby on rails.


mar 02 2008

SSHFS

Tag: linux, sshMickaël Desfrênes @ 6:15

Besoin d’éditer des fichiers, déplacer des répertoires sur un serveur distant équipé de sshd et votre éditeur ne supporte pas le sftp ? Aucun problème, avec sshfs vous pouvez monter un répertoire distant dans votre système de fichier.

Version courte:

mkdir ~/sshfs_mount
sshfs user@server:directory ~/sshfs_mount

Version longue


fév 28 2008

Un peu d’ordre !

Tag: phpMickaël Desfrênes @ 11:30

Si comme moi vous avez une grosse collection de disques que vous encodez pour votre baladeur et/ou que vous achetez beaucoup de mp3s, le rangement de ces fichiers doit être un cauchemar… afin d’y remédier je me suis fait un petit script “quick and dirty” mais qui “fait le job” comme disent les anglophones. Basé sur la librairie getID3 qui’il vous faudra évidemment ajouter à votre include_path:

#!/usr/bin/php5
< ?php
// destination !
define('SND_PATH','/media/usb0/mytunes/');
// source !
define('IMPORT_PATH','/home/me/bazar/');

mb_internal_encoding('UTF-8');
ini_set('default_charset','utf-8');
require('getid3/getid3.php');
$id3 = new getID3;

function walk_dir( $root, $callback, $recursive = true )
{
    $root = realpath($root);
    $dh = @opendir( $root );
    if( false === $dh )
    {
        return false;
    }
    while(false !==  ($file = readdir($dh)))
    {
        if( "." == $file || ".." == $file )
        {
            continue;
        }
        call_user_func( $callback, "{$root}/{$file}" );
        if( false !== $recursive && is_dir( "{$root}/{$file}" ))
        {
            walk_dir( "{$root}/{$file}", $callback, $recursive );
        }
    }
    closedir($dh);
    return true;
}

function store_mp3($file)
{
	global $id3;
        // standards anyone ?
	$mimes = array(
		'audio/mpeg',
		'audio/x-mpeg',
		'audio/mp3',
		'audio/x-mp3',
		'audio/mpeg3',
		'audio/x-mpeg3',
		'audio/mpg',
		'audio/x-mpg',
		'audio/x-mpegaudio',
		'application/ogg',
		'application/x-ogg'
		);
	$mimetype = mime_content_type($file);
	if(in_array($mimetype,$mimes))
	{
		$infos = $id3->analyze($file);
		getid3_lib::CopyTagsToComments($infos);
		$artist = getLabels($infos['comments']['artist'][0]);
		$album = getLabels($infos['comments']['album'][0]);
		$title = getLabels($infos['comments']['title'][0]);
		if($artist == 'unkown' or $title == 'unknown')
		{
			echo("\nNot importing '$file', not enough infos.");
			return false;
		}
		$new_path = SND_PATH.$artist{0}.'/'.$artist.'/'.$album.'/'.$title.'.mp3';
		// don't overwrite existing files
		if(is_file($new_path))
		{
			if(sha1_file($new_path) == sha1_file($file))
			{
				echo("\nNot importing '$file', file already exists.");
				unlink($file);
				return false;
			}
			else
			{
				$i = 1;
				while(is_file($new_path))
				{
					$new_path = SND_PATH.$artist{0}.'/'.$artist.'/'.$album.'/'.$title.'_'.$i.'.mp3';
					$i++;
				}
			}
		}
		if(!is_dir(dirname($new_path)))
		{
			mkdir(dirname($new_path),0755,true);
			usleep(50000);
		}
		echo("\nImporting '$file'.");

		if(copy($file,$new_path))
		{
			unlink($file);
			return true;
		}
		return false;
	}
}

function getLabels($label)
{
	$label = trim($label);
	if(empty($label))
	{
		return 'unknown';
	}
	$encoding = mb_detect_encoding($label,array('ISO-8859-1','UTF-8'),true);
	if($encoding == 'ISO-8859-1')
	{
		$label = utf8_encode($label);
	}
	return ereg_replace("[^A-Za-z0-9_-]","_",utf2ascii($label));
}

function utf2ascii($utf8_string)
{
	$ascii_string= strtr(utf8_decode($utf8_string),utf8_decode("ÀÁÂÃÄÅàáâãäåÒÓÔÕÖØòóôõöøÈÉÊËèéêëÇçÌÍÎÏìíîïÙÚÛÜùúûüÿÑñ"), "AAAAAAaaaaaaOOOOOOooooooEEEEeeeeCcIIIIiiiiUUUUuuuuyNn");
	return $ascii_string;
}

walk_dir(IMPORT_PATH,'store_mp3');
system('mpc update');
echo("\n");

Page suivante »