<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet title="XSL formatting" type="text/xsl" href="http://mirmodynamics.com/feed/rss2/xslt" ?><rss version="2.0"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:wfw="http://wellformedweb.org/CommentAPI/"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
  <title>Mirmo Dynamics - Tag - howto</title>
  <link>http://mirmodynamics.com/</link>
  <atom:link href="http://mirmodynamics.com/feed/tag/howto/rss2" rel="self" type="application/rss+xml"/>
  <description>Si tu kiffes pas reunoi, t'écoutes pas et puis c'est tout.</description>
  <language>en</language>
  <pubDate>Tue, 02 Mar 2010 13:27:59 +0100</pubDate>
  <copyright>2003-2009 &amp;copy; Geoffrey Bachelet</copyright>
  <docs>http://blogs.law.harvard.edu/tech/rss</docs>
  <generator>Dotclear</generator>
  
    
  <item>
    <title>Multiple domains for one symfony project, the basics, config file version</title>
    <link>http://mirmodynamics.com/post/2009/01/19/Multiple-domains-for-one-symfony-project%2C-the-basics%2C-config-file-version</link>
    <guid isPermaLink="false">urn:md5:d1988ef40eb48bba444de255d9def1cb</guid>
    <pubDate>Thu, 22 Jan 2009 22:02:00 +0100</pubDate>
    <dc:creator>Geoffrey</dc:creator>
        <category>Symfony</category>
        <category>cache</category><category>carshop</category><category>config</category><category>dispatcher</category><category>doctrine</category><category>howto</category><category>multidomain</category><category>symfony</category>    
    <description>    &lt;p&gt;When I published my post about &lt;a href=&quot;http://mirmodynamics.com/post/2008/12/22/Multiple-domains-for-one-symfony-project%2C-the-basics&quot;&gt;using a single symfony application to serve multiple domains&lt;/a&gt; using a database to store the site-specific data, there was some people to complain about the extra query you get on each request. Right. Using a (cached) config file instead is not that hard as long as you use it only to match a domain name against a numeric id. Storing more site-specific data is a bit more problematic, but we will solve this in a later post.&lt;/p&gt;


&lt;p&gt;And because I'm lazy, I won't re-write the entire article, just highlight the differences between the two methods, so you might as well read &lt;a href=&quot;http://mirmodynamics.com/post/2008/12/22/Multiple-domains-for-one-symfony-project%2C-the-basics&quot;&gt;the database version&lt;/a&gt; if you did not already.&lt;/p&gt;


&lt;p&gt;Now, first thing, the config file. We will create a simple project-wide config file: &lt;code&gt;/config/app.yml&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;
all:
  paris.carshop: 1
  auckland.carshop: 2
&lt;/pre&gt;


&lt;p&gt;Then we detect the site and add a generic &lt;code&gt;sfConfig&lt;/code&gt; entry for it. This is done, as before, in &lt;code&gt;/config/ProjectConfiguration.class.php&lt;/code&gt;. We will only change the &lt;code&gt;detectSite()&lt;/code&gt; method to take advantage of the cached config information:&lt;/p&gt;

&lt;pre&gt;
[php]
  public function detectSite(sfEvent $event)
  {
    sfConfig::add(array('site_id' =&amp;gt; sfConfig::get('app_'.$_SERVER['HTTP_HOST'])));
  }
&lt;/pre&gt;


&lt;p&gt;Easy heh ?&lt;/p&gt;


&lt;p&gt;Now we just have to adapt the model classes to use this config entry. The &lt;code&gt;CarTable::createQuery()&lt;/code&gt; method will look like:&lt;/p&gt;

&lt;pre&gt;
[php]
/**
 * Creates a query, adding the site criteria automatically
 *
 * @return Doctrine_Query
 * @see Doctrine_Table::createQuery()
 */

public function createQuery($alias = '')
{
  $query = parent::createQuery($alias);
  $query-&amp;gt;where('site_id = ?', sfConfig::get('site_id'));
  
  return $query;
}
&lt;/pre&gt;


&lt;p&gt;The &lt;code&gt;Car::save()&lt;/code&gt; method:&lt;/p&gt;

&lt;pre&gt;
[php]
/**
 * Automatically populates the site_id field if necessary
 * 
 * @see sfDoctrineRecord::save()
 */

public function save(Doctrine_Connection $conn = null)
{
  if (empty($this-&amp;gt;site_id))
  {
    $this-&amp;gt;setSiteId(sfConfig::get('site_id'));
  }

  return parent::save();
}
&lt;/pre&gt;


&lt;p&gt;And you're all set ! For the record, here are the other modified files from the last post:&lt;/p&gt;


&lt;p&gt;&lt;code&gt;config/doctrine/schema.yml&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;
Car:
  columns:
    site_id:      { type: integer }
    name:         { type: string(255) }
    description:  { type: clob }
    image:        { type: string(255) }
&lt;/pre&gt;


&lt;p&gt;&lt;code&gt;data/fixtures/&lt;/code&gt; now contains only the &lt;code&gt;Car&lt;/code&gt; fixtures:&lt;/p&gt;

&lt;pre&gt;
Car:
  car_1:
    site_id: 1
    name: Peugeot 307
    description: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  car_2:
    site_id: 1
    name: Renault Laguna
    description: Maecenas tortor nunc, aliquam et, ultrices id, ornare consectetur, mauris.
  car_3:
    site_id: 2
    name: Subaru Impreza
    description: Ut accumsan diam et orci. Sed sit amet neque ac diam rutrum iaculis.
&lt;/pre&gt;</description>
    
    
    
          <comments>http://mirmodynamics.com/post/2009/01/19/Multiple-domains-for-one-symfony-project%2C-the-basics%2C-config-file-version#comment-form</comments>
      <wfw:comment>http://mirmodynamics.com/post/2009/01/19/Multiple-domains-for-one-symfony-project%2C-the-basics%2C-config-file-version#comment-form</wfw:comment>
      <wfw:commentRss>http://mirmodynamics.com/feed/atom/comments/1169</wfw:commentRss>
      </item>
    
  <item>
    <title>Multiple domains for one symfony project, the basics</title>
    <link>http://mirmodynamics.com/post/2008/12/22/Multiple-domains-for-one-symfony-project%2C-the-basics</link>
    <guid isPermaLink="false">urn:md5:9fbb9dbd582af2cc7fb3a7b9f201cd6a</guid>
    <pubDate>Mon, 29 Dec 2008 11:10:00 +0100</pubDate>
    <dc:creator>Geoffrey</dc:creator>
        <category>Symfony</category>
        <category>apache</category><category>carshop</category><category>dispatcher</category><category>doctrine</category><category>howto</category><category>mod_rewrite</category><category>multidomain</category><category>symfony</category>    
    <description>    &lt;p&gt;So let's say you run a car franchise, and you have several shops that you want to be able to manage through a web application. The application would be same for each shop, except you'd have to manage different sets of data and have specific assets for each shop. Fear not, this is very easy to achieve using symfony (well, it's quite easy with any framework I guess, but we will be using symfony :p)&lt;/p&gt;


&lt;h3&gt;Sample project&lt;/h3&gt;


&lt;p&gt;At the beginning, there were &lt;code&gt;generate:project&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;
$ mkdir carshop
$ cd carshop
$ symfony generate:project carshop
$ symfony generate:app frontend
$ symfony generate:module frontend default
&lt;/pre&gt;


&lt;p&gt;We will also need a simple database schema for the sake of examples. We will be using Doctrine, so let's fill in our &lt;code&gt;config/doctrine/schema.yml&lt;/code&gt; with a very simple schema:&lt;/p&gt;

&lt;pre&gt;
[yml]
detect_relations: true

Car:
  columns:
    site_id:      { type: integer }
    name:         { type: string(255) }
    description:  { type: clob }
    image:        { type: string(255) }

Site:
  columns:
    name:         { type: string(255) }
    domain:       { type: string(255), unique: true }
    main:           { type: boolean, default: false }
&lt;/pre&gt;


&lt;p&gt;Also, let's not forget to enable Doctrine in our project in &lt;code&gt;config/ProjectConfiguration.class.php&lt;/code&gt;, replace the line:&lt;/p&gt;

&lt;pre&gt;
[php]
$this-&amp;gt;enableAllPluginsExcept('sfDoctrinePlugin');
&lt;/pre&gt;


&lt;p&gt;with:&lt;/p&gt;

&lt;pre&gt;
[php]
$this-&amp;gt;enableAllPluginsExcept('sfPropelPlugin');
&lt;/pre&gt;


&lt;p&gt;and to configure the database:&lt;/p&gt;

&lt;pre&gt;
$ symfony configure:database --name=doctrine --class=sfDoctrineDatabase &amp;quot;mysql:host=localhost;dbname=carshop&amp;quot; root
&lt;/pre&gt;


&lt;p&gt;Oh, we shall need some fixtures too, let's put them in &lt;code&gt;data/fixtures/01_sites.yml&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;
[yml]
Site:
  paris:
    name: Paris
    domain: paris.carshop
  auckland:
    name: Auckland
    domain: auckland.carshop
&lt;/pre&gt;


&lt;p&gt;and &lt;code&gt;data/fixtures/02_cars.yml&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;
[yml]
Car:
  car_1:
    Site: paris
    name: Peugeot 307
    description: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  car_2:
    Site: paris
    name: Renault Laguna
    description: Maecenas tortor nunc, aliquam et, ultrices id, ornare consectetur, mauris.
  car_3:
    Site: auckland
    name: Subaru Impreza
    description: Ut accumsan diam et orci. Sed sit amet neque ac diam rutrum iaculis.
&lt;/pre&gt;


&lt;p&gt;And now that everything is ready, let's create the tables and load the fixtures:&lt;/p&gt;

&lt;pre&gt;
$ symfony doctrine:build-all-load
&lt;/pre&gt;


&lt;h4&gt;Vhosts&lt;/h4&gt;


&lt;p&gt;The second thing we will need is a set of vhost with differents domains all pointing to our newly generated project. It's quite easy to do, and I will assume you run some kind of linux or unix here (although it's similar under windows if I remember well). Let's start with creating fake domain names, you just have to add the following line to your &lt;code&gt;/etc/hosts&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;
127.0.0.1 auckland.carshop paris.carshop
&lt;/pre&gt;


&lt;p&gt;And the following to your apache vhost:&lt;/p&gt;

&lt;pre&gt;
[apache]
ServerName paris.carshop
ServerAlias *.carshop
&lt;/pre&gt;


&lt;p&gt;Save and quit, reload apache, and you're all set.&lt;/p&gt;


&lt;h3&gt;Detecting the current site&lt;/h3&gt;


&lt;p&gt;First thing we need to know if we want to handle site-specific data, is which site we're currently in. This can easily be done in the project configuration. Since we will need database access, we will hook up on the &lt;code&gt;context.load_factories&lt;/code&gt; event. So, on with coding, open your &lt;code&gt;config/ProjectConfiguration.class.php&lt;/code&gt; and add the following line to the &lt;code&gt;setup()&lt;/code&gt; method:&lt;/p&gt;

&lt;pre&gt;
[php]
$this-&amp;gt;dispatcher-&amp;gt;connect('context.load_factories', array($this, 'detectSite'));
&lt;/pre&gt;


&lt;p&gt;This will hook the &lt;code&gt;detectSite()&lt;/code&gt; method to the event &lt;code&gt;context.load_factories&lt;/code&gt;. We now need to add the &lt;code&gt;detectSite()&lt;/code&gt; method to the &lt;code&gt;ProjectConfiguration&lt;/code&gt; class:&lt;/p&gt;

&lt;pre&gt;
[php]
/**
 * Detects the current site based on the url
 * Fallback to the main site (main = 1 in database) if we can't find a suitable entry
 *
 * @param sfEvent $event
 */

public function detectSite(sfEvent $event)
{
  $request = sfContext::getInstance()-&amp;gt;getRequest();
  $domain = $request-&amp;gt;getHost();

  $siteTable = Doctrine::getTable('Site');

  if (false !== $site = $siteTable-&amp;gt;retrieveByDomain($domain))
  {
    $siteTable-&amp;gt;setCurrent($site);
  }
  else
  {
    $siteTable-&amp;gt;setCurrent($siteTable-&amp;gt;retrieveMain());
  }
}
&lt;/pre&gt;


&lt;p&gt;Nothing very hard here. We get the HTTP host from the request, then try to fetch a corresponding Site from the database, fallbacking to the main site if necessary. You may have noticed that we added three methods to the &lt;code&gt;Site&lt;/code&gt; model class: &lt;code&gt;retrieveByDomain()&lt;/code&gt;, &lt;code&gt;retrieveMain()&lt;/code&gt; and &lt;code&gt;setCurrent()&lt;/code&gt;. Your &lt;code&gt;lib/model/doctrine/SiteTable.class.php&lt;/code&gt; should look like this now:&lt;/p&gt;

&lt;pre&gt;
[php]
&amp;lt;?php
/**
 * This class has been auto-generated by the Doctrine ORM Framework
 */
class SiteTable extends Doctrine_Table
{

  /**
   * Holds the current site
   * @var Site
   */

  protected $current;

  /**
   * Retrieve a Site by its domain
   *
   * @param string $domain
   * @return Site or false if no site is found
   */

  public function retrieveByDomain($domain)
  {
    return $this-&amp;gt;createQuery()-&amp;gt;where('domain = ?', $domain)-&amp;gt;fetchOne();
  }

  /**
   * Retrieve the main site
   *
   * @return Site or false if no site is found
   */

  public function retrieveMain()
  {
    return $this-&amp;gt;createQuery()-&amp;gt;where('main = 1')-&amp;gt;fetchOne();
  }
  

  /**
   * Sets the current site
   *
   * @param Site $site
   */

  public function setCurrent(Site $site)
  {
    $this-&amp;gt;current = $site;
  }

}
&lt;/pre&gt;


&lt;p&gt;While we're there, let's add a &lt;code&gt;getCurrent()&lt;/code&gt; method as well:&lt;/p&gt;

&lt;pre&gt;
[php]
/**
 * Gets the current site
 *
 * @return Site
 */

public function getCurrent()
{
  return $this-&amp;gt;current;
}
&lt;/pre&gt;



&lt;h3&gt;Handling domain-specific logic and data&lt;/h3&gt;


&lt;p&gt;Ok, so now that we know where we are, let's make the Car model aware of the current site. Open &lt;code&gt;lib/model/doctrine/CarTable.class.php&lt;/code&gt; and add the &lt;code&gt;createQuery()&lt;/code&gt; method:&lt;/p&gt;

