Tag Archives

2 Articles

Optimiser Zend – Partie 1

Cela fait un moment que je ne vous ai pas refait de tutoriaux pour le Framework Zend. Aujourd’hui je vous propose une adaptation d’un tutoriel en anglais que l’on m’avait fait parvenir et qui m’a grandement aidé (malheureusement, il n’est plus sur la toile).

Vous savez comme moi que l’un des points faibles de Zend Framework, c’est la performance, il est beaucoup plus lent que du PHP objet de base du fait de la majeur partie des classes qui sont chargées lors du lancement d’une page. Je vais donc réaliser ce tutoriel en plusieurs parties.

Aujourd’hui, je vais juste vous expliquer comment diminuer les chargements inutiles et redéfinir le fichier d’amorçage (le fameux index.php). Vous vous souvenez tous comment est composé le fichier index.php ? Et bien voici un petit rappel pour ceux qui ne savent pas :
/**
* Fichier index.php permettant de lancer le bootstrap
*
*/

//Définition du chemin vers le dossier application
defined('APPLICATION_PATH')
|| define('APPLICATION_PATH',
realpath(dirname(__FILE__) . '/../application'));

//Définition de la variable de d'application (utile pour les fichiers de configuration
defined('APPLICATION_ENV')
|| define('APPLICATION_ENV',
(getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV')
: 'production'));

//Partie permettant d'inclure les différentes librairies
//du dossier library (en théorie).
//Nécessite un complément côté bootstrap.
set_include_path(implode(PATH_SEPARATOR, array(
dirname(dirname(__FILE__)) . '/library',
get_include_path(),
)));

/** Zend_Application */
require_once 'Zend/Application.php';

// Créer l'application, le bootstrap et démarre
$application = new Zend_Application(
APPLICATION_ENV,
APPLICATION_PATH . '/configurations/app.ini'
);
$application->bootstrap()
->run();

Nous allons juste ajouter une petite partie permettant d’inclure plus rapidement les classes de Zend en repassant par Zend_Autoloader et nous redéfinirons sa méthode d’inclusion. Voici donc le nouveau index.php qui vous parlera mieux que mes explications :
// Define path to application directory
defined('APPLICATION_PATH')
|| define('APPLICATION_PATH',
realpath(dirname(__FILE__) . '/../application'));

// Define application environment
defined('APPLICATION_ENV')
|| define('APPLICATION_ENV',
(getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV')
: 'production'));

// Typically, you will also want to add your library/ directory
// to the include_path, particularly if it contains your ZF install
set_include_path(implode(PATH_SEPARATOR, array(
dirname(dirname(__FILE__)) . '/library',
get_include_path(),
)));

//redéfinition de l'autoloader par défaut afin d'optimiser le chargement
require_once 'Zend/Loader/Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->setDefaultAutoloader(create_function('$class',
"include str_replace('_', '/', \$class) . '.php';"
));

/** Zend_Application */
//require_once 'Zend/Application.php';

// Create application, bootstrap, and run
$application = new Zend_Application(
APPLICATION_ENV,
APPLICATION_PATH . '/configurations/app.ini'
);
$application->bootstrap()
->run();

Cette partie ne suffit pas, pour gagner encore du temps, je vous invite à suivre le conseil donné par l’équipe Zend en fin de documentation, à savoir, supprimer tous les require_once inutiles de la bibliothèque Zend. (je vous invite aussi à les enlever de vos controller, formulaires, …).

En effet, pour chaque require_once, le serveur va effectuer des accès disque et c’est ces accès disques qui ralentissent le serveur. La méthode est donc de les enlever et ensuite, notre redéfinition faite dans le index.php prendra le relais, elle effectuera tardivement le chargement de tous les fichiers nécessaires afin d’optimiser le chzrgement de nos classes. La documentation Zend explique mieux que moi cette partie:

