To content | To menu | To search

Friday 13 October 2006

Polymorphisme et PHP

Ou presque ! Je vois déjà vos têtes ahuries devant un tel titre, et pourtant je ne suis pas fou non, j'ai juste décidé d'avoir un titre qui ment par ommission :-) Bref, pour ceux qui ne savent pas ce qu'est le polymorphisme, c'est tout simplement le fait d'avoir plusieurs fonctions qui portent le même nom, mais dont les arguments différent, et pour ceux qui ne comprennet pas pourquoi Polymorphisme et PHP est un drole de titre, et bien sachez que le PHP est un langage non typé (ou faiblement typé selon la personne a qui vous parlez), et qu'il n'est donc pas possible de faire usage du Type Hinting dans la définition d'une fonction (pas sur les types de base en tout cas, mais PHP5.x introduit le type hinting sur les objets, ce qui ne change rien a notre histoire), ce qui fait que PHP ne peut pas gérer le polymorphisme brut.

Continue reading...

Sunday 1 October 2006

Edgy Eft, Dotdeb, php5-pdo-mysql, et moi

Bon voilà, ce n'est un secret pour personne, j'ai mis à jour ma Dapper en Edgy, et après avoir laborieusement activé l'accélération 3D de ma carte graphique, je me suis attelé à la reconstitution de mon environnement de développement favori: LAMP5. Cet enrivonnement comprend de plus l'extension PDO Mysql, très utile dans le cadre de l'utilisation du Zend Framework :-) Seulement voilà, Edgy propose un paquet php5 plus à jour que celui de dotdeb, ce qui empêche l'installation du paquet php5-pdo-mysql de dotdeb. Ayant mieux à faire qu'installer pdo_mysql via pear, j'ai décidé d'apprendre à me servir un peu d'APT, et je me propose de vous faire part de mes conclusions. Commençons par le commencement, le message d'erreur:

Les paquets suivants contiennent des dépendances non satisfaites :
  php5-pdo-mysql: Dépend: phpapi-20041225
                  Dépend: php5-common (= 5.1.6-0.dotdeb.2) mais 5.1.6-1ubuntu1 devra être installé
E: Paquets défectueux

Il suffit donc de spécifier à APT que nous souhaitons utiliser la version 5.1.6-0.dotdeb.2 du paquet php5-common. Rien de plus simple ! Cela se passe dans /etc/apt/preferences:

Package: php5-common
Pin: version 5.1.6-0.dotdeb.2
Pin-Priority: 1001

Et comme libapache2-mod-php5 dépend également de php5-common, il va nous falloir faire de même:

Package: libapache2-mod-php5
Pin: version 5.1.6-0.dotdeb.2
Pin-Priority: 1001

Voilà, désormais APT n'installera que la version 5.1.6-0.dotdeb.2 de ces deux paquets, quoiqu'il arrive. Un simple apt-get install php5-pdo-mysql suffit désormais pour que tout rentre dans l'ordre !

A noter: le tutoriel qui m'a tout appris.

Wednesday 30 August 2006

Liens du jour

Liens en vrac:

  • MySQL DBA, le blog d'un DBA de chez flickr.
  • Serving Javascript Fast, par un développeur de chez flickr (décidément...) A noter que cet article parle des méthodes de cache d'une manière assez généraliste pour être appliquée à autre chose que du JavaScript, et n'oubliez pas non plus de lire les commentaires, il y en a de très interressants.
  • AJAX & Protoype: A Primer, explication simple et claire sur l'utilisation d'AJAX et des Classes avec Prototype.
  • Akra's Devnotes: Zend Framework, la catégorie Zend Framework du blog de Rob Allen, très interressante.

Automatisation des vues

Le rebutement principal que j'ai eu au début avec le Zend Framework était l'impossibilité (a priori) d'automatiser le rendu des vues en fonction du controlleur et de l'action appelés. Mes pérénigrations webesques m'ont finalement fait entrevoir la solution.

