Apache et symfony: optimisation

Je vais vous présentez dans ce billet, les méthodes que j’utilise pour optimiser les réponses d’apache et par la même occasion le rewrite de symfony.

Pour pouvoir effectuer tous les réglages, voici les modules que je charge dans la configuration d’apache

LoadModule deflate_module modules/mod_deflate.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule rewrite_module modules/mod_rewrite.so

Je vais commencer par épurer le log apache. Pour cela, je vais ajouter à la fin de mon fichier de configuration httpd.conf ou apache2.conf (selon les install), les lignes suivantes:

SetEnvIf Request_URI "\.(css|gif|ico|jpg|js|png|txt|xml)$" dontlog
SetEnvIf Request_Method HEAD dontlog

Je vais tout simplement ignorer les fichiers qui contiennent les extensions listées ci-dessus. Ensuite, il suffit d’attribuer cette variable d’environnement à notre paramètre « CustomLog » de notre virtualhost comme ceci:

CustomLog logs/mywebsite.com-access_log combined env=!dontlog

Dès maintenant, Apache prendra moins de temps à écrire ces logs car il ne tiendra plus compte de ces fichiers.

Passons maintenant à la compression des fichiers entre le serveur et le client. Pour cela, j’ai utilisé le code suivant dans la configuration du virtualhost:

<Location />
# ------------- DEFLATE -------------
# http://httpd.apache.org/docs/2.0/mod/mod_deflate.html
<IfModule mod_deflate.c>
  SetOutputFilter DEFLATE
  BrowserMatch ^Mozilla/4 gzip-only-text/html
  BrowserMatch ^Mozilla/4\.0[678] no-gzip
  BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html
  SetEnvIfNoCase Request_URI \
  \.(?:gif|jpe?g|png)$ no-gzip dont-vary
  Header append Vary User-Agent env=!dont-vary
</IfModule>
# ------------- END DEFLATE -------------
</Location>

Cela vous permettra d’économiser un peu de bande passante 😉

Nous allons maintenant nous occuper du cache Apache:

# ------------- EXPIRES RULE -------------
# http://httpd.apache.org/docs/2.0/mod/mod_expires.html
<IfModule mod_expires.c>
  ExpiresActive On

  ExpiresByType text/css "access plus 7 days"
  ExpiresByType application/javascript "access plus 7 days"
  ExpiresByType image/gif "access plus 7 days"
  ExpiresByType image/jpeg "access plus 7 days"
  ExpiresByType image/png "access plus 7 days"
</IfModule>
# ------------- END EXPIRES RULE -------------

Comme vous pouvez le constater ci-dessous, vous avez la possibilité avec la directive « ExpiresByType » de mettre différentes validités selon le type de fichier. Attention quand même lorsque vous utilisez ce genre de cache car il se peut que le serveur ne rende pas toujours le résultat actualisé. Il faut effectuer des tests avant son passage en production.

Passons maintenant à la partie symfony. Par défaut, la configuration du mod_rewrite est située dans le fichier .htaccess à la racine de votre site (dossier web). Apache va lire ce fichier à chaque requête, ce qui prend du temps. Nous allons donc tranférer cela dans notre configuration virtualhost pour l’avoir en mémoire et désactiver la lecture physique:

<Location />
# ------------- REWRITE -------------
<IfModule mod_rewrite.c>
  RewriteEngine On
  
  # Level 0 to 9 [0 = no logging, 9 = all actions] (Default: 0)
  RewriteLogLevel 0
  
  # we check if the .html version is here (caching)
  RewriteRule ^$ index.html [QSA]
  RewriteRule ^([^.]+)$ $1.html [QSA]

  # no, so we redirect to our front web controller
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>
# ------------- END REWRITE -------------
</Location>

Dans la directive « <Directory …> », nous allons passer le paramètre « AllowOverride » à none. Vous pouvez ensuite supprimer le fichier .htaccess de votre dossier web.

Pour finir, je vous donne la représentation complète de mon fichier virtualhost:

