#15466

PNG Optimization with GD library

Quand on produit des PNG avec PHP/GD, les images sont rarement optimisées. Cette page donne quelques pistes.

Moi par exemple, je tente de reproduire ça en plus léger.
En somme, c’est une simple matrice 5x5 avec deux couleurs dans la palette.

Il me suffit d’ajouter ça, juste avant le imagepng() :
imagetruecolortopalette($image_ressource, false, 256);


La taille d’une icône 512x512 passe ainsi de 2 131 octets à 283 octets (~85% de réduction).

L’image passée dans optipng me revient à la même taille. Pour les icônes avec très peu de couleurs c’est donc idéal. Pour les autres png, voyez l’article.
http://perplexed.co.uk/1814_png_optimization_with_gd_library.htm

#15429

Note : PHP, imagerotate

Ok, donc "imagerotate" en PHP me donne un segfault si l’angle==360°.

[Sun May 22 20:01:30.476001 2016] [core:notice] [pid 1747] AH00052: child pid 14628 exit signal Segmentation fault (11)


Tout va bien

Visiblement je ne suis pas le seul : https://github.com/banister/devil/issues/13
Et j’ai aussi vu d’autres erreurs, dans d’autres langages (datant de 2010). La source serait dans les Lib en C utilisées.
http://lehollandaisvolant.net/?mode=links&id=20160522200404

#14936

GitHub - timovn/local-favicon-cache: Creates a local favicon cache using Google-Favicon grabber

Google possède un service (non documenté) pour trouver les favicons des sites : http://www.google.com/s2/favicons?domain=google.com

Mon script (PHP) constitue un cache local des favicons en utilisant le service de Google.
Je compte l’utiliser pour le lecteur RSS de Blogo.

Le truc bien c’est que c’est le serveur qui fait la requête vers Google, sans referer ni rien. Ce n’est pas l’utilisateur, donc c’est bien pour la vie privée.
Une fois qu’une requête chez Google est faite et que la favicon est en local, c’est l’icône locale qui est servie sans requête Google.

Il fonctionne en se basant sur le script de Mitsu pour Gravatar, c’est le même principe et pratiquement le même code : https://suumitsu.eu/wiki/doku.php?id=php:cache_gravatar
https://github.com/timovn/local-favicon-cache

#14918

Note : rappel PHP

NEVER TRUST USER INPUT

C’est valable pour les formulaires POST et GET mais aussi plein d’autres choses :
– cookies
– variables de sessions
– noms de fichiers uploadés ($_FILES)
– les types de fichiers (dont MIME)
– variables dans le php.ini
– …

Sans oublier des vérifications spéciales dans certains cas : si la chaîne « . » (un simple point) est grammaticalement valable pour un pseudo ou un mot de passe, ça devient très dangereux quand il s’agit d’un nom de fichier (« . » c’est un raccourcis pour dire « dossier courant », comme « .. » signifie « dossier parent »).

Si en PHP vous avez un script pour renommer un fichier, un nouveau nom « . » sera valide pour le htmlspecialchars(). Mais au moment où le serveur transmet la requête « renomme le fichier abc.jpg en "." » au système d’exploitation ou au système de fichier, dans le meilleur des cas ça génère une erreur, dans le pire ça écrase le dossier courant et son contenu (le "." c’est le dossier courant et là on renomme un fichier par dessus).

Et pour supprimer un fichier, pareil : http://lehollandaisvolant.net/tuto/php/#supprimer-un-fichier


Enfin, l’utilisateur est toujours très con quand il s’agit d’utiliser un logiciel. Il fera toujours n’importe quoi, et encore plus si ça permet de faire planter l’ordinateur.
C’est très fatiguant de devoir penser à tout, comme ici avec la gestion du CSV http://sebsauvage.net/wiki/doku.php?id=csv . Heureusement, ici certains l’ont fait.

Si ce n’est pas le cas dans ce que vous faites, je conseille de ne pas avoir peur de faire une application limitée mais qui fonctionne plutôt qu’un truc illimité qui plante, même dans seulement 0,01% des cas : la loi de Murphy ne plante pas elle.

