Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

PHP: optimiser la génération de pages très dynamiques

28 réponses
Avatar
Patrick 'Zener' Brunet
Bonjour.

J'aimerais avoir quelques retours d'expérience sur l'optimisation des pages
fortement dynamiques en PHP.

Je cherche toujours pourquoi mon site (www.ipzb.fr) est si lent à charger,
même pour les pages les plus légères.

Pour l'histoire:
- il n'y a pas de requête SQL ni de connexion à des serveurs tiers,
- le sessionning existe pour toutes les pages (gestion d'un contexte
visiteur) mais il est fait avec un système de mini-fichiers très optimisé,
- l'URL rewriting est innocent, j'avais essayé de le désactiver sans
amélioration.

Le site est hébergé par 1and1. Ils ont une réputation de lenteur, mais à ce
point c'est déprimant.

L'extension de Firefox nommée LoadTimeAnalyser montre que, même pour une
page de base et en pleine session, on passe son temps à attendre le serveur,
le transfert des données étant lui presque instantané.
Voyez ce chronogramme (HTML pur):
http://cjoint.com/?lssEIBsuuz

La caractéristique de ce site est que les pages contiennent une forte
proportion d'éléments insérés par du PHP.
Donc il y a des <?php echo GetMachin(); ?> en assez grand nombre au milieu
du HTML.

Par contre les fonctions GetMachin() utilisent au maximum des éléments
précalculés, et à défaut ne font que des opérations très simples
(arithmétique, concaténation de petites chaînes...). Beaucoup ne font même
rien du tout et rendent une chaîne vide avec les options de base du site.

Il y a un bruit qui court selon lequel la primitive echo aurait un bug sur
certains moteurs PHP, dont celui utilisé par 1and1 (j'ai lu ça sur un forum,
invérifiable).

De toute manière, je vois mal comment optimiser davantage: le truc qui
consiste à insérer des fichiers HTML plutôt que de faire du echo ne
s'applique qu'à des tronçons un peu gros, pas à des chaînes de 20
caractères. Et pour les gros tronçons je pratique déjà.
Et PHP en principe c'est fait pour ça,

J'ai même des doutes sur la part du temps de calcul: certains temps
d'attente de plus d'une seconde correspondent à des éléments qui ne sont pas
générés (simple readfile avec 3 headers devant)...
Par exemple les images à la fin du chronogramme, dont le
mArrowR.gif par exemple: 1,2s pour 265 octets :o)

J'aimerais bien trouver un moyen d'avoir un chronogramme complet, montrant
tout le timing de requête+génération de la page avec tous ses éléments au
niveau du serveur... Vous avez une idée sur la méthode ?

Savez-vous aussi comment ça se passe au niveau des fichiers susceptibles
d'être inclus en read-only dans chaque requête HTTP (un mini script PHP de
définitions par exemple) - c'est mis en cache ou il y a risque de bottleneck
?