<VirtualHost *:80>
  ServerName mywebsite.com
  DocumentRoot /www/virtualhosts/mywebsite.com/web
  ErrorLog logs/mywebsite.com-error_log
  CustomLog logs/mywebsite.com-access_log combined env=!dontlog
  RewriteLog logs/mywebsite.com-rewrite.log
  
  Alias /sf /www/virtualhosts/mywebsite.com/lib/vendor/symfony/data/web/sf
  
  <Directory "/www/virtualhosts/mywebsite.com/web">
    Options Indexes FollowSymLinks SymLinksifOwnerMatch
    AllowOverride none
    Allow from All
  </Directory>
  
  <Directory "/www/virtualhosts/mywebsite.com/lib/vendor/symfony/data/web/sf">
    Options Indexes FollowSymLinks SymLinksifOwnerMatch
    AllowOverride none
    Allow from All
  </Directory>

  <Directory "/path/to/my/sfProject/web/uploads">
    php_flag engine off
  </Directory>

  <Location />
    # ------------- REWRITE -------------
    <IfModule mod_rewrite.c>
      RewriteEngine On
      
      # Level 0 to 9 [0 = no logging, 9 = all actions] (Default: 0)
      RewriteLogLevel 0
      
      # we check if the .html version is here (caching)
      RewriteRule ^$ index.html [QSA]
      RewriteRule ^([^.]+)$ $1.html [QSA]

      # no, so we redirect to our front web controller
      RewriteCond %{REQUEST_FILENAME} !-f
      RewriteRule ^(.*)$ index.php [QSA,L]
    </IfModule>
    # ------------- END REWRITE -------------
    
    # ------------- DEFLATE -------------
    # http://httpd.apache.org/docs/2.0/mod/mod_deflate.html
    <IfModule mod_deflate.c>
      SetOutputFilter DEFLATE
      BrowserMatch ^Mozilla/4 gzip-only-text/html
      BrowserMatch ^Mozilla/4\.0[678] no-gzip
      BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html
      SetEnvIfNoCase Request_URI \
      \.(?:gif|jpe?g|png)$ no-gzip dont-vary
      Header append Vary User-Agent env=!dont-vary
    </IfModule>
    # ------------- END DEFLATE -------------
  </Location>

  # ------------- EXPIRES RULE -------------
  # http://httpd.apache.org/docs/2.0/mod/mod_expires.html
  <IfModule mod_expires.c>
    ExpiresActive On

    ExpiresByType text/css "access plus 7 days"
    ExpiresByType application/javascript "access plus 7 days"
    ExpiresByType image/gif "access plus 7 days"
    ExpiresByType image/jpeg "access plus 7 days"
    ExpiresByType image/png "access plus 7 days"
  </IfModule>
  # ------------- END REWRITE RULE -------------
  
</VirtualHost>

J’espère que ces petites optimisations pourront vous servir un jour. N’hésitez pas à me faire des remarques et éventuellement me communiquer d’autres astuces Apache/symfony.

Share

Symfony: Réaliser une identification ajax avec sfDoctrineGuardPlugin

Dans cette publication, nous allons voir comment mettre en place un système d’identification basé sur le plugin sfDoctrineGuard. Pour que cela fonctionne, il vous faut également l’excellente librairie jquery. Dans cette article, je n’aborde pas l’installation du plugin, ni l’installation et le chargement de jquery.

Nous allons commencer par générer un nouveau module « user » dans notre projet avec la commande ci-dessous:

./symfony generate:module frontend user

Nous allons dès maintenant modifier notre module « user » pour y mettre notre propre code. Pour cela nous allons inclure la classe « BasesfGuardAuthActions » dans notre classe et changer l’extends:

