WebHook Google Code – recevoir un mail à chaque commit

Dans Google code il y a la possibilité d’utiliser un gestionnaire de version tel que subversion (svn) ou mercurial (hg). C’est bien pratique, mais malheureusement, de base, rien n’est prévu pour prévenir (excepté par flux RSS) les gens de ces mises à jour. Cependant, Google code permet d’utiliser un webhook en post commit.

Qu’est-ce qu’un webhook?

Un webhook c’est un « crochet web », c’est-à-dire une URL a appelée après (avant ou pendant) une action X. Dans notre cas, après chaque commit une URL est appelée.

Utilisation

Voici un exemple de code que j’utilise pour plusieurs de mes projets :

<?php
// project name
$projects = array('mon-super-projet');
 
// google code webhook key
$keys = array(
	'b-box'	=> 'Top-Secret_key_fourni_par_google-dans-l-adminsitration'
);
 
//user agent from google code
$useragent = 'Google Code Project Hosting (+http://code.google.com/p/support/wiki/PostCommitWebHooks)';
 
//email of all owner (eg. project chief)
$owners = array('vous@example.com');
 
//email of all team members except owners
$users = array('toi@example.com');
 
//sender of email
$sender = 'WebHook mailer<webmaster@exemple.com>';
 
//----------------------------------------------------------------------
$project = (isset($_GET['p']))?$_GET['p']:'';
$revision = (isset($_GET['r']))?intval($_GET['r']):-99;
$data = file_get_contents('php://input');
$digest = (isset($_SERVER['HTTP_GOOGLE_CODE_PROJECT_HOSTING_HOOK_HMAC']))?$_SERVER['HTTP_GOOGLE_CODE_PROJECT_HOSTING_HOOK_HMAC']:'';
 
//----------------------------------------------------------------------
/**
 * Send a mail
 * @param string $from email of the sender : sample@example.com or "name"<sample@example.com>;
 * @param array $to [a] => list of email [cc], [bcc] (hidden), ...
 * @param string $subject
 * @param string $body
 * @return bool true if success
 */
function mailer($from, array $to, $subject, $body)
{
	if (empty($to))
	{
		return false;
	}
 
	$headers = 'From: ' . $from . "\n";
 
	$a = '';
 
	if (isset($to['a']) &amp;&amp; !empty($to['a']))
	{
		$a = implode(',', $to['a']);
	}
 
	if (isset($to['bcc']) &amp;&amp; !empty($to['bcc']))
	{
		$headers .= 'Bcc: ' . implode(',', $to['bcc']) . "\n";
	}
 
	if (isset($to['cc']) &amp;&amp; !empty($to['cc']))
	{
		$headers .= 'Cc: ' . implode(',', $to['cc']) . "\n";
	}
 
	$headers .= 'MIME-Version: 1.0' . "\n";
	$headers .= 'Content-Type: text/plain; charset="UTF-8"' . "\n";
	$headers .= 'Content-Transfer-Encoding: 8bit' . "\n";
	$headers .= 'X-Mailer: PHP/' . phpversion();
 
	return mail($a, '[webhook]' . $subject, $body, $headers);
}
 
function failed($test_id, $msg)
{
	global $sender, $owners;
 
	$msg .= "\n--\nWebHook mail from the Google code project";
 
	mailer($sender, array('bcc' => $owners), 'failed test #' . $test_id, $msg);
 
	die('KO');
}
 
function get_ip()
{ 
	return (isset($_SERVER['HTTP_X_FORWARDED_FOR']))?$_SERVER['HTTP_X_FORWARDED_FOR']:(isset($_SERVER['HTTP_CLIENT_IP']))?$_SERVER['HTTP_CLIENT_IP']:$_SERVER['REMOTE_ADDR'];
}
 