Le chargement tardif (« lazy loading ») est une technique d’optimisation conçue pour repousser l’opération coûteuse de chargement d’une classe jusqu’au dernier moment possible – c’est-à-dire lors de l’instanciation d’un objet de cette classe, ou lors de l’utilisation d’une constante de classe ou d’une propriété statique. PHP supporte tout ceci via l’autoloading (ou « chargement automatique »), ce qui vous permet de définir un ou plusieurs callbacks à exécuter dans le but de faire correspondre un nom de classe à un fichier.

Cependant, la plupart des avantages que vous pourrez retirer de l’autoloading sont diminués si le code de votre librairie exécute toujours des appels à require_once – ce qui est précisément le cas de Zend Framework. La question est donc : comment éliminer ces déclarations require_once dans le but de maximiser les performances de l’autoloader.

Pour ce faire, il ne vous suffit que d’exécuter cette commande sous Linux (je rajouterai celle sous Windows et FreeBSD quand je les aurais retrouvé) :
% cd chemin/vers/la/librarie/ZendFramework
% find . -name '*.php' -not -wholename '*/Loader/Autoloader.php' \
-not -wholename '*/Application.php' -print0 | \
xargs -0 sed --regexp-extended --in-place 's/(require_once)/\/\/ \1/g'

Pour finir, nous définissons une nouvelle fonction dans notre Bootstrap principal afin que lui aussi tire parti de ces changements :
public static function autoload($class)
{
include str_replace('_', '/', $class) . '.php';
return $class;
}

Voilà, après ces quelques modifications, votre site devrait se charger un poil plus vite et consommer moins de mémoires.

Dans la seconde partie, je vous expliquerai comment créer une méthode qui mesure la charge mémoire de chacune de vos pages. Si vous avez des questions, des remarques, des critiques ou même des corrections, n’hésitez pas à me les faire parvenir.

Headers – Compression CSS

Bonjours, je commence une série d’articles pour vous montrer la solution mise en place pour compresser les en-têtes de votre site.

Attention, pour le moment la solution n’est pas encore 100% fonctionnelle, à l’heure où j’écris ces lignes, sur le site je fonctionne encore avec le Zend_Cache utilisant les fichiers. En local, je travail avec Zend_Cache utilisant le cache APC. Certains problèmes me sont apparus avec les fichiers et ils ont disparus avec APC.

Donc tout d’abord je vais vous présenter le filtre que j’utilise pour compresser le contenu de mes fichiers CSS, ensuite je vous présente la méthode utilisée pour récupérer dans mon layout tous les appels à mes styles et pour finir comment je fais croire qu’il y a qu’un seul fichier CSS.

Attention: dans les exemples que je vais vous montrer, je vais faire appel à la classe App_Cache. Cette classe est donnée dans le livre Zend Framework – Bien développer en php.

Tout d’abord, pour compresser le contenu des fichiers CSS j’utilise le filtre présent ci-dessous. Il permet de supprimer toutes les tabulations, espaces en trop, commentaires présents dans le fichier.

/**
* Filtre servant à compresser des données CSS lors de la mise en cache par exemple.
*
* Supprime les espaces en trop, les commentaires et met tout sur une seule ligne.
*
* @author Throrin
*
*/
class App_Filter_CssCompressor implements Zend_Filter_Interface
{
public function filter($value)
{
$value = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $value);
$value = str_replace(array("\r\n", "\r", "\n", "\t", ' '), '', $value);
$value = str_replace('{ ', '{', $value);
$value = str_replace(' }', '}', $value);
$value = str_replace('; ', ';', $value);
$value = str_replace(', ', ',', $value);
$value = str_replace(' {', '{', $value);
$value = str_replace('} ', '}', $value);
$value = str_replace(': ', ':', $value);
$value = str_replace(' ,', ',', $value);
$value = str_replace(' ;', ';', $value);

return $value;
}
}

Ensuite, nous devons commencer par rajouter certaines lignes dans la partie contenant tous nos appels au CSS. Pour ce faire j’utilise la fonction ob_start() de PHP permettant de stocker ce qui suit dans un buffer et je le récupère avec la fonction ob_get_contents(). Pour finir à la suite de sa je ferais appel aux actions qui serviront à stocker le tout dans le cache grâce à la fonction $this->action().

