Note : PHP, curl_multi et barre de progression

#17662

Ok… donc en PHP dans mon lecteur RSS qui doit récupérer en parallèle 170 URL, j’affiche un petit compteur.

Le truc, c’est que c’est forcément dans une boucle while, et que cette boucle se fait aussi rapidement que le CPU le peu.

Donc si PHP envoie au navigateur un « +1 » à chaque fois qu’un flux est récupéré, il l’envoie quelques millions de fois par seconde : impensable, donc, ce qui fera des dizaines de Mo de données juste pour ça.

Du coup, j’avais mis un usleep(100000) dans la boucle… ça marchait. Par contre, ça bloquait également la récupération des flux ! Certains flux posaient donc problème…

Du coup j’ai remplacé ça par un test sur microtime(). Par contre, je maintiens un tout petit usleep() : la différence est flagrante : le sleep est assez faible pour pas que cURL coupe la requête, mais assez long pour éviter que le CPU ne fasse 1 milliard de tests par seconde :


	$running = 0;
	$utime = microtime(true);

	do {
		curl_multi_exec($master, $running);
		usleep(500);

		// echoes the nb of feeds remaining
		if ($utime + 1 < microtime(true)) {
			echo ($total_feed-$running).'/'.$total_feed.' '; ob_flush(); flush();
			$utime = microtime(true);
		}
	} while ($running > 0);

https://lehollandaisvolant.net/?mode=links&id=20180807154339

Des milliers d'anecdotes pour briller en soirée

#17507

Putain, ça m’énerve : le flux RSS de ce site est cassé 80% du temps !

La raison ? Parce qu’ils "coupent" les anecdotes (raison légitime : ils veulent qu’on vienne sur leur site pour lire).

Sauf que… ils coupent l’anecdote après un certain nombre de caractères. Comme si je coupais la chaîne « bonjour comment ça va ? » après 10 caractères, ce qui donnerait « bonjour co… ».

Le problème, c’est qu’on fait ça normalement en comptant les octets : Le 10e caractère débute après le 10e octet. Or, certains caractères, en particulier tout ce qui n’est pas ASCII, et en UTF-8, se codent sur plusieurs octets. C’est le cas de tous les caractères diacritiques (éàçèâê, etc.), qui sont codés en utilisant entre 2 et 4 octets.

Du coup, quand le 10e caractère est un diacritique, et qu’on coupe à 10 octet, on se retrouve en plein milieu d’un caractère et on se retrouve avec une erreur, le fameux « � ».

Et donc, les parseurs XML (du lecteur RSS) renvoient une erreur.

À la louche, je dirais que 1 caractère sur 20~25 est un diacritique en Français. Dit autrement, le N-ième caractère dans une anecdote a 1 chance sur 20 d’être un diacritique et de poser problème. Si le flux RSS contient 20 entrées, on a à peu près sûr que le flux est cassé la totalité du temps.

Il y a une solution à ça : juste dire que l’on ne doit pas couper au N-ième octet, mais au N-ième caractère, en disant au programme qui coupe les anecdotes « attention, certains caractères font plusieurs octets ».
En PHP, on fait ça en utilisant « mb_string » (où MB signifie « multibyte »).

J’en parle là : https://lehollandaisvolant.net/?id=20140424175730
Et pour le SQL : https://lehollandaisvolant.net/?id=20140504183832

La solution est SIMPLE : juste à remplacer une fonction par une autre et c’est bon.

Pourquoi ça m’énerve ?
Parce que ça fait plusieurs fois que je signal ça au site, et rien n’est fait. C’est pas le seul site qui tronque ses posts, mais c’est le seul qui pose problème comme ça.

Encore une fois, ça va être à moi (l’utilisateur) de devoir faire le boulot des webmaster. Y en a marre des incompétences ! Quand c’est pas ça, c’est une erreur d’encondage, ou un problème de format, ou de compression GZip, ou d’URL mal redirigée…

Au final, une simple fonction « récupérer une page web » c’est 5 lignes de code fonctionnelles, auquel on ajoute 5000 lignes pour corriger les erreurs parce que l’éditeur du site est un incompétent.