//----------------------------------------------------------------------
if ($useragent != $_SERVER['HTTP_USER_AGENT'])
{
	// failed 1
	failed(1, 'User agent is bad : ' . htmlspecialchars($_SERVER['HTTP_USER_AGENT']) . "\n\nFrom : " . get_ip());
}
elseif (empty($project) || !in_array($project, $projects))
{
	// failed 2
	failed(2, 'No project set : ' . htmlspecialchars($project) . "\n\nFrom : " . get_ip());
}
else
{
	$hmac = hash_hmac('md5', $data, $keys[ $project ]);
	$data = json_decode($data, true);
 
	if (empty($digest) || $digest != $hmac)
	{
		// failed 3
		failed(3, 'Bad digest : ' . $digest . ' vs ' . $hmac . "\n\nFrom : " . get_ip());
	}
	elseif (intval($data['revision_count']) != count($data['revisions']))
	{
		// failed 4
		failed(4, 'Bad count : ' . count($data['revisions']) . ' vs ' . intval($data['revision_count']) . "\n\nFrom : " . get_ip());
	}
	else
	{
		$mail_body = '';
		foreach($data['revisions'] as $_revision)
		{
			$mail_body .= 'Revision : ' . "\t" . htmlentities($_revision['revision']) . ' from ' . htmlentities($_revision['author']) . ' at ' . date('Y-m-d H:i', intval($_revision['timestamp'])) . "\n";
			$mail_body .= 'Added : ' . "\t" . implode("\n\t\t", htmlentities($_revision['added'])) . "\n";
			$mail_body .= 'Modified : ' . "\t" . implode("\n\t\t\t", htmlentities($_revision['modified'])) . "\n";
			$mail_body .= 'Removed : ' . "\t" . implode("\n\t\t\t", htmlentities($_revision['removed'])) . "\n\n";
//			$mail_body .= 'URL : ' . "\t\t" . htmlentities($_revision['url']) . "\n\n";
			$mail_body .= 'Message : ' . "\t" . htmlentities($_revision['message']) . "\n\n\n";
//			$_revision['path_count'];
		}
		$mail_body .= "\n--\nWebHook mail from the Google code project : " . $project . "\nhttp://code.google.com/p/" . $project . "/\n";
 
		mailer($sender, array('bcc' => $owners + $users), '[' . $project . ']New revision #' . $revision, $mail_body);
	}
}
 
exit('OK');
 
# EOF

Plus d’info : PostCommitWebHooks

Espace de nom PHP et chargement automatique

PHP 5.3 ajoute une notion intéressante : les espaces de nom (ou namespace en anglais). Les espaces de nom permettent de séparer différents … « espace« , permettant ainsi d’avoir deux classe portant le même nom. Idéal pour l’utilisation de framework mais aussi de « l’isolation » de certains composants. Voyons voir comment créer un chargeur automatique (ou autoloader) comprenant les espaces de nom.

Si vous êtes sous Ms Windows, aucun problème un simple spl_autoload_register(); suffit. Malheureusement, sous *nix un bug existe il faudra donc implémenté une solution maison.

Le code

Ce code provient du « PHP Standards Working Group » :

function autoload($className)
{
	$className = ltrim($className, '\\');
	$fileName  = '';
	$namespace = '';
	if ($lastNsPos = strripos($className, '\\'))
	{
		$namespace = substr($className, 0, $lastNsPos);
		$className = substr($className, $lastNsPos + 1);
		$fileName  = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
	}
	$fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
 
	require $fileName;
}

Pour l’utiliser, il faut définir deux choses :

  1. Ne pas oublier de modifier l’include path si nécessaire.
  2. Ajouter cette fonction au chargeur de classes présent.

L’exemple

Voici un exemple un peu plus complet.
index.php

<?php
set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__) . '/lib/');//on ajoute le dossier lib dans l'include path
spl_autoload_register();//sur windows ceci devrait suffire .
//le code pour les autres
//----------------------------------------------------------
function autoload($className)
{
	$className = ltrim($className, '\\');
	$fileName  = '';
	$namespace = '';
	if ($lastNsPos = strripos($className, '\\'))
	{
		$namespace = substr($className, 0, $lastNsPos);
		$className = substr($className, $lastNsPos + 1);
		$fileName  = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
	}
	$fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
 
	require $fileName;
}
spl_autoload_register('autoload');
//----------------------------------------------------------
//fin du code de fix
 
use grummfy\test\Test;
Test::sayHello();
 
\grummfy\std\Test::sayHello();
 
# EOF

