<?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/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Blog de Stéphane Legrand</title>
	<atom:link href="http://stephanelegrand.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://stephanelegrand.wordpress.com</link>
	<description></description>
	<lastBuildDate>Mon, 21 Nov 2011 09:10:53 +0000</lastBuildDate>
	<language>fr</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='stephanelegrand.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>Blog de Stéphane Legrand</title>
		<link>http://stephanelegrand.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://stephanelegrand.wordpress.com/osd.xml" title="Blog de Stéphane Legrand" />
	<atom:link rel='hub' href='http://stephanelegrand.wordpress.com/?pushpress=hub'/>
		<item>
		<title>Certification Zend Framework</title>
		<link>http://stephanelegrand.wordpress.com/2010/09/10/certification-zend-framework/</link>
		<comments>http://stephanelegrand.wordpress.com/2010/09/10/certification-zend-framework/#comments</comments>
		<pubDate>Fri, 10 Sep 2010 19:27:21 +0000</pubDate>
		<dc:creator>Stéphane Legrand</dc:creator>
				<category><![CDATA[Professionnel]]></category>
		<category><![CDATA[certification]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[zend framework]]></category>

		<guid isPermaLink="false">http://stephanelegrand.wordpress.com/?p=240</guid>
		<description><![CDATA[Suite à mon passage de la certification Zend Framework, quelques conseils pour les éventuels futurs candidats. En premier lieu, il est plus que recommandé de consulter (et plutôt plusieurs fois qu&#8217;une) le document de préparation à la certification (&#171;&#160;ZF Certification Study Guide&#171;&#160;) proposé sur le site de Zend. En 200 pages, vous aurez un condensé [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=stephanelegrand.wordpress.com&amp;blog=4517885&amp;post=240&amp;subd=stephanelegrand&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Suite à mon passage de la certification Zend Framework, quelques conseils pour les éventuels futurs candidats.</p>
<p>En premier lieu, il est plus que recommandé de consulter (et plutôt plusieurs fois qu&#8217;une) le document de préparation à la certification (&laquo;&nbsp;<a href="http://www.zend.com/community/downloads">ZF Certification Study Guide</a>&laquo;&nbsp;) proposé sur le site de Zend. En 200 pages, vous aurez un condensé de tous les éléments requis. Autre document qui peut s&#8217;avérer utile, celui rédigé par Rob Allen (&laquo;&nbsp;<a href="http://akrabat.com/wp-content/uploads/ZendCon09-ZF-Certification-Refresher.pdf">Zend Framework Certification Refresher</a>&laquo;&nbsp;) qui est un rappel des notions principales de Zend Framework. Bien sûr, cela ne dispense pas de consulter la documentation officielle afin d&#8217;avoir connaissance de tous les détails de l&#8217;implémentation des classes.</p>
<p>Cela va sans dire, une pratique du framework est également essentielle. Et en particulier de toutes les classes concernant le MVC au sens large (accès à la base de données, aides de vues, formulaires&#8230;).</p>
<p>Très important, n&#8217;oubliez pas non plus de réviser les recommandations ZF sur le style du code PHP à adopter. Les classes pour l&#8217;utilisation des services web comme REST, SOAP et XML-RPC sont aussi à connaître. </p>
<p>Il est également obligatoire de retenir les différentes manières d&#8217;utiliser les méthodes. Très souvent, il est en effet possible pour un même paramètre de passer soit un objet soit un tableau ou bien encore le nom d&#8217;une classe. Vous devez connaître ces différents moyens de réaliser la même chose.</p>
<p>Pour résumer, cette certification ne m&#8217;a pas parue plus difficile (ni plus facile d&#8217;ailleurs) qu&#8217;une autre comme la certification Linux LPI. Les 90 minutes pour répondre aux 75 questions rédigées en anglais ne sont pas de trop, certaines questions nécessitent davantage de réflexion parce que les réponses proposées sont parfois très proches mais il n&#8217;y a pas de question destinée à &laquo;&nbsp;piéger&nbsp;&raquo; le candidat. Seul petit bémol, j&#8217;ai trouvé dommage de ne pas avoir la note finale, vous savez juste que vous avez obtenu la certification. Il serait intéressant d&#8217;avoir au moins une idée de son niveau et surtout de savoir dans quelles sections vous devez approfondir vos connaissances.</p>
<br />Filed under: <a href='http://stephanelegrand.wordpress.com/category/professionnel/'>Professionnel</a> Tagged: <a href='http://stephanelegrand.wordpress.com/tag/certification/'>certification</a>, <a href='http://stephanelegrand.wordpress.com/tag/php/'>php</a>, <a href='http://stephanelegrand.wordpress.com/tag/zend-framework/'>zend framework</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/stephanelegrand.wordpress.com/240/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/stephanelegrand.wordpress.com/240/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/stephanelegrand.wordpress.com/240/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/stephanelegrand.wordpress.com/240/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/stephanelegrand.wordpress.com/240/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/stephanelegrand.wordpress.com/240/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/stephanelegrand.wordpress.com/240/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/stephanelegrand.wordpress.com/240/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/stephanelegrand.wordpress.com/240/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/stephanelegrand.wordpress.com/240/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/stephanelegrand.wordpress.com/240/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/stephanelegrand.wordpress.com/240/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/stephanelegrand.wordpress.com/240/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/stephanelegrand.wordpress.com/240/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=stephanelegrand.wordpress.com&amp;blog=4517885&amp;post=240&amp;subd=stephanelegrand&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://stephanelegrand.wordpress.com/2010/09/10/certification-zend-framework/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">stephanelegrand</media:title>
		</media:content>
	</item>
		<item>
		<title>Voyage à La Réunion</title>
		<link>http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/</link>
		<comments>http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/#comments</comments>
		<pubDate>Wed, 16 Sep 2009 16:08:05 +0000</pubDate>
		<dc:creator>Stéphane Legrand</dc:creator>
				<category><![CDATA[Personnel]]></category>
		<category><![CDATA[La Réunion]]></category>
		<category><![CDATA[voyage]]></category>

		<guid isPermaLink="false">http://stephanelegrand.wordpress.com/?p=193</guid>
		<description><![CDATA[Quelques photos de mon voyage à La Réunion en septembre 2009. Des paysages inoubliables ! Un grand merci à Cyril, Nath et leurs deux puces Célia et Loane pour leur accueil et pour les visites organisées (mieux qu&#8217;un guide péi ! ) Publié dans Personnel Tagged: La Réunion, voyage<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=stephanelegrand.wordpress.com&amp;blog=4517885&amp;post=193&amp;subd=stephanelegrand&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Quelques photos de mon voyage à La Réunion en septembre 2009. Des paysages inoubliables ! Un grand merci à Cyril, Nath et leurs deux puces Célia et Loane pour leur accueil et pour les visites organisées (mieux qu&#8217;un guide péi ! <img src='http://s0.wp.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  )</p>

<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/derriere_les_nuages_mafate/' title='Derriere_les_nuages_Mafate'><img data-attachment-id='210' data-orig-size='2592,1944' data-liked='0'width="150" height="112" src="http://stephanelegrand.files.wordpress.com/2009/09/derriere_les_nuages_mafate.jpg?w=150&#038;h=112" class="attachment-thumbnail" alt="Vue (nuageuse) sur Mafate" title="Derriere_les_nuages_Mafate" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/bebour_3/' title='Bebour_3'><img data-attachment-id='201' data-orig-size='2592,1944' data-liked='0'width="150" height="112" src="http://stephanelegrand.files.wordpress.com/2009/09/bebour_3.jpg?w=150&#038;h=112" class="attachment-thumbnail" alt="Bebour" title="Bebour_3" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/vincendo/' title='Vincendo'><img data-attachment-id='223' data-orig-size='2592,1944' data-liked='0'width="150" height="112" src="http://stephanelegrand.files.wordpress.com/2009/09/vincendo.jpg?w=150&#038;h=112" class="attachment-thumbnail" alt="Vincendo" title="Vincendo" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/route_des_tamarins/' title='Route_des_Tamarins'><img data-attachment-id='222' data-orig-size='2592,1944' data-liked='0'width="150" height="112" src="http://stephanelegrand.files.wordpress.com/2009/09/route_des_tamarins.jpg?w=150&#038;h=112" class="attachment-thumbnail" alt="Route de la forêt des Tamarins" title="Route_des_Tamarins" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/manioc_marron/' title='Manioc_marron'><img data-attachment-id='218' data-orig-size='2592,1944' data-liked='0'width="150" height="112" src="http://stephanelegrand.files.wordpress.com/2009/09/manioc_marron.jpg?w=150&#038;h=112" class="attachment-thumbnail" alt="Manioc marron" title="Manioc_marron" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/cilaos_1/' title='Cilaos_1'><img data-attachment-id='208' data-orig-size='2592,1944' data-liked='0'width="150" height="112" src="http://stephanelegrand.files.wordpress.com/2009/09/cilaos_1.jpg?w=150&#038;h=112" class="attachment-thumbnail" alt="Cilaos" title="Cilaos_1" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/bassin_boeuf/' title='Bassin_Boeuf'><img data-attachment-id='197' data-orig-size='2592,1944' data-liked='0'width="150" height="112" src="http://stephanelegrand.files.wordpress.com/2009/09/bassin_boeuf.jpg?w=150&#038;h=112" class="attachment-thumbnail" alt="Cascade du bassin Boeuf" title="Bassin_Boeuf" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/cap_mechant_2/' title='Cap_Mechant_2'><img data-attachment-id='205' data-orig-size='2592,1944' data-liked='0'width="150" height="112" src="http://stephanelegrand.files.wordpress.com/2009/09/cap_mechant_2.jpg?w=150&#038;h=112" class="attachment-thumbnail" alt="Cap Méchant" title="Cap_Mechant_2" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/grande_anse_2/' title='Grande_Anse_2'><img data-attachment-id='213' data-orig-size='1799,2592' data-liked='0'width="104" height="150" src="http://stephanelegrand.files.wordpress.com/2009/09/grande_anse_2.jpg?w=104&#038;h=150" class="attachment-thumbnail" alt="Grande Anse" title="Grande_Anse_2" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/le_souffleur/' title='Le_Souffleur'><img data-attachment-id='217' data-orig-size='1944,2592' data-liked='0'width="112" height="150" src="http://stephanelegrand.files.wordpress.com/2009/09/le_souffleur.jpg?w=112&#038;h=150" class="attachment-thumbnail" alt="Le Souffleur" title="Le_Souffleur" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/plage_de_l_hermitage/' title='Plage_de_L_Hermitage'><img data-attachment-id='219' data-orig-size='2592,1944' data-liked='0'width="150" height="112" src="http://stephanelegrand.files.wordpress.com/2009/09/plage_de_l_hermitage.jpg?w=150&#038;h=112" class="attachment-thumbnail" alt="Plage de l&#039;Hermitage" title="Plage_de_L_Hermitage" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/bassin_des_aigrettes/' title='Bassin_des_Aigrettes'><img data-attachment-id='198' data-orig-size='2592,1944' data-liked='0'width="150" height="112" src="http://stephanelegrand.files.wordpress.com/2009/09/bassin_des_aigrettes.jpg?w=150&#038;h=112" class="attachment-thumbnail" alt="Cascade du bassin des Aigrettes" title="Bassin_des_Aigrettes" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/cilaos_2/' title='Cilaos_2'><img data-attachment-id='209' data-orig-size='2592,1944' data-liked='0'width="150" height="112" src="http://stephanelegrand.files.wordpress.com/2009/09/cilaos_2.jpg?w=150&#038;h=112" class="attachment-thumbnail" alt="Cilaos" title="Cilaos_2" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/hellbourg/' title='Hellbourg'><img data-attachment-id='216' data-orig-size='1944,2592' data-liked='0'width="112" height="150" src="http://stephanelegrand.files.wordpress.com/2009/09/hellbourg.jpg?w=112&#038;h=150" class="attachment-thumbnail" alt="Hellbourg" title="Hellbourg" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/bebour_2/' title='Bebour_2'><img data-attachment-id='200' data-orig-size='1944,2592' data-liked='0'width="112" height="150" src="http://stephanelegrand.files.wordpress.com/2009/09/bebour_2.jpg?w=112&#038;h=150" class="attachment-thumbnail" alt="Bebour" title="Bebour_2" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/route_etang_sale/' title='Route_Etang_Sale'><img data-attachment-id='221' data-orig-size='2592,1944' data-liked='0'width="150" height="112" src="http://stephanelegrand.files.wordpress.com/2009/09/route_etang_sale.jpg?w=150&#038;h=112" class="attachment-thumbnail" alt="Sur la route vers l&#039;Etang Salé" title="Route_Etang_Sale" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/cap_mechant_1/' title='Cap_Mechant_1'><img data-attachment-id='204' data-orig-size='2592,1944' data-liked='0'width="150" height="112" src="http://stephanelegrand.files.wordpress.com/2009/09/cap_mechant_1.jpg?w=150&#038;h=112" class="attachment-thumbnail" alt="Cap Méchant" title="Cap_Mechant_1" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/boucan_canot/' title='Boucan_Canot'><img data-attachment-id='202' data-orig-size='2592,1944' data-liked='0'width="150" height="112" src="http://stephanelegrand.files.wordpress.com/2009/09/boucan_canot.jpg?w=150&#038;h=112" class="attachment-thumbnail" alt="Plage de Boucan Canot" title="Boucan_Canot" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/grande_anse_1/' title='Grande_Anse_1'><img data-attachment-id='212' data-orig-size='1944,2592' data-liked='0'width="112" height="150" src="http://stephanelegrand.files.wordpress.com/2009/09/grande_anse_1.jpg?w=112&#038;h=150" class="attachment-thumbnail" alt="Grande Anse" title="Grande_Anse_1" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/grande_anse_3/' title='Grande_Anse_3'><img data-attachment-id='215' data-orig-size='1944,2592' data-liked='0'width="112" height="150" src="http://stephanelegrand.files.wordpress.com/2009/09/grande_anse_3.jpg?w=112&#038;h=150" class="attachment-thumbnail" alt="Grande Anse" title="Grande_Anse_3" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/cascade_niagara/' title='Cascade_Niagara'><img data-attachment-id='207' data-orig-size='2592,1641' data-liked='0'width="150" height="94" src="http://stephanelegrand.files.wordpress.com/2009/09/cascade_niagara.jpg?w=150&#038;h=94" class="attachment-thumbnail" alt="Cascade Niagara" title="Cascade_Niagara" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/anse_des_cascades/' title='Anse_des_Cascades'><img data-attachment-id='196' data-orig-size='2592,1944' data-liked='0'width="150" height="112" src="http://stephanelegrand.files.wordpress.com/2009/09/anse_des_cascades.jpg?w=150&#038;h=112" class="attachment-thumbnail" alt="Anse des Cascades" title="Anse_des_Cascades" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/cascade_-langevin/' title='Cascade_ Langevin'><img data-attachment-id='206' data-orig-size='2592,1944' data-liked='0'width="150" height="112" src="http://stephanelegrand.files.wordpress.com/2009/09/cascade_-langevin.jpg?w=150&#038;h=112" class="attachment-thumbnail" alt="Cascade Langevin" title="Cascade_ Langevin" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/anciens_thermes_hellbourg/' title='Anciens_Thermes_Hellbourg'><img data-attachment-id='195' data-orig-size='2592,1944' data-liked='0'width="150" height="112" src="http://stephanelegrand.files.wordpress.com/2009/09/anciens_thermes_hellbourg.jpg?w=150&#038;h=112" class="attachment-thumbnail" alt="Les anciens thermes de Hellbourg" title="Anciens_Thermes_Hellbourg" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/cactus_pointe_de_sel/' title='Cactus_Pointe_de_Sel'><img data-attachment-id='203' data-orig-size='2592,1944' data-liked='0'width="150" height="112" src="http://stephanelegrand.files.wordpress.com/2009/09/cactus_pointe_de_sel.jpg?w=150&#038;h=112" class="attachment-thumbnail" alt="Gros cactus" title="Cactus_Pointe_de_Sel" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/pointe_de_sel/' title='Pointe_de_sel'><img data-attachment-id='220' data-orig-size='2592,1944' data-liked='0'width="150" height="112" src="http://stephanelegrand.files.wordpress.com/2009/09/pointe_de_sel.jpg?w=150&#038;h=112" class="attachment-thumbnail" alt="La Pointe de Sel" title="Pointe_de_sel" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/etang_sale/' title='Etang_Sale'><img data-attachment-id='211' data-orig-size='2592,1944' data-liked='0'width="150" height="112" src="http://stephanelegrand.files.wordpress.com/2009/09/etang_sale.jpg?w=150&#038;h=112" class="attachment-thumbnail" alt="Etang Salé" title="Etang_Sale" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/bebour_1/' title='Bebour_1'><img data-attachment-id='199' data-orig-size='1944,2592' data-liked='0'width="112" height="150" src="http://stephanelegrand.files.wordpress.com/2009/09/bebour_1.jpg?w=112&#038;h=150" class="attachment-thumbnail" alt="Bebour" title="Bebour_1" /></a>
<a href='http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/attachment/20090905/' title='20090905'><img data-attachment-id='194' data-orig-size='2592,1944' data-liked='0'width="150" height="112" src="http://stephanelegrand.files.wordpress.com/2009/09/20090905.jpg?w=150&#038;h=112" class="attachment-thumbnail" alt="Paysage Réunionnais" title="20090905" /></a>

<br />Publié dans Personnel Tagged: La Réunion, voyage <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/stephanelegrand.wordpress.com/193/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/stephanelegrand.wordpress.com/193/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/stephanelegrand.wordpress.com/193/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/stephanelegrand.wordpress.com/193/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/stephanelegrand.wordpress.com/193/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/stephanelegrand.wordpress.com/193/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/stephanelegrand.wordpress.com/193/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/stephanelegrand.wordpress.com/193/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/stephanelegrand.wordpress.com/193/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/stephanelegrand.wordpress.com/193/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/stephanelegrand.wordpress.com/193/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/stephanelegrand.wordpress.com/193/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/stephanelegrand.wordpress.com/193/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/stephanelegrand.wordpress.com/193/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=stephanelegrand.wordpress.com&amp;blog=4517885&amp;post=193&amp;subd=stephanelegrand&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://stephanelegrand.wordpress.com/2009/09/16/voyage-a-la-reunion/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">stephanelegrand</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/20090905.jpg?w=150" medium="image">
			<media:title type="html">20090905</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/cascade_niagara.jpg?w=150" medium="image">
			<media:title type="html">Cascade_Niagara</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/manioc_marron.jpg?w=150" medium="image">
			<media:title type="html">Manioc_marron</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/cilaos_2.jpg?w=150" medium="image">
			<media:title type="html">Cilaos_2</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/bassin_boeuf.jpg?w=150" medium="image">
			<media:title type="html">Bassin_Boeuf</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/cap_mechant_2.jpg?w=150" medium="image">
			<media:title type="html">Cap_Mechant_2</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/route_etang_sale.jpg?w=150" medium="image">
			<media:title type="html">Route_Etang_Sale</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/bassin_des_aigrettes.jpg?w=150" medium="image">
			<media:title type="html">Bassin_des_Aigrettes</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/le_souffleur.jpg?w=112" medium="image">
			<media:title type="html">Le_Souffleur</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/bebour_2.jpg?w=112" medium="image">
			<media:title type="html">Bebour_2</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/bebour_3.jpg?w=150" medium="image">
			<media:title type="html">Bebour_3</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/route_des_tamarins.jpg?w=150" medium="image">
			<media:title type="html">Route_des_Tamarins</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/cap_mechant_1.jpg?w=150" medium="image">
			<media:title type="html">Cap_Mechant_1</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/grande_anse_2.jpg?w=104" medium="image">
			<media:title type="html">Grande_Anse_2</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/vincendo.jpg?w=150" medium="image">
			<media:title type="html">Vincendo</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/anse_des_cascades.jpg?w=150" medium="image">
			<media:title type="html">Anse_des_Cascades</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/cascade_-langevin.jpg?w=150" medium="image">
			<media:title type="html">Cascade_ Langevin</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/bebour_1.jpg?w=112" medium="image">
			<media:title type="html">Bebour_1</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/etang_sale.jpg?w=150" medium="image">
			<media:title type="html">Etang_Sale</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/derriere_les_nuages_mafate.jpg?w=150" medium="image">
			<media:title type="html">Derriere_les_nuages_Mafate</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/plage_de_l_hermitage.jpg?w=150" medium="image">
			<media:title type="html">Plage_de_L_Hermitage</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/hellbourg.jpg?w=112" medium="image">
			<media:title type="html">Hellbourg</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/cilaos_1.jpg?w=150" medium="image">
			<media:title type="html">Cilaos_1</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/anciens_thermes_hellbourg.jpg?w=150" medium="image">
			<media:title type="html">Anciens_Thermes_Hellbourg</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/boucan_canot.jpg?w=150" medium="image">
			<media:title type="html">Boucan_Canot</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/pointe_de_sel.jpg?w=150" medium="image">
			<media:title type="html">Pointe_de_sel</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/cactus_pointe_de_sel.jpg?w=150" medium="image">
			<media:title type="html">Cactus_Pointe_de_Sel</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/grande_anse_3.jpg?w=112" medium="image">
			<media:title type="html">Grande_Anse_3</media:title>
		</media:content>

		<media:content url="http://stephanelegrand.files.wordpress.com/2009/09/grande_anse_1.jpg?w=112" medium="image">
			<media:title type="html">Grande_Anse_1</media:title>
		</media:content>
	</item>
		<item>
		<title>Gestion d&#8217;une structure d&#8217;arbre sous MySQL</title>
		<link>http://stephanelegrand.wordpress.com/2009/01/03/gestion-dune-structure-darbre-sous-mysql/</link>
		<comments>http://stephanelegrand.wordpress.com/2009/01/03/gestion-dune-structure-darbre-sous-mysql/#comments</comments>
		<pubDate>Sat, 03 Jan 2009 20:38:56 +0000</pubDate>
		<dc:creator>Stéphane Legrand</dc:creator>
				<category><![CDATA[Programmation]]></category>
		<category><![CDATA[mysql]]></category>

		<guid isPermaLink="false">http://stephanelegrand.wordpress.com/?p=115</guid>
		<description><![CDATA[Introduction : Il est très courant d&#8217;avoir à stocker une structure d&#8217;arbre dans une base de données. Par exemple, pour enregistrer l&#8217;organisation hiérarchique d&#8217;une entreprise ou bien pour représenter les catégories et sous-catégories des articles d&#8217;un site web. La façon la plus simple de procéder est d&#8217;ajouter simplement à la table concernée un champ &#171;&#160;identifiant_parent&#160;&#187; [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=stephanelegrand.wordpress.com&amp;blog=4517885&amp;post=115&amp;subd=stephanelegrand&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<ul>
<li>Introduction :</li>
</ul>
<p>Il est très courant d&#8217;avoir à stocker une structure d&#8217;arbre dans une base de données. Par exemple, pour enregistrer l&#8217;organisation hiérarchique d&#8217;une entreprise ou bien pour représenter les catégories et sous-catégories des articles d&#8217;un site web.</p>
<p>La façon la plus simple de procéder est d&#8217;ajouter simplement à la table concernée un champ &laquo;&nbsp;identifiant_parent&nbsp;&raquo; qui servira à stocker l&#8217;identifiant du noeud parent. Ainsi, pour stocker l&#8217;arbre suivant :</p>
<p><pre class="brush: xml;">
      A
  ---------
  |       |
  B       C
        -----
        |   |
        D   E
      -----
      |   |
      F   G
</pre></p>
<p>nous aurions les enregistrements suivants :</p>
<p><pre class="brush: xml;">
identifiant    identifiant_parent
A                 NULL
B                 A
C                 A
D                 C
E                 C
F                 D
G                 D
</pre></p>
<p>Le noeud &laquo;&nbsp;A&nbsp;&raquo; qui est la racine de l&#8217;arbre n&#8217;a évidemment aucun parent c&#8217;est pourquoi l&#8217;identifiant du parent est dans ce cas mis à NULL.</p>
<p>Cette technique de représentation d&#8217;un arbre a comme avantage sa très grande simplicité. Elle a par contre l&#8217;inconvénient de nécessiter pour certains traitements l&#8217;envoi de plusieurs requêtes SQL de manière récursive. En effet, pour retrouver tous les noeuds enfants de &laquo;&nbsp;C&nbsp;&raquo;, il n&#8217;est pas possible de le faire directement via une seule requête SQL. Il faut tout d&#8217;abord récupérer les enfants de &laquo;&nbsp;C&nbsp;&raquo; puis, pour chaque enregistrements trouvés, récupérer à leur tour leurs enfants.</p>
<p>Nous allons donc voir une autre méthode plus complexe mais aussi plus performante pour stocker dans une base SQL une structure d&#8217;arbre.</p>
<p><span id="more-115"></span></p>
<ul>
<li>Le ver sur la branche :</li>
</ul>
<p>Au lieu d&#8217;utiliser la représentation classique d&#8217;un arbre, on peut aussi imaginer un arbre comme une imbrication d&#8217;ensembles. La racine est l&#8217;ensemble le plus grand qui regroupe tous les éléments, puis ses enfants sont des sous-ensembles plus petits qui regroupents leurs enfants, etc&#8230;</p>
<p>Graphiquement, l&#8217;arbre vu précédemment se représenterait ainsi :</p>
<p><pre class="brush: xml;">
------------------------ A ------------------------
|          B               |                      |
|                          |--------- C ----------|
|                          |                E     |
|                          |--- D ----            |
|                          | F     G |            |
|-------------------------------------------------|
</pre></p>
<p>L&#8217;ensemble &laquo;&nbsp;A&nbsp;&raquo; est le plus grand et contient &laquo;&nbsp;B&nbsp;&raquo; et &laquo;&nbsp;C&nbsp;&raquo; ainsi que leurs enfants. L&#8217;ensemble &laquo;&nbsp;C&nbsp;&raquo; contient à son tour &laquo;&nbsp;D&nbsp;&raquo; et &laquo;&nbsp;E&nbsp;&raquo; et leurs enfants, etc&#8230;</p>
<p>Pour représenter ces ensembles, il suffit d&#8217;utiliser cette fois deux champs de type entier que nous appellerons &laquo;&nbsp;gauche&nbsp;&raquo; et &laquo;&nbsp;droite&nbsp;&raquo;. Ils indiqueront en fait la grandeur de l&#8217;ensemble concerné. On peut aussi imaginer un ver qui parcourt l&#8217;arbre en partant de la racine et visite chaque noeud deux fois (une fois à gauche et une fois à droite). A chaque visite, il place dans le champ (gauche ou droite) la valeur d&#8217;un compteur qui augmente de 1 à chaque fois.</p>
<p>Pour représenter notre arbre, nous aurions donc cette fois-ci les enregistrements suivants :</p>
<p><pre class="brush: xml;">
identifiant    gauche    droite
A                1         14
B                2         3
C                4         13
D                5         10
E                11        12
F                6         7
G                8         9
</pre></p>
<p>Et pour revenir à notre précédente illustration graphique de l&#8217;arbre, cela donnerait :</p>
<p><pre class="brush: xml;">

         1 A 14
       -----------
       |         |
    2 B 3      4 C 13
            ----------
            |        |
          5 D 10  11 E 12
         --------
         |      |
       6 F 7  8 G 9

</pre></p>
<p>N.B. : pour le noeud &nbsp;&raquo; B &laquo;&nbsp;, il faut lire &nbsp;&raquo; 2 B 3 &nbsp;&raquo; (l&#8217;affichage n&#8217;est pas forcément correct&#8230;).</p>
<p>On voit d&#8217;emblée que cette représentation offre des propriétés intéressantes pour nos futures requêtes SQL. Par exemple :</p>
<p>- La racine est toujours le noeud dont l&#8217;enregistrement &laquo;&nbsp;gauche&nbsp;&raquo; est égal à la valeur 1.<br />
- Pour sélectionner tous les enfants d&#8217;un noeud donné, il suffit de récupérer tous ceux dont le champ &laquo;&nbsp;gauche&nbsp;&raquo; est supérieur à la valeur du champ &laquo;&nbsp;gauche&nbsp;&raquo; de ce noeud et dont le champ &laquo;&nbsp;droite&nbsp;&raquo; est inférieur à la valeur du champ &laquo;&nbsp;droite&nbsp;&raquo; de ce noeud.<br />
- Les feuilles (les noeuds sans enfants) sont les éléments dont la valeur du champ &laquo;&nbsp;droite&nbsp;&raquo; moins la valeur du champ &laquo;&nbsp;gauche&nbsp;&raquo; est égal à 1.</p>
<ul>
<li>Implémentation sous MySQL :</li>
</ul>
<p><pre class="brush: sql;">
CREATE TABLE arbre (
  id BIGINT NOT NULL AUTO_INCREMENT,

  gauche BIGINT DEFAULT NULL,
  droite BIGINT DEFAULT NULL,

  ref CHAR(5) DEFAULT NULL,

  id_parent BIGINT NOT NULL DEFAULT 0,

  PRIMARY KEY (id),
  UNIQUE (gauche),
  UNIQUE (droite),
  KEY (ref),
  KEY (id_parent)
);
</pre></p>
<p>Nous retrouvons nos champs &laquo;&nbsp;gauche&nbsp;&raquo; et &laquo;&nbsp;droite&nbsp;&raquo;. Le champ &laquo;&nbsp;ref&nbsp;&raquo; contiendra la clef vers les données qui seront stockées dans une autre table. Ici, nous nous contenterons de placer dans ce champ une simple chaîne de caractères afin d&#8217;illustrer nos propos. Enfin, le champ &laquo;&nbsp;id_parent&nbsp;&raquo; indiquera l&#8217;identifiant du noeud parent. Ce dernier champ n&#8217;est pas obligatoire pour gérer l&#8217;arbre mais il permet toutefois d&#8217;avoir un lien direct vers le parent ce qui peut simplifier certaines requêtes.</p>
<ul>
<li>Procédure pour l&#8217;ajout d&#8217;un noeud :</li>
</ul>
<p><pre class="brush: sql;">
delimiter //

CREATE PROCEDURE ajout (IN id_parent_noeud BIGINT, IN ref_noeud CHAR(1))
MODIFIES SQL DATA
BEGIN
DECLARE gauche_parent, droite_parent BIGINT DEFAULT NULL;

INSERT INTO arbre (ref, id_parent) VALUES (ref_noeud, id_parent_noeud);

SELECT gauche INTO gauche_parent FROM arbre WHERE id = id_parent_noeud;
SELECT droite INTO droite_parent FROM arbre WHERE id = id_parent_noeud;

UPDATE arbre SET gauche = gauche + 2
  WHERE gauche &gt; gauche_parent
  ORDER BY gauche DESC;

UPDATE arbre SET droite = droite + 2
  WHERE droite &gt;= droite_parent OR (droite &gt; gauche_parent + 1 AND droite &lt; droite_parent)
  ORDER BY droite DESC;

UPDATE arbre SET gauche = gauche_parent + 1, droite = gauche_parent + 2 WHERE ref = ref_noeud;
END;

delimiter ;
</pre></p>
<p>La procédure prend deux paramètres. &laquo;&nbsp;id_parent_noeud&nbsp;&raquo; indique l&#8217;identifiant du noeud parent pour le noeud qui sera ajouté. &laquo;&nbsp;ref_noeud&nbsp;&raquo; contient la clef vers les données associées au nouveau noeud.</p>
<p>On insère tout d&#8217;abord ce nouveau noeud avec les données passées en paramètres. Puis on décale les valeurs &laquo;&nbsp;gauche&nbsp;&raquo; et &laquo;&nbsp;droite&nbsp;&raquo; des autres noeuds de manière à pouvoir placer le nouveau noeud. On met enfin à jour les valeurs &laquo;&nbsp;gauche&nbsp;&raquo; et &laquo;&nbsp;droite&nbsp;&raquo; du nouveau noeud. Notez qu&#8217;avec la méthode de décalage choisie le noeud le plus récemment ajouté possède les indices &laquo;&nbsp;gauche&nbsp;&raquo; et &laquo;&nbsp;droite&nbsp;&raquo; les plus faibles parmi tous ses frères. A l&#8217;inverse, le noeud ajouté en premier aura toujours les indices &laquo;&nbsp;gauche&nbsp;&raquo; et &laquo;&nbsp;droite&nbsp;&raquo; les plus élevés parmis tous ses frères.</p>
<p>Pour utiliser cette procédure dans une requête SQL :</p>
<p><pre class="brush: sql;">
INSERT INTO arbre (id, gauche, droite, ref, id_parent) VALUES (1,1,2,'A',0);
CALL ajout(1,'B');
CALL ajout(1,'C');
</pre></p>
<p>On insère en premier lieu le noeud racine &laquo;&nbsp;A&nbsp;&raquo;. Puis on lui ajoute deux fils &laquo;&nbsp;B&nbsp;&raquo; et &laquo;&nbsp;C&nbsp;&raquo;.</p>
<br />Publié dans Programmation Tagged: mysql <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/stephanelegrand.wordpress.com/115/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/stephanelegrand.wordpress.com/115/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/stephanelegrand.wordpress.com/115/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/stephanelegrand.wordpress.com/115/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/stephanelegrand.wordpress.com/115/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/stephanelegrand.wordpress.com/115/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/stephanelegrand.wordpress.com/115/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/stephanelegrand.wordpress.com/115/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/stephanelegrand.wordpress.com/115/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/stephanelegrand.wordpress.com/115/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/stephanelegrand.wordpress.com/115/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/stephanelegrand.wordpress.com/115/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/stephanelegrand.wordpress.com/115/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/stephanelegrand.wordpress.com/115/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=stephanelegrand.wordpress.com&amp;blog=4517885&amp;post=115&amp;subd=stephanelegrand&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://stephanelegrand.wordpress.com/2009/01/03/gestion-dune-structure-darbre-sous-mysql/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">stephanelegrand</media:title>
		</media:content>
	</item>
		<item>
		<title>Client Memcached pour Objective Caml</title>
		<link>http://stephanelegrand.wordpress.com/2008/08/27/client-memcached-pour-objective-caml/</link>
		<comments>http://stephanelegrand.wordpress.com/2008/08/27/client-memcached-pour-objective-caml/#comments</comments>
		<pubDate>Wed, 27 Aug 2008 19:55:07 +0000</pubDate>
		<dc:creator>Stéphane Legrand</dc:creator>
				<category><![CDATA[Programmation]]></category>
		<category><![CDATA[caml]]></category>
		<category><![CDATA[memcache]]></category>
		<category><![CDATA[OCaml]]></category>

		<guid isPermaLink="false">http://stephanelegrand.wordpress.com/?p=81</guid>
		<description><![CDATA[Introduction : Nous allons voir comment développer un client pour le logiciel serveur Memcached avec le langage Objective Caml. Le logiciel Memcached permet en résumé de stocker des données en mémoire vive ce qui offre des accès très rapides. Une valeur est simplement indexée par une clef unique qui est utilisée par le client pour [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=stephanelegrand.wordpress.com&amp;blog=4517885&amp;post=81&amp;subd=stephanelegrand&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<ul>
<li>Introduction :</li>
</ul>
<p>Nous allons voir comment développer un client pour le logiciel serveur Memcached avec le langage Objective Caml. Le logiciel Memcached permet en résumé de stocker des données en mémoire vive ce qui offre des accès très rapides. Une valeur est simplement indexée par une clef unique qui est utilisée par le client pour écrire, modifier ou lire cette donnée. Memcached est typiquement utilisé pour stocker des valeurs temporaires afin de gérer un cache d&#8217;où son nom. Son protocole est relativement simple ce qui en fait un bon candidat pour une première approche du développement en Objective Caml.</p>
<p>Le code source complet est disponible <a title="Archive code source Memcache Objective Caml" href="https://docs.google.com/leaf?id=0B2mDy0OkZ4yOOGUxNTcxMzctMDg1Yi00YmIzLWE0ODQtNjMyNTgxY2QwMGU2&amp;hl=fr" target="_blank">dans cette archive</a>.</p>
<p><span id="more-81"></span></p>
<ul>
<li>Protocole Memcached :</li>
</ul>
<p>La documentation disponible sur le protocole donne un premier aperçu des fonctions qui seront nécessaires :</p>
<p>- Les commandes simples comme la demande de version du serveur ou le &laquo;&nbsp;flush_all&nbsp;&raquo; nécessitent simplement l&#8217;envoi d&#8217;une seule ligne de texte au serveur qui enverra en réponse une ligne de texte.</p>
<p>- Les commandes plus complexes comme les statistiques ou l&#8217;écriture d&#8217;une donnée nécessitent de gérer l&#8217;envoi ou la réception de plusieurs lignes.</p>
<p>- Il sera nécessaire de pouvoir vérifier si la réponse du serveur à une commande du client ne correspond pas à une erreur.</p>
<p>- Nous aurons évidemment besoin d&#8217;une fonction pour ouvrir et fermer la connexion au serveur Memcached.</p>
<p>- Chaque commande prévue par le protocole sera implémentée par une fonction dédiée.</p>
<ul>
<li>Typage :</li>
</ul>
<p>Le protocole nous indique également les types à gérer. Il est par exemple souvent fait référence à des entiers non signés en 16, 32 ou 64 bits. Comme la notion d&#8217;entier non signé n&#8217;est pas disponible nativement en Objective Caml, nous allons gérer ces différents types par l&#8217;intermédiaire de modules dédiés.</p>
<p>Prenons l&#8217;exemple le plus simple qui est celui des entiers non signés 16 bits. La signature de ce module sera définie dans un fichier appelé &laquo;&nbsp;memcache_int16u.mli&nbsp;&raquo; :</p>
<p><pre class="brush: java;">
	type t
	exception Overflow of t
	val zero : t
	val min : t
	val max : t
	val to_string : t -&gt; string
	val of_string : string -&gt; t
	val of_int : int -&gt; t
</pre></p>
<p>Le type &laquo;&nbsp;t&nbsp;&raquo; correspondra au type Objective Caml natif pour stocker l&#8217;entier. En l&#8217;occurrence, pour les entiers non signés 16 bits, il s&#8217;agira du type &laquo;&nbsp;int&nbsp;&raquo;. L&#8217;exception &laquo;&nbsp;Overflow&nbsp;&raquo; sera levée si l&#8217;entier dépasse les limites. Ici ce sera le cas si le nombre est négatif ou bien dépasse la valeur 65535 (2^16 &#8211; 1).</p>
<p>On définie également les valeurs zéro (0), minimale (0) et maximale (65535). On aura également à notre disposition trois fonctions de conversions pour par exemple transformer une chaîne de caractères représentant un nombre vers le type entier non signé 16 bits.</p>
<p>Les trois modules dédiés à la gestion des entiers non signés auront cette même signature. Seule l&#8217;implémentation sera différente. Comme la signature de ce module reste très simple, il a été facile de la rédiger directement. Pour les signatures plus complexes, il est possible de générer automatiquement une première version à partir du fichier .ml d&#8217;implémentation grâce à l&#8217;option &laquo;&nbsp;-i&nbsp;&raquo; du compilateur Objective Caml.</p>
<p>Un autre type important correspond à la structure qui nous servira à stocker les informations pour la connexion au serveur :</p>
<p><pre class="brush: java;">
	type connection = {
	  host: string; (** adresse IP du serveur *)
	  port: int; (** numéro de port *)
	  in_channel: in_channel; (** canal de communication entrant *)
	  out_channel: out_channel; (** canal de communication sortant *)
	};;
</pre></p>
<p>Un enregistrement de ce type sera passé en paramètre à toutes les fonctions qui auront besoin d&#8217;échanger des données avec le serveur Memcached.</p>
<ul>
<li>Implémentation :</li>
</ul>
<p>Passons maintenant au développement des fonctions proprement dites. Nous en détaillerons uniquement certaines pour donner un aperçu du langage Objective Caml.</p>
<p>Prenons par exemple la fonction chargée de recevoir plusieurs lignes en provenance du serveur :</p>
<p><pre class="brush: java;">
(**
  Reçoit plusieurs lignes du serveur.
  @param connection Informations pour la connexion (déjà ouverte).
  @return Une FIFO contenant les lignes dans l'ordre de réception + une chaîne de caractères concaténation des lignes reçues et séparées par \n.
*)
let receive_lines_from_server ~connection =
  (* c'est par le canal de communicatio entrant que notre client reçoit les données du serveur *)
  let in_channel = connection.in_channel in
  (* on créé une structure FIFO pour stocker les lignes reçues du serveur *)
  let queue_lines = Queue.create () in
  (* on créé un tampon qui stockera lui aussi les lignes reçues du serveur mais sous forme de chaîne de caractères *)
  let text_lines = Buffer.create 1024 in
  (* on créé une variable &quot;l&quot; (une référence sur une chaîne vide) qui servira à stocker une ligne reçue *)
  let l = ref &quot;&quot; in
  (* on définie une fonction récursive &quot;one_line&quot; qui servira à traiter une ligne *)
  let rec one_line () =
    (* on récupère la ligne courante qui provient du serveur *)
    l := input_line in_channel;
    (* on supprime les fins de lignes *)
    l := Str.global_replace regexp_trail_eol &quot;&quot; !l;
    (* si ce n'est pas la dernière ligne *)
    if (!l &lt;&gt; end_line) then (
      (* on stocke la ligne courante dans la FIFO et dans le tampon *)
      Queue.add !l queue_lines;
      Buffer.add_string text_lines (!l ^ &quot;\n&quot;);
      (* on rappelle la fonction pour traiter la prochaine ligne *)
      one_line ();
    )
    (* plus de ligne à traiter, on peut renvoyer le résultat *)
    else (
      (queue_lines, Buffer.contents text_lines)
    )
  in
    (* il faut appeler une première fois la fonction &quot;one_line&quot; pour commencer le traitement *)
    one_line ()
;;
</pre></p>
<p>Notez que la variable &laquo;&nbsp;l&nbsp;&raquo; est une référence sur une chaîne vide dont la valeur est modifiée dans la fonction &laquo;&nbsp;one_line&nbsp;&raquo;. Pour assigner une nouvelle valeur à une référence, il est nécessaire d&#8217;utiliser le signe &laquo;&nbsp;:=&nbsp;&raquo;. Et pour obtenir le contenu (la valeur) de la référence, on utilise le signe &laquo;&nbsp;!&nbsp;&raquo; devant le nom de la variable.</p>
<p>La fonction renvoie un tuple (une liste de valeurs) dont le premier élément est la suite des lignes reçues du serveur. Il s&#8217;agit de la variable &laquo;&nbsp;queue_lines&nbsp;&raquo; qui est une file FIFO. Le deuxième élément renvoyé est une chaîne de caractères qui contient la concaténation des lignes reçues du serveur. Les deux éléments contiennent donc au final les mêmes données. L&#8217;intérêt de renvoyer ces données sous deux formes différentes est de pouvoir selon les besoins traiter la réponse du serveur soit comme une suite de lignes soit comme une chaîne de caractères.</p>
<p>Voyons ensuite la fonction qui envoie la commande pour stocker une valeur auprès du serveur :</p>
<p><pre class="brush: java;">
(**
  Commandes de stockage d'une clef/valeur.
  @param cmd Commande (voir la section &quot;Storage commands&quot; du protocole).
  @param key Clef de la valeur.
  @param flag Valeur &quot;flag&quot; d'une commande de stockage (voir la section &quot;Storage commands&quot; du protocole).
  @param time Expiration.
  @param bytes Nb d'octets des données à stocker.
  @param data Les données à stocker.
  @param connection Informations pour la connexion (déjà ouverte).
  @raise Bad_answer_from_server Réponse inattendue du serveur.
  @return Indique si la valeur a été stockée ou pas.
*)
let storage ~cmd ~key ~flag ~time ~bytes ~data ~connection =
  (* on construit la commande de stockage à partir des paramètres *)
  let s = (cmd ^ &quot; &quot; ^ key ^ &quot; &quot; ^ (Int16u.to_string flag) ^ &quot; &quot; ^ (string_of_int time) ^ &quot; &quot; ^ (string_of_int bytes)) in
  (* on envoie la ligne au serveur *)
  let () = send_one_line_to_server ~connection ~s in
  (* et ensuite on envoie au serveur la donnée à stocker *)
  let s = Buffer.contents data in
  let () = send_one_line_to_server ~connection:connection ~s:s in
  (* le serveur doit nous retourner une réponse *)
  let answer = receive_one_line_from_server ~connection:connection in
  (* on vérifie que cette réponse ne correspond pas à une erreur *)
  let () = check_error_answer_from_server ~answer:answer in
  (* on examine ensuite cette réponse qui doit correspondre à une des réponses possibles pour la commande de stockage *)
  let new_value =
    match answer with
      | &quot;STORED&quot; -&gt; Stored
      | &quot;NOT_STORED&quot; -&gt; Not_stored
      | _ -&gt; raise (Bad_answer_from_server answer)
  in
    (* la fonction retourne cette réponse sous forme d'un type &quot;storage_answer&quot; que nous avons défini *)
    new_value
;;
</pre></p>
<p>La fonction prend en paramètres toutes les données nécessaires au traitement qu&#8217;elle effectue. Notez que le caractère &laquo;&nbsp;~&nbsp;&raquo; au début de chaque paramètre indique que ce sont des paramètres nommés. C&#8217;est à dire que lorsque la fonction sera appelée, il sera possible d&#8217;indiquer ce nom pour chaque valeur de paramètre ce qui permet ainsi de s&#8217;affranchir du respect de l&#8217;ordre de définition des paramètres. La fonction peut ainsi être appelée de cette manière :</p>
<p><pre class="brush: java;">
	storage ~connection:connexion ~flag:(Int16u.of_int 32) ~time:1219422874 ~bytes:1024 ~cmd:&quot;commande&quot; ~key:&quot;clef&quot; ~data:tampon
</pre></p>
<p>On peut voir que l&#8217;on rappelle pour chaque paramètre son nom défini lors de la déclaration de la fonction. Il n&#8217;est donc pas obligatoire de respecter le même ordre dans les paramètres. Si le nom des paramètres est suffisamment explicite, c&#8217;est également une manière de rendre plus clair le code.</p>
<p>Notez que le caractère &laquo;&nbsp;^&nbsp;&raquo; est utilisé pour la concaténation des chaînes de caractères. C&#8217;est l&#8217;équivalent du caractère &laquo;&nbsp;.&nbsp;&raquo; dans le langage PHP.</p>
<p>Pour finir, remarquez une construction très utilisée dans le langage Objective Caml qui est le &laquo;&nbsp;match&nbsp;&raquo;. Ici, cela permet de comparer la valeur de la variable &laquo;&nbsp;answer&nbsp;&raquo; avec les chaînes de caractères &laquo;&nbsp;STORED&nbsp;&raquo; et &laquo;&nbsp;NOT_STORED&nbsp;&raquo; et selon le cas de stocker dans &laquo;&nbsp;new_value&nbsp;&raquo; soit &laquo;&nbsp;Stored&nbsp;&raquo; soit &laquo;&nbsp;Not_stored&nbsp;&raquo;. Si la comparaison échoue, le défaut indiqué par le caractère &laquo;&nbsp;_&nbsp;&raquo; est de lever l&#8217;exception &laquo;&nbsp;Bad_answer_from_server&nbsp;&raquo;. La construction &laquo;&nbsp;match&nbsp;&raquo; ne s&#8217;applique pas uniquement à des chaînes de caractères. Elle peut par exemple s&#8217;appliquer à des listes ou à un type défini dans le programme. Vous êtes invité à consulter la documentation du langage Objective Caml pour plus de détails.</p>
<p>Pour finir, voyons la fonction qui permet d&#8217;obtenir les statistiques globales du serveur :</p>
<p><pre class="brush: java;">
(**
  Statistiques globales du serveur.
  @param connection Informations pour la connexion (déjà ouverte).
  @raise Bad_answer_from_server Si réponse inattendue ou si une des réponses n'est pas dans la liste &quot;names_stats&quot;.
  @return Une liste de paires (nom, valeur) correspondant aux statistiques.
*)
let stats ~connection =
  (* on envoie la commande &quot;stats&quot; au serveur *)
  let s = &quot;stats&quot; in
  let () = send_one_line_to_server ~connection:connection ~s:s in
  (* on reçoit la réponse en plusieurs lignes de la part du serveur *)
  let (lines, answer) = receive_lines_from_server ~connection:connection in
  (* on doit au moins recevoir une ligne *)
  (* si ce n'est pas le cas, on lève une exception *)
  let () =
    if (Queue.length lines &lt;= 0) then (raise (Bad_answer_from_server answer))
  in
  (* fonction qui traite une seule ligne des statistiques reçues *)
  let one_line acc l =
    (* les lignes sont de la forme &quot;STAT &lt;nom&gt; &lt;valeur&gt;&quot; *)
    let r = Str.regexp (&quot;^STAT \\([^ ]+\\) \\(.+\\)&quot;) in
      (* si la ligne est au bon format *)
      if (Str.string_match r l 0) then (
        try
          (* on récupère la partie &quot;nom&quot; *)
          let name = Str.matched_group 1 l in
          (* on récupère la partie &quot;valeur&quot; *)
          let value = Str.matched_group 2 l in
          (* on vérifie que les clefs/valeurs reçues sont bien autorisées *)
          let () =
            if (not (List.mem_assoc name names_stats)) then (raise (Bad_answer_from_server answer))
          in
          (* conversion des valeurs en entiers non signés *)
          let new_value =
          let type_value = List.assoc name names_stats in
            match type_value with
              | Ts_32u _ -&gt; Ts_32u (Int32u.of_string value)
              | Ts_64u _ -&gt; Ts_64u (Int64u.of_string value)
              | Ts_string _ -&gt; Ts_string value
              | Ts_2x_32u _ -&gt; Ts_2x_32u value
            in
            (* on ajoute le tuple (nom, valeur) à la liste qui sera renvoyée par la fonction *)
              (name, new_value) :: acc
        with
          | Not_found -&gt; raise (Bad_answer_from_server answer) (* une des lignes n'est pas au bon format *)
      )
      else (
        raise (Bad_answer_from_server answer) (* une des lignes n'est pas au bon format *)
      )
  in
    (* pour chaque ligne reçue du serveur, on appelle la fonction &quot;one_line&quot; *)
    Queue.fold (one_line) [] lines
;;
</pre></p>
<p>On peut voir que la fonction &laquo;&nbsp;one_line&nbsp;&raquo; a notamment comme paramètre une variable appelée &laquo;&nbsp;acc&nbsp;&raquo; (pour &laquo;&nbsp;accumulateur&nbsp;&raquo;). Ce type de paramètre est souvent présent en Objective Caml. Il permet en effet d&#8217;accumuler le résultat d&#8217;appels successifs à une fonction donnée. Ici, la fonction &laquo;&nbsp;one_line&nbsp;&raquo; est appelée autant de fois qu&#8217;il y a de lignes à traiter grâce à l&#8217;utilisation de la fonction &laquo;&nbsp;Queue.fold&nbsp;&raquo;. On peut également voir dans l&#8217;appel à cette fonction que l&#8217;accumulateur a comme valeur de départ une liste vide.</p>
<p>On retrouve aussi la construction &laquo;&nbsp;match&#8230;with&nbsp;&raquo; dont nous avons déjà parlé. Cette fois-ci, la comparaison porte sur les types d&#8217;entiers non signés que nous avions définis.</p>
<ul>
<li>Compilation &laquo;&nbsp;bytecode&nbsp;&raquo; :</li>
</ul>
<p>Objective Caml offre deux modes de compilation :</p>
<p>- Un mode qui permet d&#8217;obtenir un exécutable natif, de la même manière que l&#8217;on obtiendrait un exécutable depuis des sources en langage C. L&#8217;avantage est de produire un programme avec de très bonnes performances. En contre-parti, ce programme fonctionnera uniquement sur la plate-forme (par exemple un système FreeBSD sur PC) pour lequel il a été compilé.</p>
<p>- Un mode &laquo;&nbsp;bytecode&nbsp;&raquo; qui produira un programme destiné à être exécuté par la machine virtuelle Objective Caml. C&#8217;est un mécanisme similaire à celui utilisé par le langage Java. L&#8217;exécution sera plus lente qu&#8217;avec un programme natif mais il ne sera plus dépendant de la plate-forme.</p>
<p>Vous trouverez dans l&#8217;archive contenant les sources un script shell indiquant les commandes pour compiler le code en mode &laquo;&nbsp;bytecode&nbsp;&raquo; et ainsi obtenir une librairie et un exécutable de démonstration.</p>
<p>Par exemple, pour compiler le fichier memcache_int16u.ml qui contient l&#8217;implémentation des entiers 16 bits non signés, la commande est la suivante :</p>
<p><pre class="brush: java;">
	ocamlc -c memcache_int16u.ml
</pre></p>
<p>Ce qui produira un fichier objet &laquo;&nbsp;memcache_int16u.cmo&nbsp;&raquo;.</p>
<ul>
<li>Références :</li>
</ul>
<p>Protocole Memcached : <a title="Protocole Memcached" href="http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt">http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt</a></p>
<p>Site officiel Objective Caml : <a title="Site officiel Objective Caml" href="http://www.ocaml.org/">http://www.ocaml.org/</a></p>
<br /><img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/stephanelegrand.wordpress.com/81/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/stephanelegrand.wordpress.com/81/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/stephanelegrand.wordpress.com/81/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/stephanelegrand.wordpress.com/81/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/stephanelegrand.wordpress.com/81/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/stephanelegrand.wordpress.com/81/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/stephanelegrand.wordpress.com/81/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/stephanelegrand.wordpress.com/81/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/stephanelegrand.wordpress.com/81/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/stephanelegrand.wordpress.com/81/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/stephanelegrand.wordpress.com/81/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/stephanelegrand.wordpress.com/81/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/stephanelegrand.wordpress.com/81/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/stephanelegrand.wordpress.com/81/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/stephanelegrand.wordpress.com/81/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/stephanelegrand.wordpress.com/81/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=stephanelegrand.wordpress.com&amp;blog=4517885&amp;post=81&amp;subd=stephanelegrand&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://stephanelegrand.wordpress.com/2008/08/27/client-memcached-pour-objective-caml/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">stephanelegrand</media:title>
		</media:content>
	</item>
		<item>
		<title>Gestion d&#8217;un cache SQL sous Zend Framework</title>
		<link>http://stephanelegrand.wordpress.com/2008/08/23/gestion-dun-cache-sql-sous-zend-framework/</link>
		<comments>http://stephanelegrand.wordpress.com/2008/08/23/gestion-dun-cache-sql-sous-zend-framework/#comments</comments>
		<pubDate>Sat, 23 Aug 2008 15:57:35 +0000</pubDate>
		<dc:creator>Stéphane Legrand</dc:creator>
				<category><![CDATA[Programmation]]></category>
		<category><![CDATA[cache]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[triggers]]></category>
		<category><![CDATA[zend framework]]></category>

		<guid isPermaLink="false">http://stephanelegrand.wordpress.com/?p=66</guid>
		<description><![CDATA[Introduction Nous allons voir comment éviter l&#8217;envoi inutile de requêtes SQL identiques lors de l&#8217;exécution d&#8217;un script PHP utilisant Zend Framework. L&#8217;idée est de mettre en cache le résultat de chaque nouvelle requête SQL. Pour ensuite être capable de renvoyer directement le résultat mis en cache dans le cas où les données de la base [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=stephanelegrand.wordpress.com&amp;blog=4517885&amp;post=66&amp;subd=stephanelegrand&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<ul>
<li>Introduction</li>
</ul>
<p>Nous allons voir comment éviter l&#8217;envoi inutile de requêtes SQL identiques lors de l&#8217;exécution d&#8217;un script PHP utilisant Zend Framework. L&#8217;idée est de mettre en cache le résultat de chaque nouvelle requête SQL. Pour ensuite être capable de renvoyer directement le résultat mis en cache dans le cas où les données de la base utilisées par cette requête n&#8217;ont pas été modifiées. En effet, si nous sommes capable de vérifier qu&#8217;aucun enregistrement des tables concernées par la requête n&#8217;a été modifié depuis la mise en cache, nous sommes certain de pouvoir renvoyer directement le résultat en cache puisque la sélection de données sera forcément identique. Attention toutefois, nous partons du principe que les requêtes ne comportent pas de conditions de sélection extérieures aux données elles-mêmes. Une requête qui comporterait par exemple une condition sur l&#8217;heure courante pourrait voir son résultat changer même si les données sur lesquelles portent la requête ne sont jamais modifiées. Il vous faudra donc veiller à utiliser à bon escient ce mécanisme de cache.</p>
<p><span id="more-66"></span></p>
<ul>
<li>Comment connaître la dernière date de modification des tables grâce aux triggers :</li>
</ul>
<p>Considérons la table MySQL suivante :</p>
<p><pre class="brush: sql;">
CREATE TABLE utilisateur (
  id BIGINT NOT NULL AUTO_INCREMENT,
  nom VARCHAR(255) DEFAULT NULL,
  PRIMARY KEY (id)
);
</pre></p>
<p>Nous avons besoin de conserver la dernière date de modification de cette table. Pour cela, nous allons créer une autre table appelée &laquo;&nbsp;modif_table&nbsp;&raquo; qui aura la structure suivante :</p>
<p><pre class="brush: sql;">
CREATE TABLE modif_table (
  nom_table CHAR(255) DEFAULT NULL,
  maj TIMESTAMP,
  PRIMARY KEY (nom_table),
  KEY (maj)
);
</pre></p>
<p>Grâce aux triggers, nous allons sauvegarder dans cette table &laquo;&nbsp;modif_table&nbsp;&raquo; la date de dernière modification de notre table &laquo;&nbsp;utilisateur&nbsp;&raquo;. Le champ &laquo;&nbsp;nom_table&nbsp;&raquo; aura donc ici la valeur &laquo;&nbsp;utilisateur&nbsp;&raquo; et le champ &laquo;&nbsp;maj&nbsp;&raquo; la date et heure de la dernière modification.</p>
<p>Voyons maintenant les instructions MySQL pour mettre en place les triggers :</p>
<p><pre class="brush: sql;">
delimiter //
CREATE TRIGGER trig_apres_insert_utilisateur AFTER INSERT ON utilisateur FOR EACH ROW
BEGIN
  INSERT INTO modif_table (nom_table, maj) VALUES ('utilisateur', NOW()) ON DUPLICATE KEY UPDATE maj=NOW();
END;
//
CREATE TRIGGER trig_apres_update_utilisateur AFTER UPDATE ON utilisateur FOR EACH ROW
BEGIN
  INSERT INTO modif_table (nom_table, maj) VALUES ('utilisateur', NOW()) ON DUPLICATE KEY UPDATE maj=NOW();
END;
//
CREATE TRIGGER trig_apres_delete_utilisateur AFTER DELETE ON utilisateur FOR EACH ROW
BEGIN
  INSERT INTO modif_table (nom_table, maj) VALUES ('utilisateur', NOW()) ON DUPLICATE KEY UPDATE maj=NOW();
END;
//
delimiter ;
</pre></p>
<p>Pour chaque événement (insertion, modification et suppression d&#8217;un enregistrement) qui peut avoir pour effet de modifier les données de la table &laquo;&nbsp;utilisateur&nbsp;&raquo;, la date de dernière modification est mise à jour dans la table &laquo;&nbsp;modif_table&nbsp;&raquo;. Notez l&#8217;utilisation d&#8217;une requête &laquo;&nbsp;INSERT&#8230;ON DUPLICATE&nbsp;&raquo; qui permet soit d&#8217;insérer un enregistrement s&#8217;il n&#8217;existe pas déjà soit de le mettre à jour s&#8217;il est déjà présent dans la table. En effet, l&#8217;enregistrement qui stocke la dernière modification pour la table &laquo;&nbsp;utilisateur&nbsp;&raquo; n&#8217;est pas obligatoirement déjà présent dans la table &laquo;&nbsp;modif_table&nbsp;&raquo;.</p>
<p>Nous pouvons maintenant tester si nos triggers fonctionnent comme nous le souhaitons. Après avoir créé les deux tables et les triggers vus précedemment dans une base de données, nous allons tout d&#8217;abord créer un premier enregistrement dans la table &laquo;&nbsp;utilisateur&nbsp;&raquo; :</p>
<p><pre class="brush: sql;">
mysql&gt; INSERT INTO utilisateur (nom) VALUES ('Linus Torvald');
Query OK, 1 row affected (0.03 sec)

mysql&gt; SELECT * FROM utilisateur;
+----+---------------+
| id | nom           |
+----+---------------+
|  1 | Linus Torvald |
+----+---------------+
1 row in set (0.00 sec)

mysql&gt; SELECT * FROM modif_table;
+-------------+---------------------+
| nom_table   | maj                 |
+-------------+---------------------+
| utilisateur | 2008-08-19 22:14:53 |
+-------------+---------------------+
1 row in set (0.00 sec)
</pre></p>
<p>On peut constater que l&#8217;insertion de l&#8217;enregistrement &laquo;&nbsp;utilisateur&nbsp;&raquo; a provoqué la création d&#8217;un enregistrement dans la table &laquo;&nbsp;modif_table&nbsp;&raquo; comme nous l&#8217;avons défini grâce aux triggers.</p>
<p>Modifions ensuite l&#8217;enregistrement &laquo;&nbsp;utilisateur&nbsp;&raquo; :</p>
<p><pre class="brush: sql;">
mysql&gt; UPDATE utilisateur SET nom='Donald Knuth' WHERE id=1;
Query OK, 1 row affected (0.02 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql&gt; SELECT * FROM modif_table;
+-------------+---------------------+
| nom_table   | maj                 |
+-------------+---------------------+
| utilisateur | 2008-08-19 22:18:19 |
+-------------+---------------------+
1 row in set (0.00 sec)
</pre></p>
<p>Là encore, nos triggers ont joué leurs rôles. Nous pouvons constater que la valeur du champ &laquo;&nbsp;maj&nbsp;&raquo; de la table &laquo;&nbsp;modif_table&nbsp;&raquo; a été modifiée pour refléter la mise à jour de l&#8217;enregistrement &laquo;&nbsp;utilisateur&nbsp;&raquo;.</p>
<p>Enfin, testons la suppression d&#8217;un enregistrement :</p>
<p><pre class="brush: sql;">
mysql&gt; DELETE FROM utilisateur WHERE id=1;
Query OK, 1 row affected (0.00 sec)

mysql&gt; SELECT * FROM modif_table;
+-------------+---------------------+
| nom_table   | maj                 |
+-------------+---------------------+
| utilisateur | 2008-08-19 22:22:21 |
+-------------+---------------------+
1 row in set (0.00 sec)
</pre></p>
<p>La date et l&#8217;heure de modification ont bien été modifiées.</p>
<ul>
<li>Définition de la classe Zend Framework</li>
</ul>
<p>Nous avons donc maintenant tout ce qui nous est nécessaire en ce qui concerne la base de données. Il nous reste maintenant à l&#8217;utiliser sous Zend Framework.</p>
<p>Première étape, nous avons besoin d&#8217;une classe dédiée pour prendre en charge la gestion de la table &laquo;&nbsp;modif_table&nbsp;&raquo;. Nous avons uniquement besoin d&#8217;une méthode qui nous indiquera s&#8217;il est possible d&#8217;utiliser le cache. Plus précisément, une méthode qui nous indiquera si aucune des tables utilisées dans la requête de sélection n&#8217;a été modifiée depuis la mise en cache du résultat.</p>
<p>Voici donc notre classe qui sera enregistrée dans un fichier appelé &laquo;&nbsp;sql_modif_table.php&nbsp;&raquo; :</p>
<p><pre class="brush: php;">
require_once('Zend/Db/Table/Abstract.php');
require_once('Zend/Db/Select.php');

class Sql_Modif_Table {
  // nom de la table SQL
  protected $_name = 'modif_table';
  // nom du champ clef primaire
  protected $_primary = 'nom_table';

  // indique si aucune table n'a été modifiée depuis un temps donné
  // renvoie faux si au moins une table a été modifiée, vrai sinon
  // le premier paramètre est un tableau qui donne les noms des tables
  // le deuxième paramètre est le temps unix utilisé pour la comparaison avec le temps de modification des tables
  public function no_update(array $table, $time) {
    // par défaut, renvoie FALSE (au moins une table a été modifiée)
    $r = FALSE;
    // il faut au moins une table et un temps
    if (is_array($table) &amp;&amp; (count($table) &gt; 0) &amp;&amp; $time) {
      $db = Zend_Db_Table_Abstract::getDefaultAdapter();
      // on met entre quote le nom des tables
      $tab_quote_table = array();
      foreach ($table as $t) {
        array_push($tab_quote_table, $db-&gt;quote($t));
      }
      // on construit la partie IN de la requête
      $sql_in = implode(',', $tab_quote_table);
      $sql_in = '('.$sql_in.')';
      // on construit la requête
      $select = $db-&gt;select();
      $select-&gt;from($this-&gt;_name, array('nb' =&gt; 'COUNT(*)'))
                 -&gt;where('nom_table IN '.$sql_in)
                 -&gt;where('UNIX_TIMESTAMP(maj) &lt;= ?', $time);
      $stmt = $select-&gt;query();
      $row = $stmt-&gt;fetch();
      if ($row) {
        $nb = $row['nb'];
        // si le nombre renvoyé par la requête est égal au nombre de tables,
        // cela signifie qu'aucune table n'a été modifiée depuis le temps passé en paramètre
        if ($nb == count($tab_quote_table)) {
          $r = TRUE;
        }
      }
    }
    return($r);
  }
}
</pre></p>
<p>Cette méthode &laquo;&nbsp;no_update()&nbsp;&raquo; construit une requête SQL à partir de ses paramètres afin de vérifier si une des tables contenues dans la variable &laquo;&nbsp;$table&nbsp;&raquo; a été modifiée après le temps (format Unix) indiqué dans la variable &laquo;&nbsp;$time&nbsp;&raquo;. Par exemple, si la méthode est appelée avec les paramètres suivants :</p>
<p><pre class="brush: php;">
...
$table = array('table1', 'table2');
$time = '1219422874';
$r = $o-&gt;no_update($table, $time);
...
</pre></p>
<p>La requête SQL sera :</p>
<p><pre class="brush: sql;">
SELECT COUNT(*) AS nb FROM modif_table WHERE nom_table IN ( 'table1', 'table2' ) AND UNIX_TIMESTAMP ( maj ) &lt;= '1219422874';
</pre></p>
<p>Deuxième étape, nous allons maintenant surcharger la méthode Zend_Db_Select::query() afin de faire en sorte que toutes les requêtes &laquo;&nbsp;SELECT&nbsp;&raquo; envoyées bénéficient du cache si cela est possible. Voici donc notre classe qui sera enregistrée dans un fichier appelé &laquo;&nbsp;db_select.php&nbsp;&raquo; :</p>
<p><pre class="brush: php;">
require_once('Zend/Db/Select.php');
require_once('sql_modif_table.php');

class Db_Select extends Zend_Db_Select {

  // la méthode envoie la requête vers le serveur de base de données
  // seulement s'il n'est pas possible d'utiliser le cache
  private function query_cache($fetchMode) {
    // tableau qui stocke le resultat des requêtes
    // la clef du tableau est la signature MD5 de la requête SELECT
    static $cache_value = array();
    // tableau qui stocke la date et l'heure de l'envoi des requêtes
    // la clef du tableau est la signature MD5 de la requête SELECT
    static $cache_time = array();

    // signature MD5 de la requête
    $sql = $this-&gt;__toString();
    $signature = md5($sql);
    $r = null;
    // par défaut, n'utilise pas le cache
    $use_cache = false;
    if (isset($cache_value[$signature]) &amp;&amp; isset($cache_time[$signature])) {
      // la requête a déjà été envoyée, on vérifie si l'on peut utiliser le résultat mis en cache
      $table_update = new Sql_Modif_Table;
      // on récupère la liste des tables sur lesquelles porte la requête
      $part_from_sql = $this-&gt;getPart(Zend_Db_Select::FROM);
      $tables_in_sql = array();
      foreach ($part_from_sql as $from) {
        $t = $from['tableName'];
        array_push($tables_in_sql, $t);
      }
      // peut-on utiliser le cache ?
      if ($table_update-&gt;no_update($tables_in_sql, $cache_time[$signature])) {
        // oui, donc le résultat renvoyé sera celui présent en cache
        $r = $cache_value[$signature];
        $use_cache = true;
      }
    }
    if (! $use_cache) {
      // le cache ne peut être utilisé
      $time_cache = time();
      // donc on envoie la requête SELECT à la base de données
      $r = parent::query($fetchMode);
      // et on stocke ce nouveau résultat dans le cache
      $cache_value[$signature] = $r;
      $cache_time[$signature] = $time_cache;
    }
    return($r);
  }

  // méthode surchargée afin d'utiliser la méthode avec cache
  public function query($fetchMode = null) {
    $r = $this-&gt;query_cache($fetchMode);
    return($r);
  }

}
</pre></p>
<p>Nous allons également surcharger la classe Zend_Db_Table_Abstract afin de disposer d&#8217;une classe qui interdit l&#8217;utilisation directe de la méthode Zend_Db_Table_Abstract::select(). Cette nouvelle classe sera stockée dans un fichier appelé &laquo;&nbsp;db_table.php&nbsp;&raquo; :</p>
<p><pre class="brush: php;">
require_once('Zend/Db/Table/Abstract.php');

class Db_Table extends Zend_Db_Table_Abstract {

  public function insert(array $data) {
    return parent::insert($data);
  }

  public function update(array $data, $where) {
    return parent::update($data, $where);
  }

  public function delete(array $where) {
    return parent::delete($where);
  }

  // on interdit l'utilisation directe de cette méthode
  public function select() {
    die('Db_Table::select():Don\'t use this method !');
  }

}
</pre></p>
<ul>
<li>Intégration dans une application Zend Framework :</li>
</ul>
<p>Si vous utilisez le modèle MVC que propose Zend pour votre application, les trois fichiers que nous avons vu précédemment seront typiquement à placer dans le répertoire &laquo;&nbsp;application/models/&nbsp;&raquo;.</p>
<p>L&#8217;appel aux méthodes que nous avons définies se fera par exemple de la manière suivante :</p>
<p><pre class="brush: php;">
...
$db = Db_Table::getDefaultAdapter();
$select = new Db_Select($db);
$select-&gt;from($this-&gt;tableName)
           -&gt;where('champ = ?', $valeur);
$stmt = $select-&gt;query();
$row = $stmt-&gt;fetch();
...
</pre></p>
<p>Vous remarquerez l&#8217;utilisation des classes &laquo;&nbsp;Db_Table&nbsp;&raquo; et &laquo;&nbsp;Db_Select&nbsp;&raquo; que nous avons créées. De cette manière, nous sommes certain que les requêtes utiliseront le mécanisme de cache que nous avons mis en place.</p>
<ul>
<li>Pour aller plus loin&#8230;</li>
</ul>
<p>Comme les données du cache sont stockées dans un tableau, le cache n&#8217;est actif que pendant l&#8217;exécution du script PHP. Il ne sera donc utile que si vous exécutez plusieurs fois les mêmes requêtes &laquo;&nbsp;SELECT&nbsp;&raquo; dans un même script. Ce qui est évidemment assez limité&#8230; Néanmoins, il serait tout à fait possible en modifiant uniquement la méthode Db_Select::query_cache() de rendre le cache persistant et ainsi de le partager pour tous les scrips PHP de votre application. Une possibilité intéressante serait par exemple de gérer ce cache avec le logiciel &laquo;&nbsp;memcached&nbsp;&raquo; qui permet de stocker en mémoire vive des données via un mécanisme client/serveur. Une librairie pour développer des clients &laquo;&nbsp;memcached&nbsp;&raquo; en PHP est disponible.</p>
<p>Par ailleurs, une bien meilleure implémentation serait d&#8217;utiliser la classe Zend_Cache proposée dans Zend Framework pour la gestion proprement dite du cache. Il serait ainsi possible d&#8217;utiliser les différents backends déjà disponibles et en particulier le backend &laquo;&nbsp;memcached&nbsp;&raquo;.</p>
<ul>
<li>Références :</li>
</ul>
<p>Triggers sous MySQL : <a class="wp-caption" title="Triggers sous MySQL 5.0" href="http://dev.mysql.com/doc/refman/5.0/fr/triggers.html">http://dev.mysql.com/doc/refman/5.0/fr/triggers.html</a></p>
<p>Documentation Zend Framework : <a class="wp-caption" title="Documentation Zend Framework" href="http://framework.zend.com/">http://framework.zend.com/</a></p>
<p>Memcached : <a class="wp-caption" title="Memcached" href="http://www.danga.com/memcached/">http://www.danga.com/memcached/</a></p>
<br /><img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/stephanelegrand.wordpress.com/66/" /> <img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/stephanelegrand.wordpress.com/66/" /> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/stephanelegrand.wordpress.com/66/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/stephanelegrand.wordpress.com/66/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/stephanelegrand.wordpress.com/66/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/stephanelegrand.wordpress.com/66/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/stephanelegrand.wordpress.com/66/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/stephanelegrand.wordpress.com/66/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/stephanelegrand.wordpress.com/66/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/stephanelegrand.wordpress.com/66/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/stephanelegrand.wordpress.com/66/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/stephanelegrand.wordpress.com/66/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/stephanelegrand.wordpress.com/66/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/stephanelegrand.wordpress.com/66/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/stephanelegrand.wordpress.com/66/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/stephanelegrand.wordpress.com/66/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=stephanelegrand.wordpress.com&amp;blog=4517885&amp;post=66&amp;subd=stephanelegrand&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://stephanelegrand.wordpress.com/2008/08/23/gestion-dun-cache-sql-sous-zend-framework/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">stephanelegrand</media:title>
		</media:content>
	</item>
	</channel>
</rss>