Par exemple, pour gérer les extensions de fichiers, je préfère dresser une liste avec les 20 extensions les plus communes (png, doc, jpg, mp3, apk…) et mettre tout le reste dans « autres », plutôt que d’essayer de détecter l’extension des fichiers (par régex ou autres).

Tôt ou tard, il y aura un urluberlu avec un nom de fichier de 500 caractères, ou avec une extension de 500 caractères, ou sans extensions, ou une extension en unicode, ou une extension en unicode-privé sur une fonte que vous n’avez pas, ou avec des caractères d’échappement.

Afficher 500 caractères ce n’est pas un soucis en soit, mais ça déforme l’affichage si l’absence d’espaces génère un non-retour à la ligne…

Ah et, si votre fichier est envoyé depuis GNU/Linux, il peut contenir des espaces et des caractères unicodes ou de la ponctuation. Windows n’aime pas ça et parfois il refusera d’enregistrer ce fichier au nom bizarre (ça dépend du système de fichier : FAT32 gère ça très mal en tout cas).

Bref, faut avoir une vision d’ensemble des chose : uploader un fichier, c’est pas juste une requête "files", c’est aussi penser à celui qui maintient caps-lock allumé quand il renomme ses photos avec des noms à rallonge sous Windows sur un clavier russe.
http://lehollandaisvolant.net/?mode=links&id=20160331195130

#14906

[ÉDIT] Note : astuce sécurité PHP - Le Hollandais Volant

Bon, mon code n’est toujours pas parfait : ici il y a un problème quand on utilise une réécriture .htaccess.

Par exemple chez moi, la page /contact redirige sur un post particulier (au format normalement /?d=2016/03/29…).
Le /contact ne marchait plus à cause de ce code.

Du coup je me suis rabattu sur un code bien plus simple : on teste simplement si l’URL demandée est constituée du nom du script courant directement suivi d’un slash :

if (strpos($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME'].'/') === 0) {
	header('Location: '.$_SERVER['SCRIPT_NAME']);
	exit;
}


Ça semble marcher.


Rappel : ceci est utile/nécessaire car si on appelle l’URL « example.com/page.php/autre_chose, alors le serveur considère le fichier "autre_chose" situé dans le dossier "page.php".
Si "page.php" est un nom de dossier parfaitement valide, le simple fait d’y accéder constitue une execution du script PHP éventuellement contenu dans "page.php".

Ceci peut être problématique dans certains cas (en particulier dans mon cas pour les url absolues et relatives, étant donnée que "autre_chose" est alors considéré à un niveau de profondeur supplémentaire dans l’arborescence des fichiers par rapport à "page.php")
http://lehollandaisvolant.net/?mode=links&id=20160319122329

#14878

Marre de votre webmail ? Remplacez le par Rainloop ! - Korben

D’après les commentaires, ça serait un client IMAP en PHP qui ne garde rien en cache.

Quoi qu’il en soit, il a l’air sympa, surtout au niveau du design, mais pas vraiment au niveau de l’interface, qui refait certaines des mêmes erreur que GMail et Google+ (plus de 3 clics pour faire une action basique, c’est perdre son temps).
http://korben.info/marre-de-votre-webmail-remplacez-le-par-rainloop.html

#14809

How to define local functions in PHP? - Stack Overflow

Ok, donc en PHP les fonctions sont toujours globales, même quand on fait une fonction dans une fonction.

Une variable dans une fonction n’a qu’une portée locale, mais une fonction dans une fonction est globale. C’est pas très logique et contraire au JS il me semble.

Faut le savoir…

La solution c’est soit de ne pas imbriquer les fonctions (le plus simple), soit de tester si la fonction existe déjà (la plus facile à mettre en place dans un code existant), soit d’utiliser des fonctions anonymes (pas toujours possible, mais ces fonctions là ne sont pas globales).
http://stackoverflow.com/questions/7465451/how-to-define-local-functions-in-php

#14797

Note : astuce sécurité PHP

MÀJ : voir les deux ÉDIT plus bas

Utilisez ça en haut de votre script PHP :

if ($_SERVER['PHP_SELF'] !== $_SERVER['SCRIPT_NAME']) {
	header('Location: '.$_SERVER['SCRIPT_NAME']);
}


Quand je vais sur la page "/index.php" ou sur la page "/" d’un répertoire, le contenu du fichier "index.php" est exécuté en PHP. Ceci est normal.
Par contre, quand je vais sur l’URL "/index.php/" (même chose, mais avec un slash à la fin) ou "/index.php/code_ici", alors Apache considère qu’on va dans un sous-répertoire (à cause du slash). Or, l’ouverture d’un répertoire constitue l’exécution de ce dernier (sous Unix/Linux). Du coup, le contenu de "index.php" est exécuté quand même, alors que pour n’importe quel autre fichier, ça aurait fait une erreur 404.

Le code PHP fonctionnera bien, mais tous les liens relatifs seront cassés, car apache considère qu’on a franchi un nouveau niveau de profondeur dans l’arborescence des fichiers.
Pire, quand on utilise le $_SERVER['PHP_SELF'] , ça contiendra le "/" et tout ce qui suit. Utilise le PHP_SELF est donc compromettant et peut devenir une faille XSS :
/index.php/%22onmouseover=prompt(971741)%3E



Ce que fait mon code : il compare le chemin du fichier actuellement exécuté (index.php) avec le chemin du fichier (index.php/). Si les deux sont différents, il y a un problème, et on renvoie sur (index.php).


ÉDIT :
Je viens de voir que certains serveurs, selon la config, retiraient directement tout ce qui suivait le "index.php". Mon code ci-dessus ne marche donc plus.
Du coup j’ai pondu ceci, qui a l’air de faire son job :

if (basename($_SERVER['SCRIPT_NAME']) === 'index.php' and strpos(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), 'index.php') === FALSE ) {
	$var_request_URI = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH).'index.php';
} else {
	$var_request_URI = $_SERVER['REQUEST_URI'];
}
if (parse_url($var_request_URI, PHP_URL_PATH) !== $_SERVER['SCRIPT_NAME']) {
	header('Location: '.$_SERVER['SCRIPT_NAME']);
}


