<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Grummfy&#039;s project</title>
	<atom:link href="http://grummfy.be/blog/feed" rel="self" type="application/rss+xml" />
	<link>https://grummfy.be/blog</link>
	<description>Mes projets, mes rêves, mes envies, ...</description>
	<lastBuildDate>Fri, 10 Oct 2025 21:25:46 +0000</lastBuildDate>
	<language>fr-BE</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>Jobs, queues &#038; events : anatomie des erreurs courantes et pistes de résolutions</title>
		<link>https://grummfy.be/blog/546</link>
		
		<dc:creator><![CDATA[Grummfy]]></dc:creator>
		<pubDate>Fri, 10 Oct 2025 08:55:00 +0000</pubDate>
				<category><![CDATA[Présentation]]></category>
		<category><![CDATA[conférence]]></category>
		<category><![CDATA[forumphp]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[talk]]></category>
		<guid isPermaLink="false">https://grummfy.be/blog/?p=546</guid>

					<description><![CDATA[<p>Au ForumPHP 2025, j&#8217;ai eu l’occasion de présenter une conférence, le description en était Jobs, queues &#38; events : anatomie des erreurs courantes et pistes de résolutions Aujourd’hui, difficile d’échapper à l’asynchrone! Tout projet un peu conséquent qui doit traiter des données en masse ou en longueur a souvent un ou plusieurs workers, ces process [&#8230;]</p>
<p>The post <a href="https://grummfy.be/blog/546">Jobs, queues & events : anatomie des erreurs courantes et pistes de résolutions</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Au ForumPHP 2025, j&rsquo;ai eu l’occasion de présenter une conférence, le description en était</p>



<p><strong>Jobs, queues &amp; events : anatomie des erreurs courantes et pistes de résolutions</strong></p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><bloquote>Aujourd’hui, difficile d’échapper à l’asynchrone! Tout projet un peu conséquent qui doit traiter des données en masse ou en longueur a souvent un ou plusieurs workers, ces process qui tournent en tâche de fond ou sous forme de FaaS, et qui peuvent parfois vous donner des cauchemars. Que cela soit pour du traitement lourd, du batching ou autre, cela doit tourner… Ensemble, on verra les erreurs les plus courantes, les fuites mémoires ou encore la gestion de l&rsquo;arrêt de ceux-ci. Mais aussi quelques outils utilisables pour comprendre ce qui se passe en cas de souci ou vérifier que tout tourne rond.</bloquote></p>
</blockquote>



<p>Pour ceux que cela intéresse voici les slides associé (avec des <strong>liens</strong> vers différentes ressources).</p>



<div data-wp-interactive="core/file" class="wp-block-file"><object data-wp-bind--hidden="!state.hasPdfPreview" hidden class="wp-block-file__embed" data="https://grummfy.be/blog/wp/wp-content/uploads/2025/10/2025-10-10_forumphp_2025.notes_.pdf" type="application/pdf" style="width:100%;height:400px" aria-label="Contenu embarqué 2025-10-10_forumphp_2025.notes."></object><a id="wp-block-file--media-cf500bed-c3c7-487b-b0cd-2772212e4b94" href="https://grummfy.be/blog/wp/wp-content/uploads/2025/10/2025-10-10_forumphp_2025.notes_.pdf">2025-10-10_forumphp_2025.notes</a><a href="https://grummfy.be/blog/wp/wp-content/uploads/2025/10/2025-10-10_forumphp_2025.notes_.pdf" class="wp-block-file__button wp-element-button" download aria-describedby="wp-block-file--media-cf500bed-c3c7-487b-b0cd-2772212e4b94">Télécharger</a></div>



<p>La vidéo de la conférence est à venir</p>



<p></p><p>The post <a href="https://grummfy.be/blog/546">Jobs, queues & events : anatomie des erreurs courantes et pistes de résolutions</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Recherche d&#8217;epub watermarker</title>
		<link>https://grummfy.be/blog/541</link>
		
		<dc:creator><![CDATA[Grummfy]]></dc:creator>
		<pubDate>Sat, 07 Dec 2024 22:54:43 +0000</pubDate>
				<category><![CDATA[General]]></category>
		<guid isPermaLink="false">https://grummfy.be/blog/?p=541</guid>

					<description><![CDATA[<p>Pour un besoin récent, j&#8217;avais besoin de retrouver tout les epub que j&#8217;avais acheter chez un vendeur d&#8217;epub qui n&#8217;existe plus (feedbooks). Du coup, j&#8217;ai fait un script pour aller chercher dans ma bibliothèque. On ne sait jamais que cela vous serve</p>
<p>The post <a href="https://grummfy.be/blog/541">Recherche d’epub watermarker</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Pour un besoin récent, j&rsquo;avais besoin de retrouver tout les epub que j&rsquo;avais acheter chez un vendeur d&rsquo;epub qui n&rsquo;existe plus (feedbooks). Du coup, j&rsquo;ai fait un script pour aller chercher dans ma bibliothèque. </p>



<pre class="wp-block-code"><code>#!/bin/bash

if &#91; $# -lt 2 ]
then
	echo
	echo "Usage: ${0} &lt;path-to-epub-directory (aka calibre root biblio)> &lt;name to search>"
	echo "export VERBOSE=1 to add verbosity or VERBOSE=1 ${0} ..."
	echo
	exit
fi

CUR_DIR=$(dirname "$0")
PATH_TO_SEARCH=$1
PATTERN_TO_SEARCH=$2

echo "Searching in ${PATH_TO_SEARCH} about ${PATTERN_TO_SEARCH}..."

mapfile FILES &lt; &lt;(find "$PATH_TO_SEARCH" -type f -name '*.epub')

N="${#FILES&#91;@]}"

echo "... found ${N} epubs!"

for ((I=0; I&lt;N; I++))
do
	epub_file=$(echo "${CUR_DIR}/${FILES&#91;$I]}" | sed 's/\n//')
	fileloc=$(unzip -l "${epub_file}" | grep -o 'OEBPS/.*.xhtml')
	found=0
	for xhtml in $fileloc
	do
		metafound=$(zipgrep "${PATTERN_TO_SEARCH}" "${epub_file}" "${xhtml}")
		if &#91; "$metafound" != "" ]; then
			# display only once that we have found it
			if &#91;&#91; "$found" -eq "0" ]]; then
				echo "Found in ${epub_file}"
			fi

			found=1
			if &#91;&#91; "$VERBOSE" -eq "1" ]]; then
				printf "\t|_ %s\n" "$metafound"
			else
				break
			fi
		fi
	done
done

echo
echo 'EOL'
echo
exit
</code></pre>



<p>On ne sait jamais que cela vous serve</p>



<p></p><p>The post <a href="https://grummfy.be/blog/541">Recherche d’epub watermarker</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Mousse au chocolat</title>
		<link>https://grummfy.be/blog/521</link>
		
		<dc:creator><![CDATA[Grummfy]]></dc:creator>
		<pubDate>Wed, 21 Sep 2022 10:00:00 +0000</pubDate>
				<category><![CDATA[Mmh lekker]]></category>
		<category><![CDATA[cuisine]]></category>
		<category><![CDATA[dessert]]></category>
		<category><![CDATA[recette]]></category>
		<guid isPermaLink="false">https://grummfy.be/blog/?p=521</guid>

					<description><![CDATA[<p>Une petite mousse rapide, déclinable en deux versions, et simple a faire. Pour 4 à 6 mousses 3 oeufs 200g de chocolat (en général je fait un mélange 50g de chocolat au lait, 150g de chocolat noir), en petits morceaux 200ml de crème fraîche Séparer les blancs des jaunes Faire fondre le chocolat (au bain [&#8230;]</p>
<p>The post <a href="https://grummfy.be/blog/521">Mousse au chocolat</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Une petite mousse rapide, déclinable en deux versions, et simple a faire.</p>



<p>Pour 4 à 6 mousses</p>



<ul class="wp-block-list"><li>3 oeufs</li><li>200g de chocolat (en général je fait un mélange 50g de chocolat au lait, 150g de chocolat noir), en petits morceaux</li><li>200ml de crème fraîche</li></ul>



<ol class="wp-block-list"><li>Séparer les blancs des jaunes</li><li>Faire fondre le chocolat (au bain maris de préférence pour éviter que cela ne brûle), garder 1/4 sur le côté</li><li>Pendant que cela fond, battre les blancs en neige ferme (2-3 gouttes de jus de citron peut aider)</li><li>Battre la crème fraîche a moitié (pas besoin d&rsquo;être très ferme, juste un peu monté)</li><li>Une fois le chocolat fondu, retirer le récipient du bain maris et verser-y le reste du chocolat</li><li>Une fois fondu ajouter le jaune en mélangeant directement.</li><li>Ensuite verser la crème progressivement en méleangeant</li><li>Une fois le mélange près, verser sur les blancs et mélangé délicatement </li><li>Mettre au frais, minimum 30 minutes.</li></ol>



<p>Une autre version est sans crème fraîche. La mousse sera très ferme!</p><p>The post <a href="https://grummfy.be/blog/521">Mousse au chocolat</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>OBS : debug webview</title>
		<link>https://grummfy.be/blog/518</link>
		
		<dc:creator><![CDATA[Grummfy]]></dc:creator>
		<pubDate>Sat, 10 Sep 2022 21:05:35 +0000</pubDate>
				<category><![CDATA[Réflexion du jour]]></category>
		<category><![CDATA[note-a-moi-même]]></category>
		<category><![CDATA[obs]]></category>
		<category><![CDATA[stream]]></category>
		<guid isPermaLink="false">https://grummfy.be/blog/?p=518</guid>

					<description><![CDATA[<p>Dans OBS sudio il est possible d&#8217;intégré des pages web et ce depuis un certains temps. Très pratique pour faire des inserts custom dans ce que vous streamer. Pour ce faire, il suffit d&#8217;ajouter une source Navigateur (ou web browser) et de fournir une url. Bref, rien de compliqué. Sauf si vous voulez debuger l&#8217;intégration, [&#8230;]</p>
<p>The post <a href="https://grummfy.be/blog/518">OBS : debug webview</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Dans OBS sudio il est possible d&rsquo;intégré des pages web et ce depuis un certains temps. Très pratique pour faire des inserts custom dans ce que vous streamer.</p>



<p>Pour ce faire, il suffit d&rsquo;ajouter une source Navigateur (ou web browser) et de fournir une url. Bref, rien de compliqué. Sauf si vous voulez debuger l&rsquo;intégration, être sur que les appel javascript que vous effectué se passe bien, etc.</p>



<h2 class="wp-block-heading">Debuger avec OBS et Chrome/chromium</h2>



<p>Afin de pouvoir debuger, utiliser la console, etc, il suffit de lancer obs en ligne de commande avec le paramètre <em><code>--remote-debugging-port=9222</code></em>.</p>



<p>Dès lors, vous pouvez lancer chrome ou chromium sur le port localhost:9222 (ou le port choisit) et accéder a la web console de chrome/chromium.</p>



<p> </p><p>The post <a href="https://grummfy.be/blog/518">OBS : debug webview</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>OBS &#038; chemin des plugins sous linux</title>
		<link>https://grummfy.be/blog/512</link>
		
		<dc:creator><![CDATA[Grummfy]]></dc:creator>
		<pubDate>Sun, 17 Jul 2022 15:32:32 +0000</pubDate>
				<category><![CDATA[Réflexion du jour]]></category>
		<category><![CDATA[note-a-moi-même]]></category>
		<category><![CDATA[obs]]></category>
		<category><![CDATA[stream]]></category>
		<guid isPermaLink="false">https://grummfy.be/blog/?p=512</guid>

					<description><![CDATA[<p>Sous GNU/linux, vous avez plusieurs manières d&#8217;installées OBS. Cependant, suivant la manière d&#8217;installer, l&#8217;installation de plugins peut changer. Dans cet article, j&#8217;explique le cas de l&#8217;installation via snap ou via un package système. Donc, si vous cherchez à savoir comment installer un plugin OBS sous linux, rien de bien compliqué : lisez les instructions du [&#8230;]</p>
<p>The post <a href="https://grummfy.be/blog/512">OBS & chemin des plugins sous linux</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Sous GNU/linux, vous avez plusieurs manières d&rsquo;installées OBS. Cependant, suivant la manière d&rsquo;installer, l&rsquo;installation de plugins peut changer.</p>



<p>Dans cet article, j&rsquo;explique le cas de l&rsquo;installation via <em>snap</em> ou via un <em>package système</em>.</p>



<p>Donc, si vous cherchez à savoir comment installer un plugin OBS sous linux, rien de bien compliqué :</p>



<ol class="wp-block-list"><li>lisez les instructions du plugin</li><li>99% du temps, on vous demande de déposer les fichiers dans le dossier <em>plugins</em></li><li><em>tadaa</em></li></ol>



<p>Sauf que ce dossier <em>plugins</em> est compliqué à trouver&#8230; voyons cela ensemble</p>



<h3 class="wp-block-heading">Via snap</h3>



<p>Dans le cas d&rsquo;une installation par <em>snap</em>, même si cela peut varier d&rsquo;un système à l&rsquo;autre, dans la majorité des cas, vous devriez avoir un dossier <em>~/snap/obs-studio/current/.config/obs-studio</em> dans lequel, s&rsquo;il n&rsquo;existe pas encore, vous pouvez créer un dossier <em>plugins</em> et y déposer votre plugin.</p>



<p>Donc <em>~/snap/obs-studio/current/.config/obs-studio/plugins</em></p>



<p><strong>Attention</strong>, qu&rsquo;il ne s&rsquo;agît pas d&rsquo;une distribution officielle, pour cela voyez las package système ou flatpak</p>



<h3 class="wp-block-heading">Via un package système</h3>



<p>Dans le cas d&rsquo;une installation via les packages système, vous devriez trouver votre bonheur dans <em>~/.config/obs-studio</em>. S&rsquo;il n&rsquo;existe pas encore, vous pouvez créer un dossier <em>plugins</em> et y déposer votre plugin.</p>



<p>Donc <em>~/.config/obs-studio/plugins</em></p>



<p>Attention, je lis souvent que l&rsquo;on conseille l&rsquo;installation dans un es dossier racine tel que /usr/share/&#8230; ou /var/&#8230; cela fonctionnera aussi, MAIS vous risquez d&rsquo;affecté d&rsquo;autres utilisateurs que vous en cas de soucis. Sans compter les soucis de droit. Bref, j’éviterai cela au maximum.</p>



<h3 class="wp-block-heading">Les autres?</h3>



<p>Flatpak, à priori (pas testé), le dossier dans .var/app/com.obsproject.Studio/config/plugins</p>



<p>Pour le reste, si vous êtes assez inventif pour utiliser autre chose que ces trois méthodes, vous devriez pouvoir trouver votre chemin <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f609.png" alt="😉" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p><p>The post <a href="https://grummfy.be/blog/512">OBS & chemin des plugins sous linux</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Git, gitolite et mutualisé OVH &#8211; gitolite v3</title>
		<link>https://grummfy.be/blog/396</link>
		
		<dc:creator><![CDATA[Grummfy]]></dc:creator>
		<pubDate>Sat, 26 May 2012 19:51:51 +0000</pubDate>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[git]]></category>
		<guid isPermaLink="false">http://grummfy.be/blog/?p=396</guid>

					<description><![CDATA[<p>Petites précision concernant l&#8217;article précédents qui lui travaillait avec gitolite 2. Pour la version 3 l&#8217;installation se passe uniquement du côté serveur. Une fois la migration effectuées (le guide fourni par gitolite est suffisamment explicite), j&#8217;ai simplement du réindexer le repository git de gitolite (sur mon comtpe ovh donc), via un simple rm -rf .git/index; [&#8230;]</p>
<p>The post <a href="https://grummfy.be/blog/396">Git, gitolite et mutualisé OVH – gitolite v3</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Petites précision concernant l&rsquo;<a title="Git, gitolite et mutualisé OVH" href="http://grummfy.be/blog/368">article précédents</a> qui lui travaillait avec gitolite 2. Pour la version 3 l&rsquo;installation se passe uniquement du côté serveur.</p>
<p>Une fois la migration effectuées (le guide fourni par gitolite est suffisamment explicite), j&rsquo;ai simplement du réindexer le repository git de gitolite (sur mon comtpe ovh donc), via un simple <code>rm -rf .git/index; git reset</code> depuis le dossier d&rsquo;installation de gitolite.</p><p>The post <a href="https://grummfy.be/blog/396">Git, gitolite et mutualisé OVH – gitolite v3</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Git fusion de sous répertoire et de l&#8217;historique dans un autre dépôt</title>
		<link>https://grummfy.be/blog/389</link>
		
		<dc:creator><![CDATA[Grummfy]]></dc:creator>
		<pubDate>Mon, 02 Jan 2012 00:03:47 +0000</pubDate>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[Projet]]></category>
		<category><![CDATA[FSB]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[trucs et astuces]]></category>
		<guid isPermaLink="false">http://grummfy.be/blog/?p=389</guid>

					<description><![CDATA[<p>Cet article explique comment fusionner des sous répertoire d'un dépôt existant dans un autre en en gardant l'historique.</p>
<p>The post <a href="https://grummfy.be/blog/389">Git fusion de sous répertoire et de l’historique dans un autre dépôt</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Dans le cadre du projet <a title="Fire Soft Board" href="http://grummfy.be/blog/key/fsb">Fire Soft Board</a> il a été décidé de créer un dépôts git dédié aux traductions. Pour ce faire il a fallut reprendre els fichiers mais aussi les placer dans des sous répertoires spécifiques. Voici la démarche effectuée.<br />
<span id="more-389"></span><br />
On désire prendre certaines sous répertoires et les transposer dans le nouveau dépôts.<br />
/Fire-Soft-Board-2/tpl/WhiteSummer/img/fr &#8211;&gt; pack/fr/tpl/WhiteSummer/img/fr<br />
/Fire-Soft-Board-2/lang/fr &#8211;&gt; pack/fr/lang/fr</p>
<ol>
<li>Clone du dépôt principal<br />
<code>git clone https://github.com/FSB/Fire-Soft-Board-2.git<br />
cd Fire-Soft-Board-2</code></li>
<li>Nous avons deux sous répertoires, nous créons donc deux clone local<br />
<code>cd ..<br />
git clone Fire-Soft-Board-2 fsb2-tpl-fr<br />
git clone Fire-Soft-Board-2 fsb2-lang-fr</code></li>
<li>Il faut désormais nettoyer les fichiers mais aussi l&rsquo;historique des commits dont nous n&rsquo;avons plus besoin. Pour cela, nous allons simplement dire à git de ne garder que les sous répertories dont nous avons besoin.<br />
<code>cd fsb2-lang-fr<br />
git checkout dev<br />
git filter-branch --subdirectory-filter lang/fr HEAD<br />
git gc --prune --aggressive<br />
cd ..<br />
cd fsb2-tpl-fr<br />
git checkout dev<br />
git filter-branch --subdirectory-filter tpl/WhiteSummer/img/fr HEAD<br />
git gc --prune --aggressive<br />
cd ..<br />
</code><br />
git gc permet de nettoyer l&rsquo;historique et de compacter l&rsquo;index. Cela évite ainsi de potentiel mauvaises surprise dans les logs.</li>
<li>Il faut désormais fusionner ces fichiers ainsi que l&rsquo;historique qui va avec dans le dépôt des traductions. Commençons par cloner celui-ci.<code>git clone https://github.com/FSB/FSB2-language-packs.git<br />
cd FSB2-language-packs/</code></li>
<li>Fusionnons les ressources lié au templates<code>git remote add -f tpl ../FSB2-tpl-fr/<br />
git merge -s ours --no-commit tpl/dev<br />
git read-tree --prefix=pack/fr/tpl/WhiteSummer/img/fr -u tpl/dev<br />
git commit -m'Ajout des sources et de l''historique du pack de langue fr des templates'<br />
</code>On effectue un merge mais sans commiter. En mode de merging, on dit que le merge doit s&rsquo;effectuer non pas sur la base mais sur un sous répertoire précis.</li>
<li>Ensuite, on fait de même pour la traductions en elle-même.<code>git remote add -f lang ../FSB2-lang-fr/<br />
git merge -s ours --no-commit lang/dev<br />
git read-tree --prefix=pack/fr/lang/fr -u lang/dev<br />
git commit -m'Ajout des sources et de l''historique du pack de langue fr'<br />
</code></li>
<li>On finit par un petit nettoyage<code>git gc<br />
git remote rm lang<br />
git remote rm tpl</code></li>
</ol>
<p>Pour aller plus loin :<br />
http://progit.org/book/fr/ch6-4.html</p><p>The post <a href="https://grummfy.be/blog/389">Git fusion de sous répertoire et de l’historique dans un autre dépôt</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Création de nouveau formulaire facilement avec Zend Form Maker</title>
		<link>https://grummfy.be/blog/381</link>
		
		<dc:creator><![CDATA[Grummfy]]></dc:creator>
		<pubDate>Sat, 29 Oct 2011 22:29:52 +0000</pubDate>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[programmation]]></category>
		<category><![CDATA[trucs et astuces]]></category>
		<category><![CDATA[ZF]]></category>
		<guid isPermaLink="false">http://grummfy.be/blog/?p=381</guid>

					<description><![CDATA[<p>La création de formulaire avec zend framework peut être facilitée grâce à un petit développement : Zend Form Maker. Projet disponible sur github. À la base créer pour des besoins scolaires, sarlak a eu la bonne idée de rendre le dev open source sous licence GPL 3.0. Le but du logiciel est de permettre via [&#8230;]</p>
<p>The post <a href="https://grummfy.be/blog/381">Création de nouveau formulaire facilement avec Zend Form Maker</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>La création de formulaire avec zend framework peut être facilitée grâce à un petit développement : Zend Form Maker. Projet disponible sur github. À la base créer pour des besoins scolaires, sarlak a eu la bonne idée de rendre le dev open source sous licence GPL 3.0.</p>
<p>Le but du logiciel est de permettre via une interface très simple d&rsquo;accès, de créer des formulaires et ensuite de forger une classe PHP, basé sur zend framework afin d&rsquo;obtenir un formulaire tout frais moulu. Ultra pratique, et ultra simple, et surtout cela fait gagner un temps de dingue!</p>
<p>Pour l&rsquo;installer, il vous faut de quoi faire fonctionner zend framework (celui-ci doit être installé). Sous Unix cela donnera ceci :<br />
<code><br />
git clone https://github.com/sarlak/Zend-Form-Maker.git<br />
ln -s pathToYourZFLibrary<br />
chmod 0777 Zend-Form-Maker/public/resources/xml<br />
chmod 0777 Zend-Form-Maker/public/resources/form_made<br />
</code></p>
<p>Bien entendu, à peu de choses près ceci est adaptable sous MS-Windows</p>
<p>Pour créer votre premier formulaire, il vous suffit de vous rendre sur l&rsquo;URL adéquate, aller sur « form list » et « Bazinga! ». Une <a href="http://demo.zfm.matthieudiblasio.ch/" title="démo" target="_blank">démo</a> existe en ligne!</p>
<p>Une petite capture d&rsquo;écran :<br />
<a href="http://grummfy.be/blog/381/capture-zend-form-maker-v1-0-mozilla-firefox" rel="attachment wp-att-383"><img fetchpriority="high" decoding="async" src="http://grummfy.be/blog/wp/wp-content/uploads/2011/10/Capture-Zend-Form-Maker-v1.0-Mozilla-Firefox-300x185.png" alt="" title="Capture-Zend Form Maker v1.0" width="300" height="185" class="aligncenter size-medium wp-image-383" srcset="https://grummfy.be/blog/wp/wp-content/uploads/2011/10/Capture-Zend-Form-Maker-v1.0-Mozilla-Firefox-300x185.png 300w, https://grummfy.be/blog/wp/wp-content/uploads/2011/10/Capture-Zend-Form-Maker-v1.0-Mozilla-Firefox-1024x633.png 1024w, https://grummfy.be/blog/wp/wp-content/uploads/2011/10/Capture-Zend-Form-Maker-v1.0-Mozilla-Firefox.png 1213w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<p>À noter que j&rsquo;ai corrigé un ou deux petits trucs qui me dérangeaient, il y a un bout de temps.</p><p>The post <a href="https://grummfy.be/blog/381">Création de nouveau formulaire facilement avec Zend Form Maker</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Git, gitolite et mutualisé OVH</title>
		<link>https://grummfy.be/blog/368</link>
		
		<dc:creator><![CDATA[Grummfy]]></dc:creator>
		<pubDate>Wed, 10 Aug 2011 21:46:37 +0000</pubDate>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[OVH]]></category>
		<category><![CDATA[programmation]]></category>
		<category><![CDATA[trucs et astuces]]></category>
		<guid isPermaLink="false">http://grummfy.be/blog/?p=368</guid>

					<description><![CDATA[<p>Pouvoir utilisé gitolite sur un hébergement mutualisé OVH.</p>
<p>The post <a href="https://grummfy.be/blog/368">Git, gitolite et mutualisé OVH</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>/!\ pour la version 3 pensez à lire <a href="http://grummfy.be/blog/396" title="Git, gitolite et mutualisé OVH – gitolite v3">ceci</a>.</p>
<p>Hello,<br />
Sur les mutualisés OVH, la version fournie de git est plus qu’out-of-date : version 1.4.4. Ceci signifie impossibilité d&rsquo;installé des outils tels que gitolite ou inferno. Ce billet est l&rsquo;occasion d&rsquo;un guide pour installer gitolite.</p>
<p>Le guide suivant a été rédigé sur un compte OVH pro, donc avec accès un ssh. En utilisant git 1.7.6 et gitolite 2.0.3. L&rsquo;ensemble exécuter sur une machine Linux.</p>
<h2>Téléchargement</h2>
<p>La première étape, consiste a récupérer les différentes archives nécessaires, a savoir git et gitolite.</p>
<p>Pour git, il vous suffit de prendre la dernière version http://git-scm.com/download.</p>
<p>Pour gitolite, il suffit d&rsquo;utiliser git!</p>
<pre>git clone https://github.com/sitaramc/gitolite.git</pre>
<h2>Préparation de l&rsquo;environnement</h2>
<p>Nous allons installer les exécutables de git dans ~/opt/bin. De ce fait ajoutons le path a l&rsquo;utilisateur ssh.<br />
Ouvrir ~/.bash_profile et modifier le path. Pour ce faire ajouter, avant le export :</p>
<pre>PATH=$HOME/opt/bin:$PATH</pre>
<h2>Installation de git</h2>
<p>Uploader votre archive dans un dossier (ici ~/opt/src/). En mode console par ssh, donc sur le serveur, ouvrir l&rsquo;archive et compiler les sources.</p>
<pre>tar -xvzf git-1.7.6.tar.gz
cd git-1.7.6
./configure --prefix=$HOME/opt --without-tcltk
make
make install</pre>
<p>Une fois compilé, les exécutables seront présents dans ~/opt/bin. Pour vérifier, que tout est bien en place :</p>
<pre>git --version</pre>
<h2>Gitolite</h2>
<p>Il faut se rendre dans le répertoire du clone du repository. <strong>Tout s&rsquo;exécute uniquement sur la machine cliente (donc pas chez ovh)</strong>. Si l&rsquo;utilisateur est « toto », l&rsquo;host « tata.com » et le futur administrateur « moiadmin »</p>
<pre>./src/gl-easy-install toto tata.com moiadmin</pre>
<p>Il suffit de faire enter. Dans tous les cas (mise à jour du script compris!), le fait de réexecuter la commande permet de rectifier les erreurs éventuelles. À l&rsquo;étape de l&rsquo;édition du fichier de configuration, il faut modifier la variable $GIT_PATH pour quelle pointe vers le répertoire contenant notre git. Dans notre cas :</p>
<pre>$GIT_PATH = $ENV{HOME} . "/opt/bin/";</pre>
<h2>Configuration de Gitolite</h2>
<p>La configuration de gitolite est simple et s&rsquo;exécute avec &#8230; git! En principe, un répertoire reprenant la config a été créé sur votre home dans ~/gitolite-admin, dans le cas contraire, il vous suffit de faire</p>
<pre>git clone gitolite:gitolite-admin.git</pre>
<p>Pour ajouter des clefs ssh, il suffit de les mettre dans le dossier keys. Pour ce qui est de la config, il vous suffit d&rsquo;éditer le fichier gitolite.conf et puis de faire</p>
<pre>git add conf/gitolite.conf
git commit -m 'update config'
git push</pre>
<p>Et c&rsquo;est tout!</p>
<p>Sources :<br />
<a href="http://forum.ovh.com/showthread.php?t=71543" target="_blank">http://forum.ovh.com/showthread.php?t=71543</a><br />
<a href="http://blog.lyrixx.info/admin-sys/installer-gitolite-sur-une-machine-debian-5/" target="_blank">http://blog.lyrixx.info/admin-sys/installer-gitolite-sur-une-machine-debian-5/</a><br />
<a href="http://sitaramc.github.com/gitolite/doc/gitolite.rc.html" target="_blank">http://sitaramc.github.com/gitolite/doc/gitolite.rc.html</a><br />
<a href="http://sitaramc.github.com/gitolite/doc/gitolite.conf.html#_groups" target="_blank">http://sitaramc.github.com/gitolite/doc/gitolite.conf.html#_groups</a></p>
<p>Pour vos interfaces web, trois solutions correctes :<br />
<a href="http://hjemli.net/git/cgit/" target="_blank">CGit</a><br />
<a href="http://viewgit.sourceforge.net/" target="_blank">View Git</a><br />
<a href="http://projects.ceondo.com/p/indefero/" target="_blank">Indefero</a></p><p>The post <a href="https://grummfy.be/blog/368">Git, gitolite et mutualisé OVH</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>SQL : différences entre LEFT JOIN, RIGHT JOIN, etc</title>
		<link>https://grummfy.be/blog/364</link>
		
		<dc:creator><![CDATA[Grummfy]]></dc:creator>
		<pubDate>Mon, 07 Mar 2011 12:47:21 +0000</pubDate>
				<category><![CDATA[Réflexion du jour]]></category>
		<category><![CDATA[MySql]]></category>
		<category><![CDATA[programmation]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[trucs et astuces]]></category>
		<guid isPermaLink="false">http://grummfy.be/blog/?p=364</guid>

					<description><![CDATA[<p>J&#8217;ai toujours eu quelques diffuculté a bien visualisé les différence qu&#8217;il y avait entre left join, right join, join, etc lorsque je fait des requêtes SQL. Aujourd&#8217;hui je suis tombé sur un exemple frappant, et je me suis dit que cela pouvait en aider plus d&#8217;un! Comme une image vaux mieux qu&#8217;un long discours, en [&#8230;]</p>
<p>The post <a href="https://grummfy.be/blog/364">SQL : différences entre LEFT JOIN, RIGHT JOIN, etc</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>J&rsquo;ai toujours eu quelques diffuculté a bien visualisé les différence qu&rsquo;il y avait entre left join, right join, join, etc lorsque je fait des requêtes SQL. Aujourd&rsquo;hui je suis tombé sur un exemple frappant, et je me suis dit que cela pouvait en aider plus d&rsquo;un! Comme une image vaux mieux qu&rsquo;un long discours, en voici l&rsquo;essence.</p>
<p>L&rsquo;exemple suivant se base sur une base de donnée mysql :<br />
<code lang="SQL"><br />
CREATE TABLE IF NOT EXISTS `acl_roles` (<br />
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,<br />
`name` varchar(30) CHARACTER SET utf8 NOT NULL,<br />
`build_on` int(11) unsigned DEFAULT NULL,<br />
PRIMARY KEY (`id`),<br />
KEY `build_on` (`build_on`)<br />
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_bin;</p>
<p>INSERT INTO `acl_roles` (`id`, `name`, `build_on`) VALUES<br />
(1, 'guest', NULL),<br />
(2, 'normal', 1),<br />
(3, 'modo', 2),<br />
(4, 'admin', 3);</p>
<p>ALTER TABLE `acl_roles`  ADD CONSTRAINT FOREIGN KEY (`build_on`) REFERENCES `acl_roles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;<br />
</code></p>
<p>Nous avons donc une table avec des clefs de référence pour marquer les dépendance entre les roles. En images cela donne ceci :</p>
<table boder="1">
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>build_on</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>guest</td>
<td>NULL</td>
</tr>
<tr>
<td>2</td>
<td>normal</td>
<td>1</td>
</tr>
<tr>
<td>3</td>
<td>modo</td>
<td>2</td>
</tr>
<tr>
<td>4</td>
<td>admin</td>
<td>3</td>
</tr>
</tbody>
</table>
<p>Maintenant regardons le résultats de divers SELECT, le résultat parle de lui-même.</p>
<p>SELECT ar.*, arp.name AS parent_name FROM acl_roles ar, acl_roles arp WHERE arp.id = ar.build_on</p>
<table boder="1">
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>build_on</th>
<th>parent_name</th>
</tr>
</thead>
<tbody>
<tr>
<td align="right">2</td>
<td>normal</td>
<td align="right">1</td>
<td>guest</td>
</tr>
<tr>
<td align="right">3</td>
<td>modo</td>
<td align="right">2</td>
<td>normal</td>
</tr>
<tr>
<td align="right">4</td>
<td>admin</td>
<td align="right">3</td>
<td>modo</td>
</tr>
</tbody>
</table>
<p>SELECT ar.*, arp.name AS parent_name FROM acl_roles ar LEFT JOIN acl_roles arp ON arp.id = ar.build_on</p>
<table boder="1">
<thead>
<tr>
<th>id</th>
<th> name</th>
<th> build_on</th>
<th> parent_name</th>
</tr>
</thead>
<tbody>
<tr>
<td align="right">1</td>
<td>guest</td>
<td align="right"><em>NULL</em></td>
<td><em>NULL</em></td>
</tr>
<tr>
<td align="right">2</td>
<td>normal</td>
<td align="right">1</td>
<td>guest</td>
</tr>
<tr>
<td align="right">3</td>
<td>modo</td>
<td align="right">2</td>
<td>normal</td>
</tr>
<tr>
<td align="right">4</td>
<td>admin</td>
<td align="right">3</td>
<td>modo</td>
</tr>
</tbody>
</table>
<p>SELECT ar.*, arp.name AS parent_name FROM acl_roles ar JOIN acl_roles arp ON arp.id = ar.build_on</p>
<table boder="1">
<thead>
<tr>
<th>id</th>
<th> name</th>
<th> build_on</th>
<th> parent_name</th>
</tr>
</thead>
<tbody>
<tr>
<td align="right">2</td>
<td>normal</td>
<td align="right">1</td>
<td>guest</td>
</tr>
<tr>
<td align="right">3</td>
<td>modo</td>
<td align="right">2</td>
<td>normal</td>
</tr>
<tr>
<td align="right">4</td>
<td>admin</td>
<td align="right">3</td>
<td>modo</td>
</tr>
</tbody>
</table>
<p>SELECT ar.*, arp.name AS parent_name FROM acl_roles ar RIGHT JOIN acl_roles arp ON arp.id = ar.build_on</p>
<table boder="1">
<thead>
<tr>
<th>id</th>
<th> name</th>
<th> build_on</th>
<th> parent_name</th>
</tr>
</thead>
<tbody>
<tr>
<td align="right">2</td>
<td>normal</td>
<td align="right">1</td>
<td>guest</td>
</tr>
<tr>
<td align="right">3</td>
<td>modo</td>
<td align="right">2</td>
<td>normal</td>
</tr>
<tr>
<td align="right">4</td>
<td>admin</td>
<td align="right">3</td>
<td>modo</td>
</tr>
<tr>
<td align="right"><em>NULL</em></td>
<td><em>NULL</em></td>
<td align="right"><em>NULL</em></td>
<td>admin</td>
</tr>
</tbody>
</table>
<p>J&rsquo;espère que l&rsquo;exemple servira a certain et que cela en aidera plus d&rsquo;un!</p><p>The post <a href="https://grummfy.be/blog/364">SQL : différences entre LEFT JOIN, RIGHT JOIN, etc</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>ZF : ACL et ressources multiple</title>
		<link>https://grummfy.be/blog/360</link>
		
		<dc:creator><![CDATA[Grummfy]]></dc:creator>
		<pubDate>Wed, 13 Oct 2010 21:19:21 +0000</pubDate>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[programmation]]></category>
		<category><![CDATA[trucs et astuces]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[ZF]]></category>
		<guid isPermaLink="false">http://grummfy.be/blog/?p=360</guid>

					<description><![CDATA[<p>Utiliser les ACL dans Zend Framework en ajoutant le support de ressource multiple via '*'.</p>
<p>The post <a href="https://grummfy.be/blog/360">ZF : ACL et ressources multiple</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Lorsque l&rsquo;on utilise des ACL dans Zend Framework, une chose assez embêtante est de devoir tout mettre en place<sup class='footnote'><a href='#fn-360-1' id='fnref-360-1' onclick='return fdfootnote_show(360)'>1</a></sup>. Pour ma part, j&rsquo;ai par facilité voulu ajouté le support de ressource multiple.</p>
<p>Avant de commencer, il convient de contextualisé les choses, les ACL de ZF pouvant être utilisé de bien des manière.</p>
<p>Dans le cas qui nous intéresse, j&rsquo;ai simplement défini ceci :</p>
<ul>
<li>les ressources<sup class='footnote'><a href='#fn-360-2' id='fnref-360-2' onclick='return fdfootnote_show(360)'>2</a></sup> = module.controller</li>
<li>les privilèges<sup class='footnote'><a href='#fn-360-3' id='fnref-360-3' onclick='return fdfootnote_show(360)'>3</a></sup> = action</li>
</ul>
<p>Ce que je voulait c&rsquo;est pouvoir définir une ressource pour tous les contrôleurs. La syntaxe évidente qu&rsquo;il m&rsquo;est venu est la suivante : module.*</p>
<p>Dans ma classe qui étend Zend_ACL j&rsquo;ai simplement fait ceci :</p>
<pre lang="php">
    public function isAllowed($role = null, $resource = null, $privilege = null)
    {
    	if (null === $resource)
    		return parent::isAllowed($role, $resource, $privilege);

    	$resources = $this-&gt;getResourcesPossibility($resource);
    	foreach($resources as $resource)
    	{
    		if ($this-&gt;has($resource) &amp;&amp; parent::isAllowed($role, $resource, $privilege))
    		{
    			return true;
    		}
    	}
    	return false;
    }

    public function getResourcesPossibility($resource = null)
    {
    	$ret = array($resource);
    	if (null !== $resource)
    	{
    		$resources = explode('.', $resource);
    		$cptRessources = count($resources);
    		if ($cptRessources &gt;= 2)
    		{
    			$resources[ $cptRessources - 1 ] = '*';
    		}
    		$ret[] = implode('.', $resources);
    	}
    	return $ret;
    }
</pre>
<p>Ceci peut bien entendu être enrichi mais permet au moins de profiter de l&rsquo;utilisation des ACL dans le menu et sur des aides de vue qui serait éventuellement définie comme expliqué dans la plupart des tutoriaux.</p>
<p>Pour en savoir plus sur les ACL et Zend Framework, je vous renvoi a un très bon <a href="http://julien-pauli.developpez.com/tutoriels/zend-framework/atelier/aclmvc/" target="_blank">article</a>.</p>
<div class='footnotes' id='footnotes-360'>
<div class='footnotedivider'></div>
<ol>
<li id='fn-360-1'> surtout si on utilise l&rsquo;aide de vue pour généré un menu (Zend_navigation) <span class='footnotereverse'><a href='#fnref-360-1'>&#8617;</a></span></li>
<li id='fn-360-2'> resource <span class='footnotereverse'><a href='#fnref-360-2'>&#8617;</a></span></li>
<li id='fn-360-3'> privilege <span class='footnotereverse'><a href='#fnref-360-3'>&#8617;</a></span></li>
</ol>
</div><p>The post <a href="https://grummfy.be/blog/360">ZF : ACL et ressources multiple</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>ZF : module et autoloader</title>
		<link>https://grummfy.be/blog/343</link>
		
		<dc:creator><![CDATA[Grummfy]]></dc:creator>
		<pubDate>Sun, 22 Aug 2010 22:00:55 +0000</pubDate>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[programmation]]></category>
		<category><![CDATA[trucs et astuces]]></category>
		<category><![CDATA[ZF]]></category>
		<guid isPermaLink="false">http://grummfy.be/blog/?p=343</guid>

					<description><![CDATA[<p>Avec Zend Framework, lorsque l&#8217;on travail un projet conséquent il devient vite utile de travailler avec le mécanisme des modules. Celui-ci permet d&#8217;étendre pas mal de chose et surtout une séparation poussée en &#8230; module. Le problème de ce mécanisme1 est qu&#8217;il faut définir le chemin pour le chargement automatique à l&#8217;aide de ceci : [&#8230;]</p>
<p>The post <a href="https://grummfy.be/blog/343">ZF : module et autoloader</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Avec Zend Framework, lorsque l&rsquo;on travail un projet conséquent il devient vite utile de travailler avec le mécanisme des <a href="http://framework.zend.com/manual/fr/zend.controller.modular.html" target="_blank">modules</a>. Celui-ci permet d&rsquo;étendre pas mal de chose et surtout une séparation poussée en &#8230; module.</p>
<p>Le problème de ce mécanisme<sup class='footnote'><a href='#fn-343-1' id='fnref-343-1' onclick='return fdfootnote_show(343)'>1</a></sup> est qu&rsquo;il faut définir le chemin pour le chargement automatique à l&rsquo;aide de ceci :</p>
<pre lang="php">
$autoloader = new Zend_Application_Module_Autoloader(array(
	'namespace' =&gt; 'SuperModule',
	'basePath'  =&gt; 'path to super module',
));
</pre>
<p>Je vous propose donc de faire une petite amélioration afin que ce chargement soit fait automatiquement.</p>
<p>Avant tout, dans votre configuration (ici en .ini) vous devez au moins avoir ceci de présent :</p>
<pre lang="ini">
resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"
resources.modules =
</pre>
<p>Ensuite, à la base de chaque dossier module créer un fichier Bootstrap.php<sup class='footnote'><a href='#fn-343-2' id='fnref-343-2' onclick='return fdfootnote_show(343)'>2</a></sup> :</p>
<pre lang="php">
&lt;?php
class SuperModule_Bootstrap extends Grummfy_Bootstrap{}
# EOF
</pre>
<p>Et pour finir créer le fichier<sup class='footnote'><a href='#fn-343-3' id='fnref-343-3' onclick='return fdfootnote_show(343)'>3</a></sup> library/Grummfy/Bootstrap.php</p>
<pre lang="php">
&lt;?php

abstract class Grummfy_Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
	protected function _initAutoload()
	{
		$className = get_class($this);
		$zf_namespace = explode('_', $className);
		$reflector = new ReflectionClass($className);
		$autoloader = new Zend_Application_Module_Autoloader(array(
			'namespace' =&gt; $zf_namespace[0],
			'basePath'  =&gt; dirname($reflector-&gt;getFileName()),
		));
		return $autoloader;
	}
}

# EOF
</pre>
<div class='footnotes' id='footnotes-343'>
<div class='footnotedivider'></div>
<ol>
<li id='fn-343-1'> sauf si j&rsquo;ai loupé un truc&#8230; <span class='footnotereverse'><a href='#fnref-343-1'>&#8617;</a></span></li>
<li id='fn-343-2'> ne pas oublier de changer le nom du module &#8230; <span class='footnotereverse'><a href='#fnref-343-2'>&#8617;</a></span></li>
<li id='fn-343-3'> n&rsquo;oubliez pas de déclarer le « namespace » Grummfy ou bien d&rsquo;inclure le fichier <span class='footnotereverse'><a href='#fnref-343-3'>&#8617;</a></span></li>
</ol>
</div><p>The post <a href="https://grummfy.be/blog/343">ZF : module et autoloader</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>ZF : génération automatique du fichier de navigation et ACL</title>
		<link>https://grummfy.be/blog/333</link>
		
		<dc:creator><![CDATA[Grummfy]]></dc:creator>
		<pubDate>Mon, 16 Aug 2010 20:47:14 +0000</pubDate>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[délire]]></category>
		<category><![CDATA[jouons]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[programmation]]></category>
		<category><![CDATA[trucs et astuces]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[ZF]]></category>
		<guid isPermaLink="false">http://grummfy.be/blog/?p=333</guid>

					<description><![CDATA[<p>Lors de l&#8217;utilisation du mécanisme d&#8217;ACL et de génération de menu dans Zend Framework, il est intéressant de limiter l&#8217;affichage de ce menu en utilisant les ressources et privilèges associer. Pour ma part, j&#8217;utilise un fichier XML pour construire mon menu, mon sitemap, &#8230; 1. Et comme beaucoup je génère mon projet ZF à l&#8217;aide [&#8230;]</p>
<p>The post <a href="https://grummfy.be/blog/333">ZF : génération automatique du fichier de navigation et ACL</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Lors de l&rsquo;utilisation du mécanisme d&rsquo;ACL et de génération de menu dans Zend Framework, il est intéressant de limiter l&rsquo;affichage de ce menu en utilisant les ressources et privilèges associer.</p>
<p>Pour ma part, j&rsquo;utilise un fichier XML pour construire mon menu, mon sitemap, &#8230; <sup class='footnote'><a href='#fn-333-1' id='fnref-333-1' onclick='return fdfootnote_show(333)'>1</a></sup>. Et comme beaucoup je génère mon projet ZF à l&rsquo;aide de Zend_Tool. Je trouvais donc dommage de devoir réécrire pratiquement la même chose que ce que j&rsquo;avais déclaré dans Zend_Tool pour reconstruire mon menu. J&rsquo;ai donc décidé de <em>rapidement</em> écrire un petit script qui reprendrait le fichier XML du projet et le transformerait en menu &#8230;</p>
<p>À noter que le script devrait certainement être amélioré, mais que cela permet un gain de temps considérable &#8230;</p>
<p><a href="http://grummfy.com/download/PlusScript/zfProject2Navigation.phps" target="_blank">Téléchargement</a></p>
<div class='footnotes' id='footnotes-333'>
<div class='footnotedivider'></div>
<ol>
<li id='fn-333-1'> Comme expliquer dans le manuel http://framework.zend.com/manual/fr/zend.navigation.html <span class='footnotereverse'><a href='#fnref-333-1'>&#8617;</a></span></li>
</ol>
</div><p>The post <a href="https://grummfy.be/blog/333">ZF : génération automatique du fichier de navigation et ACL</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Réécriture d’URL, alias et plusieurs développeurs sur Apache</title>
		<link>https://grummfy.be/blog/321</link>
		
		<dc:creator><![CDATA[Grummfy]]></dc:creator>
		<pubDate>Thu, 12 Aug 2010 21:07:55 +0000</pubDate>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[trucs et astuces]]></category>
		<category><![CDATA[url rewriting]]></category>
		<category><![CDATA[web]]></category>
		<guid isPermaLink="false">http://grummfy.be/blog/?p=321</guid>

					<description><![CDATA[<p>Une solution à la problématique des réécritures d'url sans rewriteBase tout en étant utilisable par plusieurs développeurs</p>
<p>The post <a href="https://grummfy.be/blog/321">Réécriture d’URL, alias et plusieurs développeurs sur Apache</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Lorsque l&rsquo;on travaille à plusieurs sur un projet, c&rsquo;est toujours intéressant. Malheureusement, cela peut aussi entrainer divers problèmes. Je vais tenter de vous expliquer un ﻿﻿obstacle qui peut vite devenir très chi*nt&#8230;</p>
<p>Pour présenter cette problématique, je vais prendre exemple sur ce que je développe actuellement. Le site en cours de création se base sur zend framework et nécessite une réécriture d&rsquo;URL. Il faut savoir qu&rsquo;Apache utilise le chemin physique<sup class='footnote'><a href='#fn-321-1' id='fnref-321-1' onclick='return fdfootnote_show(321)'>1</a></sup> comme base pour calculer le chemin vers le fichier réécrit sauf si on lui précise une directive RewriteBase différente. Le problème survient à cet endroit, plusieurs développeurs entrainent plusieurs machines et donc plusieurs configurations différentes!</p>
<p>La solution de base est que chaque personne utilisant un alias Apache définit un RewriteBase. Cependant, cela veut dire qu’a chaque nouvelle version du fichier .htaccess il faut redéfinir celui-ci.</p>
<p>La réponse la plus simple consiste à utiliser un RewriteCond sur l&rsquo;hostname du serveur et bien entendu à l&rsquo;utiliser lors de l&rsquo;accès aux tests locaux ou non &#8230;</p>
<p>Exemple de configuration :</p>
<ul>
<li> nom du serveur : grummfy</li>
<li>URL appelée : http://grummfy/serveur/dev/projet/example.com/&#8230; (Si vous utilise http://localhost/ la directive HTTP_HOST vaudra localhost)</li>
<li>ALIAS : /serveur/ =&gt; /media/data/serveur/</li>
</ul>
<p>Le fichier .htacccess contiendra ceci :<br />
<code>SetEnv APPLICATION_ENV development<br />
php_value session.auto_start 0<br />
php_flag magic_quotes_gpc off<br />
RewriteEngine On<br />
RewriteCond %{REQUEST_FILENAME} -s [OR]<br />
RewriteCond %{REQUEST_FILENAME} -l [OR]<br />
RewriteCond %{REQUEST_FILENAME} -d<br />
RewriteRule ^.*$ - [NC,L]<br />
RewriteCond %{HTTP_HOST} grummfy<br />
RewriteRule ^.*$ /serveur/dev/projet/example.com/index.php [NC,L]<br />
RewriteRule ^.*$ index.php [NC,L]</code></p>
<p>Le fait d&rsquo;utiliser le rewrite flag « L » permet de sortir de la réécriture d&rsquo;URL. Si le hostname du serveur n&rsquo;est pas grummfy il appliquera la règle par défaut, à savoir tenter de trouver index.php dans /media/data/serveur/dev/projet/example.com/ comme si on avait effectuer un appel depuis http://media/data/serveur/dev/projet/example.com/</p>
<div class='footnotes' id='footnotes-321'>
<div class='footnotedivider'></div>
<ol>
<li id='fn-321-1'> physical-directory-path <span class='footnotereverse'><a href='#fnref-321-1'>&#8617;</a></span></li>
</ol>
</div><p>The post <a href="https://grummfy.be/blog/321">Réécriture d’URL, alias et plusieurs développeurs sur Apache</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Android : Tri d&#8217;une ListView tout en gardant les ids</title>
		<link>https://grummfy.be/blog/312</link>
		
		<dc:creator><![CDATA[Grummfy]]></dc:creator>
		<pubDate>Sat, 19 Jun 2010 22:50:19 +0000</pubDate>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[programmation]]></category>
		<category><![CDATA[Projet]]></category>
		<category><![CDATA[trucs et astuces]]></category>
		<guid isPermaLink="false">http://grummfy.be/blog/?p=312</guid>

					<description><![CDATA[<p>Menu ListView dans une ListActivity, trié selon un ordre alphabétique. Récupération de l'élément cliqué...</p>
<p>The post <a href="https://grummfy.be/blog/312">Android : Tri d’une ListView tout en gardant les ids</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Durant le développement du projet <a title="Projet B-Box" href="http://code.google.com/p/b-box/" target="_blank">b-box</a> j&rsquo;ai rencontré un problème avec mon menu. Ce menu est présent sous forme de ListActivity (comprenant une ListView), était trié selon un ordre alphabétique qui est susceptible de changer puisque l&rsquo;application peut-être traduite.</p>
<p>Au début, j&rsquo;ai essayé diverses méthodes, mais les id de position étant perdus&#8230; pas moyen de savoir à quoi correspond quoi.</p>
<p>J&rsquo;ai donc du trouver <a href="http://forum.frandroid.com/forum/viewtopic.php?id=15727" target="_blank">une solution</a>, solution que je vous présente.</p>
<h2>But</h2>
<p>Le but est de créer un menu sous forme de liste (ListView) dont chaque élément est cliquable et permet de récupérer l&rsquo;id du clic. Cet id servant à lancer une autre Activity, par exemple.</p>
<h2>Exemple</h2>
<p>Tout d&rsquo;abord la classe Menu :</p>
<pre lang="java">package org.android.bbox;

import java.util.Comparator;

public class Menu
{
	private int id;
	private String label;

	public Menu(int id, String label)
	{
		this.id = id;
		this.label = label;
	}

	public String getLabel()
	{
		return this.label;
	}

	public int getId()
	{
		return this.id;
	}

	public static Menu[] factory(String[] menus)
	{
		Menu[] menu = new Menu[menus.length];
		for(int i = 0; i &lt; menus.length; i++)
		{
			menu[ i ] = new Menu(i, menus[ i ]);
		}
		return menu;
	}

	public static Comparator getComparator()
	{
		return new Comparator(){
			@Override
			public int compare(Menu m1, Menu m2)
			{
				return m1.getLabel().compareTo(m2.getLabel());
			}
		};
	}

	public String toString()
	{
		return this.getLabel();
	}
}
</pre>
<p>Et la classe de l&rsquo;activité :</p>
<pre lang="java">// ...
	public void onCreate(Bundle savedInstanceState)
	{
		//...
		Menu[] m = Menu.factory(getResources().getStringArray(R.array.main_list_array));//récupération d'un tableau de string et création d'un tableau de Menu
		ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, m);//création de l'adaptateur permettant l'affichage
		adapter.sort(Menu.getComparator());//tri du menu
		ListView myList = (ListView) findViewById(android.R.id.list);
		myList.setAdapter(adapter);//ajotu du menu à la ListView
		myList.setOnItemClickListener(this);
		//...
	}

	@Override
	public void onItemClick(AdapterView parent, View view, int position, long id)
	{
		System.out.print(position);
		System.out.print(" | ");
		System.out.print(parent.getItemAtPosition(position));
		System.out.print(" | ");
		System.out.print(parent.getItemAtPosition(position).getClass());
		System.out.print(" | ");
		System.out.print(((Menu)parent.getItemAtPosition(position)).getId());
		System.out.print(" | ");
		System.out.println(id);
		//...
	}
// ...
</pre>
<p><strong>Remarque</strong> : la méthode toString permet l&rsquo;affichage de l&rsquo;élément dans la liste.</p><p>The post <a href="https://grummfy.be/blog/312">Android : Tri d’une ListView tout en gardant les ids</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>WebHook Google Code &#8211; recevoir un mail à chaque commit</title>
		<link>https://grummfy.be/blog/310</link>
		
		<dc:creator><![CDATA[Grummfy]]></dc:creator>
		<pubDate>Fri, 18 Jun 2010 23:58:59 +0000</pubDate>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[découverte]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[programmation]]></category>
		<category><![CDATA[script]]></category>
		<category><![CDATA[trucs et astuces]]></category>
		<category><![CDATA[web]]></category>
		<guid isPermaLink="false">http://grummfy.be/blog/?p=310</guid>

					<description><![CDATA[<p>Google code et webhook : envoyer un email a chaque commit</p>
<p>The post <a href="https://grummfy.be/blog/310">WebHook Google Code – recevoir un mail à chaque commit</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Dans Google code il y a la possibilité d&rsquo;utiliser un gestionnaire de version tel que subversion (svn) ou mercurial (hg). C&rsquo;est bien pratique, mais malheureusement, de base, rien n&rsquo;est prévu pour prévenir (excepté par flux RSS) les gens de ces mises à jour. Cependant, Google code permet d&rsquo;utiliser un webhook en post commit.</p>
<h2>Qu&rsquo;est-ce qu&rsquo;un webhook?</h2>
<p>Un webhook c&rsquo;est un « crochet web », c&rsquo;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.</p>
<h2>Utilisation</h2>
<p>Voici un exemple de code que j&rsquo;utilise pour plusieurs de mes projets :</p>
<pre lang="PHP"><?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</pre>
</p>
<p>Plus d&rsquo;info : <a href="http://code.google.com/p/support/wiki/PostCommitWebHooks">PostCommitWebHooks</a></p><p>The post <a href="https://grummfy.be/blog/310">WebHook Google Code – recevoir un mail à chaque commit</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Espace de nom PHP et chargement automatique</title>
		<link>https://grummfy.be/blog/285</link>
		
		<dc:creator><![CDATA[Grummfy]]></dc:creator>
		<pubDate>Mon, 07 Jun 2010 08:42:44 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[namespace]]></category>
		<category><![CDATA[programmation]]></category>
		<category><![CDATA[web]]></category>
		<guid isPermaLink="false">http://grummfy.be/blog/?p=285</guid>

					<description><![CDATA[<p>PHP 5.3 ajoute une notion intéressante : les espaces de nom. Voyons voir comment créer un chargeur automatique (ou autoloader) comprenant ceux-ci.</p>
<p>The post <a href="https://grummfy.be/blog/285">Espace de nom PHP et chargement automatique</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></description>
										<content:encoded><![CDATA[<p><a href="./zend-framework">PHP</a> 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 &#8230; « <em>espace</em>« , permettant ainsi d&rsquo;avoir deux classe portant le même nom. Idéal pour l&rsquo;utilisation de framework mais aussi de « l&rsquo;isolation » de certains composants. Voyons voir comment créer un chargeur automatique (ou autoloader) comprenant les espaces de nom.</p>
<p>Si vous êtes sous Ms Windows, aucun problème un simple <em>spl_autoload_register();</em> suffit. Malheureusement, sous *nix un <a href="http://bugs.php.net/bug.php?id=51991" target="_blank">bug existe</a> il faudra donc implémenté une solution maison.</p>
<h2>Le code</h2>
<p>Ce code provient du « <a href="http://groups.google.com/group/php-standards/web/psr-0-final-proposal">PHP  Standards Working Group</a> » :</p>
<pre lang="PHP">
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;
}
</pre>
</p>
<p>Pour l&rsquo;utiliser, il faut définir deux choses :</p>
<ol>
<li>Ne pas oublier de modifier l&rsquo;include path si nécessaire.</li>
<li>Ajouter cette fonction au chargeur de classes présent.</li>
</ol>
<h2>L&rsquo;exemple</h2>
<p>Voici un exemple un peu plus complet.<br />
index.php</p>
<pre lang="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
</pre>
<p>./lib/grummfy/test/Test.php</p>
<pre lang="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
</pre>
<p>./lib/grummfy/std/Test.php</p>
<pre lang="PHP">
<?php
namespace grummfy\std;
echo 'Je suis inclus (' . __FILE__ . ')!';
class Test
{
	public static function sayHello()
	{
		echo 'Bonjour depuis ' . __CLASS__;
	}
}

# EOF
</pre>
<p>Le résultat devrait être :<br />
<code><br />
Je suis inclus (/.../lib/grummfy/test/Test.php)!<br />
Bonjour depuis grummfy\test\Test<br />
Je suis inclus (/.../lib/grummfy/std/Test.php)!<br />
Bonjour depuis grummfy\std\Test<br />
</code></p><p>The post <a href="https://grummfy.be/blog/285">Espace de nom PHP et chargement automatique</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Réécriture d&#8217;url et erreur interne à Apache</title>
		<link>https://grummfy.be/blog/289</link>
		
		<dc:creator><![CDATA[Grummfy]]></dc:creator>
		<pubDate>Fri, 04 Jun 2010 13:15:29 +0000</pubDate>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[deboguage]]></category>
		<category><![CDATA[serveur]]></category>
		<category><![CDATA[url rewriting]]></category>
		<category><![CDATA[web]]></category>
		<guid isPermaLink="false">http://grummfy.be/blog/?p=289</guid>

					<description><![CDATA[<p>Il arrive que lors de l'écriture d'un nombre complexe de règle de réécriture d'URL, des erreurs se produisent. Passons au débogage de celles-ci.</p>
<p>The post <a href="https://grummfy.be/blog/289">Réécriture d’url et erreur interne à Apache</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Il arrive que lors de l&rsquo;écriture d&rsquo;un nombre complexe de règle de réécriture d&rsquo;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.</p>
<h2>Exemple</h2>
<p>Prenons une règles qui vérifierait que seul certains type de caractère sont autorisés.<br />
La réécriture quant à elle se fait sur toute URL.<br />
Ajoutons un document 403 personnalisé<br />
<code>RewriteEngine on<br />
#<br />
RewriteCond %{THE_REQUEST} !^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&amp;]+\ HTTP/ [NC]<br />
RewriteRule .* - [F,NS,L]<br />
#<br />
#if not a dir or a file<br />
RewriteCond %{REQUEST_FILENAME} !-f<br />
RewriteCond %{REQUEST_FILENAME} !-d<br />
#<br />
RewriteRule ^(.*)$ index.php?p=$1 [QSA,L]<br />
ErrorDocument 403 /erreur/403<br />
</code><br />
Testons l&rsquo;URL http://localhost/test:s et on obtient un beau :</p>
<blockquote><p>Forbidden</p>
<p>You don&rsquo;t have permission to access /test:s on this server.</p>
<p>Additionally, a 500 Internal Server Error error was encountered while trying to use an ErrorDocument to handle the request.</p></blockquote>
<p>Les logs d&rsquo;erreurs d&rsquo;Apache vous diront certainement quelque chose du genre :</p>
<blockquote><p>[error] [client 127.0.0.1] Request exceeded the limit of 10 internal  redirects due to probable configuration error. Use  &lsquo;LimitInternalRecursion&rsquo; to increase the limit if necessary. Use  &lsquo;LogLevel debug&rsquo; to get a backtrace.</p></blockquote>
<h2>Passage en mode debug de Apache</h2>
<p>Modifions la règle de log du site courant (soit dans /etc/apache2/sites-available/default soit dans /etc/apache2/apache.conf ou l&rsquo;équivalent suivant votre configuration ou OS) et modifions la directive <em>« LogLevel warn »</em> en <em>« LogLevel debug »</em>. Ceci ne change rien dans notre cas, mais parfois cela s&rsquo;avère utile&#8230;</p>
<p>Ajoutons ensuite les log<sup class='footnote'><a href='#fn-289-1' id='fnref-289-1' onclick='return fdfootnote_show(289)'>1</a></sup> de réécriture d&rsquo;url (moi je l&rsquo;ai ajouter dans /etc/apache2/mods-available/rewrite.conf puis j&rsquo;ai fait un a2enmod rewrite (car le fichier .conf n&rsquo;existait pas) et enfin j&rsquo;ai relancer Apache) :<br />
<code>&lt;IfModule mod_rewrite.c&gt;<br />
RewriteLog "/var/log/apache2/rewrite.log"<br />
RewriteLogLevel 9<br />
&lt;/IfModule&gt;</code></p>
<p>Maintenant si vous aller voir dans error.log vous aurez :</p>
<blockquote><p>[error] [client 127.0.0.1] Request exceeded the limit of 10 internal redirects due to probable configuration error. Use &lsquo;LimitInternalRecursion&rsquo; to increase the limit if necessary. Use &lsquo;LogLevel debug&rsquo; to get a backtrace.<br />
[debug] core.c(3063): [client 127.0.0.1] r-&gt;uri = /&#8230;/erreur/403<br />
[debug] core.c(3069): [client 127.0.0.1] redirected from r-&gt;uri = /&#8230;/erreur/403<br />
[debug] core.c(3069): [client 127.0.0.1] redirected from r-&gt;uri = /&#8230;/erreur/403<br />
[debug] core.c(3069): [client 127.0.0.1] redirected from r-&gt;uri = /&#8230;/erreur/403<br />
[debug] core.c(3069): [client 127.0.0.1] redirected from r-&gt;uri = /&#8230;/erreur/403<br />
[debug] core.c(3069): [client 127.0.0.1] redirected from r-&gt;uri = /&#8230;/erreur/403<br />
[debug] core.c(3069): [client 127.0.0.1] redirected from r-&gt;uri = /&#8230;/erreur/403<br />
[debug] core.c(3069): [client 127.0.0.1] redirected from r-&gt;uri = /&#8230;/erreur/403<br />
[debug] core.c(3069): [client 127.0.0.1] redirected from r-&gt;uri = /&#8230;/erreur/403<br />
[debug] core.c(3069): [client 127.0.0.1] redirected from r-&gt;uri = /&#8230;/erreur/403<br />
[debug] core.c(3069): [client 127.0.0.1] redirected from r-&gt;uri = /&#8230;/test:s</p></blockquote>
<p>Donc on voit que la redirection s&rsquo;effectue correctement mais qu&rsquo;il « <em>n&rsquo;accroche</em> » pas. Regardons donc les logs de la réécriture d&rsquo;URL (rewrite.log) :</p>
<blockquote><p>/initial] (3) [perdir /&#8230;/] strip per-dir prefix: /&#8230;/test:s -&gt; test:s<br />
/initial] (3) [perdir /&#8230;/] applying pattern &lsquo;.*&rsquo; to uri &lsquo;test:s&rsquo;<br />
/initial] (4) [perdir /&#8230;/] RewriteCond: input= » pattern=&rsquo;200&prime; =&gt; not-matched<br />
/initial] (3) [perdir /&#8230;/] strip per-dir prefix: /&#8230;/test:s -&gt; test:s<br />
/initial] (3) [perdir /&#8230;/] applying pattern &lsquo;.*&rsquo; to uri &lsquo;test:s&rsquo;<br />
/initial] (4) [perdir /&#8230;/] RewriteCond: input=&rsquo;GET /&#8230;/test:s HTTP/1.1&prime; pattern=&rsquo;!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&amp;]+\ HTTP/&rsquo; [NC] =&gt; matched<br />
/initial] (2) [perdir /&#8230;/] forcing responsecode 403 for /&#8230;/test:s<br />
/initial/redir#1] (3) [perdir /&#8230;/] add path info postfix: /&#8230;/erreur -&gt; /&#8230;/erreur/403<br />
/initial/redir#1] (3) [perdir /&#8230;/] strip per-dir prefix: /&#8230;/erreur/403 -&gt; erreur/403<br />
/initial/redir#1] (3) [perdir /&#8230;/] applying pattern &lsquo;.*&rsquo; to uri &lsquo;erreur/403&prime;<br />
/initial/redir#1] (4) [perdir /&#8230;/] RewriteCond: input=&rsquo;403&prime; pattern=&rsquo;200&rsquo; =&gt; not-matched<br />
/initial/redir#1] (3) [perdir /&#8230;/] add path info postfix: /&#8230;/erreur -&gt; /&#8230;/erreur/403<br />
/initial/redir#1] (3) [perdir /&#8230;/] strip per-dir prefix: /&#8230;/erreur/403 -&gt; erreur/403<br />
/initial/redir#1] (3) [perdir /&#8230;/] applying pattern &lsquo;.*&rsquo; to uri &lsquo;erreur/403&prime;<br />
/initial/redir#1] (4) [perdir /&#8230;/] RewriteCond: input=&rsquo;GET /&#8230;/test:s HTTP/1.1&prime; pattern=&rsquo;!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&amp;]+\ HTTP/&rsquo; [NC] =&gt; matched<br />
/initial/redir#1] (2) [perdir /&#8230;/] forcing responsecode 403 for /&#8230;/erreur<br />
/initial/redir#2] (3) [perdir /&#8230;/] add path info postfix: /&#8230;/erreur -&gt; /&#8230;/erreur/403<br />
/initial/redir#2] (3) [perdir /&#8230;/] strip per-dir prefix: /&#8230;/erreur/403 -&gt; erreur/403<br />
/initial/redir#2] (3) [perdir /&#8230;/] applying pattern &lsquo;.*&rsquo; to uri &lsquo;erreur/403&prime;<br />
/initial/redir#2] (4) [perdir /&#8230;/] RewriteCond: input=&rsquo;403&prime; pattern=&rsquo;200&rsquo; =&gt; not-matched<br />
/initial/redir#2] (3) [perdir /&#8230;/] add path info postfix: /&#8230;/erreur -&gt; /&#8230;/erreur/403<br />
/initial/redir#2] (3) [perdir /&#8230;/] strip per-dir prefix: /&#8230;/erreur/403 -&gt; erreur/403<br />
/initial/redir#2] (3) [perdir /&#8230;/] applying pattern &lsquo;.*&rsquo; to uri &lsquo;erreur/403&prime;<br />
/initial/redir#2] (4) [perdir /&#8230;/] RewriteCond: input=&rsquo;GET /&#8230;/test:s HTTP/1.1&prime; pattern=&rsquo;!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&amp;]+\ HTTP/&rsquo; [NC] =&gt; matched<br />
/initial/redir#2] (2) [perdir /&#8230;/] forcing responsecode 403 for /&#8230;/erreur<br />
/initial/redir#3] (3) [perdir /&#8230;/] add path info postfix: /&#8230;/erreur -&gt; /&#8230;/erreur/403<br />
/initial/redir#3] (3) [perdir /&#8230;/] strip per-dir prefix: /&#8230;/erreur/403 -&gt; erreur/403<br />
/initial/redir#3] (3) [perdir /&#8230;/] applying pattern &lsquo;.*&rsquo; to uri &lsquo;erreur/403&prime;<br />
/initial/redir#3] (4) [perdir /&#8230;/] RewriteCond: input=&rsquo;403&prime; pattern=&rsquo;200&rsquo; =&gt; not-matched<br />
/initial/redir#3] (3) [perdir /&#8230;/] add path info postfix: /&#8230;/erreur -&gt; /&#8230;/erreur/403<br />
/initial/redir#3] (3) [perdir /&#8230;/] strip per-dir prefix: /&#8230;/erreur/403 -&gt; erreur/403<br />
/initial/redir#3] (3) [perdir /&#8230;/] applying pattern &lsquo;.*&rsquo; to uri &lsquo;erreur/403&prime;<br />
/initial/redir#3] (4) [perdir /&#8230;/] RewriteCond: input=&rsquo;GET /&#8230;/test:s HTTP/1.1&prime; pattern=&rsquo;!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&amp;]+\ HTTP/&rsquo; [NC] =&gt; matched<br />
/initial/redir#3] (2) [perdir /&#8230;/] forcing responsecode 403 for /&#8230;/erreur<br />
/initial/redir#4] (3) [perdir /&#8230;/] add path info postfix: /&#8230;/erreur -&gt; /&#8230;/erreur/403<br />
/initial/redir#4] (3) [perdir /&#8230;/] strip per-dir prefix: /&#8230;/erreur/403 -&gt; erreur/403<br />
/initial/redir#4] (3) [perdir /&#8230;/] applying pattern &lsquo;.*&rsquo; to uri &lsquo;erreur/403&prime;<br />
/initial/redir#4] (4) [perdir /&#8230;/] RewriteCond: input=&rsquo;403&prime; pattern=&rsquo;200&rsquo; =&gt; not-matched<br />
/initial/redir#4] (3) [perdir /&#8230;/] add path info postfix: /&#8230;/erreur -&gt; /&#8230;/erreur/403<br />
/initial/redir#4] (3) [perdir /&#8230;/] strip per-dir prefix: /&#8230;/erreur/403 -&gt; erreur/403<br />
/initial/redir#4] (3) [perdir /&#8230;/] applying pattern &lsquo;.*&rsquo; to uri &lsquo;erreur/403&prime;<br />
/initial/redir#4] (4) [perdir /&#8230;/] RewriteCond: input=&rsquo;GET /&#8230;/test:s HTTP/1.1&prime; pattern=&rsquo;!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&amp;]+\ HTTP/&rsquo; [NC] =&gt; matched<br />
/initial/redir#4] (2) [perdir /&#8230;/] forcing responsecode 403 for /&#8230;/erreur<br />
/initial/redir#5] (3) [perdir /&#8230;/] add path info postfix: /&#8230;/erreur -&gt; /&#8230;/erreur/403<br />
/initial/redir#5] (3) [perdir /&#8230;/] strip per-dir prefix: /&#8230;/erreur/403 -&gt; erreur/403<br />
/initial/redir#5] (3) [perdir /&#8230;/] applying pattern &lsquo;.*&rsquo; to uri &lsquo;erreur/403&prime;<br />
/initial/redir#5] (4) [perdir /&#8230;/] RewriteCond: input=&rsquo;403&prime; pattern=&rsquo;200&rsquo; =&gt; not-matched<br />
/initial/redir#5] (3) [perdir /&#8230;/] add path info postfix: /&#8230;/erreur -&gt; /&#8230;/erreur/403<br />
/initial/redir#5] (3) [perdir /&#8230;/] strip per-dir prefix: /&#8230;/erreur/403 -&gt; erreur/403<br />
/initial/redir#5] (3) [perdir /&#8230;/] applying pattern &lsquo;.*&rsquo; to uri &lsquo;erreur/403&prime;<br />
/initial/redir#5] (4) [perdir /&#8230;/] RewriteCond: input=&rsquo;GET /&#8230;/test:s HTTP/1.1&prime; pattern=&rsquo;!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&amp;]+\ HTTP/&rsquo; [NC] =&gt; matched<br />
/initial/redir#5] (2) [perdir /&#8230;/] forcing responsecode 403 for /&#8230;/erreur<br />
/initial/redir#6] (3) [perdir /&#8230;/] add path info postfix: /&#8230;/erreur -&gt; /&#8230;/erreur/403<br />
/initial/redir#6] (3) [perdir /&#8230;/] strip per-dir prefix: /&#8230;/erreur/403 -&gt; erreur/403<br />
/initial/redir#6] (3) [perdir /&#8230;/] applying pattern &lsquo;.*&rsquo; to uri &lsquo;erreur/403&prime;<br />
/initial/redir#6] (4) [perdir /&#8230;/] RewriteCond: input=&rsquo;403&prime; pattern=&rsquo;200&rsquo; =&gt; not-matched<br />
/initial/redir#6] (3) [perdir /&#8230;/] add path info postfix: /&#8230;/erreur -&gt; /&#8230;/erreur/403<br />
/initial/redir#6] (3) [perdir /&#8230;/] strip per-dir prefix: /&#8230;/erreur/403 -&gt; erreur/403<br />
/initial/redir#6] (3) [perdir /&#8230;/] applying pattern &lsquo;.*&rsquo; to uri &lsquo;erreur/403&prime;<br />
/initial/redir#6] (4) [perdir /&#8230;/] RewriteCond: input=&rsquo;GET /&#8230;/test:s HTTP/1.1&prime; pattern=&rsquo;!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&amp;]+\ HTTP/&rsquo; [NC] =&gt; matched<br />
/initial/redir#6] (2) [perdir /&#8230;/] forcing responsecode 403 for /&#8230;/erreur<br />
/initial/redir#7] (3) [perdir /&#8230;/] add path info postfix: /&#8230;/erreur -&gt; /&#8230;/erreur/403<br />
/initial/redir#7] (3) [perdir /&#8230;/] strip per-dir prefix: /&#8230;/erreur/403 -&gt; erreur/403<br />
/initial/redir#7] (3) [perdir /&#8230;/] applying pattern &lsquo;.*&rsquo; to uri &lsquo;erreur/403&prime;<br />
/initial/redir#7] (4) [perdir /&#8230;/] RewriteCond: input=&rsquo;403&prime; pattern=&rsquo;200&rsquo; =&gt; not-matched<br />
/initial/redir#7] (3) [perdir /&#8230;/] add path info postfix: /&#8230;/erreur -&gt; /&#8230;/erreur/403<br />
/initial/redir#7] (3) [perdir /&#8230;/] strip per-dir prefix: /&#8230;/erreur/403 -&gt; erreur/403<br />
/initial/redir#7] (3) [perdir /&#8230;/] applying pattern &lsquo;.*&rsquo; to uri &lsquo;erreur/403&prime;<br />
/initial/redir#7] (4) [perdir /&#8230;/] RewriteCond: input=&rsquo;GET /&#8230;/test:s HTTP/1.1&prime; pattern=&rsquo;!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&amp;]+\ HTTP/&rsquo; [NC] =&gt; matched<br />
/initial/redir#7] (2) [perdir /&#8230;/] forcing responsecode 403 for /&#8230;/erreur<br />
/initial/redir#8] (3) [perdir /&#8230;/] add path info postfix: /&#8230;/erreur -&gt; /&#8230;/erreur/403<br />
/initial/redir#8] (3) [perdir /&#8230;/] strip per-dir prefix: /&#8230;/erreur/403 -&gt; erreur/403<br />
/initial/redir#8] (3) [perdir /&#8230;/] applying pattern &lsquo;.*&rsquo; to uri &lsquo;erreur/403&prime;<br />
/initial/redir#8] (4) [perdir /&#8230;/] RewriteCond: input=&rsquo;403&prime; pattern=&rsquo;200&rsquo; =&gt; not-matched<br />
/initial/redir#8] (3) [perdir /&#8230;/] add path info postfix: /&#8230;/erreur -&gt; /&#8230;/erreur/403<br />
/initial/redir#8] (3) [perdir /&#8230;/] strip per-dir prefix: /&#8230;/erreur/403 -&gt; erreur/403<br />
/initial/redir#8] (3) [perdir /&#8230;/] applying pattern &lsquo;.*&rsquo; to uri &lsquo;erreur/403&prime;<br />
/initial/redir#8] (4) [perdir /&#8230;/] RewriteCond: input=&rsquo;GET /&#8230;/test:s HTTP/1.1&prime; pattern=&rsquo;!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&amp;]+\ HTTP/&rsquo; [NC] =&gt; matched<br />
/initial/redir#8] (2) [perdir /&#8230;/] forcing responsecode 403 for /&#8230;/erreur<br />
/initial/redir#9] (3) [perdir /&#8230;/] add path info postfix: /&#8230;/erreur -&gt; /&#8230;/erreur/403<br />
/initial/redir#9] (3) [perdir /&#8230;/] strip per-dir prefix: /&#8230;/erreur/403 -&gt; erreur/403<br />
/initial/redir#9] (3) [perdir /&#8230;/] applying pattern &lsquo;.*&rsquo; to uri &lsquo;erreur/403&prime;<br />
/initial/redir#9] (4) [perdir /&#8230;/] RewriteCond: input=&rsquo;403&prime; pattern=&rsquo;200&rsquo; =&gt; not-matched<br />
/initial/redir#9] (3) [perdir /&#8230;/] add path info postfix: /&#8230;/erreur -&gt; /&#8230;/erreur/403<br />
/initial/redir#9] (3) [perdir /&#8230;/] strip per-dir prefix: /&#8230;/erreur/403 -&gt; erreur/403<br />
/initial/redir#9] (3) [perdir /&#8230;/] applying pattern &lsquo;.*&rsquo; to uri &lsquo;erreur/403&prime;<br />
/initial/redir#9] (4) [perdir /&#8230;/] RewriteCond: input=&rsquo;GET /&#8230;/test:s HTTP/1.1&prime; pattern=&rsquo;!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&amp;]+\ HTTP/&rsquo; [NC] =&gt; matched<br />
/initial/redir#9] (2) [perdir /&#8230;/] forcing responsecode 403 for /&#8230;/erreur<br />
/initial/redir#10] (3) [perdir /&#8230;/] add path info postfix: /&#8230;/erreur -&gt; /&#8230;/erreur/403<br />
/initial/redir#10] (3) [perdir /&#8230;/] strip per-dir prefix: /&#8230;/erreur/403 -&gt; erreur/403<br />
/initial/redir#10] (3) [perdir /&#8230;/] applying pattern &lsquo;.*&rsquo; to uri &lsquo;erreur/403&prime;<br />
/initial/redir#10] (4) [perdir /&#8230;/] RewriteCond: input=&rsquo;403&prime; pattern=&rsquo;200&rsquo; =&gt; not-matched<br />
/initial/redir#10] (3) [perdir /&#8230;/] add path info postfix: /&#8230;/erreur -&gt; /&#8230;/erreur/403<br />
/initial/redir#10] (3) [perdir /&#8230;/] strip per-dir prefix: /&#8230;/erreur/403 -&gt; erreur/403<br />
/initial/redir#10] (3) [perdir /&#8230;/] applying pattern &lsquo;.*&rsquo; to uri &lsquo;erreur/403&prime;<br />
/initial/redir#10] (4) [perdir /&#8230;/] RewriteCond: input=&rsquo;GET /&#8230;/test:s HTTP/1.1&prime; pattern=&rsquo;!^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&amp;]+\ HTTP/&rsquo; [NC] =&gt; matched<br />
/initial/redir#10] (2) [perdir /&#8230;/] forcing responsecode 403 for /&#8230;/erreur</p></blockquote>
<p>En gros, nous avions une boucle de redirection. Pour l&rsquo;éviter ajoutons,d ans le .htaccess, une condition qui dira de ne pas rentrer dans le bloc, s&rsquo;il y a une erreur 403 :<br />
<code>RewriteEngine on<br />
#<br />
RewriteCond %{ENV:REDIRECT_STATUS} !403<br />
RewriteCond %{THE_REQUEST} !^[A-Z]{3,9}\ [a-zA-Z0-9\.\+_/\-\?\=\&amp;]+\ HTTP/ [NC]<br />
RewriteRule .* - [F,NS,L]<br />
#<br />
#if not a dir or a file<br />
RewriteCond %{REQUEST_FILENAME} !-f<br />
RewriteCond %{REQUEST_FILENAME} !-d<br />
#<br />
RewriteRule ^(.*)$ index.php?p=$1 [QSA,L]<br />
ErrorDocument 403 /erreur/403<br />
</code><br />
Maintenant tout fonctionne comme prévu!</p>
<h2>La fin</h2>
<p>Pour finir, ceci montre encore l&rsquo;importance des logs lors de la rencontre de problème. S&rsquo;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.</p>
<p>Un dernier conseil, pensez a désactiver le mode debug de Apache une fois fini!</p>
<p>S&rsquo;il y avait un site a conseiller sur ce sujet (en dehors du manuel Apache) je vous renverrait vers <a href="http://www.askapache.com/htaccess/mod_rewrite-tips-and-tricks.html" target="_blank">askapache</a></p>
<div class='footnotes' id='footnotes-289'>
<div class='footnotedivider'></div>
<ol>
<li id='fn-289-1'> Si le fichier ne se créer pas tout seul penser à le créer <span class='footnotereverse'><a href='#fnref-289-1'>&#8617;</a></span></li>
</ol>
</div><p>The post <a href="https://grummfy.be/blog/289">Réécriture d’url et erreur interne à Apache</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>URL rewriting et SEO : duplication de contenu</title>
		<link>https://grummfy.be/blog/268</link>
		
		<dc:creator><![CDATA[Grummfy]]></dc:creator>
		<pubDate>Wed, 02 Jun 2010 20:09:53 +0000</pubDate>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[référencement]]></category>
		<category><![CDATA[SEO]]></category>
		<category><![CDATA[url rewriting]]></category>
		<category><![CDATA[web]]></category>
		<guid isPermaLink="false">http://grummfy.be/blog/?p=268</guid>

					<description><![CDATA[<p>Article présentant l'URL rewriting et ce qu'est SEO pour arriver à la problématique de la réécriture d'URL : la duplication de contenu et comment l'éviter.</p>
<p>The post <a href="https://grummfy.be/blog/268">URL rewriting et SEO : duplication de contenu</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Cet article présente rapidement l&rsquo;<acronym title="Uniform Resource Locators">URL</acronym> rewriting et ce qu&rsquo;est SEO pour arriver au cœur du sujet, à savoir, la problématique de la réécriture d&rsquo;URL : la duplication de contenu. Pourquoi la duplication de contenu est gênante et surtout comment l&rsquo;éviter sont expliqué dans la suite!<span id="more-268"></span></p>
<h2>Mise en bouche</h2>
<p>L&rsquo;URL rewriting ou réécriture d&rsquo;<acronym title="Uniform Resource Locators">URL</acronym><sup class='footnote'><a href='#fn-268-1' id='fnref-268-1' onclick='return fdfootnote_show(268)'>1</a></sup> permet de réécrire de manière plus ou moins dynamique des URL (par exemple transformer http://www.example.com/ en http://example.com/ avec une redirection ou encore http://example.com/contact/moi/par/formulaire en http://example.com/contact.php).</p>
<p>SEO ou Search Engine Optimization consiste à optimiser le référencement d&rsquo;un site.</p>
<h2>Exemple</h2>
<p>Afin de débuter, il convient de donner un exemple fonctionnel. Reprenons donc un fichier .htaccess de base :<code><br />
&lt;IfModule mod_rewrite.c&gt;<br />
RewriteEngine on<br />
#si votre fichier se trouve dans /chemin vers votre répertoire de base de votre serveur/test/<br />
RewriteBase /test/<br />
#<br />
#On élimine les fichiers et répertoires qui existent déjà de la réécriture<br />
RewriteCond %{REQUEST_FILENAME} !-f<br />
RewriteCond %{REQUEST_FILENAME} !-d<br />
#réécrit l'url<br />
RewriteRule ^(.+)$ index.php?path=$1 [QSA,L]<br />
&lt;/IfModule&gt;<br />
</code><br />
Je suppose que vous tester votre script localement, que vous êtes dans le dossier test et que le fichier php est index.php (qui suit).</p>
<pre lang="PHP"><?php
#
echo '<pre>';
var_dump($_SERVER['SCRIPT_FILENAME']);
var_dump($_SERVER['REDIRECT_QUERY_STRING']);
var_dump($_SERVER['REDIRECT_URL']);
var_dump($_SERVER['REQUEST_URI']);
var_dump($_SERVER['SCRIPT_NAME']);
var_dump($_SERVER['PHP_SELF']);
var_dump($_SERVER['QUERY_STRING']);
var_dump($_REQUEST);
echo '</ pre>';
#
# EOF
</pre>
<p>Si vous testez le script, essayez d&rsquo;y accéder par http://localhost/test/ceci-est_un-test?toto=tata puis par http://127.0.0.1/ceci-est_un-test?toto=tata . Dans le deuxième cas, une redirection 301 (fichier bouger de manière permanente) est effectuée. Concernant, l&rsquo;explication en détail de la réécriture d&rsquo;URL je vous renvoi vers votre moteur de recherche favoris.</p>
<h2>Double accès</h2>
<p>Maintenant, un  problème se pose. En effet, votre site est accessible par plusieurs URL différentes pour un même contenu, les moteurs de recherche interpréteront cela comme du duplicata de contenu <sup class='footnote'><a href='#fn-268-2' id='fnref-268-2' onclick='return fdfootnote_show(268)'>2</a></sup>! Pour résoudre ce problème, il faut simplement faire en sorte que seule une seule et unique adresse soit accessible.</p>
<p>Premièrement, vérifier que l&rsquo;on provient du bon domaine (par exemple : n&rsquo;autoriser que example.com et non pas www.example.com ou uniquement localhost et non pas 127.0.0.1), le fichier .htaccess devient :<br />
<code><br />
&lt;IfModule mod_rewrite.c&gt;<br />
RewriteEngine on<br />
#si votre fichier se trouve dans /chemin vers votre répertoire de base de votre serveur/test/<br />
RewriteBase /test/<br />
#<br />
#test si on est bien sur le domaine localhost et non 127.0.0.1 ou autre chose<br />
RewriteCond %{HTTP_HOST} !^localhost<br />
#QSA pour transmettre le query string<br />
#L pour dire de sortir de la boucle de réécriture<br />
#R pour rediriger avec le code HTTP 301<br />
RewriteRule ^(.*) http://localhost/test/$1 [QSA,L,R=301]<br />
#<br />
#On élimine les fichiers et répertoires qui existent déjà de la réécriture<br />
RewriteCond %{REQUEST_FILENAME} !-f<br />
RewriteCond %{REQUEST_FILENAME} !-d<br />
#réécrit l'url<br />
RewriteRule ^(.+)$ index.php?path=$1 [QSA,L]<br />
&lt;/IfModule&gt;<br />
</code></p>
<p>Ensuite, il faut dire aux moteurs de recherche de ne pas indexer les URL du genre index.php?mesparametres. Pour cela, on créé ou modifie le fichier robots.txt<sup class='footnote'><a href='#fn-268-3' id='fnref-268-3' onclick='return fdfootnote_show(268)'>3</a></sup> :<br />
<code>User-agent: *<br />
Disallow: /test/index.php<br />
Allow: /test/</code></p>
<p>Maintenant, si vous testez le script, que cela soit par  http://localhost/test/ceci-est_un-test?toto=tata ou par  http://127.0.0.1/ceci-est_un-test?toto=tata seul localhost est accessible, l&rsquo;autre effectue une redirection HTTP 301.</p>
<h2>Le mot de la fin</h2>
<p>Dans le cadre de la réécriture d&rsquo;URL, il faut se méfier des possibilités d&rsquo;inversion de paramètre. En effet, avec <a href="http://grummfy.be/blog/zend-framework">ZF</a> par exemple, une URL telle que http://example.com/test/controller/action/param1/var1/param2/var2 est équivalente à http://example.com/test/controller/action/param2/var2/param1/var1 mais les moteurs de recherche marqueront le contenu comme dupliqué. Dans ce cas-là, ce n&rsquo;est plus du côté d’apache qu&rsquo;il faut regarder mais bien du côté du moteur de votre site &#8230;</p>
<div class='footnotes' id='footnotes-268'>
<div class='footnotedivider'></div>
<ol>
<li id='fn-268-1'> On devrait dire <acronym title="Uniform Resource Identifiers">URI</acronym> selon la RFC 3986 mais <acronym title="Tout Le Monde S'En Fout">TLMSF</acronym> <span class='footnotereverse'><a href='#fnref-268-1'>&#8617;</a></span></li>
<li id='fn-268-2'> au pire vous serez blacklinker, au mieux votre référencement en pâtira <span class='footnotereverse'><a href='#fnref-268-2'>&#8617;</a></span></li>
<li id='fn-268-3'> Pour rappel, ce fichier se place à la base de votre URL, soit pour que dans notre exemple (http://www.example.com/test/) le fichier soit accessible via http://www.example.com/robots.txt <span class='footnotereverse'><a href='#fnref-268-3'>&#8617;</a></span></li>
</ol>
</div><p>The post <a href="https://grummfy.be/blog/268">URL rewriting et SEO : duplication de contenu</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Wampserver : quick switch xdebug menu</title>
		<link>https://grummfy.be/blog/250</link>
					<comments>https://grummfy.be/blog/250#comments</comments>
		
		<dc:creator><![CDATA[Grummfy]]></dc:creator>
		<pubDate>Wed, 24 Mar 2010 22:22:42 +0000</pubDate>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[deboguage]]></category>
		<category><![CDATA[découverte]]></category>
		<category><![CDATA[jouons]]></category>
		<category><![CDATA[programmation]]></category>
		<category><![CDATA[script]]></category>
		<category><![CDATA[trucs et astuces]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[XDebug]]></category>
		<guid isPermaLink="false">http://grummfy.be/blog/?p=250</guid>

					<description><![CDATA[<p>Activer et désactiver XDebug sur Wampserver à souhait! Ceci via un menu supplémentaire.</p>
<p>The post <a href="https://grummfy.be/blog/250">Wampserver : quick switch xdebug menu</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Pour une fois, un article sous Ms Windows. Stage oblige, je passe du temps sous celui-ci (XP <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f641.png" alt="🙁" class="wp-smiley" style="height: 1em; max-height: 1em;" /> ). Afin d&rsquo;optimiser son travail, il y a parfois des petites choses bien pratiques, telles que ce que je vais vous présenter.</p>
<p>XDebug est un outil merveilleux, parfois capricieux, certes, mais très utile. Il permet, notamment<sup class='footnote'><a href='#fn-250-1' id='fnref-250-1' onclick='return fdfootnote_show(250)'>1</a></sup> :</p>
<ul>
<li>Affichage de tracé d&rsquo;erreur</li>
<li>Meilleures lectures des exceptions</li>
<li>Débogage pas-à-pas</li>
<li>Profiling d&rsquo;application</li>
<li>&#8230;</li>
</ul>
<p>Bref, des choses essentielles en développement. Et, contrairement à ce que certains IDE<sup class='footnote'><a href='#fn-250-2' id='fnref-250-2' onclick='return fdfootnote_show(250)'>2</a></sup> font, il permet surtout de le faire sur un serveur « réel », donc avec une utilisation « réel ».</p>
<p>Le but de ce billet n&rsquo;est pas de présenter XDebug, d&rsquo;autres le font mieux que moi <sup class='footnote'><a href='#fn-250-3' id='fnref-250-3' onclick='return fdfootnote_show(250)'>3</a></sup>, mais bien de vous présenter un petit script vous permettant d&rsquo;activer et désactiver XDebug sur wampserver.</p>
<h2>Installation</h2>
<ol>
<li>Téléchargez le fichier <sup class='footnote'><a href='#fn-250-4' id='fnref-250-4' onclick='return fdfootnote_show(250)'>4</a></sup> et décompressez-le.</li>
<li>Suivez les instructions d&rsquo;installation décrite dans le fichier installe. Il y a seulement 1 fichier à modifier + 1 fichier par version de PHP installée.</li>
<li>Relancer wampserver et tester!</li>
</ol>
<p>Si vous avez des questions, n&rsquo;hésitez pas.</p>
<h2>Plus d&rsquo;informations</h2>
<ul>
<li><a href="http://blog.pascal-martin.fr/post/xdebug-installation-premiers-pas" target="_blank">Xdebug : Installation et premier pas</a></li>
<li><a href="http://blog.wampserver.com/index.php/2009/08/28/debugger-avec-wampserver-xdebug-et-pdt/" target="_self">Débugger avec WampServer, Xdebug et PDT</a></li>
<li><a href="http://evoilliot.u7n.org/2010/02/installer-xdebug-avec-xampp/" target="_blank">Installer Xdebug avec XAMPP</a></li>
<li><a href="http://www.wampserver.com" target="_blank">Wampserver</a></li>
<li><a href="http://xdebug.com/" target="_blank">XDebug</a></li>
<li>Pièce jointe : <a href="http://grummfy.be/blog/250/xdebug-tar">XDebug quick switch menu for wampserver</a></li>
</ul>
<div class='footnotes' id='footnotes-250'>
<div class='footnotedivider'></div>
<ol>
<li id='fn-250-1'> On parle de serveur web avec PHP &#8230; <span class='footnotereverse'><a href='#fnref-250-1'>&#8617;</a></span></li>
<li id='fn-250-2'> Par exemple, Zend Studio permet un débogage pas à pas mais en interne donc réduit &#8230;  <span class='footnotereverse'><a href='#fnref-250-2'>&#8617;</a></span></li>
<li id='fn-250-3'> cf. plus d&rsquo;informations <span class='footnotereverse'><a href='#fnref-250-3'>&#8617;</a></span></li>
<li id='fn-250-4'> <a href="http://grummfy.be/blog/250/xdebug-tar">XDebug quick switch menu for wampserver</a> <span class='footnotereverse'><a href='#fnref-250-4'>&#8617;</a></span></li>
</ol>
</div><p>The post <a href="https://grummfy.be/blog/250">Wampserver : quick switch xdebug menu</a> first appeared on <a href="https://grummfy.be/blog">Grummfy's project</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://grummfy.be/blog/250/feed</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
	</channel>
</rss>