require_once(sfConfig::get('sf_plugins_dir').'/sfDoctrineGuardPlugin/modules
/sfGuardAuth/lib/BasesfGuardAuthActions.class.php');

class userActions extends BasesfGuardAuthActions
{
  ...
}

J’avais un problème a résoudre lors de l’affichage de la page signin lors de l’appel d’une page protégée. J’ai choisi d’y mettre uniquement une information utilisateur lui indiquant de s’identifier avec le formulaire. J’ai créé le fichier signinSuccess.php dans le dossier templates.

Nous allons maintenant monter le formulaire d’identification dans un component.

class userComponents extends sfComponents
{
  public function executeSignin(sfWebRequest $request)
  {
    $class = sfConfig::get('app_sf_guard_plugin_signin_form', 'sfGuardFormSignin');
    $this->form = new $class();
  }
}

template: _signin.php

<div id="form_message">&nbsp;</div>
<form id="guard" action="<?php echo url_for('@sf_guard_signin') ?>" method="post">
  <?php echo $form->renderHiddenFields(); ?>
  <label>Utilisateur:</label>
  <?php echo $form['username']->render(); ?>
  <label>Mot de passe:</label>
  <?php echo $form['password']->render(); ?>
  <?php echo $form['remember']->render(array('id' => 'remember')); ?>
  Se souvenir de moi

  <input type="submit" value="S'identifier" />
  <a href="<?php echo url_for('@sf_guard_password') ?>">Mot de passe oublié ?</a>
</form>

<script type="text/javascript">
  $('#guard').submit(function() {
    $.post("<?php echo url_for('@sf_guard_signin'); ?>", $('#guard').serialize(), function(response) {
      switch(response.status) {
        case 'success':
        $('#form_message').html(response.message);
        $(location).attr('href',response.url);
        break;
        case 'failure':
        $('#form_message').html(response.message);
        $('#form_message').show();
        break;
      }
    }, 'json');
    return false;
  });
</script>

Nous allons rajouter un peu de css pour cacher notre zone « form_message »:

#form_message {
  display: none;
  font-weight: bold;
  color: red;
}

Prochaine phase, désactiver les routes et y mettre nos propres paramètres. Nous allons pour cela toucher 3 fichiers:

app.yml

all:
  sf_guard_plugin:
    routes_register: false

routing.yml

sf_guard_signin:
  url:      /login
  param:    { module: user, action: signin }

sf_guard_signout:
  url:      /logout
  param:    { module: user, action: signout }

sf_guard_password:
  url:      /password
  param:    { module: user, action: password }

settings.yml

all:
  .actions:
    login_module:           user
    login_action:           signin
        
    secure_module:          user
    secure_action:          secure

La dernière phase de cette réalisation est l’implémentation de notre fonction signin. Voici le code utilisé pour un dialogue ajax:

class userActions extends BasesfGuardAuthActions
{
  public function executeSignin($request)
  {
    $user = $this->getUser();
    if ($user->isAuthenticated())
    {
      return $this->redirect('@homepage');
    }

    if($request->isMethod('post') && $request->isXmlHttpRequest())
    {
      $class = sfConfig::get('app_sf_guard_plugin_signin_form', 'sfGuardFormSignin');
      $form = new $class();
      $form->bind($request->getParameter($form->getName()));
      if ($form->isValid())
      {
        $values = $form->getValues();
        $user->signin($values['user'], array_key_exists('remember', $values) ? $values['remember'] : false);
        $signinUrl = sfConfig::get('app_sf_guard_plugin_success_signin_url', $user->getReferer($request->getReferer()));

        return $this->renderText(json_encode(array('status' => 'success', 'url' => $signinUrl)));
      }
      else
      {
        return $this->renderText(json_encode(array('status' => 'failure','message' => 'Identification incorrecte')));
      }
    }

    $this->getResponse()->setStatusCode(401);
  }
}

Il nous reste plus qu’à insérer notre component dans notre layout ou notre template:

<?php if (!$sf_user->isAuthenticated()): ?>
<?php include_component('user', 'signin'); ?>
<?php endif; ?>

L’implémentation est terminée. J’espère que ce petit article vous permettra de mettre en place un formulaire à la web 2.0 😉

Share

Symfony: Contrôle de la disponibilité de la base de données avec un event

Symfony, depuis la version 1.2, offre un système d’événements (sfEventDispatcher et sfEvent). Cela va permettre la mise en place de notre contrôle. Pour cela nous allons nous connecter à l’événement « doctrine.configure_connection ». Nous allons effectuer cela dans notre application configuration (frontend) avec le code ci-dessous:

class frontendConfiguration extends sfApplicationConfiguration
{
  public function configure()
  {
    $this->dispatcher->connect('doctrine.configure_connection', array($this, 'listenToAddCheckDatabaseConnection'));
  }
  
  public function listenToAddCheckDatabaseConnection(sfEvent $event)
  {
    $actions = $event->getSubject();
    $actions->getCurrentConnection()->connect();
    sfConfig::set('sf_database_is_connected', $actions->getCurrentConnection()->isConnected());
  }
}

Nous allons ensuite intercepter notre nouvelle configuration en personnalisant notre frontController. Pour cela, nous allons ajouter une nouvelle classe dans notre projet sous lib/controller/mySfFrontWebController.class.php