Merci de vos conseils (dans la mesure ou ce n'est pas "fais du HTML de
base", je sais faire aussi mais c'est hors-sujet ;-).

--
Cordialement.
--
/**************************************************\
* Patrick BRUNET
* E-mail: lien sur http://zener131.free.fr/ContactMe
\**************************************************/

8 réponses

1 2 3
Avatar
SAM
Denis Beauregard a écrit :

Autrefois, j'avais 2 domaines sur le même serveur. Un était
www.genealogie.com et l'autre www.francogene.com . Avec
Apache, je pouvais choisir le fichier affiché avec les énoncés
suivants. Il y a peut-être moyen d'injecter l'heure exacte que
l'on pourra comparer avec celle du fichier log.



Le code suivant est du SSI, il faut activer cela dans Apache.
Usuellement les fichiers contenant du SSI sont suffixés en shtml.
Et bien sûr en SSI on a accès à tout un tas de choses en rapport avec
l'heure et la date.

<!--#if expr="${SERVER_NAME} = /genealogie.com/" -->
<!--#include file="index_fr.htm" -->
<!--#else -->
<!--#include file="index_en.htm" -->
<!--#endif -->

Je suis passé à PHP ves 2003, donc cette syntaxe était valide à
cette époque. Depuis, j'ai remplacé cela par l'équivalent en PHP.



La plupart des serveurs (Apache) ont les SSI ouverts, même si beaucoup
en brident les fonctions de mail.

<?
$rep = stristr ($HTTP_HOST, 'genealogie.com');
if ($rep == FALSE)
{ require ("./index_en.php"); }
else
{ require ("./index_fr.php"); }
?>



Ne pourrait-on faire :

<?
$rep = stristr ($HTTP_HOST, 'genealogie.com')?
'en' : 'fr';
require ("./index_$rep.php");
?>

Je ne sais si en php on peut faire :
index.en.php et index.fr.php
et qu'ils soient choisis en fonction de la préférence de langue du
navigateur comme on peut le faire en htm ?



--
sm
Avatar
Denis Beauregard
Le Fri, 23 Nov 2007 21:49:46 +0100, SAM
écrivait dans
fr.comp.infosystemes.www.auteurs:

<?
$rep = stristr ($HTTP_HOST, 'genealogie.com');
if ($rep == FALSE)
{ require ("./index_en.php"); }
else
{ require ("./index_fr.php"); }
?>



Ne pourrait-on faire :

<?
$rep = stristr ($HTTP_HOST, 'genealogie.com')?
'en' : 'fr';
require ("./index_$rep.php");
?>

Je ne sais si en php on peut faire :
index.en.php et index.fr.php
et qu'ils soient choisis en fonction de la préférence de langue du
navigateur comme on peut le faire en htm ?



J'ai réglé le cas depuis longtemps (j'ai vendu un de mes noms de
domaine en 2003).

Ceci dit, j'avais trouvé une fonction un peu compliquée pour
déterminée la langue du visiteur. Je préfère aussi avoir un
seul point dans mes noms de fichiers. En fait, je pense qu'il
y a seulement 3 noms de fichiers pouvant porter à confusion,
3 index.php dans un répertoire biliingue, une erreur de parcours...


Denis
Avatar
Olivier Miakinen
Le 23/11/2007 19:35, Patrick 'Zener' Brunet a écrit :

URL('/getdata.php?type=cssimage&nameÞfault/Images/RedPin.gif');
</cit.>



OK, le 21/11 j'ai activé le rewriting pour ces ressources, donc maintenant
elle apparaît comme:
URL('/cssimage/Default/Images/RedPin.gif').

Ca permet au navigateur de ne plus se poser de question sur sa nature
dynamique (s'il y avait lieu), même si au niveau du serveur elle est
toujours injectée par le getdata.php qui ne fait rien d'autre (pour des
histoires de chemins et de headers "expire" notamment ).



??? Je ne vois pas en quoi le rewriting changerait quoi que ce soit
pour le navigateur ! Et je ne vois pas non plus comment Apache pourrait
décider à la place de PHP des informations de cache à envoyer, même avec
rewriting.

Pour en avoir le c½ur net j'ai essayé de récupérer l'image avec un
SeaMonkey équipé de l'extension WebDeveloper (qui donne des infos sur
les entêtes HTTP reçus) :
http://www.ipzb.fr/cssimage/Default/Images/RedPin.gif
http://www.ipzb.fr/getdata.php?type=cssimage&nameÞfault/Images/RedPin.gif
... mais malheureusement ça ne marche pas car tu t'es planté sur le type
MIME, qui est "img/gif" au lieu de "image/gif".

[...]
De toute manière, je ne vois pas comment le fait de mettre la page en cache
pourrait constituer une optimisation à ce niveau, elle ferait toujours
l'objet d'une requête HTTP séparée qui serait servie de la même manière.



L'intérêt des feuilles de style communes à plusieurs pages du site est
qu'elles ne sont demandées qu'une seule fois. Idem pour les images.
Si tu as une image de fond qui est servie par PHP, sans infos de mise
en cache, alors c'est encore pire si jamais tu l'affiches plusieurs
fois sur la page : le navigateur fera autant de requêtes qu'il y a
d'occurrences sur la page.

