Il existe différentes méthodes et outils permettant de backuper ses bases de données MySQL. Cela va d’une sauvegarde “offline” par copie des fichiers du dossier data de mysql à une sauvegarde “online” avec mysqlhotcopy ou mysqldump , en passant par l’utilisation d’un serveur esclave MySQL réplicant votre serveur maître.
Ne possédant qu’un seul serveur, il est difficile de faire de la réplication. C’est ainsi que j’utilise les outils mysqlhotcopy et mysqldump pour réaliser les sauvegardes de mon serveur SQL.
Pour une sauvegarde du jour J, j’utilise toujours deux backups :
mysqldump permet de sauvegarder des dumps SQL soit d’une base entière, soit de tables précises dans cette base, soit d’enregistrements précis (option –where), soit carrément sauvegarder toutes les bases (option –all-databases). C’est d’ailleurs cette dernière option que j’utilisais jusqu’à présent, avant d’être confronté au problème suivant :
Je dispose d’un fichier de dump complet de mes bases de données, or ce fichier fait plusieurs centaines de mégaoctets. Je peux aisément restaurer tout le serveur. Mais si j’ai besoin de restaurer une table particulière, comment procéder ?
Etant donné la taille du fichier, toute édition classique afin de copier coller la partie intéressante n’est pas possible. Peut-être que la solution se trouve dans une utilisation astucieuse de cat, grep, awk etc … mais je n’ai pas cherché de ce coté là. Je n’ai pas trouvé non plus du coté de la suite MySQL Workbench qui, bien qu’elle fournisse des outils très intéressants dans la modélisation SQL, celle-ci n’offre pas d’analyseur au niveaux des données.
Exemple : Il est possible d’importer un gros dump SQL dans MySQL Workbench qui permet de reconsituter par rétro-ingénierie la modélisation structurelle des bases de données et des tables contenues dans le script. On peut ensuite exporter les différents objets reconstitués (base ou table par exemple), mais l’export ne concerne que la structure de l’objet, et non ses données (on exporte ainsi le script permettant de créer la table ou la base, mais pas les enregistrements originaux de celles-ci)
Voici la solution que j’ai donc implémentée :
Cette solution a plusieurs avantages :
#!/bin/bash
#Dump table par table MySQL - phpnews.fr
#Déclaration de l'utilisateur MySQL aux droits suffisants pour parcourir vos bases de données
#Droit requis : SELECT global, LOCK TABLES
user='backuper'
pass='p@ssw0rd'
#On définit un nom temporaire utilisé par le script (ici la date jusqu'à la minute)
dirname="dump_`date +%d`.`date +%m`.`date +%y`@`date +%H`h`date +%M`"
#On crée sur le disque un répertoire temporaire (changer le chemin précédant /$dirname)
mkdir "/$dirname"
#On place dans un tableau le nom de toutes les bases de données du serveur
#On peut choisir ici d'exclure certaines bases de données de la sauvegarde grâce à la clause LIKE
#Ex : -e "show databases LIKE 'blog_%'"
databases=( $(mysql -u $user -p"$pass" -e "show databases" | grep -v Database) )
#Pour chacune des bases de données trouvées ...
for database in ${databases[@]}
do
#... on crée dans le dossier temporaire un dossier portant le nom de la base
mkdir "/${dirname}/${database}"
#... on récupère chacune des tables de cette base de données dans un tableau ...
tables=( $(mysql $database -u $user -p$pass -e 'show tables' | grep -v Tables_in) )
#... et on parcourt chacune de ces tables ...
for table in ${tables[@]}
do
#... que l'on dump avec mysqldump dans un fichier portant le nom de la table dans le dossier de la bdd parcourue
$(mysqldump -u $user -p$pass --quick --add-locks --lock-tables --extended-insert $database $table > /${dirname}/${database}/${table}.sql)
done
done
#On bzip le tout
tar -cjf /home/server/backup/sql/dump_tables/tables_${dirname}.tar.bz2 /$dirname/
#On supprime le répertoire temporaire
rm -rf /$dirname/
Et voilà le travail
Note : Ce script pose un problème de respect de la consistance de la base de donnée car les différentes tables ne sont pas sauvegardées au même moment. Je vous invite à lire les commentaires de ce billet et d’utiliser ce script avec discernement.
Sun microsystems vient d'annoncer la disponibilité de son pack AMP pour systèmes d'exploitation Solaris et Linux.
Ce pack est constitué du serveur HTTP Apache, du système de gestion de base de données MySQL et du langage PHP (et PERL). Le pack est disponible en téléchargement sur le site de Sun.