class mySfFrontWebController extends sfFrontWebController
{
  public function dispatch()
  {
    if (!sfConfig::get('sf_database_is_connected', true))
    {
      $request    = $this->context->getRequest();
      $moduleName = $request->getParameter('module');
      $actionName = $request->getParameter('action');
      
      if ((!$module = sfConfig::get('app_database_not_connected_module')) ||
      (!$action = sfConfig::get('app_database_not_connected_action')))
      {
        throw new sfConfigurationException('Missing parameter(s): app_database_not_connected_module and app_database_not_connected_action are required in app.yml');
      }
      else
      {
        if (($moduleName != $module) && ($actionName != $action))
        {
          $this->forward($module, $action);
          exit;
        }
      }
    }
    parent::dispatch();
  }
}

Pour charger notre nouvelle classe, nous allons le faire dans le fichier factories.yml de notre application (frontend):

all:
  controller:
    class: mySfFrontWebController

Nous allons définir nos nouveaux paramètres dans le fichier app.yml de notre application (frontend):

all:
  database_not_connected:
    module:       main
    action:       notconnected

Il nous reste maintenant à définir notre nouvelle action dans le module main (ou autre).

Voilà. La mise en place du contrôle de connexion sur notre base de données est terminée.

J’espère que ce petit bout de code vous servira.

Share

Symfony: Afficher un message en cas de non disponibilité de la base de données

Symfony ne proposant pas une fonctionnalité me permettant de définir un message en cas de non disponibilité de la base de données, j’ai réalisé un filtre pour contrôler cela. Pour le rendre flexible, j’ai ajouté deux options permettant la définition du module et de l’action appelé lors de l’erreur.

J’ai commencé par créer dans mon module default, une action checkAvailibility me permettant de réaliser un template pour l’affichage du message (je passe sur cette étape car je pense que vous savez le faire). Ensuite, il suffit de les définir dans le fichier app.yml:

all:
  checkdb:
    module: default
    action: checkAvailibility

J’ai ensuite créé mon fichier filtre appelé « checkAvailibilityDbFilter.class.php » dans le dossier /lib de mon projet:

class checkAvailibilityDbFilter extends sfFilter
{
  public function execute($filterChain)
  {
    if ($this->isFirstCall())
    {
      $context = $this->getContext();

      $module = sfConfig::get('app_checkdb_module', 'default');
      $action = sfConfig::get('app_checkdb_action', 'error404');

      if (($module != $context->getModuleName()) || ($action != $context->getActionName()))
      {
        $configuration = sfProjectConfiguration::getActive();
        $db = new sfDatabaseManager($configuration);
        
        foreach ($db->getNames() as $connection)
        {
          try
          {
            @$db->getDatabase($connection)->getConnection();
          } 
          catch(Exception $e)
          {
             $context->getController()->forward($module, $action);
             exit;
          }
        }
      }
    }
    
    $filterChain->execute();
  }
}

Il reste encore à activer ce filtre pour que cela fonctionne. J’ai ajouté les lignes suivantes dans le fichier filters.yml du dossier config de l’application:

rendering: ~
security:  ~

# insert your own filters here
db:
  class:  checkAvailibilityDbFilter

cache:     ~
common:    ~
execution: ~

J’espère que cette petite astuce vous sera utile.

Share

sfDoctrineGuardPlugin: Récupération de l’id utilisateur

Il y a souvent des questions concernant la récupération de l’id utilisateur lorsque l’on utilise sfDoctrineGuardPlugin. En faite vous avez le choix entre deux possibilités. La première va lancer une requête dans la base de données, la seconde va récupérer l’id dans la session courante.

Exemple 1:

$this->user_id = $this->getUser()->getGuardUser()->getId();

Exemple 2:

$this->user_id = $this->getUser()->getAttribute(
'user_id', null, 'sfGuardSecurityUser');

Nous allons maintenant créer une nouvelle fonction dans notre classe myUser permettant d’accéder directement à notre id:

class myUser extends sfGuardSecurityUser
{
  public function getId()
  {
    return $this->getAttribute('user_id', null, 'sfGuardSecurityUser');
  }
}

Après avoir créer cette nouvelle fonction, vous avez accès à votre id en exécutant le code suivant:

$this->user_id = $this->getUser()->getId();

Voilà pour la petite mise en place.