Vous pouvez essayer sur mon site :
http://lehollandaisvolant.net/
http://lehollandaisvolant.net/index.php
http://lehollandaisvolant.net/index.php?parametre
http://lehollandaisvolant.net/index.php/
http://lehollandaisvolant.net/index.php/?param
http://lehollandaisvolant.net/index.php/codeIci
http://lehollandaisvolant.net/index.php/codeIci/?param

J’ai vu que le problème était présent sur Shaarli aussi : allez sur votre shaarli, allez sur la page "index.php" (qui n’est habituellement pas affichée, mais qui existe quand même) et ajoutez un "/" à la fin de l’URL.

RÉ-ÉDIT : voir là : http://lehollandaisvolant.net/?mode=links&id=20160330174432
http://lehollandaisvolant.net/?mode=links&id=20160319122329

#14486

[PHP] Note : Parser du texte pour en faire des paragraphes

Dans un CMS, quand on doit parser le texte en du HTML, on utilise par exemple le nl2br() pour transformer les retours à la ligne en <br/>. Pour faire beau, on met tout ça dans un <div> et basta.

Sauf que ce n’est pas bien propre.
Le mieux c’est d’utiliser des balises <p>, les <br/> quand il faut (le second en cas d’un retour à la ligne, le premier en bas de deux retours à la ligne successifs, donc avec une ligne blanche entre les deux.

Ça semble simple.
Ça ne l’est pas.

Si votre texte contient déjà du HTML, vous vous retrouverez avec des <p>, des <ul> ou des éléments de type block dans des paragraphes. Ceci n’est pas valide W3C et c’est mal.

Voici une fonction qui prend du texte avec du HTML dedans et qui transforme ça en paragraphes : http://sebsauvage.net/paste/?39bac6fcd6d7e61e#EMx40oNNZwiVmkn3j5FYmjLHudTj9NkZ6J3PQvhNRmM=

Le fonctionnement est simple :
— 1) il analyse la première ligne.
— 2) si c’est un élément HTML de type block, il le passe dans le nl2br(), met ça de côté et retourne au 1)
— 3) sinon, si c’est du texte brute ou un élément de type inline, il grep le texte jusqu’à trouver deux retours à la ligne successifs (marquant le début d’un autre paragraphe) ou un élément de type block. Il capture le texte, le passe au nl2br(), le met entre deux balises <p>, met ça de côté et retourne au 1).