EDIT: mes nouvelles pérégrinations dans les sources du Zend Framework m'ont fait entrevoir une meilleure solution :-)

Tout se passe dans le destructeur du controlleur, et nous allons voir ici une version édulcorée de celle disponible dans le lien sus-cité. L'astuce qui sauve, c'est de savoir que l'objet Controller possède un membre _action, qui lui même propose (entre autres) deux méthodes bien utiles: getControllerName et getActionName. Il suffit donc de récupérer ces informations pour construire dynamiquement le chemin de la vue à utiliser pour un controlleur donné:

abstract class My_Controller_Action extends Zend_Controller_Action {
	public function __destruct() {
		$view = Zend::registry('view');
		$controller = $this->_action->getControllerName();
		$action = $this->_action->getActionName();
		$viewPath = sprintf('%s/%s.php', $controller, $action);
		try {
			echo $view->render($viewPath);
		} catch (Exception $e) {
			trigger_error('Unable to render view: ' . $e->getMessage(), E_USER_ERROR);
		}
	}
}

Fichier source

Ce controlleur requiert que vous ayez instancié, configuré et enregistré Zend_View au préalable. Il ira, lors de sa destruction, chercher la vue dont le nom correspond à controller/action.php, c'est à dire:

  • pour une URL du type http://example.com/foo/bar/, foo/bar.php,
  • pour http://example.com/, index/index.php,
  • pour http://example.com/foo/, foo/index.php.

Pour une raison obscure, on ne peut pas laisser trainer l'éventuelle exception générée par Zend_View dans le cas d'une vue inexistante. Cela entre en conflit avec une autre Exception qui traine, j'essayerai de tirer ça au clair :-)

Saturday 12 August 2006

Zend Framework et les sessions

Bon voilà, j'ai commencé le développement d'un projet, et j'ai décidé d'utiliser le Zend Framework (pour des raisons que je détaillerais dans un autre billet si ça vous interresse). Après mise en application du tutoriel de Chris, Un premier problème s'est posé à moi: l'intégration des sessions. Après quelques recherches, on peut trouver un blog développé avec ZF. Me ruant sur la partie d'admin, je découvre a ma grande stupeur des session_start() incrustés a chaque méthode du controlleur, un peu comme ça:

class AdminController extends Zend_Controller_Action {
	function indexAction() {
		session_start();
	}
	function postAction() {
		session_start();
	}
}

Ce n'est évidemment pas une solution acceptable. J'ai opté pour ma part pour une surcharge de la classe Zend_Controller_Action, avec un appel à session_start() dans le constructeur:

abstract class My_Controller_Action extends Zend_Controller_Action {
	public function __construct() {
		session_start();
	}
}

class AdminController extends My_Controller_Action {
	function indexAction() {
		// code
	}
}

Bon là, la problèmatique de base est déjà résolue, mais on peut aller plus loin, et ajouter un destructeur:

abstract class My_Controller_Action extends Zend_Controller_Action {
	public function __construct() {
		session_start();
	}

	public function __destruct() {
		$session_id = session_id();
		if (!empty($session_id)) {
			session_write_close();
		}
	}
}

Voilà c'est tout pour aujourd'hui, la prochaine fois, on parlera de l'utilisation des Views, et nottament, comment éviter un appel rébarbatif à Zend::registry() dans chaque controlleur.

Tuesday 25 July 2006

Nouveau projet en branle