Je reviens sur mes mesures: elles montrent que le problème n'est pas au
niveau de la génération des contenus, qui est presque marginal.
En fait il y a un temps d'attente parasite qui représente 80% du délai.
Qu'est-ce qui se passe sous la plume de l'apache, est-ce qu'il roupille ? Là
est la vraie question, et je sèche :-@



Pour le moment, mon avis est que tu fais trop de requêtes simultanées et
que peut-être Apache est configuré pour ne pas lancer plus de n fois
simultanément le processeur PHP. Le délai correspondrait alors au temps
d'attente qu'un processus PHP s'arrête avant de pouvoir en exécuter un
nouveau. Quoi qu'il en soit, les questions sur Apache seraient beaucoup
plus en charte sur fr.comp.infosystemes.www.serveurs.
Avatar
Olivier Miakinen
Le 23/11/2007 21:49, SAM a écrit :

Je ne sais si en php on peut faire :
index.en.php et index.fr.php
et qu'ils soient choisis en fonction de la préférence de langue du
navigateur comme on peut le faire en htm ?



On peut le faire, bien sûr, mais ce n'est ni en php ni « en htm »
puisque c'est justement après résolution de l'URL qu'on saura si on
appelle l'interpréteur PHP ou si l'on affiche simplement le contenu
du fichier.

D'ailleurs tu peux très bien avoir :
index.en.php
index.fr.htm
index.de.gif
index.es.js
... et que le choix de la langue détermine le type de contenu retourné
(ou inversement, le type de contenu qui détermine la langue).
Avatar
Nicolas Krebs
Patrick Brunet écrivit dans l'article
news:fhpvq0$fuf$

Bonjour.



Bonjour,

J'aimerais avoir quelques retours d'expérience sur l'optimisation des pages
fortement dynamiques



Cela ne signifie rien.

en PHP.



Hors-charte.

Je cherche toujours pourquoi mon site (www.ipzb.fr) est si lent à charger,
même pour les pages les plus légères.



- news:fr.comp.infosystemes.www.serveurs
- news:fr.comp.lang.php
- Éric Daspet, « Cachez-moi çà ! », blog.dreams4net.com,
http://blog.dreams4net.com/cachez-moi-ca
- Éric Daspet et Cyril Pierre de Geyer, « PHP 5 avancé » (quatrième
édition), chapitre 24 « Les systèmes de cache », éd. Eyrolles,
urn:isbn:978-2-212-12167-4 ,
http://www.eyrolles.com/Informatique/Livre/9782212121674/livre-php-5-avance.php
Vous avez de la chance, il était justement présent aux Forum PHP Paris
2007 http://www.afup.org/pages/forumphp2007/conferenciers.php#edaspet et
Paris Web 2007 http://2007.paris-web.fr/
http://www.paris-web.fr/Les-membres-de-l-association#eric-daspet .
Avatar
Patrick 'Zener' Brunet
Bonjour.

"Denis Beauregard" a
écrit dans le message de news:
Le Fri, 23 Nov 2007 21:49:46 +0100, SAM
écrivait dans
fr.comp.infosystemes.www.auteurs:



[...]
Ceci dit, j'avais trouvé une fonction un peu compliquée pour
déterminée la langue du visiteur.



Mon site étant bilingue (potentiellement multilingue), je me suis attaqué au
problème et c'est vrai que ce n'est pas simple.

J'ai intégré les points de vue suivants:

- le navigateur transmet normalement la langue souhaitée dans
HTTP_ACCEPT_LANGUAGE, mais le visiteur a le droit d'être parano, d'utiliser
Proxomitron ou un autre moyen de taire cette information,

- si elle est disponible, il s'agit d'une liste de préférences incluant des
variantes de dialecte:
"fr-fr,fr;q=0.8,en-us;q=0.5,en;q=0.3",

- donc on peut ne pas connaître la langue de l'utilisateur, et devoir faire
une supposition (par exemple mon site perso est visité à 95% par des
Français ou des visiteurs a priori francophones, le nom de domaine étant en
.fr), et l'anglais reste la langue de repli à l'international,