./lib/grummfy/test/Test.php

<?php
namespace grummfy\test;//pour rappel ceci doit-être la première instruction php (et on ne doit pas avoir de HTML avant)
echo 'Je suis inclus (' . __FILE__ . ')!';
class Test
{
	public static function sayHello()
	{
		echo 'Bonjour depuis ' . __CLASS__;
	}
}
 
# EOF

./lib/grummfy/std/Test.php

<?php
namespace grummfy\std;
echo 'Je suis inclus (' . __FILE__ . ')!';
class Test
{
	public static function sayHello()
	{
		echo 'Bonjour depuis ' . __CLASS__;
	}
}
 
# EOF

Le résultat devrait être :

Je suis inclus (/.../lib/grummfy/test/Test.php)!
Bonjour depuis grummfy\test\Test
Je suis inclus (/.../lib/grummfy/std/Test.php)!
Bonjour depuis grummfy\std\Test

Réécriture d’url et erreur interne à Apache

Il arrive que lors de l’écriture d’un nombre complexe de règle de réécriture d’URL (URL rewriting), des erreurs se produisent sans pour autant être compréhensibles. Je vous propose donc de regarder comment déboguer cela à travers un exemple pratique.

Exemple

Prenons une règles qui vérifierait que seul certains type de caractère sont autorisés.
La réécriture quant à elle se fait sur toute URL.
Ajoutons un document 403 personnalisé
RewriteEngine on
#
RewriteCond %{THE_REQUEST} !^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/ [NC]
RewriteRule .* - [F,NS,L]
#
#if not a dir or a file
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
#
RewriteRule ^(.*)$ index.php?p=$1 [QSA,L]
ErrorDocument 403 /erreur/403

Testons l’URL http://localhost/test:s et on obtient un beau :

Forbidden

You don’t have permission to access /test:s on this server.

Additionally, a 500 Internal Server Error error was encountered while trying to use an ErrorDocument to handle the request.

Les logs d’erreurs d’Apache vous diront certainement quelque chose du genre :

[error] [client 127.0.0.1] Request exceeded the limit of 10 internal redirects due to probable configuration error. Use ‘LimitInternalRecursion’ to increase the limit if necessary. Use ‘LogLevel debug’ to get a backtrace.

Passage en mode debug de Apache

Modifions la règle de log du site courant (soit dans /etc/apache2/sites-available/default soit dans /etc/apache2/apache.conf ou l’équivalent suivant votre configuration ou OS) et modifions la directive « LogLevel warn » en « LogLevel debug ». Ceci ne change rien dans notre cas, mais parfois cela s’avère utile…

Ajoutons ensuite les log1 de réécriture d’url (moi je l’ai ajouter dans /etc/apache2/mods-available/rewrite.conf puis j’ai fait un a2enmod rewrite (car le fichier .conf n’existait pas) et enfin j’ai relancer Apache) :
<IfModule mod_rewrite.c>
RewriteLog "/var/log/apache2/rewrite.log"
RewriteLogLevel 9
</IfModule>

Maintenant si vous aller voir dans error.log vous aurez :

[error] [client 127.0.0.1] Request exceeded the limit of 10 internal redirects due to probable configuration error. Use ‘LimitInternalRecursion’ to increase the limit if necessary. Use ‘LogLevel debug’ to get a backtrace.
[debug] core.c(3063): [client 127.0.0.1] r->uri = /…/erreur/403
[debug] core.c(3069): [client 127.0.0.1] redirected from r->uri = /…/erreur/403
[debug] core.c(3069): [client 127.0.0.1] redirected from r->uri = /…/erreur/403
[debug] core.c(3069): [client 127.0.0.1] redirected from r->uri = /…/erreur/403
[debug] core.c(3069): [client 127.0.0.1] redirected from r->uri = /…/erreur/403
[debug] core.c(3069): [client 127.0.0.1] redirected from r->uri = /…/erreur/403
[debug] core.c(3069): [client 127.0.0.1] redirected from r->uri = /…/erreur/403
[debug] core.c(3069): [client 127.0.0.1] redirected from r->uri = /…/erreur/403
[debug] core.c(3069): [client 127.0.0.1] redirected from r->uri = /…/erreur/403
[debug] core.c(3069): [client 127.0.0.1] redirected from r->uri = /…/erreur/403
[debug] core.c(3069): [client 127.0.0.1] redirected from r->uri = /…/test:s