Ainsi, ligne après ligne, le texte est placé entre paragraphes ou conservé si c’est déjà un type block et on reconstitue un code HTML avec des paragraphes.

Mon code n’est pas parfait (il peut encore ajouter des <br/> là où il ne faut pas, par exemple dans un <pre> ou du code HTML mal indenté) mais fonctionne assez bien.
http://lehollandaisvolant.net/?mode=links&id=20160210185224

#14398

Note : PHP récupérer titre d’une page - Le Hollandais Volant

MÀJ.

Bon, finalement la lib DOM dans PHP n’est pas parfaite.
Exemple avec mon dernier lien : http://shelgon.tumblr.com/post/137159949929/it-has-been-announced-that-the-mythical-pok%C3%A9mon

L’encodage de la page est "utf-8".
L’encodage dans headers est "utf-8"
Il n’y a pas de caractères unicode échappés dans la balise title de la page (certains caractères sont bien échappés ailleurs dans la page, mais ça ne devrait pas gêner).

Pourtant ce con me retourne un titre comme ça : « The Problematic Pokémon ».

C’est la seule page qui me retourne ça. Une autre page Tumblr avec d’autres caractères unicode dans le titre fonctionne très bien. Tout comme les articles de mon site. Tout comme un lien en local.

J’ai aussi essayé de mettre mon script en UTF-8, avec iconv_set_encoding("internal_encoding", "UTF-8") et consorts, mais rien n’y fait.

WTF.


Bon bah on retourne à la bonne vieille regex manuelle, hein. Je ne veux pas d’une lib qui merde comme elle veut.
Je viens d’ailleurs d’améliorer ladite regex, passant de ça :

preg_match('#<title>(.*)</title>#Usi', $ext_file, $titles);

à ça :
preg_match('#<title ?[^>]*>(.*)</title>#Usi', $ext_file, $titles);


Certains sites (en particulier Google Play) utilisent des attributs sur la balise title, et il faut les prendre en compte.

(BTW, cette regex fonctionne sur mon lien avec « The Problematic Pokémon », et me donne bien le « é »)


Vivement le jour où tout le monde, tous les logiciels, partout, tout le temps, utiliseront la même chose.
http://lehollandaisvolant.net/?mode=links&id=20160128180954

#14377

Note : PHP récupérer titre d’une page

On peut faire de plein de moyens différents, généralement on passe par des preg_grep(), mais on a aussi le problème de l’encodage qu’il faut gérer.

Voilà une solution que je teste pour le moment avec assez de succès :

$url = "http://example.com/";

$html_source = file_get_contents($url);
$dom_tree = new DOMDocument();
$dom_tree->loadHTML($html_source);
$titles = $dom_tree->getElementsByTagName('title');
$title = trim($titles->item(0)->nodeValue);

return $title;



Ça utilise bien-sûr les lib intégrées à PHP pour parser du HTML. Évidemment, ça retournera plein d’erreurs si le HTML n’est pas valide (99% des pages web), donc j’ajoute aussi ça :

libxml_use_internal_errors(true);
// dom parsing
libxml_clear_errors();


Ça va laisser les erreurs de code dans la lib de parsage. Libre à vous de les ignorer ou non, mais elles ne viendront plus s’afficher en HTML.
N’oubliez pas d’ajouter les gardes-fou habituels : URL ne répond pas, URL n’est pas du HTML, etc.

Autrement, voilà :
mon code de récupération d’une page Web en PHP (avec cURL)
le même code, mis à jour pour les pages qui sont Gzipé par défaut

cURL me permet de faire une requête qui envoie des headers complètes (avec UA et autres, pour passez un éventuel "UA-gate"), de suivre les redirections, d’accepter les cookies et d’autres.

Avec ça je fais à peu près le tour.