- la préférence du visiteur peut différer de ce qu'affiche son navigateur:
par exemple mon employeur (international) a tout configuré en EN-US, et donc
je suis bien content, lorsque je suis passé sur la section française d'un
site, qu'il accepte de mémoriser cette préférence !

- ceci pour la préférence générale.

- ensuite une page donnée peut ne pas (encore) exister dans la langue
préférée, tout en étant disponible dans une autre langue que le visiteur
comprend. Que faire dans ce cas ?

- lui balancer une erreur 404 ou équivalent local, c'est pas fin.

- moi je lui envoie une page de diagnostic dans sa langue préférée, lui
proposant de voir la page dans la langue disponible. Ce qui implique
l'existence de liens interlangues dans le contenu, construits dynamiquement:
la mécanique doit être "consciente" du fait qu'il existe plusieurs langues,

- enfin, ça dépasse le texte de la page, certaines images et aussi les
documents et liens inclus (vers Wikipédia par exemple) peuvent être
dépendants de la langue, alors que d'autres (la plupart des images par
exemple) sont internationales...

Donc au niveau de cette mécanique, il nous faut:
- une logique d'analyse de la langue préférée et de prise de décision,
- comme c'est un peu coûteux, ce résultat est mis en cache,
- une sélection volontaire de la langue parmi les disponibilités, qui est
mémorisée dans le contexte visiteur (session),
- et réutilisée par défaut, sauf en cas de version d'une page inexistante
dans cette langue,
- et donc un moyen rapide de savoir dans quelles langues existe une page
donnée (j'ai un préfixe dans les noms de fichiers).

Pour exemple pratique, voici l'essentiel du code qui fait ça sur
www.ipzb.fr:

$_tPreferedLanguage = "";

function ztGetPreferedLanguage()
{
global $_tPreferedLanguage;
if( $_tPreferedLanguage == "") /* Not computed yet */
ztComputePreferedLanguage( "");
return( $_tPreferedLanguage);
}

function ztComputePreferedLanguage( $tFromCookie)
{
global $_SERVER, $_tPreferedLanguage;
$catLanguages = array();
$catLanguages["fr"] = "Fr";
$catLanguages["en"] = "En";
$tPreferedLanguage = "";
if( $tFromCookie != "" && $tFromCookie != "**") /* Take it if valid */
$tPreferedLanguage = ztScanLanguageArray( $catLanguages,

$tFromCookie);
if( $tPreferedLanguage == "") /* Else use the Browser Accept string */
{
/* String aspect is: "fr-fr,fr;q=0.8,en-us;q=0.5,en;q=0.3" */
$catTemp = explode( ",", $_SERVER["HTTP_ACCEPT_LANGUAGE"]);
$catTemp = explode( ";", $catTemp[0]);
$catAccepted = explode( "-", $catTemp[0]);
$tPreferedLanguage = $catAccepted[0];
$tPreferedLanguage = ucfirst( strtolower( substr(
$tPreferedLanguage, 0, 2)));
$tPreferedLanguage = ztScanLanguageArray( $catLanguages,
$tPreferedLanguage);
}
if( $tPreferedLanguage == "") /* French will be the very default one */
$tPreferedLanguage = "Fr";
$_tPreferedLanguage = $tPreferedLanguage;
}

function ztScanLanguageArray( $catLanguages, $tCandidate)
{
foreach( $catLanguages as $tLanguageXmlTag => $tLanguageSiteTag)
{
if( $tLanguageXmlTag == $tCandidate
|| $tLanguageSiteTag == $tCandidate)
return( $tLanguageSiteTag);
}
return( "");
}

function ztGetBestPageLanguage( $tPageID, $tPassedLanguage)
{
global $_catLabelLanguages;

$tPreferedLanguage = "";
$tWantedLanguage = $tPassedLanguage;
if( $tWantedLanguage == "")
{
$tPreferedLanguage = ztGetPreferedLanguage();
$tWantedLanguage = $tPreferedLanguage;
}
$tBestLanguage = "";
for( ;;)
{
$tBestLanguage = $tWantedLanguage;
if( ztPageExists( $tPageID, $tBestLanguage))
break;
$tBestLanguage = "";
if( $tPreferedLanguage == "")
$tPreferedLanguage = ztGetPreferedLanguage();
if( $tPreferedLanguage != $tWantedLanguage)
{
$tBestLanguage = $tPreferedLanguage;
if( ztPageExists( $tPageID, $tBestLanguage))
break;
}
$tBestLanguage = "";
for( $i = 1; $i < count( $_catLabelLanguages); $i++) /* Skip "**" */
{
$tLanguage = substr( $_catLabelLanguages[$i], 0, 2);
if( $tLanguage != $tWantedLanguage
&& $tLanguage != $tPreferedLanguage)
{
$tBestLanguage = $tLanguage;
if( ztPageExists( $tPageID, $tBestLanguage))
break;
$tBestLanguage = "";
}
}
break;
}
if( $tBestLanguage == $tWantedLanguage)
$tResult = "=".$tBestLanguage; /* Found the match */
else
{
$tDiagnoseLanguage = $tWantedLanguage;
if( ! ztPageExists( "OtherLanguage", $tDiagnoseLanguage))
$tDiagnoseLanguage = $tPreferedLanguage;
if( ! ztPageExists( "OtherLanguage", $tDiagnoseLanguage))
$tDiagnoseLanguage = "En";
if( $tBestLanguage != ""
&& ! ztPageExists( $tPageID, $tBestLanguage))
$tBestLanguage = "";
$tResult = "!".$tDiagnoseLanguage."[".$tBestLanguage;
/* Diagnose page with alternative choice if available */
}
return( $tResult);
}

Hope it helps someone...

--
Best regards, Cordialement.
--
/**************************************************
* Patrick BRUNET
* E-mail: lien sur http://zener131.free.fr/ContactMe
**************************************************/
Avatar
Patrick 'Zener' Brunet
Bonjour.

"Olivier Miakinen" <om+ a écrit dans le message de news:
47475163$
Le 23/11/2007 19:35, Patrick 'Zener' Brunet a écrit :
>>
>> URL('/getdata.php?type=cssimage&nameÞfault/Images/RedPin.gif');
>> </cit.>
>
> OK, le 21/11 j'ai activé le rewriting pour ces ressources, donc
> maintenant elle apparaît comme:
> URL('/cssimage/Default/Images/RedPin.gif').
>
> Ca permet au navigateur de ne plus se poser de question sur sa nature
> dynamique (s'il y avait lieu), même si au niveau du serveur elle est
> toujours injectée par le getdata.php qui ne fait rien d'autre (pour des
> histoires de chemins et de headers "expire" notamment ).

??? Je ne vois pas en quoi le rewriting changerait quoi que ce soit
pour le navigateur ! Et je ne vois pas non plus comment Apache pourrait
décider à la place de PHP des informations de cache à envoyer, même
avec rewriting.



Moi non plus ! C'était pour ne pas avoir l'air têtu depuis le début de la
conversation.

Pour en avoir le c½ur net j'ai essayé de récupérer l'image avec un
SeaMonkey équipé de l'extension WebDeveloper (qui donne des infos
sur les entêtes HTTP reçus) :
http://www.ipzb.fr/cssimage/Default/Images/RedPin.gif