Bon voilà, n'écoutant que ma fébrile excitation, j'ai refais a neuf mon laptop hier avec une Dapper Drake flambant neuve (oui moi aussi je commande des CDs sur shipit), et je me suis empressé d'y installer un environnement de développement digne de ce nom (bon tout dépend des religions hein, chez moi ça veut dire LAMP5 + Zend Framework). J'ai déjà commencé à reproduire le tutoriel de Chris Shifflet pour me remettre dans le bain (pas bien compliqué vu comme c'est génial ce framework).

Je me lance donc dans un projet qui devrait comporter des technologies bien web 2.0, comme google maps, youtube et script.aculo.us. Pas la peine d'en demander plus je ne dirais rien (et au pire je ferme les commentaires).

C'est un projet qui n'ira peut être pas très loin, ou bien peut être que si, mais c'est avant tout un projet pour apprendre de nouvelles choses.

Tuesday 13 June 2006

PEAR::Net_FTP utilise ftp_nb_put

Oui bon, ça vous fait une belle jambe. Alors laissez moi vous expliquer. ftp_nb_put est la version non bloquante (non blocking) de ftp_put, ce qui veut dire qu'une fois balancé le $ftp->put(), le programme continue, que le transfert soit fini ou non. Je vous laisse imaginer le genre de problème que ça peut engendrer quand on pense utiliser des sockets bloquants alors que non.

EDIT: J'oublie de préciser que ce n'est pas configurable, et que donc, ça fait bien chier.

Sunday 11 June 2006

XAMPP c'est bien...

...mais c'est quand même légèrement too much. Quand on cherche juste un package pour installer une plate-forme AMP rapidement, je déconseille XAMPP. Maintenant si vous voulez un AMP plus un ftpd, plus un serveur de mail, avec des stats, etc, alors oui, pourquoi pas.

Wednesday 7 June 2006

str_multicut, découper une chaine de caractères

/**
 * Cuts a string according to defined breakpoints
 *
 * @param string $string The string to cut
 * @param int $breakpoint,... Breakpoints
 * @return array
 *
 * - Cut the given string according to given breakpoints
 * and return a array with each part. If the last breakpoint
 * does not go to the end of the string, the remaining
 * string part is appended to the result array.
 * - str_multicut will stop cutting on (whichever comes first):
 * end of string, no more breakpoints
 * - If there is no breakpoint, str_multicut will return
 * an empty array
 *
 * - example: str_multicut('foobar', 3, 2) will return:
 * array('foo', 'ba', 'r');
 */
 
function str_multicut($string) {
        $args = func_get_args();
        $string = array_shift($args);
        $result = array();
        while (strlen($string) > 0 && !empty($args)) {
                $breakpoint = array_shift($args);
                $result[] = substr($string, 0, $breakpoint);
                $string	= substr($string, $breakpoint);
        }
        return $result;
}

Monday 29 May 2006

php_sapi_name, pour des includes dynamiques ?

Tout a l'heure je me suis dis que j'aurai bien voulu faire un include en fonction du SAPI dans lequel un script s'execute. Comme PHP est (presque) bien fait, il existe la fonction php_sapi_name qui retourne le nom du SAPI courant. Ce qui nous permet de faire quelque chose comme:

$sapi_include = dirname(__FILE__) . '/sapi/'.php_sapi_name().'.php';
$sapi_default = dirname(__FILE__) . '/sapi/default.php';
 
if (file_exists($sapi_include)) {
	require_once $sapi_include;
} else if (file_exists($sapi_default)) {
	require_once $sapi_default;
}

C'est pas très joli, mais ça fonctionne. Le gros avantage, c'est qu'on a des includes spécifiques au SAPI, et donc on aura par exemple, dans sapi/apache2handler.php:

header('Content-Type: text/html; charset=ISO-8859-15');
require_once 'libraries/Template.php';
session_start();

et dans sapi/cli.php:

Var_Dump::displayInit(array('display_mode' => 'text'));

Le problème, résolu par l'utilisation de sapi/default.php, était que faire un fichier par SAPI possible serait fastidieux (il faut connaitre le nom de tous les SAPI disponibles (bon ok c'est disponible ici mais cette liste n'a rien d'officiel) puis faire un fichier pour chaque.

Maintenant la question que je me (et vous) pose, c'est: y'a-t'il des effets secondaires que je n'aurai pas encore remarqué ?