Dans le même gens, lisez ça : http://sebsauvage.net/wiki/doku.php?id=csv

ÉDIT : bon, finalement je résous ça avec ceci :

$string = iconv("UTF-8", "UTF-8//IGNORE", $string)

Ça prend la chaîne reçue (de la requête) en entrée et tente de la convertir en UTF-8. Si la fonction rencontre des caractères invalides, elle les supprime. La chaîne retournée est donc toujours du UTF-8 valide.

Par contre, visiblement, il y a un problème dans l’implémentation des bibliothèques UTF-8 depuis presque 10 ans et tout le monde s’en bat les steaks : http://php.net/manual/fr/function.iconv.php#108643
Ils conseillent de faire ça, en PHP :

  ini_set('mbstring.substitute_character', "none");
 $text= mb_convert_encoding($text, 'UTF-8', 'UTF-8'); 

Ça convertit de UTF-8 en UTF-8, mais en remplaçant tout ce qui est invalide par une chaîne vide (le "NONE").

Dans les deux cas, ça suggère que l’on possède déjà en entrée une chaîne en UTF-8, même cassée. Pour le moment, sur 165 flux RSS je ne vois pas d’erreurs, mais ça pourrait en poser. Je propose donc ceci :

  ini_set('mbstring.substitute_character', "none");
 $text= mb_convert_encoding($text, 'UTF-8'); 

Comme ça essaye de trouver le bon encodage dans une liste interne au serveur (définissable soi-même avec mb_detect_order()), puis de traduire ça en UTF-8.

Date.prototype.getTimezoneOffset() - JavaScript | MDN

#16855

gnîîîîî !

Encore ces incohérences entre JS et PHP.
Ici sur la sortie d’une date en une chaîne au format ISO 8601 : en PHP, la chaîne prend en compte le décalage par rapport à l’UTC. En JS, il n’y a pas de décalage : tout est en UTC.

Quant à calculer ce décalage : un UTC+2 (correspondant à Paris en été, par exemple), il est positif pour PHP (logique) mais négatif en JS (probablement car UTC est 120 minutes de moins que l’heure locale).

Date.prototype.getMonth() - JavaScript | MDN

#16800

Donc…
En JS, les mois vont de 0 à 11.
En PHP, les mois vont de 1 à 12.

Et les jours ?
En JS, les jours vont de 1 à 31.
En PHP, les jours vont de 1 à 31.

Le JS c’est à se taper la tête contre les murs, vraiment. Et là c’est juste une broutille (suffit de faire un −1 entre les deux langages [oui car mon PHP écrit du JS]). Mais parfois c’est bien plus chiant à corriger…

PHP: Possible modifiers in regex patterns - Manual

#15790

Donc pour faire en sorte que les caractéristiques accentués soient pris en compte dans \w, il faut ajouter le flag "u" (en minuscule).

Retourne false :

preg_match('#\w#', 'é') 

Retourne true :

preg_match('#\w#u', 'é') 

http://php.net/manual/en/reference.pcre.pattern.modifiers.php

Note : PHP, file_get_contents et URL

#15487

Sur certains serveurs, file_get_contents(), copy() et quelques autres fonctions qui peuvent fonctionner avec des URL (chemins distants) ne marchent pas : la directive INI allow_url_fopen est mise à Off.

Pour palier à ça, on peut utiliser cURL pour récupérer le fichier. Sur les serveurs qui n’ont que cette directive mise à Off, cURL (s’il est installé) fonctionnera.

On utilisera alors ça :

// request
$curl_handle = curl_init();
curl_setopt($curl_handle, CURLOPT_URL, $url);
curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, 1); 
$file_content = curl_exec($curl_handle);
curl_close($curl_handle);
// saving
file_put_contents($file_name, $file_content);


Dans les cas où le serveur bloque toutes les connexions sortantes, il n’y a pas de solutions pour faire des requêtes externes.

PNG Optimization with GD library

#15466

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

Note : PHP, imagerotate

#15429

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