Edit : il suffit que j'écrive un billet sur le sujet pour que la RC2 sorte avec enfin la correction des problèmes d'URL Alias. La version finale est aussi annoncée d'ici une à deux semaines sur la mailing liste si tout va bien.
eZ Publish 4.0 est sorti en décembre 2007 et depuis rien, aucune version stable. 8 mois c'est vraiment très très très long, il y a donc forcément des bugs gênants dans eZ Publish 4.0 d'autant plus que cette version est un portage vers PHP5 de la version 3.10 ce qui occasionne encore quelques bugs supplémentaires. La roadmap du bugtracker liste les problèmes résolus et non résolus mais il n'est pas toujours simple de faire le lien entre un comportement suspect et un bug dans cette liste.
Par exemple en développant la nouvelle version de ce site, je me suis trouvé face à plusieurs problèmes qui ont nécessité l'inclusion de patchs issus du SVN.
Il s'agit du bug #12175 qui empêche l'utilisation des cache-block expirant avec une sous-arborescence. Pour règler ce problème sans passer à la version 4.0.1rc1 il faut appliquer 3 patchs successifs sur l'arborescence d'eZ Publish 4.
$ cd /tmp $ wget http://pwet.fr/content/download/71716/214901/file/patch_cache_block_12175.tar.gz $ tar -zxvf patch_cache_block_12175.tar.gz $ cd /path/to/ez/publish/ $ patch -p0 < /tmp/01_cache-block.patch $ patch -p0 < /tmp/02_cache-block.patch $ patch -p0 < /tmp/03_cache-block.patch
Les erreurs sur l'application du patch sur le change log peuvent être tranquillement ignorées.
Celui la, c'est un bug vicieux, on s'en rend compte lors de la mise en production quand la machine est à plat par tous les convert (ou apache si on utilise GD) en train de générer les différentes variations encore et encore. 80 de load average sur ma pauvre Dedibox, elle a du avoir chaud ;-)
Là un seul patch est nécessaire et c'est immédiat et magique sur la charge de la machine
$ cd /tmp $ wget http://pwet.fr/content/download/71717/214904/file/image_variations.patch $ cd /path/to/ez/publish $ patch -p0 < /tmp/image_variations.patch
J'ai pas eu d'autres bugs bloquants (enfin je les ai pas encore remarqué :)), j'en ai d'ailleurs découvert un sur le SmartCacheClear avec les keywords, la correction est dans le rapport et tient sur une unique ligne.
Ce texte est une des traductions d'une série d'articles de Lorenzo Alberton
(original)
La plupart des classes de pagination PHP peuvent fonctionner très bien avec des paramètres GET, correctement envoyés par les pages.
Cependant, rares sont celles qui vous laissent le contrôle sur les liens qu'elles créent.
Ceci peut être particulièrement ennuyant quand vous travaillez avec des cool URL (grâce à mod_rewrite ordonne ou fait à la main par votre contrôleur) et la classe de de pagination ne peut pas les respecter, en affichant les liens réels et laids.
Si le scénario ci-dessus n'est pas nouveau pour vous, alors vous devriez probablement tester PEAR::Pager.
C'est un paquet entièrement personnalisable qui devrait satisfaire tous vos besoins, y compris votre format préféré de lien.
Supposons-vous ont un site de commerce électronique, avec un catalogue de produit que l'utilisateur peut passer en revue par catégorie.
Par exemple, pour énumérer tous les produits de la catégorie AAA, vous passez ce paramètre à votre page : http://monserveur.com/produits.php?cat=AAA.
Si le catalogue est assez grand, la dispersion des produits dans plusieurs pages serait une décision intéressante,
mais maintenant vous devez également passer le numéro de page à l'URL : http://monserveur.com/produits.php?cat=AAA&pageID=3.
Naturellement votre application intelligente peut produire des URL plus propres, comme ceci : /produits/AAA/3.html qui sont facilement décodés par le mod_rewrite.
Voyons l'exemple de règle .htaccess pour cette transformation d'URL :
[apache] RewriteEngine on RewriteBase / RewriteRule ^produits/(\w+)/(\d+)\.html$ /produits.php?cat=$1&pageID=$2 [L]
Normalement, la classe de pagination les paramètres de cat et de pageID et établirait les liens comme d'habitude, c.-à-d.
/produits.php?cat=AAA&pageID=1 /produits.php?cat=AAA&pageID=2 ... /produits.php?cat=AAA&pageID=6
mais vous pouvez apprendre à PEAR::Pager à produire les liens qui respectent votre convention :
[php]
<?php
require_once 'Pager/Pager.php';
//toujours valider paramètres GET
if (!empty($_GET['cat']) && $myApp->is_valid_cat($_GET['cat'])) {
$cat = $_GET['cat'];
} else {
$cat = 'AAA'; //default category
}
// Récupération des produits.
// S'il y a beaucoup de produits,
// vous pouvez envisager d'employer les fonctions de Pager_Wrapper
$produitList = $myApp->getProductsByCat($cat);
$pager_params = array(
'mode' => 'Sliding',
'append' => false, //ne pas ajouter les paramètres GET à l'URL
'path' => 'http://monserveur.com/produits/' . $cat,
'fileName' => '%d.html', //Pager remplace "%d" par le numéro de la page
'perPage' => 10, //afficher 10 articles par page
'itemData' => $produitList,
);
$pager = & Pager::factory($pager_params);
$data = $pager->getPageData();
//Afficher les produits de la page courante
echo 'Données de la page courante: ';
print_r($data);
//afficher les liens pour la navigation dans la catégorie courante
echo $pager->links;
?>
Comme vous pouvez voir, vous pouvez dire au pager le chemin et le nom de fichier qu'il devrait employer.
N'oubliez pas de placer l'option de append à FALSE, sinon le pager essayera d'apposer les vars GET à l'URL comme d'habitude.
Et si le numéro de la page dans votre url se trouve dans le path et non dans nom fichier?
Ca pourrait ressembler à un problème, puisque Pager recherche la chaîne « %d » dans le paramètre nom de fichier, et se plaint s'il ne peut pas y trouver cette chaîne.
Non, vous ne pouvez pas le mettre dans le paramètre path, mais puisque le pager combinera simplement les paramètres path et nom de fichier pour former l'URL, nous pouvons le duper et mettre une partie du path dans le nom de fichier lui-même.
Pour le moment, si nous avons une url comme
http://monserveur.com/produits/AAA/3/index.html,
où "3" est le numéro de la page,
cette règle de mod_rewrite et ce script php le feront fonctionner:
[apache] RewriteEngine on RewriteBase / RewriteRule ^produits/(\w+)/(\d+)/index\.html$ /produits.php?cat=$1&pageID=$2 [L]
[php]
<?php
require_once 'Pager/Pager.php';
// ...
$pager_params = array(
'mode' => 'Sliding',
'append' => false, //ne pas ajouter les paramètres GET à l'URL
'path' => 'http://monserveur.com/produits/' . $cat,
'fileName' => '%d/index.html', //Pager remplaces "%d" par le numéro de la page
'perPage' => 10, //afficher 10 articles par page
'itemData' => $produitList,
);
$pager = & Pager::factory($pager_params);
$data = $pager->getPageData();
//afficher les produits de la page courante
echo 'Données de la page courante: ';
print_r($data);
//afficher les liens pour la navigation dans la catégorie courante
echo $pager->links;
?>
Voilà c'est fait. J'espère que ce tuto était utile.
Ce texte est une des traductions d'une série d'articles de Lorenzo Alberton
Vous avez probablement vu beaucoup de sites Web comporter des articles longs et détaillés, qui sont coupés en paragraphes, chacun présenté dans une page séparée.
Les utilisateurs préfèrent souvent lire les morceaux courts du texte au lieu de faire défiler une très(trop) longue page (à moins qu'ils ne veulent l'imprimer).
Dans ce tuto, nous allons voir comment nous pouvons établir un système de paginations d'article, a l'aide de PEAR::Pager.
Nous aurons besoin de deux tables pour stocker nos articles :
Techniquement, nous pourrions employer juste une table, utilisant quelques délimiteurs spéciaux incorporés dans le texte (tel que le « ====FIN DE PAGE====") pour découper les paragraphes, mais croyez moi qu'à la longue cette une solution bien meilleure
[SQL] CREATE TABLE articles ( id INTEGER NOT NULL, title VARCHAR(250) NOT NULL, abstract TEXT, submission_date TIMESTAMP NOT NULL, author_id INTEGER NOT NULL, CONSTRAINT articles_pkey PRIMARY KEY(id) ); CREATE TABLE paragraphs ( article_id INTEGER NOT NULL, paragraph_id INTEGER NOT NULL, title VARCHAR(250), content TEXT, CONSTRAINT paragraphs_pkey PRIMARY KEY(article_id, paragraph_id), CONSTRAINT paragraphs_fk FOREIGN KEY (article_id) REFERENCES articles(id) ON DELETE CASCADE );
Voici quelques données d'exemple si vous voulez tester tout en lisant ce tuto :
[SQL] -- Premier Article INSERT INTO articles (id, title, abstract, submission_date, author_id) VALUES ( 1, 'How to navigate through the paragraphs of an article with Pager', 'You\'ve probably seen a lot of websites featuring long, detailed articles, which are split into paragraphs, each of them in a separate page. Users often prefer reading short chunks of text instead of scrolling a very long page (unless they want to print the page, then the opposite applies). In this tutorial, we\'re going to see how we can build an article pagination system, with a little help from PEAR::Pager.', CURRENT_TIMESTAMP, 1 ); INSERT INTO paragraphs (article_id, paragraph_id, title, content) VALUES (1, 1, 'The database structure', 'First paragraph here'); INSERT INTO paragraphs (article_id, paragraph_id, title, content) VALUES (1, 2, 'Sample data', 'Second paragraph here'); INSERT INTO paragraphs (article_id, paragraph_id, title, content) VALUES (1, 3, 'Showtime', 'Third paragraph here'); INSERT INTO paragraphs (article_id, paragraph_id, title, content) VALUES (1, 4, 'Alternate navigation', 'Fourth paragraph here'); INSERT INTO paragraphs (article_id, paragraph_id, title, content) VALUES (1, 5, 'Article summary', 'Fifth paragraph here'); INSERT INTO paragraphs (article_id, paragraph_id, title, content) VALUES (1, 6, 'Printer friendly version', 'Sixth paragraph here'); -- Second Article INSERT INTO articles (id, title, abstract, submission_date, author_id) VALUES ( 2, 'PEAR::Pager tutorials', 'New series of tutorials about PEAR::Pager', CURRENT_TIMESTAMP, 1 ); INSERT INTO paragraphs (article_id, paragraph_id, title, content) VALUES (2, 1, 'Articles index', '1. How to efficiently paginate database results. 2. Create pretty links with Pager and mod_rewrite. 3. Navigation with Pager and AJAX (or simple Javascript). 4. Article pagination.');
Maintenant que nous sommes ok avec la structure de base de données, nous pouvons afficher les articles sur notre site, un paragraphe par la page.
Pour cette tâche, nous allons employer PEAR::MDB2 DBAL et le bien pratique Pager_Wrapper que nous avons déjà vu dans un tuto précédent :
[php]
<?php
//Copier le fichier Pager_Wrapper file là où il peut-être inclu
require_once 'Pager_Wrapper.php';
require_once 'MDB2.php';
$article_id = 1; //if you fetch this parameter via GET/POST, remember to validate it!
//on passe le code de connexion DB
//on suppose qu'on a une connexion valide dans $db
$pager_options = array(
'mode' => 'Sliding',
'perPage' => 1, //On ne veut qu'un paragraphe par page
'delta' => 3,
);
$query = 'SELECT articles.title AS article_title,
articles.submission_date,
articles.abstract,
paragraphs.title AS paragraph_title,
paragraphs.content
FROM paragraphs
LEFT JOIN articles ON articles.id = paragraphs.article_id
WHERE articles.id = '. (int)$article_id .'
ORDER BY paragraphs.paragraph_id;
$paged_data = Pager_Wrapper_MDB2($db, $query, $pager_options);
//Afficher les résultats
echo '<h1>'.$paged_data['data'][0]['article_title'].'</h1>';
echo '<p><i>'.$paged_data['data'][0]['submission_date'].'</i></p>';
if ($paged_data['page_numbers']['current'] == 1) {
// afficher aussi le résumé.
echo '<p>'.$paged_data['data'][0]['abstract'].'</p>';
}
echo '<h2>'.$paged_data['data'][0]['paragraph_title'].'</h2>';
echo '<p>'.$paged_data['data'][0]['content'].'</p>';
//afficher les liens
echo $paged_data['links'];
?>
Puisque nous avons demandé au paginateur de couper les articles (les paragraphes, dans notre cas) en groupes de "un", il renverra seulement un paragraphe, et nous pouvons naviguer vers les autres paragraphes avec les liens générés par paginateur.
Voici le rendu du script :

Si vous n'aimez pas des liens normaux pour la navigation, et préférez un menu de <select> à la place, vous devez ajouter un appel à la la fonction getPageSelectBox () dans Pager_Wrapper_MDB2, avant de renvoyer le tableau de données paginée, puisqu'elle n'est pas incluse dans la version par défaut :
[php]
function Pager_Wrapper_MDB2(&$db, $query, $pager_options = array(), $disabled = false, $fetchMode = MDB2_FETCHMODE_ASSOC)
{
// Pager_Wrapper_MDB2() body omitted. Add the following lines before the return call:
// ===== Début du CODE ajouté ======
$selectbox_options = array(
'optionText' => 'page %d',
'autoSubmit' => true,
);
$page['select_menu'] = $pager->getPageSelectBox($selectbox_options);
// ===== Fin du CODE ajouté =====
return $page;
}
Maintenant on affiche le menu de navigation :
[php] <?php // [snip: même code que dans le paragraphe précédent] //Affichage du menu echo 'Choisissez une page: '. $paged_data['select_menu']; ?>
Et voilà ce que ca devrait donner:

Parfois, vous pouvez vouloir montrer un sommaire de l'article, avec une liste des titres de paragraphe, chacun indiquant le paragraphe complet.
Aucun problème, vous pouvez faire cela.
[php]
<?php
$query = 'SELECT title
FROM paragraphs
WHERE article_id = '. (int)$article_id
.' ORDER BY paragraph_id';
$paragraph_titles = $db->queryCol($query);
//on oublide le code de vérification
echo '<h2>Sommaire</h2>';
echo '<ul>';
foreach ($paragraph_titles as $k => $title) {
if ($k == ($paged_data['page_numbers']['current'] -1)) {
// Page courrante, ne pas afficher le lien
echo '<li>' . $title . '</li>';
} else {
echo '<li><a href="article.php?id='. (int)$article_id .'&pageID='. ($k+1) .'">'. $title . '</a></li>';
}
}
echo '</ul>';
?>
Le résultat est un résumé des titres de paragraphe, avec leurs liens

Si vous voulez proposer une version pour l'impression avec l'article complet, vous pouvez chercher tous les paragraphes et simplement les afficher l'un après l'autre :
[php]
<?php
$query = 'SELECT title,
submission_date,
abstract
FROM articles
WHERE id = '. (int)$article_id;
$article = $db->queryRow($query, null, MDB2_FETCHMODE_ASSOC);
$query = 'SELECT title,
content
FROM paragraphs
WHERE article_id = '. (int)$article_id
.' ORDER BY paragraph_id';
$paragraphs = $db->queryAll($query, null, MDB2_FETCHMODE_ASSOC);
//Afficher les données principales de l'article:
echo '<h1>'.$article['title'].'</h1>';
echo '<p><i>'.$article['submission_date'].'</i></p>';
echo '<p>'.$article['abstract'].'</p>';
// Afficher les paragraphes de l'article:
foreach ($paragraphs as $paragraph) {
echo '<h2>'.$paragraph['title'].'</h2>';
echo '<p>'.$paragraph['content'].'</p>';
}
?>
J'espère que ce tuto était utile (la traduc aussi :).
Si vous avez besoin de quelques clarifications, ou avez quelques suggestions , envoyez-nous un mail :
J'ai envie de m'intéresser à la Standard PHP Librairy. J'ai cherché quelques tutos et références.
Tuto& articles
références
Ce texte est une des traductions d'une série d'articles de Lorenzo Alberton
OK, vous ne pouvez pas résister à la tendance du Web 2.0 vous avez appris que tous que vous pourriez se renseigner sur cette « nouvelle » technologie appelée AJAX, et maintenant vous vous demandez comment vous pourriez vivre sans lui.
Tout le monde n'a pas sauté dans le mouvement, bien que, et beaucoup de bibliothèques existent toujours sans mettre en application ce dispositif, ainsi vous est confronté au dilemme :
Dois-je je continuer à employer que vieille bibliothèque et abandonner mes idées fraîches d'AJAX, ou devrais je mettre en application ma propre version ?
Si vous recherchez une classe de pagination avec ces conditions, je suis heureux de vous rassurer au sujet de PEAR::Pager : il est AJAX-ready, et a été depuis il y a longtemps.
garanti 100% buzzword-compliance !
D'abord, regardons un exemple simple sur la façon d'armer le paginateur pour créer des liens de Javascript.
Dans cet exemple plutôt simpliste, nous récupérons toutes les données dans les morceaux paginés, stockons chaque page dans un <div> et employons quelques scripting DOM pour cacher toutes les couches sauf la page courante.
[php]
<?php
require_once 'Pager/Pager.php';
$data = range(1, 100); //un tableau de données à paginer
$pager_params = array(
'mode' => 'Sliding',
'append' => false, //ne pas ajouter les paramètres GET
'path' => '',
'fileName' => 'javascript:revealDiv(%d)', //Pager replaces "%d" with the page number...
'perPage' => 10, //afficher 10 item par page
'delta' => 5,
'itemData' => $data,
);
$pager = & Pager::factory($pager_params);
$n_pages = $pager->numPages();
$links = $pager->getLinks();
?>
<html>
<head>
<script type="text/javascript">
var n_pages = <?php echo $n_pages ?>;
function revealDiv(n)
{
for (var count = 1; count <= n_pages; count++) {
document.getElementById("page"+count).style.display = 'none';
}
document.getElementById("page"+n).style.display = 'block';
}
</script>
<style type="text/css">
div.page {
background: #FFFF99;
border-top: 1px solid #FFBF99;
border-bottom: 1px solid #FFBF99;
}
</style>
</head>
<body>
<h1>PEAR::Pager exemple with JavaScript</h1>
<?php echo $links['pages']; ?>
<hr />
<?php
for ($i=1; $i <= $n_pages; ++$i) {
echo '<div class="page" id="page'.$i.'">';
echo '<h2>Page '.$i.'</h2>';
foreach ($pager->getPageData($i) as $item) {
echo 'Item '.$item.'<br />';
}
echo '</div>';
}
?>
<hr />
<script type="text/javascript">
revealDiv(1);
</script>
</body>
</html>
L'équipe de développement de PHP vient d'annoncer la sortie de PHP 4.4.9. Cette version a pour but d'améliorer la stabilité de la branche 4.4.x.
Parmi les correctifs on notera :
Une nouvelle version de MySQL vient de voir le jour. Cette nouvelle version apporte quelques modifications parmi lesquelles :
Lire un livre sur comment optimiser son site web c'est bien, appliquer les conseils qui s'y trouvent c'est encore mieux. Parmi les 14 bonnes pratiques, 3 peuvent être appliquées très rapidement au niveau système en quelques lignes de commande et de configuration du serveur web pour un résultat quasi immédiat :
Dans un premier temps, je vais m'intéresser à la règle 3, je suppose que vous avez déjà un serveur web Apache2 actif servant des fichiers (peu importe la technologie autour). La configuration suivante est utilisée sur ma Dedibox sous Ubuntu avec Apache2 mais doit pouvoir s'appliquer à peu près partout.
L'en-tête Expires indique quand un élément devra expirer du cache du navigateur; mettre une date d'expiration dans un futur lointain permet de maximiser l'utilisation du cache navigateur et donc d'éviter les téléchargements inutiles, ce qui est particulièrement utile pour les éléments statiques (images, feuilles de style, ...) qui ont tendances à changer ... peu fréquemment mais à ralentir l'affichage de la page si ils ne sont pas en cache. Pour ces éléments, il est possible de configurer l'expiration dans Apache avec le module expires. Pour les pages dynamiques ou éléments générés dynamiquement, c'est au script d'envoyer l'en-tête et sa valeur adéquate par exemple avec la fonction header() en PHP.
L'activation du module pour Apache2, il faut utiliser a2enmod avec la ligne suivante et ensuite recharger apache :
$ sudo a2enmod expires $ sudo /etc/init.d/apache2 reload
Il reste alors à configurer ce module. Je stocke la configuration de ce module dans le fichier /etc/apache2/conf.d/expires dont voici le détail :
ExpiresActive On ExpiresByType image/gif "access plus 30 days" ExpiresByType image/jpg "access plus 30 days" ExpiresByType image/jpeg "access plus 30 days" ExpiresByType image/png "access plus 30 days" ExpiresByType image/x-icon "access plus 30 days" ExpiresByType text/css "access plus 30 days" ExpiresByType application/x-javascript "access plus 30 days"
Tous les éléments statiques des types listés expirent 30 jours après leur premier téléchargement. Après un nouveau reload d'Apache, vous devriez voir apparaître l'en-tête Expires par exemple avec l'extension Firebug de Firefox au premier chargement des éléments de la page. Ensuite le navigateur utilisera son cache ce qui devrait accélérer l'affichage des pages suivantes utilisant les mêmes éléments.
Dotclear 2.0 est dès à présent disponible. Cette version apporte quelques nouveautés par rapport à la version 2.0 RC2.1 tel que :
![]()
Ce texte est une des traductions d'une série d'articles de Lorenzo Alberton
Suite de PEAR::Pager tuto : Navigation avec pagination et Javascript simple
Comme vous avez pu le voir, the trick was setting the path parameter to an empty string and the fileName parameter to a javascript link, avec l'habituel marqueur "%s" pour le pageID.
Ok, maintenant que vous avez vu les bases, vous devriez avoir tous les éléments à aller plus loin.
Mais si vous êtes paresseux et voulez le voir quand même, voici un exemple sur la façon de faire la même chose que ce que nous avons vu avant, cette fois utilisant des appels AJAX chercher seulement les données utiles pour la page montrée.
Dans cet exemple, j'utilise la librairie PEAR::HTML_AJAX (docs):
Si vous ne pouvez pas attendre voyez cette démo, c'est l'exemple en fonctionnement, regardons comment il est fait :
nous incluons les fichiers js dynamiques (server.php) pour traiter les demandes AJAX,
et appellons HTML_AJAX.replace ("target","testdata.php"),
ce qui remplacera le contenu du DIV cible par le rendu du script testdata.php en utilisant un appel d'AJAX.
[php]
<html>
<body>
<h1>PEAR::Pager exemple avec AJAX</h1>
<script type="text/javascript" src="server.php?client=all"></script>
<div id="target">Je suis la cible</div>
<script type="text/javascript">
HTML_AJAX.replace('target', 'testdata.php');
</script>
</body>
</html>
c'est un simple script php qui récupère les données que vous désirez afficher (Dais cet exemple, 100 entiers) et les renvoie au Paginateur.
L'output de ce script remplacera le contenu du DIV cible dans le premier fichier html.
Nous affichons également la date et l'heure courante pour nous prouver que les données sont « fraîches » et construites à chaque appel (c.-à-d. chaque fois que vous cliquez sur un lien de navigation).
[php]
<?php
require_once 'Pager.php';
$data = range(1, 100); //un tableau de données à paginer
$pager_params = array(
'mode' => 'Sliding',
'append' => false, //ne pas ajouter les paramètres GET
'path' => '',
'fileName' => 'javascript:HTML_AJAX.replace(\'target\',\'testdata.php?pageID=%d\');', //Pager replaces "%d" with the page number...
'perPage' => 10, //afficher 10 item par page
'delta' => 1,
'itemData' => $data,
);
$pager = & Pager::factory($pager_params);
$n_pages = $pager->numPages();
$links = $pager->getLinks();
echo '<p>Ce containeur est rempli avec un appel AJAX</p>';
echo '<p><span class="datetime">DateTime: '. date('Y-m-d H:i:s') .'</span></p>';
echo '<h3>Page '. $pager->getCurrentPageId() .'</h3>';
foreach ($pager->getPageData() as $item) {
echo 'Item '. $item .'<br />';
}
echo '<hr />'.$pager->links;
?>
nous créons une instance de HTML_AJAX_Server pour livrer les les deux les bibliothèques de Javascript et pour traiter les demandes d'AJAX des navigateurs.
[php] <?php include 'HTML/AJAX/Server.php'; $server = new HTML_AJAX_Server(); $server->handleRequest(); ?>
Drupal 6.4 et Drupal 5.10 sont disponible.
Ces deux versions n'apportent aucune nouvelle fonctionnalité mais corrigent plusieurs bogues de sécurité présent dans les branches 5.x et 6.x.
La mise à jour de ces versions est fortement recommandée.
Télécharger Drupal 5.10
Télécharger Drupal 6.4
Site officiel
Tutoriel d'utilisation de l'extension PEAR pour internationeliser son application PHP.
Le site DevX.com et Octavia Andreea Anghel expose dans ce tutorial comment internationaliser son application PHP à l'aide de l'extension PEAR. L'internationalisation consiste à rendre une application compatible aux différents langues que l'on souhaite supporter. Il est toujours possible de ne changer que les textes pour assurer la traduction de la langue mais cela n'est pas très abouti et peu professionnel. En effet l'internationnalisation dans sa globalité demande également de supporter le format des nombres, des monnaies ainsi que des dates et heures.
L'extension PEAR met à disposition des classes afin de gérer ces fonctionnalités très simplement et ce tutoriel vous en donne des exemples d'utilisation tout aussi simples.
Lire l'article
Une version 2.0.1 de Dotclear est disponible au téléchargement depuis quelques jours. Cette version publiée de façon anticipée permet essentiellement la correction d'un bug depuis l'import de Dotclear 1.2.
Il est à noter que le bug de l'import depuis Dotclear 1.2 ne concerne que le module d'import depuis la base de données, l'import depuis un fichier texte fonctionnant correctement.
Pour plus d'information, vous pouvez consulter l'annonce sur le blog Dotclear.
Sur le site de Zend, Vikram Vaswani a récemment publié un tutoriel sur l'utilisation conjointe de PHP et de GDChart pour générer dynamiquement des graphiques et des cartes.
Ayant pu observer que la génération d'images complexes avec l'extension GD de PHP devient rapidement fastidieuse et compliquée, il propose un tutoriel complet sur l'installation et l'utilisation de l'extension GDChart, exemples à l'appui.
Une fonction Javascript toute bete pour faire patienter vos visiteurs durant le chargement d’images un peu lourdes.
http://sahid.funraill.org/wp-content/uploads/2008/09/preload-with-fadein.html
eZ System a publié une nouvelle série de mise à jour pour son CMS, eZ Publish.
Ces versions apportent un très grand nombre de correctifs et il est fortement recommandé d'effectuer la mise à niveau. eZ System a aussi annoncé que les versions 3.9 et 3.10 seraient problablement les dernières de la série 3.x. Pour toute nouvelle installation, il est recommandé d'utiliser la branche 4.x.
eZ Publish 4.0.1
Télécharger
Changelog
eZ Publish 3.10.1
Télécharger
Changelog
eZ Publish 3.9.5
Télécharger
Changelog
Après deux années de développement, la version 1.3 de Propel est désormais disponible.
Parmi les nouveautés nous trouvons entre autres :
Ce texte est une des traductions d'une série d'articles de Lorenzo Alberton
Je pense avoir déjà parlé de tous les cas de figure que vous pourriez rencontrer où il est utile d'utiliser PEAR::Pager, mais j'ai reçu de nombreux mails me demander comment utiliser le pager pour faire ceci ou cela.
Les 2 questions les plus récurrentes sont
a) Comment je peux utiliser Pager avec moteur_de_template?
et
b) Comment je peux utiliser Pager_Wrapper avec AJAX?"
En espérant réduire le flot de mails, je vais présenter 2 exemples complets.
La deuxième à trouvé réponse ici : PEAR::Pager tutorials : Pager_Wrapper and Ajax
Les exemples suivants utilisent le populaire moteur de template Smarty, mais vous pouvez facilement transposer à un autre. N'hésitez pas à tester cette transposition et la poster en commentaire.
Premièrement on créée une instance de Pager, ensuite nous assignons les données paginées et les liens à certaines variables du template:
[php]
<?php
require_once 'Pager.php';
// tableau des données à paginer
$liste_des_elements = array(...);
$option_du_pager = array(
'mode' => 'Sliding',
'delta' => 3,
'perPage' => 10,
'itemData' => $liste_des_elements,
);
$pager =& Pager::factory($option_du_pager);
//On récupère les données paginées dans la variable $data
$data = $pager->getPageData();
if (!is_array($data)) {
$data = array();
}
//On considère que vous avez déjà instancié Smarty dans $smarty
//Alors on assigne les données et les liens aux variables du template
$smarty->assign('liste_des_elements', $data);
$smarty->assign('pager_links', $pager->links);
$smarty->assign(
'page_numbers', array(
'current' => $pager->getCurrentPageID(),
'total' => $pager->numPages()
)
);
$smarty->display('page.tpl');
?>
Et voici le template:
[smarty]
...
{if $page_numbers.total > 1}
(page {$page_numbers.current} / {$page_numbers.total})<br />
{$pager_links}
{/if}
<ul>
{foreach item=row from=$liste_des_elements}
<li>{$row}</li>
{/foreach}
<ul>
...
Comme vous pouvez le voir, il n'y a rien de difficile.
Complet et clair.
Je pense avoir déjà parlé de tous les cas de figure que vous pourriez rencontrer où il est utile d'utiliser PEAR::Pager, mais j'ai reçu de nombreux mails me demander comment utiliser le pager pour faire ceci ou cela.
Les 2 questions les plus récurrentes sont
a) Comment je peux utiliser Pager avec _moteur_de_template_ ?
et
b) Comment je peux utiliser Pager_Wrapper avec AJAX?"
En espérant réduire le flot de mails, je vais présenter 2 exemples complets.
La première à trouvé réponse ici : PEAR::Pager tutorials : Pager and Smarty
Ce second exemple montre comment utiliser Pager_Wrapper pour paginer les données d'une DB et utiliser HTML_AJAX pour les afficher.
Ce texte est une des traductions d'une série d'articles de Lorenzo Alberton
Le fichier html est identique que celui montré dans le tutorial Pager + AJAX :
[html]
<html>
<body>
<h1>Exemple de PEAR::Pager avec AJAX</h1>
<script type="text/javascript" src="server.php?client=all"></script>
<div id="cible_dont_on_modifie_le_contenu">Je suis là cible</div>
<script type="text/javascript">
HTML_AJAX.replace('cible_dont_on_modifie_le_contenu', 'testdata.php');
</script>
</body>
</html>
Le fichier testdata.php est un simple script qui récupère des données dans une base de donnée à l'aide de Pager_Wrapper.
L'affichage de ce script va remplacer le contenu du div dans le premier fichier html.
Nous ajoutons aussi la date courante pour prouver qu'on affiche des données "fraîche" et qu'on reconstruit le contenu à chaque appel (C'est à dire à chaque fois qu'on clique sur un lien de navigation)
[php]
<?php
//le fichier Pager_Wrapper a été placé où on peut l'inclure
require_once 'Pager_Wrapper.php';
require_once 'MDB2.php';
//supposons qu'on a une connexion dans $db
$option_du_pager = array(
'mode' => 'Sliding',
'append' => false, //ne pas ajouter les paramètres GET aux url générées
'path' => '',
'fileName' => 'javascript:HTML_AJAX.replace(\'target\',\'testdata.php?pageID=%d\');', //Pager remplace "%d" par le n° de la page...
'perPage' => 10, //afficher 10 éléments par page
'delta' => 1,
'itemData' => $data,
);
$statement = 'SELECT prod_name, prod_description FROM products';
$paged_data = Pager_Wrapper_MDB2($db, $statement, $option_du_pager);
if (PEAR::isError($paged_data)) {
//Ouch 'y a un stuut, reglez le problème.
}
//affichage des résultats
echo '<p>Ce conteneur est récupéré par un appel AJAX</p>';
echo '<p><span class="datetime">DateTime: '. date('Y-m-d H:i:s') .'</span></p>';
echo '<h3>Page ' . $paged_data['page_numbers']['current'] . '/' . $paged_data['page_numbers']['total'] . '</h3>';
foreach ($paged_data['data'] as $item)
{
echo '<strong>' . $item['prod_name'] . '</strong>: ' . $item['prod_description'] . '<br />';
}
//Affichage des liens
echo '<hr />'.paged_data['links'];
?>
En fait vous pouvez observer que c'est le même exemple qu'ici
mais avec quelques petits changement repris d'ici.
Dans un article paru sur le site IBM destiné à des développeurs PHP avertis, Thomas Myer explique comment réaliser une application web minimaliste en 1 heure à l'aide du framework PHP CodeIgniter.
L'article commence par aborder les raisons d'utiliser un framework MVC, avant d'expliquer les avantages CodeIgniter dans cette catégorie. Viennent ensuite l'installation et la configuration de CodeIgniter, puis la création d'un projet simple : la réalisation d'une application avec un formulaire de contact de A à Z.
Clochix a publié cette semaine deux articles à propos de sécurité; le premier sur les CMS en général et le second plus spécifiquement sur eZ Publish. Le problème pointé est l'affichage par défaut de tous les objets dans eZ Publish par les templates par défaut même lorsque cela ne devrait pas arriver. La solution (simple) proposée est de faire des surcharges s'appliquant en dernier et n'affichant rien pour éviter d'afficher tout ce qui n'a pas été prévu. Évidemment il est toujours mieux de restreindre les droits, mais c'est un bon dernier rempart à la divulgation d'informations...
Il y a évidemment d'autres éléments à considérer et j'en oublie probablement d'ailleurs mais voici ceux qui me viennent à l'esprit.
Au niveau template, il faut toujours penser à utiliser l'opérateur wash(), il permet de s'assurer que tous les caractères spéciaux sont échappés pour produire du code XHTML valide mais aussi pour éviter des attaques de type XSS si surtout votre site propose aux internautes de contribuer.
Au niveau système pour un site en production, seul le répertoire var devrait permettre l'écriture au serveur web. On peut aussi restreindre les droits de l'utilisateur MySQL utilisé par eZ Publish pour limiter la portée d'une éventuelle mauvaise utilisation de ce compte.
On peut aussi penser à désactiver les modules et/ou les vues inutiles pour un siteaccess donné. Par exemple, pour ce site, le fichier site.ini.append.php de mon siteaccess correspondant au front comporte la configuration suivante :
[SiteAccessRules] Rules[] Rules[]=access;enable Rules[]=moduleall Rules[]=access;disable Rules[]=module;user/register Rules[]=module;user/forgotpassword Rules[]=module;user/activate Rules[]=module;user/success Rules[]=module;ezinfo
Ces quelques lignes désactivent quelques vues du module user ainsi que le module ezinfo qui sont accessibles aux utilisateurs anonymes alors qu'ils ne me sont pas nécessaires. La vue ezinfo/about en particulier donne des informations sur les extensions activées et surtout sur la version d'eZ Publish ce qui permet de savoir à quoi est potentiellement vulnérable le site. Dans tous les cas, il vaut mieux être à jour, les versions 4.0.0, 3.10.0 et 3.9.4 sont vulnérables à quelques failles connues.
Il faut aussi penser à nommer les fichiers de configuration en .ini.append.php et à encadrer le contenu par des commentaires PHP ce qui évite toute possibilité de lecture via un accès direct par le serveur web. À ce niveau, avoir un site eZ Publish en mode Virtual Host devrait aussi apporter un gain en cachant presque complètement l'arborescence "physique" du site.
Enfin au niveau des extensions il faut évidemment penser à échapper toutes les données inconnues avant de l'utiliser dans une requête SQL (ça n'est pas spécifique à eZ Publish !), la méthode escapeString() de la classe eZDBInterface est faite pour ça.
![]()
Ce texte est une des traductions d'une série d'articles de Lorenzo Alberton
Suite de PEAR::Pager tuto : Navigation avec pagination et Javascript simple
Comme vous avez pu le voir, the trick was setting the path parameter to an empty string and the fileName parameter to a javascript link, avec l'habituel marqueur "%s" pour le pageID.
Ok, maintenant que vous avez vu les bases, vous devriez avoir tous les éléments à aller plus loin.
Mais si vous êtes paresseux et voulez le voir quand même, voici un exemple sur la façon de faire la même chose que ce que nous avons vu avant, cette fois utilisant des appels AJAX chercher seulement les données utiles pour la page montrée.
Dans cet exemple, j'utilise la librairie PEAR::HTML_AJAX (docs):
Si vous ne pouvez pas attendre voyez cette démo, c'est l'exemple en fonctionnement, regardons comment il est fait :
nous incluons les fichiers js dynamiques (server.php) pour traiter les demandes AJAX,
et appellons HTML_AJAX.replace ("target","testdata.php"),
ce qui remplacera le contenu du DIV cible par le rendu du script testdata.php en utilisant un appel d'AJAX.
[php]
<html>
<body>
<h1>PEAR::Pager exemple avec AJAX</h1>
<script type="text/javascript" src="server.php?client=all"></script>
<div id="target">Je suis la cible</div>
<script type="text/javascript">
HTML_AJAX.replace('target', 'testdata.php');
</script>
</body>
</html>
c'est un simple script php qui récupère les données que vous désirez afficher (Dais cet exemple, 100 entiers) et les renvoie au Paginateur.
L'output de ce script remplacera le contenu du DIV cible dans le premier fichier html.
Nous affichons également la date et l'heure courante pour nous prouver que les données sont « fraîches » et construites à chaque appel (c.-à-d. chaque fois que vous cliquez sur un lien de navigation).
[php]
<?php
require_once 'Pager.php';
$data = range(1, 100); //un tableau de données à paginer
$pager_params = array(
'mode' => 'Sliding',
'append' => false, //ne pas ajouter les paramètres GET
'path' => '',
'fileName' => 'javascript:HTML_AJAX.replace(\'target\',\'testdata.php?pageID=%d\');', //Pager replaces "%d" with the page number...
'perPage' => 10, //afficher 10 item par page
'delta' => 1,
'itemData' => $data,
);
$pager = & Pager::factory($pager_params);
$n_pages = $pager->numPages();
$links = $pager->getLinks();
echo '<p>Ce containeur est rempli avec un appel AJAX</p>';
echo '<p><span class="datetime">DateTime: '. date('Y-m-d H:i:s') .'</span></p>';
echo '<h3>Page '. $pager->getCurrentPageId() .'</h3>';
foreach ($pager->getPageData() as $item) {
echo 'Item '. $item .'<br />';
}
echo '<hr />'.$pager->links;
?>
nous créons une instance de HTML_AJAX_Server pour livrer les les deux les bibliothèques de Javascript et pour traiter les demandes d'AJAX des navigateurs.
[php] <?php include 'HTML/AJAX/Server.php'; $server = new HTML_AJAX_Server(); $server->handleRequest(); ?>
Sur le site Developper Shed, Alejandro Gervasio propose un tutoriel sur CodeIgniter pour l'affichage paginé d'une liste de résultats en provenance d'une base de données MySQL.
Ce tutoriel est composé de quatre parties :
Il est à noter que ce tutoriel fait suite à deux tutoriels parus précédemment :
Il y en a comme moi qui traduisent, d'autres écrivent directement.
Voici 4 articles de plus faite chez Samalyse.
Cet exemple montre comment lier deux tables de données avec le composant Structures_DataGrid.
Ce tutoriel explique comment facilement générer des tableaux HTML à partir de données SQL, avec tri et pagination, en couplant deux composants PEAR : Structures_Datagrid et DB_Dataobject.
Librairie d'indexation de contenu XML. Ce composant permet de diminuer les temps d'accès aux données XML.
Contribution permettant d'utiliser des fichiers XML avec la librairie de gestion multi-langues Translation2.
Un mini-tutoriel est disponible sur le blog de Symfony pour profiter de la possibilité de configurer entièrement la barre d'outil de débuggage, disponible depuis Symfony 1.2.
Cette nouvelle fonctionnalité permet notamment au développeur de choisir quelles seront les informations qui lui seront affichées et d'en ajouter de nouvelles.
Pour plus d'information, vous consulter consulter l'article sur le blog de Symfony.
Puisque la question a été posée sur la liste de diffusion de django, voici quelques remarques sur l'éditeur que j'utilise et les raisons de mon choix.
Il s'agit
donc de Komodo Edit, qui succède depuis maintenant une bonne année à PSPad (qui succédait lui-même à
Context). L'essentiel
de mon activité de développement se fait au bureau sous Windows, mais je
développe également chez moi sous Mac, après quelques années sous Linux. Je
souhaitais un éditeur qui soit multi-plateformes afin de n'avoir qu'une
interface à apprendre. Pour le reste, voici un résumé :
C'est aussi très
utile pour les directives d'importation en python, quand on ne connait pas
encore par coeur l'arborescence des paquets et modules que l'on utilise.arg ! Je dois mettre à jour d’urgence :
Article original publié sur Glagla Dot Org. Tous droits réservés.