&lt;pre&gt;
[php]
/**
 * Creates a query, adding the site criteria automatically
 *
 * @return Doctrine_Query
 * @see Doctrine_Table::createQuery()
 */

public function createQuery($alias = '')
{
  $query = parent::createQuery($alias);
  $query-&amp;gt;where('site_id = ?', Doctrine::getTable('Site')-&amp;gt;getCurrent()-&amp;gt;getId());
  
  return $query;
}
&lt;/pre&gt;


&lt;p&gt;This is the method used internally by &lt;code&gt;Doctrine_Table&lt;/code&gt; to create queries related to the Car model, so now, all &lt;code&gt;find&lt;/code&gt; methods fired from the Car model will only fetch cars from the current site. We need to take care of this at &lt;code&gt;save()&lt;/code&gt; time too, in &lt;code&gt;lib/model/doctrine/Car.class.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;
[php]
/**
 * Automatically populates the site_id field if necessary
 * 
 * @see sfDoctrineRecord::save()
 */

public function save(Doctrine_Connection $conn = null)
{
  if (empty($this-&amp;gt;site_id))
  {
    $this-&amp;gt;setSite(Doctrine::getTable('Site')-&amp;gt;getCurrent());
  }

  return parent::save();
}
&lt;/pre&gt;


&lt;h3&gt;Handling domain specific assets&lt;/h3&gt;


&lt;p&gt;On with the assets. We are going to use our good friend &lt;code&gt;mod_rewrite&lt;/code&gt; to handle this. First, let's create a specific directory for each domain we're going to manage:&lt;/p&gt;

&lt;pre&gt;
$ mkdir -p web/perhost/{paris,auckland}.carshop/{css,images,js,uploads}
&lt;/pre&gt;


&lt;p&gt;Also, for testing purpose, let's create some fake js file:&lt;/p&gt;

&lt;pre&gt;
$ echo 'paris.carshop' &amp;gt; web/perhost/paris.carshop/js/foo.js
$ echo 'auckland.carshop' &amp;gt; web/perhost/auckland.carshop/js/foo.js
$ echo 'fallback' &amp;gt; web/js/fallback.js
&lt;/pre&gt;


&lt;p&gt;Then add some voodoo magic in your &lt;code&gt;web/.htaccess&lt;/code&gt;, before the &lt;em&gt;we skip all files with .something&lt;/em&gt; rules generated by symfony:&lt;/p&gt;

&lt;pre&gt;
[apache]
RewriteCond /home/ash/projects/carshop/trunk/web/perhost/%{HTTP_HOST}%{REQUEST_FILENAME} -f
RewriteRule (.*) /perhost/%{HTTP_HOST}/$1 [L]
&lt;/pre&gt;


&lt;p&gt;Please note that &lt;code&gt;%{REQUEST_FILENAME}&lt;/code&gt; includes the starting &lt;code&gt;/&lt;/code&gt;, so that we don't need to add one after &lt;code&gt;%{HTTP_HOST}&lt;/code&gt;.&lt;/p&gt;


&lt;p&gt;Now check that you have &lt;code&gt;mod_rewrite&lt;/code&gt; enabled (for example check that &lt;code&gt;/etc/apache2/mods-enable/rewrite.load&lt;/code&gt; exists). If it's not enabled it, do it now:&lt;/p&gt;

&lt;pre&gt;
$ sudo a2enmod rewrite
$ sudo /etc/init.d/apache2 reload
&lt;/pre&gt;


&lt;p&gt;You can test the following urls to see if everything is going well, the results should be pretty obvious:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://paris.carshop/js/foo.js&quot; title=&quot;http://paris.carshop/js/foo.js&quot;&gt;http://paris.carshop/js/foo.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://auckland.carshop/js/foo.js&quot; title=&quot;http://auckland.carshop/js/foo.js&quot;&gt;http://auckland.carshop/js/foo.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://paris.carshop/js/fallback.js&quot; title=&quot;http://paris.carshop/js/fallback.js&quot;&gt;http://paris.carshop/js/fallback.js&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;See you next time !&lt;/h3&gt;


&lt;p&gt;Now you have a working multi-domain architecture for your symfony project, but there are still a couple of things we could (and will) discuss:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;uploading assets (from forms for example)&lt;/li&gt;
&lt;li&gt;having a different layout for each site&lt;/li&gt;
&lt;li&gt;having specific css files for each site (eg: paris.css and auckland.css)&lt;/li&gt;
&lt;li&gt;cache problematic for shared assets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All these points will be addressed in the next part of this tutorial, but not before 2009 has came !&lt;/p&gt;</description>
    
    
    
          <comments>http://mirmodynamics.com/post/2008/12/22/Multiple-domains-for-one-symfony-project%2C-the-basics#comment-form</comments>
      <wfw:comment>http://mirmodynamics.com/post/2008/12/22/Multiple-domains-for-one-symfony-project%2C-the-basics#comment-form</wfw:comment>
      <wfw:commentRss>http://mirmodynamics.com/feed/atom/comments/1163</wfw:commentRss>
      </item>
    
  <item>
    <title>Débuter avec le Zend Framework</title>
    <link>http://mirmodynamics.com/post/2007/04/12/Debuter-avec-le-Zend-Framework</link>
    <guid isPermaLink="false">urn:md5:e1875eb76e5eb90ebb656be4b001833a</guid>
    <pubDate>Thu, 12 Apr 2007 00:31:00 +0200</pubDate>
    <dc:creator>Geoffrey</dc:creator>
        <category>Coding</category>
        <category>doc</category><category>howto</category><category>php</category><category>traduction</category><category>tutorial</category><category>zend framework</category>    
    <description>    &lt;p&gt;&lt;a href=&quot;http://akrabat.com/2007/04/11/premiers-pas-avec-le-zend-framework/&quot;&gt;Pour&lt;/a&gt; &lt;a href=&quot;http://g-rossolini.developpez.com/tutoriels/php/zend-framework/debuter/?page=sommaire&quot;&gt;info&lt;/a&gt;.&lt;/p&gt;


&lt;p&gt;Je suis un peu triste parce que je voulais m'atteler à cette tâche, mais bon il faut croire que j'ai trop lambiné :)&lt;/p&gt;</description>
    
    
    
          <comments>http://mirmodynamics.com/post/2007/04/12/Debuter-avec-le-Zend-Framework#comment-form</comments>
      <wfw:comment>http://mirmodynamics.com/post/2007/04/12/Debuter-avec-le-Zend-Framework#comment-form</wfw:comment>
      <wfw:commentRss>http://mirmodynamics.com/feed/atom/comments/756</wfw:commentRss>
      </item>
    
  <item>
    <title>Mini Howto: Serveur et Packages PEAR</title>
    <link>http://mirmodynamics.com/post/2007/03/02/Mini-Howto%3A-Serveur-et-Packages-PEAR</link>
    <guid isPermaLink="false">urn:md5:a4af0a22fcb4f4e307699e4207bc6406</guid>
    <pubDate>Fri, 02 Mar 2007 12:11:00 +0100</pubDate>
    <dc:creator>Geoffrey</dc:creator>
        <category>Geekeries</category>
        <category>chiara</category><category>doc</category><category>howto</category><category>package</category><category>pear</category><category>php</category>    
    <description>    &lt;p&gt;Pour installer le serveur &lt;acronym&gt;PEAR&lt;/acronym&gt;, c'est simple:&lt;/p&gt;


&lt;pre&gt;pear channel-discover pear.chiaraquartet.net
pear install chiara/Chiara_PEAR_Server
pear run-scripts Chiara_PEAR_Server&lt;/pre&gt;


&lt;p&gt;Ensuite on répond aux question, et c'est automagique.&lt;/p&gt;


&lt;p&gt;Après, pour créer des packages, ça se corse. Tout d'abord, on a besoin de &lt;code&gt;PEAR_PackageFileManager&lt;/code&gt;:&lt;/p&gt;


&lt;pre&gt;pear install PEAR_PackageFileManager&lt;/pre&gt;


&lt;p&gt;Ensuite, le but est de générer les &lt;code&gt;package.xml&lt;/code&gt; qui vont bien. Voilà comment j'ai procédé pour le package &lt;code&gt;Zend&lt;/code&gt;:&lt;/p&gt;


&lt;pre&gt;mkdir -p ~/pear/zend/src &amp;amp;&amp;amp; cd ~/pear/zend
wget http://framework.zend.com/download/tgz -O - | tar xzC ~
mv ~/ZendFramework-0.8.0/library/Zend* src/
php ./mkpkg.php make
cd src &amp;amp;&amp;amp; pear package&lt;/pre&gt;


&lt;p&gt;Ceux qui ont bien suivi auront noté que &lt;code&gt;mkpkg.php&lt;/code&gt; est sorti un peu de l'espace :-) Vous trouverez son contenu à la fin du billet.&lt;/p&gt;


&lt;p&gt;Pour résumer, on créé un répertoire de travail (&lt;code&gt;~/pear/zend/&lt;/code&gt;), dans lequel on créé un répertoire &lt;code&gt;src&lt;/code&gt;, qui contiendra tous les fichiers du package, puis le script &lt;code&gt;mkpkg.php&lt;/code&gt; se charge 1) d'analyser le contenu de &lt;code&gt;src&lt;/code&gt;, et 2) de créer le &lt;code&gt;package.xml&lt;/code&gt; qui va bien, selon les instructions qui lui sont fournies.&lt;/p&gt;


&lt;p&gt;Nous disposons désormais  d'un package &lt;code&gt;Zend-0.8.0.tgz&lt;/code&gt; dans &lt;code&gt;~/pear/zend/src&lt;/code&gt;, que nous n'avons plus qu'a uploader via l'interface d'administration du serveur &lt;acronym&gt;PEAR&lt;/acronym&gt; précédemment installé :-)&lt;/p&gt;


&lt;p&gt;Le fichier &lt;code&gt;mkpkg.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php

require_once('PEAR/PackageFileManager2.php');

PEAR::setErrorHandling(PEAR_ERROR_DIE);

$packagexml = new PEAR_PackageFileManager2;

$e = $packagexml-&amp;gt;setOptions(array(
    'baseinstalldir' =&amp;gt; '/',
    'packagedirectory' =&amp;gt; dirname(__FILE__).'/src',
));

$packagexml-&amp;gt;setPackage('Zend');
$packagexml-&amp;gt;setSummary('Zend Framework');
$packagexml-&amp;gt;setDescription('The Zend\'s PHP Framework');
$packagexml-&amp;gt;setChannel('pear.phpmafia.net');
$packagexml-&amp;gt;setAPIVersion('0.8.0');
$packagexml-&amp;gt;setReleaseVersion('0.8.0');
$packagexml-&amp;gt;setReleaseStability('devel');
$packagexml-&amp;gt;setAPIStability('devel');
$packagexml-&amp;gt;setNotes(&amp;quot;Still a preview release&amp;quot;);
$packagexml-&amp;gt;setPackageType('php'); // this is a PEAR-style php script package
$packagexml-&amp;gt;addRelease(); // set up a release section
$packagexml-&amp;gt;setPhpDep('5.1.2');
$packagexml-&amp;gt;setPearinstallerDep('1.4.0a12');
$packagexml-&amp;gt;addMaintainer('lead', 'ash', 'Geoffrey Bachelet', 'geoffrey+pear@zubrowka.org');
$packagexml-&amp;gt;setLicense('New BSD License', 'http://framework.zend.com/license/new-bsd');
$packagexml-&amp;gt;generateContents(); // create the &amp;lt;contents&amp;gt; tag

$pkg = &amp;amp;$packagexml-&amp;gt;exportCompatiblePackageFile1(); // get a PEAR_PackageFile object

if (isset($_GET['make']) || (isset($_SERVER['argv']) &amp;amp;&amp;amp; @$_SERVER['argv'][1] == 'make')) {
    $pkg-&amp;gt;writePackageFile();
    $packagexml-&amp;gt;writePackageFile();
} else {
    $pkg-&amp;gt;debugPackageFile();
    $packagexml-&amp;gt;debugPackageFile();
}

?&amp;gt;
&lt;/pre&gt;</description>
    
    
    
          <comments>http://mirmodynamics.com/post/2007/03/02/Mini-Howto%3A-Serveur-et-Packages-PEAR#comment-form</comments>
      <wfw:comment>http://mirmodynamics.com/post/2007/03/02/Mini-Howto%3A-Serveur-et-Packages-PEAR#comment-form</wfw:comment>
      <wfw:commentRss>http://mirmodynamics.com/feed/atom/comments/751</wfw:commentRss>
      </item>
    
  <item>
    <title>Remplacer un caractère par un &quot;Carriage Return&quot; dans vim</title>
    <link>http://mirmodynamics.com/post/2007/01/03/Remplacer-un-caractere-par-un-Carriage-Return-dans-vim</link>
    <guid isPermaLink="false">urn:md5:262aed0ad72a0fbf7adfd05d63caefe3</guid>
    <pubDate>Wed, 03 Jan 2007 11:04:00 +0100</pubDate>
    <dc:creator>Geoffrey</dc:creator>
        <category>Geekeries</category>
        <category>howto</category><category>substitute</category><category>vim</category>    
    <description>    &lt;p&gt;Par exemple, imaginons qu'on récupère une query string, et qu'on veuille
l'exploser sur plusieurs lignes, on peut faire:&lt;/p&gt;
&lt;pre&gt;
:s/&amp;amp;/\=&amp;quot;\&amp;lt;CR&amp;gt;&amp;quot;/g
&lt;/pre&gt;
&lt;p&gt;le &lt;code&gt;\=&amp;quot;&amp;quot;&lt;/code&gt; annonce à &lt;acronym&gt;VIM&lt;/acronym&gt; que la chaine de
substitution sera une chaine a interpréter avant utilisation, et le
&lt;code&gt;\&amp;lt;CR&amp;gt;&lt;/code&gt; représente tout simplement le &lt;em&gt;Carriage
Return&lt;/em&gt;.&lt;/p&gt;</description>
    
    
    
          <comments>http://mirmodynamics.com/post/2007/01/03/Remplacer-un-caractere-par-un-Carriage-Return-dans-vim#comment-form</comments>
      <wfw:comment>http://mirmodynamics.com/post/2007/01/03/Remplacer-un-caractere-par-un-Carriage-Return-dans-vim#comment-form</wfw:comment>
      <wfw:commentRss>http://mirmodynamics.com/feed/atom/comments/736</wfw:commentRss>
      </item>
    
  <item>
    <title>Annuler un changeset SVN</title>
    <link>http://mirmodynamics.com/post/2006/12/17/Annuler-un-changeset-SVN</link>
    <guid isPermaLink="false">urn:md5:6541ee25c7c01f781a28cdbe6b510d81</guid>
    <pubDate>Sun, 17 Dec 2006 16:43:00 +0100</pubDate>
    <dc:creator>Geoffrey</dc:creator>
        <category>Coding</category>
        <category>cancel</category><category>howto</category><category>merge</category><category>svn</category>    
    <description>    &lt;p&gt;Méthode bourrin, je ne sais pas si il en existe une meilleure. Disons qu'on veut annuler la révision 329 (et que c'est la dernière en date):&lt;/p&gt;