Share

InstantClient 10 compatible snow leopard 10.6

Bonne nouvelle pour les utilisateurs d’Oracle. Le projet MacPorts a rendu la version InstantClient 10 compatible 64 bits pour l’installation sur snow leopard. Voici donc la marche à suivre pour son installation.

Pour être sur d’avoir les dernières sources, nous allons au préalable faire une mise à jour de l’arbre de MacPorts:

sudo port selfupdate

Première chose, nous allons télécharger la version d’InstantClient 10 directement sur le site d’Oracle en cliquant ici.
Récupérer les paquets suivants:

instantclient-basic-10.2.0.4.0-macosx-x64.zip
instantclient-sdk-10.2.0.4.0-macosx-x64.zip

La démarche suivante consiste à copier ces 2 paquets dans le projet MacPorts à l’emplacement suivant:

 /opt/local/var/macports/distfiles/oracle-instantclient

Dès que vous aurez déplacé ces paquets au bon endroit, il ne vous reste plus qu’à les installer

port install oracle-instantclient

Nous allons insérer la ligne suivante dans le profile (/etc/profile).

export DYLD_LIBRARY_PATH=/opt/local/lib/oracle

Nous allons également insérer ce path dans la configuration apache en ajoutant la ligne ci-dessous dans le fichier envvars se trouvant dans /opt/local/apache2/bin:

...
export DYLD_LIBRARY_PATH="/opt/local/lib/oracle"

Pour finir, nous allons installer l’interface pour php

port install php5-oracle

Il ne reste plus qu’à relancer apache pour que le tout soit pris en compte

sudo /opt/local/apache2/bin/apachectl stop
sudo /opt/local/apache2/bin/apachectl start

J’utilise le stop/start pour vraiment vider les choses en mémoire.

Bonne découverte.

Share

Filtre de redirection basé sur le langage

Pour compléter mon article précédent De la culture au language, j’ai réalisé un filtre permettant de faire une re-direction sur la page d’accueil selon le langage du navigateur web. Si ce langage n’est pas disponible, le navigateur sera automatiquement redirigé vers la langue par défaut.

Nous allons commencer par définir quelques paramètres dans le fichier app.yml du dossier config:

all:
  language:
    homepage_route:   home_language
    allowed:
      fr: fr_CH
      de: de_CH
      en: en_US

Nous allons également définir la route par défaut inscrite dans le paramètre « homepage_route »:

home_language:
  url:  /:sf_language
  param:  { module: main, action: index }

Nous poursuivons par la mise en place du filtre. Ajoutons le fichier nommé « languageFilter.class.php » dans le dossier lib de votre application:

class languageFilter extends sfFilter
{
  public function execute($filterChain)
  {
    // Execute this filter only once
    if ($this->isFirstCall())
    {
      $is_correct_url = true;
      
      $context = $this->getContext();

      $language = $context->getRequest()->getParameter('sf_language');
      
      $language_allowed = sfConfig::get('app_language_allowed');

      // Si la langue n'est pas dans l'url
      if($language == NULL)
      {
        // Récupération de la langue préférée du navigateur
        $language = substr($context->getRequest()->getPreferredCulture(), 0, 2);
        
        // Nous contrôlons si cette langue est dans la liste des langues valident
        // Si ce n'est pas le cas, nous prenons la langue par défaut
        if(!array_key_exists($language, $language_allowed))
        {
          $language = substr(sfConfig::get('sf_default_culture'), 0, 2);
        }
        $is_correct_url = false;
      }
      
      // Si la langue n'est pas définie comme active
      // Nous redirigeons sur la page erreur 404
      if(!array_key_exists($language, $language_allowed))
      {
        $module = sfConfig::get('sf_error_404_module', 'default');
        $action = sfConfig::get('sf_error_404_action', 'error404');
        $context->getController()->forward($module, $action);
        exit;
      }
      else
      {
        // Nous récupérons la culture valide selon le code de langue
        $user_culture = $language_allowed[$language];

        // Si la culture est différente de la culture de session
        // Nous la changeons
        if($user_culture != $context->getUser()->getCulture())
        {
          $context->getUser()->setCulture($user_culture);
        }
        
        // Nous renseignons la valeur meta de la langue
        $context->getResponse()->addHttpMeta('content-language', $language);
        
        // Nous effectuons la redirection
        $actionname = $context->getActionName();
        if($is_correct_url == false && $actionname != 'error404')
        {
          $context->getController()->redirect(
                             sfConfig::get('app_language_homepage_route'));
        }
      }
    }
    
    // Execute next filter
    $filterChain->execute();
  }
}