<

headScript()."\n"; ?>


jQuery()->addStylesheet($this->baseUrl().'/styles/jquery-ui-1.7.2.custom.css')
->setUiLocalPath($this->baseUrl().'/scripts/jquery-ui-1.7.2.custom.min.js')
->setLocalPath($this->baseUrl().'/scripts/jquery-1.3.2.min.js')."\n"; ?>

action('compresscss', 'header', 'compress', array('contenu' => $output));
ob_end_clean();
?>

Voilà pour ici, maintenant on s’attaque à notre contrôleur qui va parcourir le contenu du buffer pour récupérer le lien vers chaque CSS. Ensuite il va lire le contenu des CSS, le compresser grâce à notre filtre et envoyer son contenu en cache. Pour éviter de répéter cette action assez lourde à chaque appel de l’action, on testera si le cache a bien été créé.

class Compress_HeaderController extends Zend_Controller_Action
{
public function compresscssAction()
{
$css = App_Cache::get('csssite');

if(!$css) {
$cssoffset = $this->_request->getParam('contenu');

//on récupère tous les fichiers CSS
preg_match_all('/link rel="stylesheet" href="(.*?)"/', $cssoffset, $matches);
$i = 0;
$n = count($matches[1]);
$contenucss = '';
//on parcour le tableau des fichiers CSS
while($i < $n) { $lienscss = str_replace('//', '/', $_SERVER['DOCUMENT_ROOT']."/zendtuto18/public"); //lien absolu vers votre dossier public $liens = str_replace('/test18', '', $matches[1][$i]); $handle = fopen($lienscss.$liens,'r'); $contenucss .= fread($handle, filesize($lienscss.$liens)); $i++; } $filtrecss = new App_Filter_CssCompressor(); $contenucss = $filtrecss->filter($contenucss);

App_Cache::set($contenucss, 'csssite');
}
}
}

Voilà pour le contrôleur. Le problème c’est qu’il faut bien dire quel CSS on affiche. C’est pourquoi dans la vue de cette action vous allez mettre juste cette ligne:


Maintenant il y a une question qui vous brûle les levres et qui doit être: « Qu’est-ce que ce fichier style.css? ». Ce fichier est en fait un lien vers notre action d’affichage. Pour ce faire nous devons créer la règle de réécriture présente ci-dessous (htaccess et zend_route).

.htaccess :

RewriteRule ^(.*)styles/style.css$ /compress/header/css [L]

bootstrap.php dans l’action _initRoute() :

$router->addRoute(
'manageCSS',
new Zend_Controller_Router_Route('styles/style.css', array('module' => 'compress', 'controller'=>'header','action'=>'css'))
);

Voilà, cette règle a été nécessaire pour pas casser tous les liens relatifs présents dans les fichiers CSS. Ensuite il ne vous reste plus qu’à créer cette action dans le contrôleur header afin d’afficher le style:

public function cssAction()
{
$this->_helper->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
$css = App_Cache::get('css');

$this->getResponse()->setHeader("Content-type","text/css; charset: UTF-8");
$this->getResponse()->setHeader("Cache-Control","must-revalidate");
$offset = 60 * 60 ;
$ExpStr = gmdate("D, d M Y H:i:s",time() + $offset) . " GMT";
$this->getResponse()->setHeader("Expires: ", $ExpStr);
echo $css;
}

Après ceci tout devrai fonctionner comme sur des roulettes. Pour la partie JS tout est à peu près de ce genre. Seules quelques étapes supplémentaires sont nécessaires pour tout récupérer. J’espère que cet article vous aura permi de trouver une méthode pour compresser vos styles. Par contre évitez de l’utiliser pour un site necessitant un fichier CSS différent par pages. Effectivement en cache vous aurez que le contenu des css inclus dans la première page.