&lt;pre&gt;$ svn update
$ svn merge -r 329:328 .
$ svn commit -m &amp;quot;cancel 329&amp;quot;&lt;/pre&gt;


&lt;p&gt;En tout cas chez moi ça a marché.&lt;/p&gt;</description>
    
    
    
          <comments>http://mirmodynamics.com/post/2006/12/17/Annuler-un-changeset-SVN#comment-form</comments>
      <wfw:comment>http://mirmodynamics.com/post/2006/12/17/Annuler-un-changeset-SVN#comment-form</wfw:comment>
      <wfw:commentRss>http://mirmodynamics.com/feed/atom/comments/732</wfw:commentRss>
      </item>
    
  <item>
    <title>ViewHelper de génération d'urls</title>
    <link>http://mirmodynamics.com/post/2006/11/24/ViewHelper-de-generation-durls</link>
    <guid isPermaLink="false">urn:md5:c86ea8dd7be65bd1b6c3bd524fa962e3</guid>
    <pubDate>Fri, 24 Nov 2006 20:27:00 +0100</pubDate>
    <dc:creator>Geoffrey</dc:creator>
        <category>Coding</category>
        <category>configuration</category><category>howto</category><category>i18n</category><category>organisation</category><category>php</category><category>zend framework</category>    
    <description>    &lt;p&gt;Pré-requis: &lt;a href=&quot;http://fashion.hosmoz.net/blog/post/2006/10/31/Howto%3A-Utiliser-Zend_Controller_RewriteRouter-avec-Zend_Config&quot;&gt;Zend_Controller_RewriteRouter&lt;/a&gt;.&lt;/p&gt;


&lt;p&gt;Nous allons voir aujourd'hui comment générer automagiquement des &lt;acronym&gt;URL&lt;/acronym&gt;s à partir des routes définies dans le &lt;em&gt;RewriteRouter&lt;/em&gt;, ainsi que les avantages que cela présente. Le &lt;code&gt;Helper&lt;/code&gt; que nous allons utiliser nécessite le stockage du routeur dans le registre:&lt;/p&gt;

&lt;pre&gt;
Zend::register('router', $router);
&lt;/pre&gt;


&lt;p&gt;Avant de voir le &lt;code&gt;Helper&lt;/code&gt; lui même, un petit &lt;em&gt;Use Case&lt;/em&gt;. Admettons que vous développiez une application de gestion de petites annonces, vous aurez à un moment ou un autre à créer un lien quelconque pour, par exemple, créer une annonce, et en voir les détails. Disons que vous ayez des routes route du genre (je zappe les &lt;code&gt;defaults&lt;/code&gt;):&lt;/p&gt;

&lt;pre&gt;
announceCreate.route = /announce/create
announceDetails.route = /announce/:id/details
&lt;/pre&gt;


&lt;p&gt;L'objectif est de pouvoir créer les liens grâce au code suivant (à partir de la view):&lt;/p&gt;

&lt;pre&gt;
&amp;lt;a href=&amp;quot;&amp;lt;?php echo $this-&amp;gt;href('announceCreate'); ?&amp;gt;&amp;quot;&amp;gt;Créer une annonce&amp;lt;/a&amp;gt;
&amp;lt;a href=&amp;quot;&amp;lt;?php echo $this-&amp;gt;href('announceDetails', array('id' =&amp;gt; $announce-&amp;gt;id)); ?&amp;gt;&amp;quot;&amp;gt;Voir l'annonce&amp;lt;/a&amp;gt;
&lt;/pre&gt;


&lt;p&gt;Et comme le &lt;a href=&quot;http://framework.zend.com/&quot;&gt;Zend Framework&lt;/a&gt; est bien fait, c'est très simple à réaliser sous forme de &lt;code&gt;ViewHelper&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;
class Zend_View_Helper_Href {
	
	/**
	 * Returns the href to a given route
	 *
	 * @param string $routeName
	 * @param array $args
	 * @return string
	 */
	
	public function href($routeName, $args = array()) {
		try {
			return Zend::registry('router')-&amp;gt;getRoute($routeName)-&amp;gt;assemble($args);
		} catch (Zend_Controller_Router_Exception $e) {
			return '#404';
		}
	}
}
&lt;/pre&gt;


&lt;p&gt;Tellement simple que pour combler un peu je vous offre le &lt;em&gt;docblock&lt;/em&gt; qui va avec ;-)&lt;/p&gt;


&lt;p&gt;Là où ça devient &lt;strong&gt;très&lt;/strong&gt; pratique, c'est quand on souhaite localiser les &lt;acronym&gt;URL&lt;/acronym&gt;s. Par exemple, imaginons que vous souhaitiez &lt;em&gt;françiser&lt;/em&gt; les &lt;acronym&gt;URL&lt;/acronym&gt;s pour, par exemple, améliorer votre référencement. Vous n'avez qu'a définir un jeu de routes &lt;em&gt;fr_FR&lt;/em&gt;, par exemple ainsi:&lt;/p&gt;

&lt;pre&gt;
[routes_fr_FR]
announceCreate.route = /annonce/creer
announceDetails.route = /annonce/:id/details

[routes_en_UK]
announceCreate.route = /announce/create
announceDetails.route = /announce/:id/details

[routes:route_fr_FR]
announceCreate.defaults.controller = announce
announceCreate.defaults.action = create

announceDetails.defaults.controller = announce
announceDetails.defaults.action = details
&lt;/pre&gt;


&lt;p&gt;L'utilisation de l'héritage géré par &lt;code&gt;Zend_Config&lt;/code&gt; nous permet ici d'éviter la redondance des &lt;code&gt;defaults&lt;/code&gt;.&lt;/p&gt;


&lt;p&gt;Elle est pas belle la vie ?&lt;/p&gt;