Il nous suffit maintenant d’activer ce filtre en ajoutant ces lignes dans le fichier filters.yml du dossier config:

rendering: ~
security:  ~

# insert your own filters here
language:
  class:  languageFilter
  
cache:     ~
common:    ~
execution: ~

Dès à présent, si votre navigateur est en langue française, si vous tapez « http://monsite.com », vous allez être redirigé vers l’adresse « http://monsite.com/fr ». Dans le cas d’une langue non reconnue, vous obtiendrez une erreur 404.

Avec ce filtre, nous avons une solution robuste pour gérer correctement notre langue.

Bonne découverte.

Share

De la culture au langage

Symfony ne proposant pas de système pour la gestion du langage selon la culture utilisée, j’ai cherché une solution viable. Je m’explique. Dans mon cas, un utilisateur reçoit une culture « fr_CH » pour permettre un formatage correct sur les nombres. Par contre, dans les urls du site, je désire y mettre uniquement la première partie « fr ». Je vous propose dans ce post de vous livrer ma solution. J’ai pour cela utilisé le système d’Event, qui au passage est vraiment un truc super. Je vous conseille vraiment de découvrir cette petite merveille.

Pour débuter, j’ai rajouté ces quelques lignes dans mon fichier myUser.class.php se trouvant dans le dossier lib de l’application:

class myUser extends sfGuardSecurityUser
{
  public function initialize(sfEventDispatcher $dispatcher, sfStorage $storage, $options = array())
  {
    $dispatcher->connect('user.change_culture', array($this, 'listenToChangeCultureEvent'));
    parent::initialize($dispatcher, $storage, $options);
  }
  
  public function listenToChangeCultureEvent(sfEvent $event)
  {
    $result = explode('_', $this->culture);
    $this->setLanguage($result[0]);
  }
  
  public function setLanguage($language)
  {
    $this->language = $language;
  }
  
  public function getLanguage()
  {
    return $this->language;
  }
}

Comme vous pouvez le voir dans ce code, je me suis connecté à l’event « user.change_culture ». Avec cette fonction, je vais scinder les deux parties de la culture pour ne garder plus que le terme « fr ».

Je voulais ensuite avoir la possibilité d’accéder au paramètre « sf_language » dans mon fichier de routing. J’ai pour cela surchargé la méthode « listenToChangeCultureEvent » de la classe « sfPatternRouting ». J’ai créé un nouveau fichier nommé « mysfPatternRouting.class.php » dans mon dossier lib (toujours dans mon app) avec le code suivant:

class mysfPatternRouting extends sfPatternRouting
{
  public function listenToChangeCultureEvent(sfEvent $event)
  {
    $result = explode('_', $event['culture']);
    $this->setDefaultParameter('sf_language', $result[0]);
    parent::listenToChangeCultureEvent($event);
  }
}

Pour charger ma classe routing, j’ai également modifié le fichier factories.yml en activant cette partie.

all:
  ...
  routing:
    class: mysfPatternRouting
    param:
      load_configuration: true
      suffix:             .
      default_module:     default
      ...

J’ai remplacé le contenu du tag class par « mysfPatternRouting » (Valeur par défaut: sfPatternRouting).

Ensuite, il suffit de vider le cache de symfony pour qu’il charge la nouvelle classe

$ ./symfony cc

Dès maintenant, on aura accès au nouveau paramètre « sf_language » dans notre fichier de routing.yml. Voici un exemple

user_index:
 url:  /:sf_language/user
 param:  { module: user, action: index }
 requirements:
   - sf_language: (?:fr|en|de)

J’espère que cette solution vous sera utile dans vos prochains développements 😛

Share

Mettre de la couleur sur votre svn diff

Nous allons commencer par installer le package colordiff avec macport (Macintosh).

sudo port install colordiff

Ensuite, nous ajoutons un alias svnd dans le fichier /etc/profile

alias svnd='svn diff --diff-cmd /opt/local/bin/colordiff'

Nous allons recharger la configuration avec la commande suivante:

source /etc/profile