http://www.ipzb.fr/getdata.php?type=cssimage&nameÞfault/Images/RedPin.gif
... mais malheureusement ça ne marche pas car tu t'es planté sur le type
MIME, qui est "img/gif" au lieu de "image/gif".




Exact SACREBLEU ! j'avais pourtant fait gaffe !
http://www.w3schools.com/media/media_mimeref.asp
j'avais dû prendre ça il y a longtemps sur un forum de trucs foireux...

Je les ai corrigés (cela sera monté sur le serveur aujourd'hui).

Merci du tuyau, ça peut arranger pas mal de choses.
Je vais peut-être installer ce singe marin moi aussi, on ne peut pas tout
installer :-/
Actuellement je code tout avec Ten BrainDriven Fingers.

> [...]
> De toute manière, je ne vois pas comment le fait de mettre la page en
> cache pourrait constituer une optimisation à ce niveau, elle ferait


toujours
> l'objet d'une requête HTTP séparée qui serait servie de la même manière.

L'intérêt des feuilles de style communes à plusieurs pages du site est
qu'elles ne sont demandées qu'une seule fois. Idem pour les images.



C'est bien le but, les URL sont identiques d'une page à l'autre, sauf
changement d'options, donc je compte sur le cache pour faire son boulot.

Si tu as une image de fond qui est servie par PHP, sans infos de mise
en cache, alors c'est encore pire si jamais tu l'affiches plusieurs
fois sur la page : le navigateur fera autant de requêtes qu'il y a
d'occurrences sur la page.




Ben oui, et ça en a l'air... On va voir avec la correction.

> Je reviens sur mes mesures: elles montrent que le problème n'est pas au
> niveau de la génération des contenus, qui est presque marginal.
> En fait il y a un temps d'attente parasite qui représente 80% du délai.
> Qu'est-ce qui se passe sous la plume de l'apache, est-ce qu'il roupille


?
> Là est la vraie question, et je sèche :-@

Pour le moment, mon avis est que tu fais trop de requêtes simultanées et
que peut-être Apache est configuré pour ne pas lancer plus de n fois
simultanément le processeur PHP. Le délai correspondrait alors au temps
d'attente qu'un processus PHP s'arrête avant de pouvoir en exécuter un
nouveau.



Peut-être, mais le chronogramme montre que les premiers composants (dont la
page elle-même) sont déjà retardés, bien que sérialisés.

Pour le nombre maxi de scripts concurrents, c'était pas prévu en effet :-(
Je vais creuser.

Quoi qu'il en soit, les questions sur Apache seraient beaucoup
plus en charte sur fr.comp.infosystemes.www.serveurs.



Oui, tout à fait (même si LAMP est plus ou moins un package).
// j'ai honte, je ne sais pas rediriger le suivi :o)

Merci pour ton oeil exercé :-)