&lt;p&gt;Note: cette fonctionnalitée est prévue pour être &lt;em&gt;builtin&lt;/em&gt; plus tard.&lt;/p&gt;</description>
    
    
    
          <comments>http://mirmodynamics.com/post/2006/11/24/ViewHelper-de-generation-durls#comment-form</comments>
      <wfw:comment>http://mirmodynamics.com/post/2006/11/24/ViewHelper-de-generation-durls#comment-form</wfw:comment>
      <wfw:commentRss>http://mirmodynamics.com/feed/atom/comments/725</wfw:commentRss>
      </item>
    
  <item>
    <title>Zend Framework: Un plugin simple pour le Front Controller</title>
    <link>http://mirmodynamics.com/post/2006/11/19/Zend-Framework%3A-Un-plugin-simple-pour-le-Front-Controller</link>
    <guid isPermaLink="false">urn:md5:6c829cc8dbd18739b84ef7758d852a70</guid>
    <pubDate>Sun, 19 Nov 2006 21:30:00 +0100</pubDate>
    <dc:creator>Geoffrey</dc:creator>
        <category>Coding</category>
        <category>bootstrap</category><category>howto</category><category>MVC</category><category>organisation</category><category>php</category><category>plugin</category><category>vues</category><category>zend framework</category>    
    <description>&lt;p&gt;L'implémentation du &lt;code&gt;Front Controller&lt;/code&gt; du &lt;a href=&quot;http://framework.zend.com/&quot;&gt;Zend Framework&lt;/a&gt; inclue un système bien pratique de plugins, qui va nous permettre de réaliser plus simplement et proprement &lt;a href=&quot;http://fashion.hosmoz.net/blog/post/2006/08/30/Automatisation-des-vues&quot;&gt;l'automatisation des vues&lt;/a&gt;, initialement embarquée dans une extension de &lt;code&gt;Zend_Controller_Action&lt;/code&gt; (ce qui pose quelques problèmes, nottament au lors de l'utilisation de &lt;code&gt;__forward()&lt;/code&gt; et &lt;code&gt;_redirect()&lt;/code&gt;).&lt;/p&gt;    &lt;p&gt;Techniquement, un plugin est une classe qui implémente l'interface des plugins (&lt;code&gt;Zend_Controller_Plugin_Interface&lt;/code&gt;). Dans la pratique, une classe abstraite est fournie pour simplifier la tache (&lt;code&gt;Zend_Controller_Plugin_Abstract&lt;/code&gt;). Notre premier plugin s'appellera &lt;code&gt;AutoView&lt;/code&gt; (&lt;code&gt;My_Controller_Plugin_AutoView&lt;/code&gt;, stocké dans &lt;code&gt;My/Controller/Plugin/AutoView.php&lt;/code&gt;).&lt;/p&gt;


&lt;p&gt;Un plugin est donc une classe dont les méthodes seront appelées a différents moments de la &lt;em&gt;dispatch loop&lt;/em&gt; du controlleur frontal, comme on peut le voir dans &lt;code&gt;Zend_Controller_Front::dispatch()&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;routeStartup()&lt;/code&gt;, à la mise en route du routeur&lt;/li&gt;
&lt;li&gt;&lt;code&gt;routeShutdown($action)&lt;/code&gt;, à l'arrêt du routeur&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dispatchLoopStartup($action)&lt;/code&gt;, avant la &lt;em&gt;dispatch loop&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;preDispatch($action)&lt;/code&gt;, avant d'executer une action&lt;/li&gt;
&lt;li&gt;&lt;code&gt;postDispatch($action)&lt;/code&gt;, après avoir executé une action&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dispatchLoopShutdown()&lt;/code&gt;, pour finir.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A chaud, on pourrait se dire que &lt;code&gt;postDispatch&lt;/code&gt; est exactement ce dont on n'a besoin, et on se tromperait. Ce qu'il faut savoir, c'est que l'argument &lt;code&gt;$action&lt;/code&gt; passé à &lt;code&gt;postDispatch&lt;/code&gt; contient l'action qui suit celle qui vient d'être executé. Dans la majorité des cas, n'ayant qu'une seule action à executer, &lt;code&gt;$action&lt;/code&gt; sera vide, et on se retrouve l'air un peu con.&lt;/p&gt;


&lt;p&gt;Bon donc, on va user d'un stratagème: notre plugin va stocker les actions qui passent dans &lt;code&gt;preDispatch&lt;/code&gt;, pour s'occuper de la dernière à être passée dans &lt;code&gt;dispatchLoopShutdown&lt;/code&gt;. Dernier détail, notre plugin s'occupera lui même de gérer son instance de &lt;code&gt;Zend_View&lt;/code&gt;. Et sans plus attendre, le code, qui n'a finalement rien de compliqué:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?php

require_once 'Zend/Controller/Plugin/Abstract.php';

class My_Controller_Plugin_AutoView extends Zend_Controller_Plugin_Abstract {

	private $_lastAction;

	public function __construct() {
		$view = new Zend_View;
		$view-&amp;gt;setScriptPath(APP_ROOT.'/views/');
		Zend::register('view', $view);
	}

	public function preDispatch($action) {
		$this-&amp;gt;_lastAction = clone($action);
		return $action;
	}

	public function dispatchLoopShutdown() {
		if (!is_null($this-&amp;gt;_lastAction)) {
			$viewPath = sprintf('%s/%s.php', $this-&amp;gt;_lastAction-&amp;gt;getControllerName(), $this-&amp;gt;_lastAction-&amp;gt;getActionName());
			echo Zend::registry('view')-&amp;gt;render($viewPath);
		}
	}
}

?&amp;gt;
&lt;/pre&gt;


&lt;p&gt;Bien, notre plugin vaillament codé, il nous faut désormais expliquer au controlleur frontal qu'on aimerai bien qu'il en tienne compte. Cela se passe fort logiquement dans le &lt;em&gt;bootstrap&lt;/em&gt;, via la méthode &lt;code&gt;registerPlugin&lt;/code&gt; du sus-cité controlleur frontal, qui prend en argument une instance d'un objet implémentant &lt;code&gt;Zend_Controller_Plugin_Interface&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;
$controller = Zend_Controller_Front::getInstance();
$controller-&amp;gt;registerPlugin(new My_Controller_Plugin_AutoView);
&lt;/pre&gt;


&lt;p&gt;And viola !&lt;/p&gt;</description>
    
    
    
          <comments>http://mirmodynamics.com/post/2006/11/19/Zend-Framework%3A-Un-plugin-simple-pour-le-Front-Controller#comment-form</comments>
      <wfw:comment>http://mirmodynamics.com/post/2006/11/19/Zend-Framework%3A-Un-plugin-simple-pour-le-Front-Controller#comment-form</wfw:comment>
      <wfw:commentRss>http://mirmodynamics.com/feed/atom/comments/720</wfw:commentRss>
      </item>
    
  <item>
    <title>Migrer un dépot subversion</title>
    <link>http://mirmodynamics.com/post/2006/11/09/Migrer-un-depot-subversion</link>
    <guid isPermaLink="false">urn:md5:84c41cd08be4b33fce92f3f42254d080</guid>
    <pubDate>Thu, 09 Nov 2006 17:25:00 +0100</pubDate>
    <dc:creator>Geoffrey</dc:creator>
        <category>Geekeries</category>
        <category>doc</category><category>howto</category><category>svn</category><category>unix</category>    
    <description>    &lt;p&gt;Imaginons que vous souhaitiez déplacer votre dépôt subversion &lt;em&gt;myproject&lt;/em&gt; d'une machine &lt;em&gt;old-server&lt;/em&gt; à une machine &lt;em&gt;new-server&lt;/em&gt;. Vite fait, bien fait:&lt;/p&gt;


&lt;pre&gt;old-server# svnadmin dump /var/lib/subversion/myproject &amp;gt; ~/myproject.svndump
old-server# scp ~/myproject.svndump new-server:
new-server# svnadmin create /var/lib/subversion/myproject
new-server# svnadmin load /var/lib/subversion/myproject &amp;lt; ~/myproject.svndump&lt;/pre&gt;


&lt;p&gt;&lt;strong&gt;Attention&lt;/strong&gt;, il vous faut par contre migrer vos éventuelles &lt;em&gt;hooks&lt;/em&gt; à la main, ils ne sont effectivement pas gérés par &lt;code&gt;svndump&lt;/code&gt;. Une autre méthode, incluant les &lt;em&gt;hooks&lt;/em&gt; celle-ci, serait d'utiliser &lt;code&gt;svnadmin hotcopy&lt;/code&gt;, mais je n'ai pas testé.&lt;/p&gt;


&lt;p&gt;Plus d'infos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://svnbook.red-bean.com/nightly/en/svn.ref.svnadmin.c.dump.html&quot;&gt;svnadmin help dump&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://svnbook.red-bean.com/nightly/en/svn.ref.svnadmin.c.load.html&quot;&gt;svnadmin help load&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://svnbook.red-bean.com/nightly/en/svn.ref.svnadmin.c.hotcopy.html&quot;&gt;svnadmin help hotcopy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
    
    
    
          <comments>http://mirmodynamics.com/post/2006/11/09/Migrer-un-depot-subversion#comment-form</comments>
      <wfw:comment>http://mirmodynamics.com/post/2006/11/09/Migrer-un-depot-subversion#comment-form</wfw:comment>
      <wfw:commentRss>http://mirmodynamics.com/feed/atom/comments/716</wfw:commentRss>
      </item>
    
  <item>
    <title>Howto: Utiliser Zend_Controller_RewriteRouter avec Zend_Config</title>
    <link>http://mirmodynamics.com/post/2006/10/31/Howto%3A-Utiliser-Zend_Controller_RewriteRouter-avec-Zend_Config</link>
    <guid isPermaLink="false">urn:md5:58ec785aef55507ca8784ce7b8d798b3</guid>
    <pubDate>Tue, 31 Oct 2006 20:26:00 +0100</pubDate>
    <dc:creator>Geoffrey</dc:creator>
        <category>Coding</category>
        <category>bootstrap</category><category>configuration</category><category>controlleurs</category><category>doc</category><category>howto</category><category>ini</category><category>MVC</category><category>php</category><category>zend framework</category>    
    <description>&lt;p&gt;Comme je le disais plus bas, le Zend Framework Preview 0.2.0 est dans les bacs ! Cette nouvelle mouture apporte son lot de nouveautés, et nous allons nous pencher sur une des plus interressantes: le &lt;em&gt;RewriteRouter&lt;/em&gt;. Le &lt;em&gt;RewriteRouter&lt;/em&gt; est un routeur pour le composant &lt;acronym&gt;MVC&lt;/acronym&gt; du Zend Framework qui va nous permettre de configurer nos &lt;acronym&gt;URL&lt;/acronym&gt; comme dans Ruby on Rails, c'est à dire (en gros), via un fichier de configuration, et c'est là que &lt;em&gt;Zend_Config&lt;/em&gt; entre en jeu.&lt;/p&gt;    &lt;h3&gt;Mettre en place le routeur&lt;/h3&gt;


&lt;p&gt;C'est bien beau tout ça, mais pour l'utiliser, il faut déjà en informer le controlleur. Pour cela, rien de plus simple, on utilise la méthode &lt;code&gt;addRouter&lt;/code&gt;:&lt;/p&gt;


&lt;pre&gt;$router = new Zend_Controller_RewriteRouter;
$controller = Zend_Controller_Front::getInstance();
$controller-&amp;gt;setRouter($router);&lt;/pre&gt;


&lt;p&gt;Le &lt;em&gt;RewriteRouter&lt;/em&gt; est pré-configuré avec deux routes qui permettent d'assurer la compatibilité descendante avec l'ancien routeur, donc jusque là, tout va bien.&lt;/p&gt;


&lt;h3&gt;Déclarer les routes&lt;/h3&gt;


&lt;p&gt;Nous utiliserons un fichier de configuration dédié aux routes, que nous appellerons sobrement &lt;em&gt;routes.ini&lt;/em&gt;, et que nous placerons dans &lt;code&gt;app/&lt;/code&gt;, à coté de son compagnon &lt;em&gt;config.ini&lt;/em&gt;. Ce fichier de configuration contiendra une unique section &lt;code&gt;[routes]&lt;/code&gt;, qui contiendra à son tour les multiples définitions de routes que nous souhaitons y caser. Une route se compose de 5 éléments principaux:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;L'identifiant de la route. C'est un identifiant interne utilisé par le &lt;code&gt;RewriteRouter&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;l'&lt;acronym&gt;URL&lt;/acronym&gt; de la route, c'est ce qui permet au routeur de reconnaitre la route à utiliser,&lt;/li&gt;
&lt;li&gt;les valeurs par défauts des variables,&lt;/li&gt;
&lt;li&gt;les requirements de la route, qui permettent de déterminer si une route est bien formée ou non.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Sans plus attendre, un petit exemple: la route qui &lt;em&gt;pourrait&lt;/em&gt; permettre d'accéder à un billet dans dotclear 2:&lt;/p&gt;


&lt;pre&gt;post.route = post/:year/:month/:day/:title
post.reqs.year = &amp;quot;\d+&amp;quot;
post.reqs.month = &amp;quot;\d+&amp;quot;
post.reqs.day = &amp;quot;\d+&amp;quot;
post.reqs.title = &amp;quot;[a-zA-Z0-9_:-]&amp;quot;+
post.defaults.controller = post
post.defaults.action = view
post.defaults.title =&lt;/pre&gt;


&lt;p&gt;Elle &lt;em&gt;pourrait&lt;/em&gt; seulement, car en fait, la regexp du &lt;code&gt;title&lt;/code&gt; est trop restrictive (une &lt;acronym&gt;URL&lt;/acronym&gt; peut contenir d'autres caractères que ceux là), et de plus, les &lt;acronym&gt;URL&lt;/acronym&gt; stockées par dotclear 2 en base de données comprennent tout ce qui se trouve après &lt;code&gt;post/&lt;/code&gt; (par exemple le champs &lt;code&gt;post_url&lt;/code&gt; de ce billet contient: &lt;code&gt;2006/10/31/Howto:-Utiliser-Zend_Controller_RewriteRouter-avec-Zend_Config&lt;/code&gt;).&lt;/p&gt;


&lt;p&gt;Cela étant dit, la route que nous venons de décrire s'appliquera aux &lt;acronym&gt;URL&lt;/acronym&gt;s suivantes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/post/2006/30/10/ZendFramework&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/post/42/2006/56/Foobar&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/post/2006/30/10/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mais pas à:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/post/2006/Foobar&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/post/Foobar/2006/30/01&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On note donc d'ores et déjà:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;que la validation des composants d'une route se fait par expression régulière&lt;/li&gt;
&lt;li&gt;qu'on peut ommettre le dernier composant d'une route si il possède une valeur par défaut (récursivement)&lt;/li&gt;
&lt;li&gt;qu'on ne peut pas modifier l'ordre des composants d'une route&lt;/li&gt;
&lt;li&gt;qu'on doit spécifier manuellement le nom du controlleur et de l'action à utiliser&lt;/li&gt;
&lt;li&gt;et c'est déjà pas mal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Décortiquons maintenant une &lt;acronym&gt;URL&lt;/acronym&gt;: &lt;code&gt;/post/2006/30/10/ZendFramework&lt;/code&gt;. Le routeur va diviser cette &lt;acronym&gt;URL&lt;/acronym&gt; ainsi:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Controller: &lt;code&gt;post&lt;/code&gt; (d'après les &lt;em&gt;defaults&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Action: &lt;code&gt;view&lt;/code&gt; (d'après les &lt;em&gt;defaults&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Paramètres: &lt;code&gt;year&lt;/code&gt;: 2006, &lt;code&gt;month&lt;/code&gt;: 30, &lt;code&gt;day&lt;/code&gt;: 10, &lt;code&gt;title&lt;/code&gt;: ZendFramework (d'après l'&lt;acronym&gt;URL&lt;/acronym&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Les paramètres ainsi détectés seront disponibles via la méthode &lt;code&gt;$this-&amp;gt;_getParam($name);&lt;/code&gt; a l'intérieur du controlleur.&lt;/p&gt;


&lt;p&gt;Dernier point de ce chapitre, une déclaration de route peut comporter deux variables spéciales: &lt;code&gt;:controller&lt;/code&gt; et &lt;code&gt;:action&lt;/code&gt;, qui servent respectivement à détecter le controller et l'action à utiliser dans l'url. Par exemple, la route &lt;em&gt;builtin&lt;/em&gt; &lt;code&gt;compat&lt;/code&gt; chargé d'assurer la compatibilité descendante se déclare ainsi:&lt;/p&gt;


&lt;pre&gt;compat.route = :controller/:action
compat.defaults.controller = index
compat.defaults.action = index&lt;/pre&gt;


&lt;h3&gt;Assembler le tout&lt;/h3&gt;


&lt;p&gt;Dernière étape, transférer directement toutes ces déclarations dans le Routeur: n'y allons pas par quatre chemin:&lt;/p&gt;


&lt;pre&gt;$router-&amp;gt;addConfig(new Zend_Config_Ini('/path/to/routes.ini', null), 'routes');&lt;/pre&gt;


&lt;p&gt;C'est aussi simple que ça. Ce qui nous donne, en prenant en compte le morceau de code du début de l'article:&lt;/p&gt;


&lt;pre&gt;$router = new Zend_Controller_RewriteRouter;
$router-&amp;gt;addConfig(new Zend_Config_Ini($routes_path, null), 'routes');
$controller = Zend_Controller_Front::getInstance();
$controller-&amp;gt;setRouter($router);&lt;/pre&gt;


&lt;p&gt;And voilà. Je ne saurais que vous conseiller la lecture du &lt;a href=&quot;http://framework.zend.com/manual/en/zend.controller.rewriterouter.html&quot;&gt;manuel&lt;/a&gt; pour plus d'informations (sachant qu'en fait, &lt;code&gt;addRoute&lt;/code&gt; attend en deuxième argument une instance de &lt;code&gt;Zend_Controller_Router_Route&lt;/code&gt;, mais qui s'instancie avec les paramètres qu'ils passent à &lt;code&gt;addRoute&lt;/code&gt;, petite coquille du manuel :p).&lt;/p&gt;</description>
    
    
    
          <comments>http://mirmodynamics.com/post/2006/10/31/Howto%3A-Utiliser-Zend_Controller_RewriteRouter-avec-Zend_Config#comment-form</comments>
      <wfw:comment>http://mirmodynamics.com/post/2006/10/31/Howto%3A-Utiliser-Zend_Controller_RewriteRouter-avec-Zend_Config#comment-form</wfw:comment>
      <wfw:commentRss>http://mirmodynamics.com/feed/atom/comments/710</wfw:commentRss>
      </item>
    
  <item>
    <title>Mettre en place un SSO avec Invision Power Board</title>
    <link>http://mirmodynamics.com/post/2006/10/24/Mettre-en-place-un-SSO-avec-Invision-Power-Board</link>
    <guid isPermaLink="false">urn:md5:e31ee19b3caa179f39e785cafe1ae2d1</guid>
    <pubDate>Wed, 25 Oct 2006 23:02:00 +0200</pubDate>
    <dc:creator>Geoffrey</dc:creator>
        <category>Coding</category>
        <category>configuration</category><category>hack</category><category>howto</category><category>ip.board</category><category>php</category><category>sso</category><category>work</category>    
    <description>&lt;p&gt;Rien de plus simple, tout est déjà prévu. Après l'installation de votre forum &lt;acronym&gt;IPB&lt;/acronym&gt;, nous allons enregistrer une nouvelle méthode de login. Pour se faire, dans le panneau d'administration, nous nous dirigeons vers &lt;em&gt;Tools and Settings&lt;/em&gt;, puis dans &lt;em&gt;Create New Log In&lt;/em&gt; du menu &lt;em&gt;Log In Manager&lt;/em&gt;. On se retrouve devant un formulaire (assez explicite) que je vous laisse le soin de remplir. On dira juste que nous appellerons cette méthode de login &lt;em&gt;Mon SSO&lt;/em&gt; (&lt;em&gt;Log In Title&lt;/em&gt;) et qu'il vivra dans le répertoire &lt;code&gt;mon_sso&lt;/code&gt; (&lt;em&gt;Log In Files Folder Name&lt;/em&gt;). Pour que votre méthode de login soit active, vous devez cocher &lt;em&gt;Log In Enabled&lt;/em&gt;, et il est toujours bon de passer en mode &lt;em&gt;On-Fail&lt;/em&gt;, ainsi que d'autoriser la création d'utilisateurs (&lt;em&gt;Log In Allow Member Creation&lt;/em&gt;), qui créera automagiquement les utilisateurs dans la base locale d'&lt;acronym&gt;IPB&lt;/acronym&gt;.&lt;/p&gt;    &lt;p&gt;Ensuite on met les mains dans le camboui. Dans le répertoire &lt;code&gt;sources/loginauth/&lt;/code&gt; de votre installation, il doit exister un repertoire &lt;code&gt;external/&lt;/code&gt;, qui nous servira de base. On commence donc par le dupliquer:&lt;/p&gt;


&lt;pre&gt;cp -r external mon_sso&lt;/pre&gt;


&lt;p&gt;Puis on regarde un peu ce qu'il y a dedans:&lt;/p&gt;


&lt;pre&gt;mon_sso/
	auth.php
	conf.php
	index.html&lt;/pre&gt;


&lt;p&gt;Le fichier &lt;code&gt;index.html&lt;/code&gt; n'est qu'un empécheur de tourner en rond pour les gens qui explorent un peu trop les arbos web. Le premier fichier qui nous interresse est &lt;code&gt;conf.php&lt;/code&gt;, il contient les informations relatives à la base de données distantes. Là encore, c'est assez explicite et bien commenté. Le seul petit truc à savoir est que l'utilisateur qui se connectera à la base a besoin des droits &lt;em&gt;SELECT&lt;/em&gt; sur &lt;strong&gt;toute&lt;/strong&gt; la table, pas seulement sur les champs login / pass.&lt;/p&gt;


&lt;p&gt;Dans le meilleur des cas, votre &lt;acronym&gt;SSO&lt;/acronym&gt; est prêt :p Le fichier &lt;code&gt;auth.php&lt;/code&gt; contient les méthodes appelées par le gestionnaire d'identification. Vous pouvez avoir besoin de modifier &lt;code&gt;__compare_password&lt;/code&gt;, pour la vérification du mot de passe (j'ai par exemple changé la méthode de cryptage qui est md5 par défaut).&lt;/p&gt;


&lt;p&gt;A ce stade, vos utilisateurs peuvent s'identifier, mais le forum leur demande quand même leur mail et quelques infos que j'ai oublié. Il est bien évidemment possible d'éviter ça :-)
Dans la méthode &lt;code&gt;authenticate&lt;/code&gt;, vers la fin, quand un utilisateur local est créé, la main est passée à la méthode &lt;code&gt;create_local_member&lt;/code&gt;, qui redirige ensuite vers l'écran de complétion du compte. C'est là que nous intervenons. La méthode &lt;code&gt;create_local_member&lt;/code&gt; n'est pas implémenté dans &lt;code&gt;login_method&lt;/code&gt;, mais dans &lt;code&gt;login_core&lt;/code&gt;, qui se trouve dans le fichier &lt;code&gt;sources/loginauth/login_core.php&lt;/code&gt;, nous commençons par la copier/coller dans notre &lt;code&gt;login_method&lt;/code&gt;, puis nous modifions sa déclaration pour lui permettre de recevoir &lt;code&gt;$remote_member&lt;/code&gt; en paramètre:&lt;/p&gt;


&lt;pre&gt;function create_local_member( $remote_member )
{&lt;/pre&gt;


&lt;p&gt;Cette méthode permet de piocher dans les informations récupérées dans la base distante. Dans la partie &lt;em&gt;Populate member table(s)&lt;/em&gt;, par défaut, un faux mail est créé:&lt;/p&gt;


&lt;pre&gt;$email_tmp = $username.'@'.$timenow;&lt;/pre&gt;


&lt;p&gt;Qu'on peut remplacer par le mail qu'on a déjà potentiellement dans la base distante (disons que le champs s'appelle &lt;code&gt;mail&lt;/code&gt;):&lt;/p&gt;


&lt;pre&gt;$email_tmp = $remote_member['mail'];&lt;/pre&gt;


&lt;p&gt;On peut ensuite récupérer les champs que l'on veut pour les ajouter au membre local, par exemple dans mon cas, le &lt;code&gt;display_name&lt;/code&gt; (&lt;code&gt;nickname&lt;/code&gt; chez moi):&lt;/p&gt;


&lt;pre&gt;$display_name = $remote_member['nickname'];&lt;/pre&gt;


&lt;p&gt;en les rajoutant dans le tableau &lt;code&gt;$member&lt;/code&gt;.&lt;/p&gt;


&lt;p&gt;Les deux derniers appels de méthode dans &lt;code&gt;create_local_member&lt;/code&gt;, &lt;code&gt;$this-&amp;gt;ipsclass-&amp;gt;DB-&amp;gt;do_insert&lt;/code&gt; et &lt;code&gt;$this-&amp;gt;ipsclass-&amp;gt;print-&amp;gt;redirect_screen&lt;/code&gt; sont à commenter, et on rajouter avant le bout de code suivant:&lt;/p&gt;


&lt;pre&gt;$this-&amp;gt;return_code = 'SUCCESS';
return;&lt;/pre&gt;


&lt;p&gt;Retour dans &lt;code&gt;authenticate&lt;/code&gt;, dans la partie &lt;em&gt;Got no member - but auth passed - create?&lt;/em&gt;, on modifie la première partie du &lt;code&gt;if&lt;/code&gt; ainsi:&lt;/p&gt;


&lt;pre&gt;if ( $this-&amp;gt;allow_create )
{
	$this-&amp;gt;create_local_member( $remote_member );
	$this-&amp;gt;_load_member($username);

	if ( $this-&amp;gt;member['id'] )
	{
		$this-&amp;gt;return_code = 'SUCCESS';
	}
	else
	{
		$this-&amp;gt;return_code = 'NO_USER';
	}
	return;
}&lt;/pre&gt;


&lt;p&gt;And voilà, nos utilisateurs peuvent désormais s'identifier sans avoir a re-entrer des informations que l'on possède déjà :-)&lt;/p&gt;</description>
    
    
    
          <comments>http://mirmodynamics.com/post/2006/10/24/Mettre-en-place-un-SSO-avec-Invision-Power-Board#comment-form</comments>
      <wfw:comment>http://mirmodynamics.com/post/2006/10/24/Mettre-en-place-un-SSO-avec-Invision-Power-Board#comment-form</wfw:comment>
      <wfw:commentRss>http://mirmodynamics.com/feed/atom/comments/693</wfw:commentRss>
      </item>
    
  <item>
    <title>Relayer un stream audio avec icecast2</title>
    <link>http://mirmodynamics.com/post/2006/10/20/Relayer-un-stream-audio-avec-icecast2</link>
    <guid isPermaLink="false">urn:md5:0df1adaf1cd213b3a277c6eb891f838d</guid>
    <pubDate>Fri, 20 Oct 2006 22:05:00 +0200</pubDate>
    <dc:creator>Geoffrey</dc:creator>
        <category>Geekeries</category>
        <category>bidouille</category><category>configuration</category><category>doc</category><category>howto</category><category>optimisation</category><category>rigolo</category><category>ubuntu</category><category>unix</category><category>way of life</category><category>work</category>    
    <description>    &lt;p&gt;Pour économiser la bande passante au boulot, j'ai décidé de relayer le stream &lt;em&gt;Club !&lt;/em&gt; de &lt;a href=&quot;http://1.fm/&quot;&gt;1.fm&lt;/a&gt; sur le &lt;acronym&gt;LAN&lt;/acronym&gt;. Après avoir vainement tenté d'utiliser streamripper (on verra plus tard pourquoi vainement), j'ai sorti l'artillerie lourde: &lt;a href=&quot;http://www.icecast.org/&quot;&gt;icecast2&lt;/a&gt;:&lt;/p&gt;


&lt;pre&gt;sudo apt-get install icecast2&lt;/pre&gt;


&lt;p&gt;Si on sait un peu lire, le script de post-configuration nous incite à aller fourrer notre nez dans &lt;code&gt;/etc/default/icecast2&lt;/code&gt;, où l'on apprend (vers la fin) que icecast est désactivé par défaut à cause de la directive &lt;code&gt;ENABLED=false&lt;/code&gt;. C'est en fait une feinte pour nous pousser à configurer le bousin (de toute façon si on le configure pas, il marchera pas). Direction &lt;code&gt;/etc/icecast2/icecast.xml&lt;/code&gt; donc, pour un brin de configuration (les explications qui suivent se basent sur le fichier par défaut d'une installation sur une ubuntu).&lt;/p&gt;


&lt;p&gt;La première partie qui nous interresse s'intitule &lt;code&gt;authentication&lt;/code&gt; (vers la ligne 23). Elle contient les informations d'authentification pour les clients qui se connectent en tant que source (&lt;code&gt;source-password&lt;/code&gt;), les serveurs qui se connectent en tant que slave (&lt;code&gt;relay-password&lt;/code&gt;, en fait je ne suis pas sur à 100%, c'est une déduction) et pour l'interface d'administration (&lt;code&gt;admin-user&lt;/code&gt; et &lt;code&gt;admin-password&lt;/code&gt;). Une fois ces informations modifiées, direction la directive &lt;code&gt;hostname&lt;/code&gt;, qu'on remplira avec au choix, le nom de la machine, son ip, etc. J'ai personellement mis l'ip privée de ma machine (&lt;code&gt;172.16.x.y&lt;/code&gt;), pour que ça correspondent à la prochaine directive qui nous interresse: &lt;code&gt;listen-socket&lt;/code&gt;. Ici on définit le port et l'ip sur laquelle icecast va écouter. En gros, si vous spécifier &lt;code&gt;127.0.0.1&lt;/code&gt;, votre serveur de streaming ne sera accessible qu'en local. On y met donc en général la même chose que dans &lt;code&gt;hostname&lt;/code&gt; (&lt;code&gt;172.16.x.y&lt;/code&gt; par exemple), avec un port qui va bien, libre de préférence (&lt;code&gt;8000&lt;/code&gt; par défaut).&lt;/p&gt;


&lt;p&gt;Maintenant on passe a la partie qui nous interresse vraiment, la section &lt;code&gt;relay&lt;/code&gt;. Rien de bien compliqué ici. Le stream que je souhaite relayer se trouve là: http://64.62.253.223:8060/, or icecast nous demande un &lt;code&gt;server&lt;/code&gt;, un &lt;code&gt;port&lt;/code&gt;, un &lt;em&gt;point de montage&lt;/em&gt; (&lt;code&gt;mount&lt;/code&gt;) et un &lt;em&gt;point de montage local&lt;/em&gt; (&lt;code&gt;local-mount&lt;/code&gt;). Vous avez déjà compris qu'on arrive a cette configuration:&lt;/p&gt;


&lt;pre&gt;   &amp;lt;relay&amp;gt;
       &amp;lt;server&amp;gt;64.62.253.223&amp;lt;/server&amp;gt;
       &amp;lt;port&amp;gt;8060&amp;lt;/port&amp;gt;
       &amp;lt;mount&amp;gt;/&amp;lt;/mount&amp;gt;
       &amp;lt;local-mount&amp;gt;/1.fm&amp;lt;/local-mount&amp;gt;
       &amp;lt;on-demand&amp;gt;0&amp;lt;/on-demand&amp;gt;
       &amp;lt;relay-shoutcast-metadata&amp;gt;1&amp;lt;/relay-shoutcast-metadata&amp;gt;
   &amp;lt;/relay&amp;gt;&lt;/pre&gt;


&lt;p&gt;Ainsi parés, il ne nous reste plus qu'a lancer modifier la directive &lt;code&gt;ENABLED=false&lt;/code&gt; en &lt;code&gt;ENABLED=true&lt;/code&gt; dans &lt;code&gt;/etc/default/icecast2&lt;/code&gt; et à lancer icecast:&lt;/p&gt;


&lt;pre&gt;sudo /etc/init.d/icecast2 start&lt;/pre&gt;


&lt;p&gt;Si vous avez bien tout fait, vous devriez pouvoir streamer depuis &lt;code&gt;http://172.16.x.y:8000/1.fm&lt;/code&gt;, et vos collègues également ! Vous pouvez avoir une vue d'ensemble du serveur ainsi que quelques options d'administration en vous rendant sur l'interface d'admin: http://172.16.x.y:8000/ et en utilisant &lt;code&gt;admin-user&lt;/code&gt; et &lt;code&gt;admin-password&lt;/code&gt; pour vous authentifier.&lt;/p&gt;


&lt;p&gt;A cela on peut ajouter un petit &lt;code&gt;streamripper&lt;/code&gt;:&lt;/p&gt;


&lt;pre&gt;streamripper http://172.16.x.y:8000/1.fm -d ~/streamripped&lt;/pre&gt;


&lt;p&gt;Pour enregistrer. En parlant de streamripper, j'avais tenté au début de relayer avec &lt;code&gt;streamripper -r&lt;/code&gt;, mais malgrès les apparences du &lt;code&gt;netstat -pl&lt;/code&gt; (&lt;code&gt;*:8000 LISTEN&lt;/code&gt;), il ne bind qu'en local, donc impossible d'en faire profiter les collègues :-)&lt;/p&gt;</description>
    
    
    
          <comments>http://mirmodynamics.com/post/2006/10/20/Relayer-un-stream-audio-avec-icecast2#comment-form</comments>
      <wfw:comment>http://mirmodynamics.com/post/2006/10/20/Relayer-un-stream-audio-avec-icecast2#comment-form</wfw:comment>
      <wfw:commentRss>http://mirmodynamics.com/feed/atom/comments/692</wfw:commentRss>
      </item>
    
  <item>
    <title>Vous prendrez bien un peu de ssh avec votre tunnel ?</title>
    <link>http://mirmodynamics.com/post/2006/10/20/Vous-prendrez-bien-un-peu-de-ssh-avec-votre-tunnel</link>
    <guid isPermaLink="false">urn:md5:0114979e945f69f9eb437f87337a6c0e</guid>
    <pubDate>Fri, 20 Oct 2006 20:06:00 +0200</pubDate>
    <dc:creator>Geoffrey</dc:creator>
        <category>Geekeries</category>
        <category>howto</category><category>rigolo</category><category>ssh</category><category>sécurité</category><category>unix</category>    
    <description>    &lt;p&gt;Il arrive des fois où on aimerait pouvoir relier directment deux machines appartenant a deux réseaux distincts. C'est par exemple mon cas quand j'ai besoin (envie on va dire) d'accéder à ma machine du boulot depuis une machine non connectée au &lt;acronym&gt;VPN&lt;/acronym&gt;. Dans ce genre de cas, il existe en général une machine qui possède des interfaces susceptibles d'accéder à chacune des machines (le concentrateur &lt;acronym&gt;VPN&lt;/acronym&gt; par exemple). Nous appellerons cette machine &lt;code&gt;relay&lt;/code&gt;, car elle servira de relai au tunnel. Pour éviter les sempiternelles appellations &lt;code&gt;A&lt;/code&gt; et &lt;code&gt;B&lt;/code&gt; qui embrouillent plus qu'autre chose, les machines s'appelleront &lt;code&gt;startpoint&lt;/code&gt; pour la machine sur laquelle on a la main et &lt;code&gt;endpoint&lt;/code&gt; pour la machine à laquelle on souhaite accéder.&lt;/p&gt;


&lt;p&gt;Postulats de base:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;relay&lt;/code&gt; possède un serveur &lt;acronym&gt;SSH&lt;/acronym&gt; qui tourne&lt;/li&gt;
&lt;li&gt;&lt;code&gt;startpoint&lt;/code&gt; possède un client &lt;acronym&gt;SSH&lt;/acronym&gt; capable de créer un tunnel (&lt;code&gt;ssh&lt;/code&gt;, par exemple)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;relay&lt;/code&gt; est accessible depuis &lt;code&gt;startpoint&lt;/code&gt; et peut se connecter à &lt;code&gt;endpoint&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bien, allons y franchement, la commande, à executer depuis &lt;code&gt;startpoint&lt;/code&gt;, permettant de créer un tunnel &lt;acronym&gt;SSH&lt;/acronym&gt; entre &lt;code&gt;startpoint&lt;/code&gt; et &lt;code&gt;endpoint&lt;/code&gt; est la suivante:&lt;/p&gt;


&lt;pre&gt;ssh -L 2222:endpoint:22 relay&lt;/pre&gt;


&lt;p&gt;Qu'avons nous fait là ? L'option &lt;code&gt;-L&lt;/code&gt; de &lt;acronym&gt;SSH&lt;/acronym&gt; sert à &lt;em&gt;binder&lt;/em&gt; un port de la machine locale (&lt;code&gt;startpoint&lt;/code&gt; donc), à un autre port (ou le même) de la machine distante (&lt;code&gt;endpoint&lt;/code&gt;). Ici, on associe le port local 2222 (22 étant déjà pris par mon serveur &lt;acronym&gt;SSH&lt;/acronym&gt;, mais on pourrait utiliser le port 22 si aucun serveur ne tournait, à la différence près qu'il faudrait lancer la commande en root pour pouvoir &lt;em&gt;binder&lt;/em&gt; un port inférieur à 1024 (c'est comme ça)) au port 22 de &lt;code&gt;endpoint&lt;/code&gt;, c'est à dire le serveur &lt;acronym&gt;SSH&lt;/acronym&gt;. Il nous est dès lors possible d'ouvrir une connection &lt;acronym&gt;SSH&lt;/acronym&gt; sur &lt;code&gt;endpoint&lt;/code&gt; en se connectant au port 2222 de notre machine locale:&lt;/p&gt;


&lt;pre&gt;ssh -p 2222 localhost&lt;/pre&gt;


&lt;p&gt;Magique non ? Bien sur, il est possible de forwarder n'importe quel port au travers du tunnel:&lt;/p&gt;


&lt;pre&gt;ssh -L 8080:endpoint:80 relay&lt;/pre&gt;


&lt;p&gt;Faire pointer votre navigateur sur &lt;code&gt;http://localhost:8080/&lt;/code&gt; vous ammenera sur le serveur web de &lt;code&gt;endpoint&lt;/code&gt;.&lt;/p&gt;


&lt;p&gt;Mais un tunnel ne se limite pas à joindre deux machines d'un réseau différents. On peut également imaginer un tunnel entre deux machines dans l'unique but de sécuriser une transmission, par exemple, des échanges de mails. Imaginons que votre serveur mail preferré, &lt;code&gt;pop.example.com&lt;/code&gt;, ne propose pas de connection &lt;acronym&gt;POP&lt;/acronym&gt; sécurisée. Vous pouvez remédier à ce manque flagrant de confidentialité en créant un tunnel &lt;acronym&gt;SSH&lt;/acronym&gt;:&lt;/p&gt;


&lt;pre&gt;ssh -L 1100:localhost:110 pop.example.com&lt;/pre&gt;


&lt;p&gt;Bien sur, ce cas de figure nécessite d'avoir un compte permettant une connexion &lt;acronym&gt;SSH&lt;/acronym&gt; sur &lt;code&gt;pop.example.com&lt;/code&gt;, ce qui n'est pas forcément le cas. Pour remédier a ceci, deux solutions: utiliser un relay qui possède un serveur &lt;acronym&gt;SSH&lt;/acronym&gt;, ou installer un serveur &lt;acronym&gt;SSH&lt;/acronym&gt; sur &lt;code&gt;startpoint&lt;/code&gt; pour s'en servir comme relai (&lt;code&gt;sudo apt-get install openssh-server&lt;/code&gt; sur toute distribution debian-like qui se respecte):&lt;/p&gt;


&lt;pre&gt;ssh -L 1100:pop.example.com:110 localhost&lt;/pre&gt;


&lt;p&gt;And voila, il n'y a plus qu'a indiquer à notre client mail que le pop se situe sur &lt;code&gt;localhost&lt;/code&gt; au port 1100, et le tour est joué :-)&lt;/p&gt;</description>
    
    
    
          <comments>http://mirmodynamics.com/post/2006/10/20/Vous-prendrez-bien-un-peu-de-ssh-avec-votre-tunnel#comment-form</comments>
      <wfw:comment>http://mirmodynamics.com/post/2006/10/20/Vous-prendrez-bien-un-peu-de-ssh-avec-votre-tunnel#comment-form</wfw:comment>
      <wfw:commentRss>http://mirmodynamics.com/feed/atom/comments/691</wfw:commentRss>
      </item>
    
  <item>
    <title>Bootstrap CLI</title>
    <link>http://mirmodynamics.com/post/2006/10/13/Bootstrap-CLI</link>
    <guid isPermaLink="false">urn:md5:1e9ff0bf3342af30f0bcc1b219d7f60a</guid>
    <pubDate>Fri, 13 Oct 2006 18:31:00 +0200</pubDate>
    <dc:creator>Geoffrey</dc:creator>
        <category>Coding</category>
        <category>arborescence</category><category>bootstrap</category><category>CLI</category><category>howto</category><category>librairie</category><category>organisation</category><category>surclassage</category><category>zend framework</category>    
    <description>&lt;p&gt;Nous avons parcouru dernièrement la création du bootstrap &lt;acronym&gt;HTTP&lt;/acronym&gt; qui sert de socle à une application utilisant le Zend Framework. Le même principe peut s'appliquer aux scripts &lt;acronym&gt;CLI&lt;/acronym&gt;, mais comme les besoins sont fondamentalements différents, l'implémentation sera elle aussi complètement différente. Nous viserons les objectifs suivants:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ne pas être obligé d'executer le bootstrap en ligne de commande&lt;/li&gt;
&lt;li&gt;Simplifier au maximum l'écriture ultérieure des scripts &lt;acronym&gt;CLI&lt;/acronym&gt;&lt;/li&gt;
&lt;li&gt;Définir un jeu d'options de ligne de commande obligatoire&lt;/li&gt;
&lt;li&gt;Mettre en place un environnement d'execution&lt;/li&gt;
&lt;/ol&gt;    &lt;p&gt;La tentation serait de créer un système qui obligerait ce genre de commandes:&lt;/p&gt;


&lt;pre&gt;$ php batch/bootstrap.php monscript&lt;/pre&gt;


&lt;p&gt;Pour executer, par exemple, le script &lt;code&gt;batch/monscript.php&lt;/code&gt;. Cela simplifierait grandement les choses, mais c'est moche, et puis on peut faire autrement, donc on ne va pas se priver ! Nous utiliserons a cette fin la fonction magique &lt;code&gt;register_shutdown_function&lt;/code&gt;, qui s'occupera d'appeler une fonction qui executera les scripts &lt;em&gt;ad hoc&lt;/em&gt;.&lt;/p&gt;


&lt;p&gt;Avant d'aller plus loin, l'arborescence de fichiers que nous allons utiliser:&lt;/p&gt;


&lt;pre&gt;batch/
	monscript.php
	runtime.php
library/
	Zend.php
	My/
		Batch.php&lt;/pre&gt;


&lt;p&gt;&lt;code&gt;runtime.php&lt;/code&gt; sera le fichier à inclure dans chaque script, tandis que &lt;code&gt;My/Batch.php&lt;/code&gt; contiendra la classe d'abstraction &lt;code&gt;My_Batch&lt;/code&gt;, qui définira les comportements par défaut de nos scripts.&lt;/p&gt;


&lt;p&gt;Voici donc la première partie de notre bootstrap, &lt;code&gt;batch/runtime.php&lt;/code&gt;:&lt;/p&gt;


&lt;pre&gt;ini_set('include_path', get_include_path().PATH_SEPARATOR.realpath(dirname(__FILE__).'/../library/'));

require_once 'Zend.php';
require_once 'My/Batch.php';

register_shutdown_function(array('My_Batch', 'runAll'));&lt;/pre&gt;


&lt;p&gt;Bien sur, cette méthode nous oblige à définir un format à respecter pour les fonctions à executer par la suite, nous partirons donc sur les bases suivantes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chaque batch doit être constitué d'une classe dont le nom commence par &lt;code&gt;My_Batch_&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Chaque batch doit posséder une méthode executable &lt;code&gt;run&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ce qui nous donne donc:&lt;/p&gt;


&lt;pre&gt;abstract class My_Batch {
	static public function runAll() {
		$classes = get_declared_classes();
		foreach($classes as $className) {
			if (preg_match('/^My_Batch_(.+)$/', $className, $matches) &amp;amp;&amp;amp; method_exists($className, 'run')) {
				try {
					$batch = new $className;
					$batch-&amp;gt;run();
				} catch (Exception $e) {
					die(sprintf(&amp;quot;%s: %s\n&amp;quot;, $matches[1], $e-&amp;gt;getMessage()));
				}
			}
		}
	}
	
	abstract public function run();
}&lt;/pre&gt;


&lt;p&gt;On en profite pour catcher toute Exception indésirable pour la transformer en un &lt;code&gt;die&lt;/code&gt;, bien plus lisible dans un terminal qu'une backtrace :-)&lt;/p&gt;


&lt;p&gt;Maintenant que nous avons résolu les points 1 et 2, attaquons nous à la suite. Le problème principal que j'ai rencontré lors de l'établissement d'un environnement d'execution, c'est que la gestion de la configuration repose en grande partie sur la valeur de &lt;code&gt;$_SERVER['HTTP_HOST']&lt;/code&gt;, qui n'est bien évidemment pas disponible en &lt;acronym&gt;CLI&lt;/acronym&gt; :-) Il me faut donc forcer le passage en argument du nom de la configuration à utiliser. C'est très simple à faire, grace a la méthode que nous avons employé: il suffit de vérifier la présence de cet argument dans le constructeur de &lt;code&gt;My_Batch&lt;/code&gt; :-)&lt;/p&gt;


&lt;pre&gt;public function __construct() {
	foreach($GLOBALS['argv'] as $i =&amp;gt; $arg) {
		switch ($arg) {
			case '--configname':
			case '-c':
				$this-&amp;gt;_args['configname'] = $GLOBALS['argv'][$i+1];
			break;
		}
	}
	if (!isset($this-&amp;gt;_args['configname'])) {
		throw new Exception('-c or --configname is mandatory');
	}
}&lt;/pre&gt;


&lt;p&gt;Voilà, le gros du travail est fait. Pour le reste, c'est la routine, mise en place d'un environnement avec &lt;code&gt;Zend_Config&lt;/code&gt; et &lt;code&gt;Zend_Db&lt;/code&gt;, a la suite dans le constructeur:&lt;/p&gt;


&lt;pre&gt;	$this-&amp;gt;_config = new Zend_Config(
		Zend_Config_Ini::load(
			dirname(__FILE__).'/../../app/config.ini',
			$this-&amp;gt;_args['configname']
		)
	);
	
	$this-&amp;gt;_db = Zend_Db::factory(
		$this-&amp;gt;_config-&amp;gt;db-&amp;gt;adapter,
		$this-&amp;gt;_config-&amp;gt;db-&amp;gt;config-&amp;gt;asArray()
	);
	
	Zend_Db_Table::setDefaultAdapter($this-&amp;gt;_db);&lt;/pre&gt;


&lt;p&gt;Enfin, voila un exemple de script utilisant ce bootstrap:&lt;/p&gt;


&lt;pre&gt;require_once 'path/to/runtime.php';

class My_Batch_MonScript extends My_Batch {
	public function run() {
		// le code ici
	}
}&lt;/pre&gt;


&lt;p&gt;And voila !&lt;/p&gt;</description>
    
    
    
          <comments>http://mirmodynamics.com/post/2006/10/13/Bootstrap-CLI#comment-form</comments>
      <wfw:comment>http://mirmodynamics.com/post/2006/10/13/Bootstrap-CLI#comment-form</wfw:comment>
      <wfw:commentRss>http://mirmodynamics.com/feed/atom/comments/666</wfw:commentRss>
      </item>
    
  <item>
    <title>L'environnement d'execution</title>
    <link>http://mirmodynamics.com/post/2006/09/28/Lenvironnement-dexecution</link>
    <guid isPermaLink="false">urn:md5:e76eb4e24c2dc5797a4c4b451158f001</guid>
    <pubDate>Thu, 12 Oct 2006 21:51:00 +0200</pubDate>
    <dc:creator>Geoffrey</dc:creator>
        <category>Coding</category>
        <category>bootstrap</category><category>environnement</category><category>howto</category><category>organisation</category><category>zend framework</category>    
    <description>&lt;p&gt;Voilà un article que j'avais déjà proposé sur un site a caractère privé, un peu remis au gout du jour et étoffé de quelques informations et ressources non négligeables. Nous verrons ici comment mettre en place un environement d'execution standard du Zend Framework, également appelé &lt;em&gt;bootstrap&lt;/em&gt; de l'autre coté de l'atlantique.&lt;/p&gt;    &lt;p&gt;Un petit dessin valant mieux qu'un long discours, entrons dans le vif du sujet. On assume ici un environnement préinstallé: Apache / PHP5 / MySQL, avec une copie du Zend Framework dans &lt;code&gt;/usr/share/zend/&lt;/code&gt;, ainsi que le &lt;code&gt;.htaccess&lt;/code&gt; suivant à la racine de votre site (il semblerait que cette dépendance à &lt;code&gt;mod_rewrite&lt;/code&gt; soit sur la liste des choses à éliminer avant la release):&lt;/p&gt;


&lt;pre&gt;RewriteEnfine On
RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php&lt;/pre&gt;


&lt;p&gt;On peut également utiliser les règles suivantes, qui comportent l'avantage d'une plus grande simplicité pour intégrer des éléments d'application externe:&lt;/p&gt;


&lt;pre&gt;RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .* index.php&lt;/pre&gt;


&lt;h2&gt;L'environnement&lt;/h2&gt;


&lt;p&gt;Les plus perspicaces d'entre vous l'auront surement déjà deviné, le fichier &lt;code&gt;www/index.php&lt;/code&gt; servira de &lt;em&gt;bootstrap&lt;/em&gt;, c'est à dire de point d'entrée unique pour toute l'application. Le Zend Framework se chargera lui même de décortiquer l'&lt;acronym&gt;URL&lt;/acronym&gt; pour tenter de deviner ce à quoi l'utilisateur pouvait bien penser quand il a cliqué !&lt;/p&gt;


&lt;p&gt;Pour ce qui est de l'arborescence de fichiers conseillée, &lt;a href=&quot;http://fashion.hosmoz.net/blog/post/2006/09/16/Debuter-un-projet%3A-un-brin-dorganisation&quot;&gt;un peu d'organisation est recommandée&lt;/a&gt;.&lt;/p&gt;


&lt;p&gt;Pour utiliser le Zend Framework en toute quiétude, il nous faut nous assurer qu'il pourra accéder à ses propres composants. La première chose à faire est donc de modifier l'&lt;code&gt;include_path&lt;/code&gt;:&lt;/p&gt;


&lt;pre&gt;set_include_path(get_include_path().PATH_SEPARATOR.realpath(dirname(__FILE__).'/../library/'));&lt;/pre&gt;


&lt;p&gt;Il n'est pas inutile également de prendre quelques précautions d'usage:&lt;/p&gt;


&lt;pre&gt;ini_set('error_reporting', E_ALL); # | E_STRICT);

if (get_magic_quotes_gpc()) {
	die('&amp;lt;strong&amp;gt;E_NOOB_ERROR&amp;lt;/strong&amp;gt;: magic_quotes_gpc must be turned off.');
}

set_magic_quotes_runtime(false);&lt;/pre&gt;


&lt;p&gt;&lt;em&gt;Last but not least&lt;/em&gt;, on charge la librairie &lt;code&gt;Zend&lt;/code&gt;:&lt;/p&gt;


&lt;pre&gt;require_once 'Zend.php';&lt;/pre&gt;


&lt;p&gt;Ainsi préparés, nous pouvons nous plonger en toute quiétude dans la mise en place de quelques objets qui nous serons bien utiles tout au long du développement de notre application.&lt;/p&gt;


&lt;h2&gt;Les objets&lt;/h2&gt;


&lt;p&gt;La mise en place des objets est on ne peut plus claire. On configure le front controller (&lt;code&gt;Zend_Controller_Front&lt;/code&gt;) pour lui dire où trouver les &lt;em&gt;controllers&lt;/em&gt;, puis on configure les vues (&lt;code&gt;Zend_View&lt;/code&gt;) pour leur dire où trouver... les vues :-) On enregistre ensuite l'objet &lt;code&gt;$view&lt;/code&gt; dans le registre, qui n'est rien de plus qu'un endroit pratique où stocker des objets/données pour y avoir accès plus tard (&lt;code&gt;Zend::register($name, $value)&lt;/code&gt; et &lt;code&gt;$var = Zend::registry($name);&lt;/code&gt;).&lt;/p&gt;


&lt;p&gt;Une petite liste (non exhaustive) d'objets interressants à stocker dans le registre:&lt;/p&gt;


&lt;h3&gt;Zend_Db&lt;/h3&gt;


&lt;pre&gt;Zend::loadClass('Zend_Db');
Zend::loadClass('Zend_Db_Table');

$params = array(
	'host' =&amp;gt; 'localhost',
	'username' =&amp;gt; 'someobscureusername',
	'password' =&amp;gt; 'someobscurepassword',
	'dbname' =&amp;gt; 'yourdbname'
);

$db = Zend_Db::factory('PDO_MYSQL',$params);
Zend_Db_Table::setDefaultAdapter($db);

Zend::register('db', $db);&lt;/pre&gt;


&lt;h3&gt;Zend_Config&lt;/h3&gt;


&lt;p&gt;Je vous renvois à mon article sur la &lt;a href=&quot;http://fashion.hosmoz.net/blog/post/2006/09/04/Gestion-de-la-configuration&quot;&gt;gestion de la configuration&lt;/a&gt; :-)&lt;/p&gt;


&lt;h3&gt;Zend_View&lt;/h3&gt;


&lt;pre&gt;Zend::loadClass('Zend_View');

$view = new Zend_View;
$view-&amp;gt;setScriptPath(realpath(dirname(__FILE__).'/../app/views/'));

Zend::register('view', $view);&lt;/pre&gt;


&lt;h2&gt;Le Dispatch&lt;/h2&gt;


&lt;p&gt;Dernière étape, le dispatch, qui utilise les objets &lt;code&gt;Zend_Controller_Router&lt;/code&gt; et &lt;code&gt;Zend_Controller_Dispatcher&lt;/code&gt;. Ces deux objets sont au coeur du processus de sélection du controller à executer. Le Router va déterminer, à partir de l'environnement (en général l'URL, via $_SERVER['REQUEST_URI'] par exemple) quelle action executer et dans quel controller, tandis que le Dispatcher va s'occuper lui de les trouver (par exemple dire FooController::bar(), dans controllers/FooController.php).&lt;/p&gt;