Tuesday 23 May 2006

Surligner des mots sans se fouler

/**
 * Hightlight words in a string
 *
 * @param string $haystack The string to highlight words in
 * @param array $needles The array containing the words to highlight
 * @param string $format The format to use in preg_replace
 *
 * highlight_words will return $haystack unmodified if $needles is empty
 * or if $format is invalid (eg: there is no "\1", which would cause the
 * preg_replace call to fail)
 */
 
function highlight_words($haystack, $needles, $format = '<strong class="highlight">\1</strong>') {
	if (empty($needles) || (strstr($format, '\1') == false)) {
		return $haystack;
	}
	return preg_replace('/('.implode('|', $needles).')/i', $format, $haystack);
}

(Oui, ça aurait pu être aussi Poster sur son blog sans se fouler le cerveau)

EDIT: la voila la phpdoc :p

Thursday 11 May 2006

Limite de mémoire en PHP

Problème: ini_get('memory_limit') renvoit un truc du genre 8M, or memory_get_usage() renvoit un entier (en octets), rendant toute comparaison impossible.

Solution:

function memory_get_limit() {
	$shorthands = array('K' => 1024, 'M' => 1048576);
	$memory_limit = ini_get('memory_limit');
	$parts = array();
	if (is_numeric($memory_limit)) {
		return $memory_limit;
	} elseif (preg_match('/(\d+)(K|M)/', $memory_limit, $parts)) {
		if (isset($shorthands[$parts[2]])) {
			return $parts[1] * $shorthands[$parts[2]];
		}
	}
	return false;
}

Tuesday 14 March 2006

Zend Framework: le futur de PHP ?