--
Cordialement.
--
/**************************************************
* Patrick BRUNET
* E-mail: lien sur http://zener131.free.fr/ContactMe
**************************************************/
Avatar
Olivier Miakinen
Le 24/11/2007 08:45, Patrick 'Zener' Brunet a écrit :

Pour en avoir le c½ur net j'ai essayé de récupérer l'image avec un
SeaMonkey équipé de l'extension WebDeveloper (qui donne des infos
sur les entêtes HTTP reçus) :
http://www.ipzb.fr/cssimage/Default/Images/RedPin.gif



http://www.ipzb.fr/getdata.php?type=cssimage&nameÞfault/Images/RedPin.gif
... mais malheureusement ça ne marche pas car tu t'es planté sur le type
MIME, qui est "img/gif" au lieu de "image/gif".



Je les ai corrigés (cela sera monté sur le serveur aujourd'hui).



Oui, c'est bon. Et le résultat est assez surprenant (différent entre les
deux URL). Si j'ai le temps j'irai demander sur fciw.serveurs ce qu'en
pensent les spécialistes.

Merci du tuyau, ça peut arranger pas mal de choses.
Je vais peut-être installer ce singe marin moi aussi, on ne peut pas tout
installer :-/



Tu n'as pas besoin d'installer SeaMonkey si tu as déjà Mozilla ou
Firefox : l'extension WebDeveloper fonctionne aussi bien sur les
trois. Au fait, je n'ai pas eu besoin de l'extension pour constater
le problème : il a suffi que j'essaye d'afficher l'image pour me
rendre compte que le navigateur ne savait pas le faire.

Actuellement je code tout avec Ten BrainDriven Fingers.



Je ne m'en sers pas non plus pour coder.

// j'ai honte, je ne sais pas rediriger le suivi :o)



Je ne connais pas du tout Outlook Express, mais je sais que
l'explication est donnée de temps en temps sur fcul. Il doit
même y avoir des pages web qui l'expliquent.

Le principe, c'est de mettre les deux noms de groupes dans un champ dont
le nom officiel est « Newsgroups: » et un seul des deux (celui où tu
veux faire suivre) dans un champ de nom officiel est « Followup-To: ».

Merci pour ton oeil exercé :-)



C'était facile... ;-)
1 2 3