&lt;p&gt;Le Zend Framework gère en natif les friendly urls, et ça c'est chouette. Par exemple: les urls suivantes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;http://example.com/foo/bar&lt;/li&gt;
&lt;li&gt;http://example.com/foo&lt;/li&gt;
&lt;li&gt;http://example.com/&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Seront analysées comme suit par le Routeur par défaut:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;controller: foo, action: bar&lt;/li&gt;
&lt;li&gt;controller: foo, action: index&lt;/li&gt;
&lt;li&gt;controller: index, action: index&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dernières choses à savoir sur la théorie:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;quand le controlleur est défini par l'url mais non trouvé par le &lt;code&gt;Dispatcher&lt;/code&gt;, celui ci execute &lt;code&gt;index/noRoute&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;les controllers sont des classes qui étendent &lt;code&gt;Zend_Controller_Action&lt;/code&gt; (ou une classe qui elle même l'étend)&lt;/li&gt;
&lt;li&gt;le Dispatcher par défaut va executer(en supposant une url type &lt;code&gt;http://example.org/foo/bar&lt;/code&gt;) &lt;code&gt;FooController::barAction&lt;/code&gt; dans &lt;code&gt;FooController.php&lt;/code&gt; (modulo le path déterminé avec &lt;code&gt;Zend_Controller_Front::setControllerPath&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;Les vues&lt;/h2&gt;


&lt;p&gt;Dernier point de cette brève excursion dans le Zend Framework, les vues. Pas grand chose à en dire pour le moment, si ce n'est que par défaut l'objet Zend_View se contente d'include() les fichiers templates. Il parait que l'intégration avec des moteurs de template (Smarty par exemple) est très aisée, mais comme je ne suis pas fan de ce genre de choses, ce ne sera pas couvert ici :-)&lt;/p&gt;</description>
    
    
    
          <comments>http://mirmodynamics.com/post/2006/09/28/Lenvironnement-dexecution#comment-form</comments>
      <wfw:comment>http://mirmodynamics.com/post/2006/09/28/Lenvironnement-dexecution#comment-form</wfw:comment>
      <wfw:commentRss>http://mirmodynamics.com/feed/atom/comments/626</wfw:commentRss>
      </item>
    
  <item>
    <title>Ma première impression sur XGL</title>
    <link>http://mirmodynamics.com/post/2006/10/03/Ma-premiere-impression-sur-XGL</link>
    <guid isPermaLink="false">urn:md5:60e3ea8cf9e08590e0213f9595acc399</guid>
    <pubDate>Tue, 03 Oct 2006 17:08:00 +0200</pubDate>
    <dc:creator>Geoffrey</dc:creator>
        <category>Geekeries</category>
        <category>howto</category><category>interfaces utilisateurs</category><category>ubuntu</category>    
    <description>    &lt;p&gt;Bon voilà, je viens de suivre &lt;a href=&quot;http://wiki.beryl-project.org/index.php/Install/Ubuntu/Edgy/XGL&quot;&gt;ce howto&lt;/a&gt;, et ça marche très bien. Passé le stade &lt;em&gt;mais où sont mes efferalgans ?&lt;/em&gt;, on arrive rapidement au stade &lt;em&gt;mais où mon smecta ?&lt;/em&gt;, tellement c'est gerbant de fluidité. Bref, le desktop mappé sur un cube, c'est sympa, mais on lui preferrera de loin les effets moins psychédéliques tels que la &lt;em&gt;vraie&lt;/em&gt; transparence et les animations des fenetres (et encore).&lt;/p&gt;</description>
    
    
    
          <comments>http://mirmodynamics.com/post/2006/10/03/Ma-premiere-impression-sur-XGL#comment-form</comments>
      <wfw:comment>http://mirmodynamics.com/post/2006/10/03/Ma-premiere-impression-sur-XGL#comment-form</wfw:comment>
      <wfw:commentRss>http://mirmodynamics.com/feed/atom/comments/640</wfw:commentRss>
      </item>
    
  <item>
    <title>Empecher la sauvegarde d'un mot de passe</title>
    <link>http://mirmodynamics.com/post/2006/09/24/Empecher-la-sauvegarde-dun-mot-de-passe</link>
    <guid isPermaLink="false">urn:md5:dad0b3837f014953dab7e916fc904221</guid>
    <pubDate>Sun, 24 Sep 2006 22:22:00 +0200</pubDate>
    <dc:creator>Geoffrey</dc:creator>
        <category>Coding</category>
        <category>firefox</category><category>formulaires</category><category>howto</category><category>interfaces utilisateurs</category><category>optimisation</category>    
    <description>    &lt;p&gt;Le titre n'est pas très explicite, malheureusement, j'ai du mal a réfléchir ce soir. Nous parlons ici de la sauvegarde d'un mot de passe par Firefox quand on valide un formulaire contenant un champs &lt;code&gt;password&lt;/code&gt;. Je ne connais pas exactement les règles qui régissent ce comportement, mais quand on à un formulaire d'enregistrement, en général, cette &lt;em&gt;fonctionnalitée&lt;/em&gt; est plus une gène qu'autre chose. Pour la désactiver, il suffit donc d'affecter un attribut &lt;em&gt;autocomplete&lt;/em&gt; de valeur &lt;em&gt;off&lt;/em&gt; à votre champs:&lt;/p&gt;