Donc on voit que la redirection s’effectue correctement mais qu’il « n’accroche » pas. Regardons donc les logs de la réécriture d’URL (rewrite.log) :

/initial] (3) [perdir /…/] strip per-dir prefix: /…/test:s -> test:s
/initial] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘test:s’
/initial] (4) [perdir /…/] RewriteCond: input= » pattern=’200′ => not-matched
/initial] (3) [perdir /…/] strip per-dir prefix: /…/test:s -> test:s
/initial] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘test:s’
/initial] (4) [perdir /…/] RewriteCond: input=’GET /…/test:s HTTP/1.1′ pattern=’!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/’ [NC] => matched
/initial] (2) [perdir /…/] forcing responsecode 403 for /…/test:s
/initial/redir#1] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#1] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#1] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#1] (4) [perdir /…/] RewriteCond: input=’403′ pattern=’200’ => not-matched
/initial/redir#1] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#1] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#1] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#1] (4) [perdir /…/] RewriteCond: input=’GET /…/test:s HTTP/1.1′ pattern=’!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/’ [NC] => matched
/initial/redir#1] (2) [perdir /…/] forcing responsecode 403 for /…/erreur
/initial/redir#2] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#2] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#2] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#2] (4) [perdir /…/] RewriteCond: input=’403′ pattern=’200’ => not-matched
/initial/redir#2] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#2] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#2] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#2] (4) [perdir /…/] RewriteCond: input=’GET /…/test:s HTTP/1.1′ pattern=’!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/’ [NC] => matched
/initial/redir#2] (2) [perdir /…/] forcing responsecode 403 for /…/erreur
/initial/redir#3] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#3] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#3] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#3] (4) [perdir /…/] RewriteCond: input=’403′ pattern=’200’ => not-matched
/initial/redir#3] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#3] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#3] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#3] (4) [perdir /…/] RewriteCond: input=’GET /…/test:s HTTP/1.1′ pattern=’!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/’ [NC] => matched
/initial/redir#3] (2) [perdir /…/] forcing responsecode 403 for /…/erreur
/initial/redir#4] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#4] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#4] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#4] (4) [perdir /…/] RewriteCond: input=’403′ pattern=’200’ => not-matched
/initial/redir#4] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#4] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#4] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#4] (4) [perdir /…/] RewriteCond: input=’GET /…/test:s HTTP/1.1′ pattern=’!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/’ [NC] => matched
/initial/redir#4] (2) [perdir /…/] forcing responsecode 403 for /…/erreur
/initial/redir#5] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#5] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#5] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#5] (4) [perdir /…/] RewriteCond: input=’403′ pattern=’200’ => not-matched
/initial/redir#5] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#5] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#5] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#5] (4) [perdir /…/] RewriteCond: input=’GET /…/test:s HTTP/1.1′ pattern=’!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/’ [NC] => matched
/initial/redir#5] (2) [perdir /…/] forcing responsecode 403 for /…/erreur
/initial/redir#6] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#6] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#6] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#6] (4) [perdir /…/] RewriteCond: input=’403′ pattern=’200’ => not-matched
/initial/redir#6] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#6] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#6] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#6] (4) [perdir /…/] RewriteCond: input=’GET /…/test:s HTTP/1.1′ pattern=’!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/’ [NC] => matched
/initial/redir#6] (2) [perdir /…/] forcing responsecode 403 for /…/erreur
/initial/redir#7] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#7] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#7] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#7] (4) [perdir /…/] RewriteCond: input=’403′ pattern=’200’ => not-matched
/initial/redir#7] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#7] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#7] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#7] (4) [perdir /…/] RewriteCond: input=’GET /…/test:s HTTP/1.1′ pattern=’!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/’ [NC] => matched
/initial/redir#7] (2) [perdir /…/] forcing responsecode 403 for /…/erreur
/initial/redir#8] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#8] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#8] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#8] (4) [perdir /…/] RewriteCond: input=’403′ pattern=’200’ => not-matched
/initial/redir#8] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#8] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#8] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#8] (4) [perdir /…/] RewriteCond: input=’GET /…/test:s HTTP/1.1′ pattern=’!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/’ [NC] => matched
/initial/redir#8] (2) [perdir /…/] forcing responsecode 403 for /…/erreur
/initial/redir#9] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#9] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#9] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#9] (4) [perdir /…/] RewriteCond: input=’403′ pattern=’200’ => not-matched
/initial/redir#9] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#9] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#9] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#9] (4) [perdir /…/] RewriteCond: input=’GET /…/test:s HTTP/1.1′ pattern=’!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/’ [NC] => matched
/initial/redir#9] (2) [perdir /…/] forcing responsecode 403 for /…/erreur
/initial/redir#10] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#10] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#10] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#10] (4) [perdir /…/] RewriteCond: input=’403′ pattern=’200’ => not-matched
/initial/redir#10] (3) [perdir /…/] add path info postfix: /…/erreur -> /…/erreur/403
/initial/redir#10] (3) [perdir /…/] strip per-dir prefix: /…/erreur/403 -> erreur/403
/initial/redir#10] (3) [perdir /…/] applying pattern ‘.*’ to uri ‘erreur/403′
/initial/redir#10] (4) [perdir /…/] RewriteCond: input=’GET /…/test:s HTTP/1.1′ pattern=’!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/’ [NC] => matched
/initial/redir#10] (2) [perdir /…/] forcing responsecode 403 for /…/erreur