Sinon, pour faire 150 requêtes en parallèle, file_get_contents() ne vous servira pas. CURL permet de le faire : http://lehollandaisvolant.net/?d=2014/05/20/19/21/36-php-faire-plusieurs-requetes-http-simultanees-avec-curl
http://lehollandaisvolant.net/?mode=links&id=20160128180954

#13307

Early Markdown implementation · timovn/blogotext · GitHub

J’inclus désormais une lib Markdown dans BT.

Pour l’activer, sur BT-3 : éditer le fichier « config/config-advanced.ini et ajoutez
use_markdown = 1;

à la suite des autres options (si il y a), sinon créez le fichier et ajouter cette ligne dedans.

NOTE : C’est une inclusion très basique et il reste des bug (les boutons de formatage dans les articles et les commentaires sont encore en BBCode), mais la conversion marche déjà bien.

La Lib utilisée est Parsedown : https://github.com/erusev/parsedown .
https://github.com/timovn/blogotext/commit/eeec080cb474a8ce27ea59febe1cb71671f58a80

#13253

Note : le problème des lecteur RSS en PHP (et une possible solution)

Un des problèmes des lecteurs RSS en PHP, et la raison pour laquelle j’utilise le mien en local et non sur mon site (même ça m’empêche de pouvoir lire mes flux en dehors de chez moi — chose qui ne me gène pas), c’est à cause de la latence des requêtes.

Une requête réseau, c’est lent : on a parfois largement le temps de lire un autre post (ou au moins cliquer sur "marquer comme lu") pendant que la réponse à la requête précédente nous revient.

Dans le cas de Blogotext, ceci génère une erreur : BT a besoin de la réponse précédente pour lancer la suivante. La raison à cela est la protection contre la faille CSRF : j’envoie un "jeton" unique à chaque requête, et la réponse m’en donne un nouveau (les jetons sont à usage unique).

Avec un lecteur RSS, on a ainsi beaucoup plus de requêtes réseau : à chaque fois qu’on marque un élément comme lu, en fait, ce qui dans une liste de sites suivis comme la mienne, représente ~300 posts par jour.

Si je n’ai pas vu d’erreurs sur les autres lecteurs RSS (j’ai pas regardé comment se passait la gestion des jetons de validation, non plus, mais si ces derniers ne font pas d’allers-retours entre serveur et navigateur à chaque requête, alors ils ne sont pas uniques et donc pas sécurisés au maximum), j’ai vu le problème de latence réseau sur chacun d’entre eux (et c’est vraiment quelque chose de chiant).

La solution à ça et que j’essaye d’implémenter actuellement, c’est de tenir (côté navigateur) une liste des éléments lus (en JS), et de n’envoyer la requête au serveur qu’une fois que la liste atteint une certaine longueur (10 éléments) : la liste des 10 éléments lus est envoyée au serveur en une seule fois. On divise ainsi le nombre de requêtes réseau par 10 (et le nombre de requêtes SQL également, du coup).
Une autre façon de faire serait de patienter un certain temps, et d’envoyer les requêtes toutes les X minutes, plutôt que tous les X éléments lus.

Bien-sûr, si je ferme mon onglet et qu’il reste moins de 10 éléments dans la liste côté navigateur, il faut envoyer une dernière requête (avec le JS "onUnload", par exemple).


Bref, ceci est juste une idée pour ceux qui veulent faire ou qui codent un lecteur RSS (ou autre chose qui demande beaucoup de ressources réseau) en PHP et hébergé sur un serveur distant, et qui veulent améliorer l’ensemble au niveau de l’utilisation.
http://lehollandaisvolant.net/?mode=links&id=20150913162722

#13092

My Collection of PHP Performance Benchmarks

if ("" == $var) est douze fois plus rapide que if ($var == ""). :O
Pas 12%, mais 12 fois. 1200% ! Je suis sur le cul. Qu’en est-il de JS et des autres langages ?
Ça ne vaut pas le empty($var), ceci dit, encore plus rapide évidemment.

Aussi, "$a == $b" est 3 fois plus rapide que "!strcmp($a, $b)". Marrant : strcmp() sert à rien, donc ? xD

Les benchmarks sont téléchargeables, btw, j’ai la même chose chez moi.
(via)
http://maettig.com/code/php/php-performance-benchmarks.php