&lt;pre&gt;&amp;lt;input type=&amp;quot;password&amp;quot; name=&amp;quot;password&amp;quot; id=&amp;quot;password&amp;quot; autocomplete=&amp;quot;off&amp;quot; /&amp;gt;&lt;/pre&gt;


&lt;p&gt;Je sais, ça fait beaucoup de password.&lt;/p&gt;</description>
    
    
    
          <comments>http://mirmodynamics.com/post/2006/09/24/Empecher-la-sauvegarde-dun-mot-de-passe#comment-form</comments>
      <wfw:comment>http://mirmodynamics.com/post/2006/09/24/Empecher-la-sauvegarde-dun-mot-de-passe#comment-form</wfw:comment>
      <wfw:commentRss>http://mirmodynamics.com/feed/atom/comments/625</wfw:commentRss>
      </item>
    
  <item>
    <title>Automatisation des vues</title>
    <link>http://mirmodynamics.com/post/2006/08/30/Automatisation-des-vues</link>
    <guid isPermaLink="false">urn:md5:1d9d59b981f8ce7f914908cf7957d4da</guid>
    <pubDate>Wed, 30 Aug 2006 13:17:00 +0200</pubDate>
    <dc:creator>Geoffrey</dc:creator>
        <category>Coding</category>
        <category>howto</category><category>MVC</category><category>php</category><category>zend framework</category>    
    <description>    &lt;p&gt;Le rebutement principal que j'ai eu au début avec le Zend Framework était l'impossibilité (a priori) d'automatiser le rendu des vues en fonction du controlleur et de l'action appelés. Mes pérénigrations webesques m'ont finalement fait entrevoir &lt;a href=&quot;http://svn.akrabat.com/svnvi.pl?file=Action.php&amp;amp;repos=AkCom&amp;amp;path=/trunk/library/AkCom/Controller&quot;&gt;la solution&lt;/a&gt;.&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;EDIT&lt;/strong&gt;: mes nouvelles pérégrinations dans les sources du Zend Framework m'ont fait entrevoir &lt;a href=&quot;http://fashion.hosmoz.net/blog/post/2006/11/18/Howto%3A-Un-plugin-simple-pour-le-controller&quot;&gt;une meilleure solution&lt;/a&gt; :-)&lt;/p&gt;


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


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