En gros, nous avions une boucle de redirection. Pour l’éviter ajoutons,d ans le .htaccess, une condition qui dira de ne pas rentrer dans le bloc, s’il y a une erreur 403 :
RewriteEngine on
#
RewriteCond %{ENV:REDIRECT_STATUS} !403
RewriteCond %{THE_REQUEST} !^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&]+\ HTTP/ [NC]
RewriteRule .* - [F,NS,L]
#
#if not a dir or a file
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
#
RewriteRule ^(.*)$ index.php?p=$1 [QSA,L]
ErrorDocument 403 /erreur/403

Maintenant tout fonctionne comme prévu!

La fin

Pour finir, ceci montre encore l’importance des logs lors de la rencontre de problème. S’il y a moyen penser a les activer, cela simplifie franchement la tâche de débogage que cela soit pour Apache mais aussi pour tout projet informatique.

Un dernier conseil, pensez a désactiver le mode debug de Apache une fois fini!

S’il y avait un site a conseiller sur ce sujet (en dehors du manuel Apache) je vous renverrait vers askapache

  1. Si le fichier ne se créer pas tout seul penser à le créer

Wampserver : quick switch xdebug menu

Pour une fois, un article sous Ms Windows. Stage oblige, je passe du temps sous celui-ci (XP 🙁 ). Afin d’optimiser son travail, il y a parfois des petites choses bien pratiques, telles que ce que je vais vous présenter.

XDebug est un outil merveilleux, parfois capricieux, certes, mais très utile. Il permet, notamment1 :

  • Affichage de tracé d’erreur
  • Meilleures lectures des exceptions
  • Débogage pas-à-pas
  • Profiling d’application

Bref, des choses essentielles en développement. Et, contrairement à ce que certains IDE2 font, il permet surtout de le faire sur un serveur « réel », donc avec une utilisation « réel ».

Le but de ce billet n’est pas de présenter XDebug, d’autres le font mieux que moi 3, mais bien de vous présenter un petit script vous permettant d’activer et désactiver XDebug sur wampserver.

Installation

  1. Téléchargez le fichier 4 et décompressez-le.
  2. Suivez les instructions d’installation décrite dans le fichier installe. Il y a seulement 1 fichier à modifier + 1 fichier par version de PHP installée.
  3. Relancer wampserver et tester!

Si vous avez des questions, n’hésitez pas.

Plus d’informations

  1. On parle de serveur web avec PHP …
  2. Par exemple, Zend Studio permet un débogage pas à pas mais en interne donc réduit …
  3. cf. plus d’informations
  4. XDebug quick switch menu for wampserver