On parle beaucoup de Ruby On Rails en ce moment, mais au final, qu'est-ce vraiment ? RoR n'est qu'une implémentation d'un (enfin de plusieurs mais on va résumer grossièrement) Design Pattern appelé MVC: Model View Controller. Je ne m'étendrais pas sur la théorie décrivant le design MVC, mais voyons plutot pourquoi RoR est si populaire en ce moment:

  1. il implémente MVC d'une manière simple et accessible
  2. il inclut en "builtin" des fonctionnalités AJAX (quoi que ça puisse vouloir dire :p)
  3. il implémente ActiveRecord (un modèle d'accès aux sgbdr qui "rox")
  4. il est implémenté en ruby, qui est un "langage objet"

(nb: les puristes m'excuseront les raccourcis et les abus de langages merci (ou au pire les dénoncerons sauvagement en commentaires :p))

Bon, mais quel rapport avec PHP ? Et bien si RoR est si populaire, ce n'est pas fondamentalement parcequ'il est implémenté en Ruby, mais surtout (je pense) pour les 2 première raisons sus-citées: MVC / AJAX. le Framework Zend, dont la preview release 0.1.2 est sortie dernièrement, propose la même approche du MVC, résolument simple.

Continue reading...

Wednesday 1 February 2006

Ajouter une ligne à la fin d'un array dans une boucle

Quand on est dans une boucle, et qu'on veut remplir un array, à chaque itération on doit y rajouter une ligne et garder la trace de l'index courant du tableau, cela peut se faire de plusieurs façons, en voilà au moins deux:

$array = array();
 
while(foo()) {
 
	$array[] = array();
	$row = & $array[count($array) - 1];
 
}
$array = array();
 
while(foo()) {
 
	$row = & $array[];
 
}

qu'on peut fonctionniser comme suit (dans le but par exemple de gagner en lisibilité et en flexibilité):

function & array_newrow(&$array, $default = array()) {
 
	$array[] = $default
	return $array[count($array) - 1];
 
}
 
function & array_newrow(&$array) {
 
	$row = & $array[];
	return $row;
 
}

EDIT

Il parait que c'est pas très clair, alors on va expliquer mieux. En gros, dans une boucle, si on veut ajouter une ligne à un array et qu'on procède comme ça:

$array[]['foo'] = 'bar';
$array[]['bar'] = 'foo';

Ca ne fonctionne pas comme on le désire (ça créé deux lignes d'une entrée chacune). On peut donc faire comme ça:

$array[] = array('foo' => 'bar', 'bar' => 'foo');

Ce qui fonctionne, mais n'est pas pratique ni flexible. Reste alors la solution de récupérer une référence vers la dernière ligne du tableau, qu'on créé spécialement pour l'occasion (sortez le champomy !) en utilisant la méthode sus-expliquée:

$row = & $data[];
$row['foo'] = 'bar';
$row['bar'] = 'foo';

Saturday 28 January 2006

Considérations nocturne sur PHP

Souvent le week end, je fais une nuit blanche du samedi au dimanche pour travailler. Bon ok, là on est la nuit de vendredi à samedi, et je vais pas tarder à aller me coucher, mais quand même. Bon enfin de toute façon c'est pas le sujet. Le truc, c'est que du coup je suis obligé de me taper la compagnie de Christophe, et je crois que finalement, c'est avec lui que j'ai le plus de discussions en rapport avec le nom du chan sur lequel on traine (#phpmafia (d'ailleurs, note pour plus tard: renouveler phpmafia.net, ça urge)).

On va commencer par parler du chroot. En gros, j'ai émis l'idée qu'une fonction chroot builtin serait utile. Elle existe, mais ne fait pas exactement ce que je veux, ou du moins pas comme je le veux. En l'état actuel des choses, elle execute un vrai chroot, et n'est donc utilisable qu'avec les droits root, ce qui est bien, mais pas top. Le but ultime serait d'éviter tout risque de faille du type remote file access, alors si c'est pour me taper a coté de ça des remote code execution with privilege escalation, ben ça perd de son interêt.

Deuxième challenge, implémenter la structure with() en PHP. Pour ceux qui ne connaissent pas, un petit dessin en javascript:

with(document) {
	getElelementById('foo');
}

Bon, l'exemple que je viens de donner ne sert a rien, mais je pense qu'on comprend tout de suite le but de la structure sus-citée. J'aimerai donc pouvoir disposer de la même chose en PHP. Etant donné que je n'ai pas les compétences C requises, la seule solution qu'il me reste est de l'implémenter d'une manière ou d'une autre en PHP. C'est d'autant plus complexe que je m'impose le cahier des charges simples mais strict suivant:

  • Conserver les références
  • Ne pas casser la coloration syntaxique
  • Ne pas avoir une syntaxe trop tarabiscotée

Ce qui exclut une solution du genre:

$object = new Object();
$with = array('method();', 'autremethode();' 'foo = "bar"');
foreach($with as $item) { eval('$object->' . $item); }

Puisque ça casse la coloration syntaxique. J'ai pensé a un truc genre:

function foo() {
	method();
	foo = 'bar';
}
 
with(new Object, 'foo');

Mais j'ai des doutes sur la faisabilité de la chose. A voir si on peut en tirer quelque chose via l'API de Réflexion (décidemment je l'aime bien celle là).

Dernière chose, on aimerait bien avoir des trucs genre bash, par exemple pouvoir faire:

require_once('header-*.php');
require('{foo,bar}.php');

Voilà voilà, petit papa noël, si tu passes par là...

Saturday 5 March 2005

Faire du CLI en PHP

Souvent les gens ne voient PHP que comme un langage de script orienté web. Même si il est vrai que c'est son but premier, ce n'est pas la seule chose que PHP peut faire. Penchons nous un peu sur l'utilisation de PHP en CLI, qui permet d'utiliser PHP comme un langage de script classique. Nous utiliserons pour se faire un système linux (ubuntu pour être précis) avec un binaire php-cli déjà installé.

Continue reading...

page 3 of 3 -