&lt;p&gt;&lt;a href=&quot;http://fashion.hosmoz.net/public/sources/577.phps&quot;&gt;Fichier source&lt;/a&gt;&lt;/p&gt;


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

&lt;ul&gt;
&lt;li&gt;pour une URL du type &lt;code&gt;http://example.com/foo/bar/&lt;/code&gt;, &lt;code&gt;foo/bar.php&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;pour &lt;code&gt;http://example.com/&lt;/code&gt;, &lt;code&gt;index/index.php&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;pour &lt;code&gt;http://example.com/foo/&lt;/code&gt;, &lt;code&gt;foo/index.php&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pour une raison obscure, on ne peut pas laisser trainer l'éventuelle exception générée par &lt;code&gt;Zend_View&lt;/code&gt; dans le cas d'une vue inexistante. Cela entre en conflit avec une autre Exception qui traine, j'essayerai de tirer ça au clair :-)&lt;/p&gt;</description>
    
    
    
          <comments>http://mirmodynamics.com/post/2006/08/30/Automatisation-des-vues#comment-form</comments>
      <wfw:comment>http://mirmodynamics.com/post/2006/08/30/Automatisation-des-vues#comment-form</wfw:comment>
      <wfw:commentRss>http://mirmodynamics.com/feed/atom/comments/577</wfw:commentRss>
      </item>
    
  <item>
    <title>Zend Framework et les sessions</title>
    <link>http://mirmodynamics.com/post/2006/08/12/Zend-Framework-et-les-sessions</link>
    <guid isPermaLink="false">urn:md5:a2cd430d79098a5196160a3a1f1bcc7c</guid>
    <pubDate>Sat, 12 Aug 2006 20:31:00 +0200</pubDate>
    <dc:creator>Geoffrey</dc:creator>
        <category>Coding</category>
        <category>howto</category><category>php</category><category>session</category><category>zend framework</category>    
    <description>    &lt;p&gt;Bon voilà, j'ai commencé le développement d'un projet, et j'ai décidé d'utiliser le &lt;a href=&quot;http://framework.zend.com/&quot;&gt;Zend Framework&lt;/a&gt; (pour des raisons que je détaillerais dans un autre billet si ça vous interresse). Après mise en application du &lt;a href=&quot;http://www.phparch.com/zftut/&quot; hreflang=&quot;en&quot;&gt;tutoriel de Chris&lt;/a&gt;, Un premier problème s'est posé à moi: l'intégration des sessions. Après quelques recherches, on peut trouver &lt;a href=&quot;http://www.alexatnet.com/Blog/Index/2006-06-06/blog-cms-with-php-zend-framework-and-sqlite&quot;&gt;un blog développé avec ZF&lt;/a&gt;. Me ruant sur la partie d'admin, je découvre a ma grande stupeur des &lt;code&gt;session_start()&lt;/code&gt; incrustés a chaque méthode du controlleur, un peu comme ça:&lt;/p&gt;


&lt;pre&gt;class AdminController extends Zend_Controller_Action {
	function indexAction() {
		session_start();
	}
	function postAction() {
		session_start();
	}
}&lt;/pre&gt;


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


&lt;pre&gt;abstract class My_Controller_Action extends Zend_Controller_Action {
	public function __construct() {
		session_start();
	}
}

class AdminController extends My_Controller_Action {
	function indexAction() {
		// code
	}
}&lt;/pre&gt;


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


&lt;pre&gt;abstract class My_Controller_Action extends Zend_Controller_Action {
	public function __construct() {
		session_start();
	}

	public function __destruct() {
		$session_id = session_id();
		if (!empty($session_id)) {
			session_write_close();
		}
	}
}&lt;/pre&gt;