Vous avez également la possibilité de personnaliser la configuration de colordiff. Pour cela, nous allons copier colordiffrc dans notre home:

cp /opt/local/etc/colordiffrc ~/.colordiffrc

Nous pouvons dés maintenant éditer ce fichier et personnaliser les paramètres

Par défaut, nous avons les valeurs suivantes:

plain=off
newtext=blue
oldtext=red
diffstuff=magenta
cvsstuff=green

Nous allons les changer en:

plain=off
newtext=yellow
oldtext=red
diffstuff=magenta
cvsstuff=green

En tapant la commande suivante:

home$ svnd 19-Mastering-Symfony-s-Configuration-Files.txt

Vous devriez voir cela comme résultat final:

colordiff

Share

Passage de votre site en maintenance lors de vos mises à jour

Prérequis: L’activation du plugin sfDoctrineGuardPlugin

Dans le cadre d’un développement, je cherchais à passer à mon application en mode maintenance pour un utilisateur normal mais que le super admin puisse continuer à visualiser les pages du site. La tâche symfony n’est pas satisfaisante dans ce cas car elle verrouille complètement l’accès à celui-ci. Voici donc ma solution:

Premièrement, j’ai activé le paramètre check_lock dans le fichier settings.yml de mon application.

Path: /apps/[NomApp]/config/settings.yml

all:
  .settings:
    check_lock:             on

Pour stocker mon fichier de verrouillage, j’ai créé un dossier « lock » dans le dossier data

mkdir data/lock

Attention: ce dossier doit être en écriture

J’ai ensuite inséré le code suivant dans ProjectConfiguration

Path: /config/ProjectConfiguration.class.php

class ProjectConfiguration extends sfProjectConfiguration
{
  public function setup()
  {
    ...
  }
  
  public function getApplicationLockFile()
  {
    return sfConfig::get('sf_data_dir').DIRECTORY_SEPARATOR.
               'lock'.DIRECTORY_SEPARATOR.$this->getApplication().'.lck';
  }
}

Vient maintenant la partie contrôle de l’accès. Pour cela j’ai créé un filtre que voici

Path: /lib/isApplicationActivatedFilter.class.php

class isApplicationActivatedFilter extends sfFilter
{
  public function execute ($filterChain)
  {
    if ($this->isFirstCall())
    {
      if (sfConfig::get('sf_check_lock'))
      {
        $context = $this->getContext();
        $config = $context->getConfiguration();
        $user = $context->getUser();

        $fileLock = $config->getApplicationLockFile();
        if (file_exists($fileLock) && !$user->isSuperAdmin())
        {
          $files = array(
            sfConfig::get('sf_app_config_dir').'/unavailable.php',
            sfConfig::get('sf_config_dir').'/unavailable.php',
            sfConfig::get('sf_web_dir').'/errors/unavailable.php',
            sfConfig::get('sf_symfony_lib_dir').'/exception/data/unavailable.php',
          );
          
          foreach ($files as $file)
          {
            if (is_readable($file))
            {
              include $file;
              break;
            }
          }

          die(1);
        }
      }
    }
    $filterChain->execute();
  }
}

Il reste maintenant à charger ce filtre dans notre application

Path: /apps/[NomApp]/config/filters.yml

rendering: ~
security:  ~

# insert your own filters here
isApplicationActivated:
  class:  isApplicationActivatedFilter
  
cache:     ~
common:    ~
execution: ~

Il faut suffit maintenant de personnaliser votre page de maintenance en créant le fichier unavailable.php dans votre dossier config.

Dès à présent, si vous créer un fichier [NomApp].lck dans le dossier lock, votre application passera en mode maintenance.

Dans cette article, je ne détaille pas la création et la suppression de ce fichier lck. Je vous laisse le soin de le faire avec votre propre gestion.

Pour tester le bon fonctionnement de ce procédé, vous pouvez créer deux comptes dans votre base de données:

  • Compte admin avec le flag is_super_admin: true
  • Compte user sans le flag is_super_admin

Vous vous identifiez avec le compte admin et ensuite vous créer le fichier [NomApp].lck dans votre dossier lock et vous vous rendez compte que vous avez toujours accès au page de votre site. Si vous répétez la chose avec le compte user, vous verrez apparaître la page de maintenance.

J’espère que ce code vous servira dans votre prochain développement.

Bonne découverte.

Share