&lt;p&gt;Voilà c'est tout pour aujourd'hui, la prochaine fois, on parlera de l'utilisation des &lt;code&gt;Views&lt;/code&gt;, et nottament, comment éviter un appel rébarbatif à &lt;code&gt;Zend::registry()&lt;/code&gt; dans chaque controlleur.&lt;/p&gt;</description>
    
    
    
          <comments>http://mirmodynamics.com/post/2006/08/12/Zend-Framework-et-les-sessions#comment-form</comments>
      <wfw:comment>http://mirmodynamics.com/post/2006/08/12/Zend-Framework-et-les-sessions#comment-form</wfw:comment>
      <wfw:commentRss>http://mirmodynamics.com/feed/atom/comments/573</wfw:commentRss>
      </item>
    
  <item>
    <title>Faire du CLI en PHP</title>
    <link>http://mirmodynamics.com/post/2005/03/05/325-faire-du-cli-en-php</link>
    <guid isPermaLink="false">urn:md5:1325b5a09c1e84802f8f787b0c0c867f</guid>
    <pubDate>Sat, 05 Mar 2005 13:02:47 +0000</pubDate>
    <dc:creator>Geoffrey</dc:creator>
        <category>Geekeries</category>
        <category>cli</category><category>howto</category><category>php</category>    
    <description>&lt;p&gt;Souvent les gens ne voient &lt;acronym&gt;PHP&lt;/acronym&gt; que comme un langage de script orienté web. Même si il est vrai que c'est son but premier, ce n'est pas la seule &lt;a href=&quot;http://fr.php.net/manual/en/intro-whatcando.php&quot; hreflang=&quot;en&quot;&gt;chose que &lt;acronym&gt;PHP&lt;/acronym&gt; peut faire&lt;/a&gt;. Penchons nous un peu sur l'utilisation de PHP en &lt;acronym title=&quot;Command Line Interface&quot;&gt;CLI&lt;/acronym&gt;, qui permet d'utiliser &lt;acronym&gt;PHP&lt;/acronym&gt; comme un langage de script &lt;em&gt;classique&lt;/em&gt;. Nous utiliserons pour se faire un système linux (ubuntu pour être précis) avec un binaire php-cli déjà installé.&lt;/p&gt;    &lt;h5&gt;Au commencement...&lt;/h5&gt;


&lt;p&gt;Au commencement est le &lt;em&gt;shebang&lt;/em&gt;. Qu'est-ce que c'est donc que ça&amp;nbsp;? C'est simplement la première ligne d'un script shell, celle qui indique au shell quel interpréteur utiliser pour executer le script. Elle début obligatoirement par les caractères &lt;code&gt;#!&lt;/code&gt; (qu'on appelle &lt;em&gt;shebang&lt;/em&gt;), suivis du chemin absolu de l'interpréteur. Dans la plupart des cas, l'intérpréteur &lt;acronym&gt;PHP&lt;/acronym&gt; se situe dans &lt;code&gt;/usr/bin&lt;/code&gt; et s'appelle &lt;code&gt;php&lt;/code&gt;, tout simplement. Ce qui nous donne donc:&lt;/p&gt;


&lt;pre&gt;#!/usr/bin/php&lt;/pre&gt;


&lt;p&gt;Bien sur, il est possible que votre interpréteur se situe ailleurs, &lt;code&gt;/usr/local/bin/php&lt;/code&gt; par exemple. Pour le trouve, vous pouvez utiliser la commande &lt;code&gt;which&lt;/code&gt;:&lt;/p&gt;


&lt;pre&gt;ash@sushi:~$ which php
/usr/bin/php&lt;/pre&gt;


&lt;p&gt;Si cette commande ne retourne rien, c'est que php-cli n'est pas installé, et là ça dépasse le cadre de cet article :)&lt;/p&gt;


&lt;h5&gt;Un petit détour par la doc&lt;/h5&gt;


&lt;p&gt;A ce stade, il est bon d'aller voir ce que la doc a a nous dire sur &lt;a href=&quot;http://fr.php.net/manual/en/features.commandline.php&quot; hreflang=&quot;en&quot;&gt;l'utilisation de &lt;acronym&gt;PHP&lt;/acronym&gt; en ligne de commande&lt;/a&gt;. On y apprend toutes sortes de choses, a commencer par la compilation de &lt;acronym&gt;PHP&lt;/acronym&gt; en &lt;acronym&gt;CLI&lt;/acronym&gt;. On y apprend également que certaines valeurs du &lt;code&gt;php.ini&lt;/code&gt; sont différentes de celles de &lt;acronym&gt;PHP&lt;/acronym&gt; utilisé avec un server web, et nottement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;html_errors&lt;/code&gt;, pour ne pas afficher de &lt;acronym&gt;HTML&lt;/acronym&gt; dans les erreurs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;max_execution_time&lt;/code&gt;, pour autoriser un temps d'execution infini&lt;/li&gt;
&lt;li&gt;&lt;code&gt;register_argc_argv&lt;/code&gt;, pour peupler les variables $argc et $argv, sur lesquelles nous reviendrons plus tard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On y apprend aussi que php-cli enregistre automatiquement de nouvelles constantes telles que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;STDIN&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;STDOUT&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;STDERR&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Qui représentent respectivement l'entrée standard, la sortie standard, et la sortie d'erreur standard. Nous reviendronts sur ces constantes et leur utilisation plus tard.&lt;/p&gt;


&lt;p&gt;La dernière chose que nous retiendrons de cette page est que php-cli ne modifie pas le repertoire courant a l'execution d'un script. C'est à dire que si vous vous trouvez dans le répertoire &lt;code&gt;/tmp&lt;/code&gt;, et que vous executez &lt;code&gt;php /home/ash/foo.php&lt;/code&gt;, la fonction &lt;code&gt;&lt;a href=&quot;http://fr.php.net/getcwd&quot; hreflang=&quot;en&quot;&gt;getcwd&lt;/a&gt;&lt;/code&gt; (GET Current Working Directory) retournera &lt;code&gt;/tmp&lt;/code&gt;, et pas &lt;code&gt;/home/ash&lt;/code&gt;.&lt;/p&gt;


&lt;h5&gt;Notre premier script en &lt;acronym&gt;CLI&lt;/acronym&gt;&lt;/h5&gt;


&lt;p&gt;Il est grand temps d'entrer dans le vif du sujet et de faire notre premier script. Soyons originaux, créons un &lt;code&gt;hello_world.php&lt;/code&gt;, avec le code suivant:&lt;/p&gt;

&lt;pre&gt;&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;#!/usr/bin/php&lt;/span&gt;
&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;a href=&quot;http://www.php.net/print&quot;&gt;&lt;span style=&quot;color: #000066;&quot;&gt;print&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #ff0000;&quot;&gt;&quot;hello world !&quot;&lt;/span&gt;; &lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/pre&gt;


&lt;p&gt;Une fois le fichier créé, nous avons deux possibilités pour l'executer. Soit lancer directement l'interpréteur &lt;acronym&gt;PHP&lt;/acronym&gt; en lui précisant le script a executer:&lt;/p&gt;


&lt;pre&gt;$ /usr/bin/php ./hello_world.php&lt;/pre&gt;


&lt;p&gt;Soit rendre le script executable et l'executer directement:&lt;/p&gt;


&lt;pre&gt;$ chmod +x ./hello_world.php
$ ./hello_world.php&lt;/pre&gt;


&lt;p&gt;Dans tous les cas, le résultat est le même:&lt;/p&gt;


&lt;pre&gt;hello world !&lt;/pre&gt;


&lt;p&gt;Facile non&amp;nbsp;? Notez bien que si vous décidez de rendre le script executable, vous n'aurez a executer la commande &lt;code&gt;chmod&lt;/code&gt; qu'une seule fois.&lt;/p&gt;


&lt;p&gt;Vous remarquerez surement que votre &lt;em&gt;prompt&lt;/em&gt; s'affiche directement après le &lt;em&gt;!&lt;/em&gt;, c'est normal, nous n'avons pas inclus de caractère de nouvelle ligne (&lt;code&gt;
&lt;/code&gt;) dans notre &lt;code&gt;print&lt;/code&gt;. Vous pouvez donc modifier le script ainsi:&lt;/p&gt;

&lt;pre&gt;&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;#!/usr/bin/php&lt;/span&gt;
&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;a href=&quot;http://www.php.net/print&quot;&gt;&lt;span style=&quot;color: #000066;&quot;&gt;print&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #ff0000;&quot;&gt;&quot;hello world !&lt;span style=&quot;color: #000099; font-weight: bold;&quot;&gt;
&lt;/span&gt;&quot;&lt;/span&gt;; &lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/pre&gt;


&lt;p&gt;Et le tour est joué.&lt;/p&gt;


&lt;h5&gt;Utilisation de $argc et $argv&lt;/h5&gt;


&lt;p&gt;Tout ça c'est bien beau, mais un script c'est quand même mieux quand on peut lui passer des arguments. Et &lt;strong&gt;non&lt;/strong&gt;, executer &lt;code&gt;./hello_world.php?arg=value&lt;/code&gt; ne fonctionne pas :-) Comment faire alors&amp;nbsp;? Les variables &lt;code&gt;$argc&lt;/code&gt; et &lt;code&gt;$argv&lt;/code&gt; sont là pour ça.&lt;/p&gt;


&lt;p&gt;Pour passer des paramètres à un shell script, on utilise usuellement la syntaxe suivante:&lt;/p&gt;


&lt;pre&gt;$ ./script [arg [arg...]]&lt;/pre&gt;


&lt;p&gt;C'est comme cela que nous procéderons en PHP-CLI. La variable &lt;code&gt;$argc&lt;/code&gt; (ARGuments Count) contient le nombre de paramètres passés au script, tandis que la variable &lt;code&gt;$argv&lt;/code&gt; (ARGuments Values) est un &lt;code&gt;array&lt;/code&gt; contient les valeurs de ces arguments. Modifions donc notre script de tout à l'heure:&lt;/p&gt;

&lt;pre&gt;&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;#!/usr/bin/php&lt;/span&gt;
&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;a href=&quot;http://www.php.net/var_dump&quot;&gt;&lt;span style=&quot;color: #000066;&quot;&gt;var_dump&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #0000ff;&quot;&gt;$argc&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;a href=&quot;http://www.php.net/print_r&quot;&gt;&lt;span style=&quot;color: #000066;&quot;&gt;print_r&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;span style=&quot;color: #0000ff;&quot;&gt;$argv&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/pre&gt;


&lt;p&gt;Et executons le en lui passant quelques paramètres:&lt;/p&gt;


&lt;pre&gt;$ ./hello_world.php foo bar&lt;/pre&gt;


&lt;p&gt;Voyons le résultat:&lt;/p&gt;


&lt;pre&gt;ash@sushi:~$ ./hello_world.php foo bar
int(3)
Array
(
    [0] =&amp;gt; ./hello_world.php
    [1] =&amp;gt; foo
    [2] =&amp;gt; bar
)&lt;/pre&gt;


&lt;p&gt;On remarque que le nom du script compte comme un argument.&lt;/p&gt;


&lt;h5&gt;Utilisation de STDIN&lt;/h5&gt;


&lt;p&gt;Ces constantes dont nous avons parlé plus tôt sont très importantes. Elles définissent les flux standards à votre disposition, mais nous ne nous interresserons qu'a &lt;code&gt;STDIN&lt;/code&gt; dans cet article.&lt;/p&gt;


&lt;p&gt;&lt;code&gt;STDIN&lt;/code&gt;  (STanDard INput) est l'entrée standard. C'est elle que vous devez utiliser que vous voulez interragir avec l'utilisateur. Par exemple pour lui faire entrer des données. Modifions encore une fois notre &lt;code&gt;hello_world.php&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;span style=&quot;color: #808080; font-style: italic;&quot;&gt;#!/usr/bin/php&lt;/span&gt;
&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;&amp;lt;?php&lt;/span&gt;
&lt;span style=&quot;color: #0000ff;&quot;&gt;$name&lt;/span&gt; = &lt;a href=&quot;http://www.php.net/trim&quot;&gt;&lt;span style=&quot;color: #000066;&quot;&gt;trim&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;&lt;a href=&quot;http://www.php.net/fgets&quot;&gt;&lt;span style=&quot;color: #000066;&quot;&gt;fgets&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#40;&lt;/span&gt;STDIN&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;&lt;span style=&quot;color: #66cc66;&quot;&gt;&amp;#41;&lt;/span&gt;;
&lt;a href=&quot;http://www.php.net/print&quot;&gt;&lt;span style=&quot;color: #000066;&quot;&gt;print&lt;/span&gt;&lt;/a&gt; &lt;span style=&quot;color: #ff0000;&quot;&gt;&quot;hello $name !&lt;span style=&quot;color: #000099; font-weight: bold;&quot;&gt;
&lt;/span&gt;&quot;&lt;/span&gt;;
&lt;span style=&quot;color: #000000; font-weight: bold;&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;/pre&gt;


&lt;p&gt;Et executons le:&lt;/p&gt;


&lt;pre&gt;ash@sushi:~$ ./hello_world.php 
geoffrey
hello geoffrey !&lt;/pre&gt;


&lt;p&gt;Le script a donc attendu que je tape quelque chose en terminant par un &lt;acronym&gt;CRLF&lt;/acronym&gt; (Cariage Return Line Feed, la touche Entrée quoi ;). J'ai donc tapé &lt;em&gt;geoffrey&lt;/em&gt;, et voilà. Notons l'utilisation de &lt;code&gt;&lt;a href=&quot;http://fr.php.net/trim&quot; hreflang=&quot;en&quot;&gt;trim&lt;/a&gt;&lt;/code&gt; pour supprimer le &lt;acronym&gt;CRLF&lt;/acronym&gt; en question.&lt;/p&gt;


&lt;h5&gt;Le mot de la fin&lt;/h5&gt;


&lt;p&gt;Voilà, avec ça, vous devriez avoir les bases nécessaire pour commencer a faire du PHP-CLI. Cela dit, cet article n'explore pas toutes les subtilités du &lt;acronym&gt;CLI&lt;/acronym&gt;, et une bonne lecture de documentation est fortement conseillée :)&lt;/p&gt;</description>
    
    
    
          <comments>http://mirmodynamics.com/post/2005/03/05/325-faire-du-cli-en-php#comment-form</comments>
      <wfw:comment>http://mirmodynamics.com/post/2005/03/05/325-faire-du-cli-en-php#comment-form</wfw:comment>
      <wfw:commentRss>http://mirmodynamics.com/feed/atom/comments/323</wfw:commentRss>
      </item>
    
</channel>
</rss>