PHP : is_file vs file_exists
lundi 14 octobre 2013 à 17:32is_file, comme la documentation l'indique, vérifie si le fichier est un véritable fichier, tandis que file_exists vérifie si un fichier ou un dossier existe. À savoir que ces deux fonctions utilisent un système de cache, elles partent donc sur un pied d'égalité.
Rien de mieux que le retour aux sources pour voir les entrailles de ces deux fonctions. Il s'avère qu'elles utilisent la même function en interne, seules les routines de vérification diffèrent.
Ci-dessous, le pseudo code très simplifié du processus de vérification :
Pour les curieux, voici les sources des fonctions :
Il faudra attendre de voir le résultat des tests pour se faire une meilleure idée.
Dans ce commentaire, DeyV laisse à penser que stream_resolve_include_path serait une bonne alternative àToujours d'après les sources, le pseudo code suivant en ressort :
Pour les curieux, le détail de chaque fonction :
Deux types de tests : un réaliste où on teste la présence de fichiers différents, et un autre moins parlant dans lequel le même fichier est testé 1 000 000 de fois.
Pour le test réaliste, j'ai créé un dossier test qui contient 1 000 000 fichiers (le premier fichier s'appelant 0 et le dernier 999999).
D'après le script qui permet de lancer et chronométrer les tests, le graphique suivant en résulte :
MàJ du 2013-10-23 : ajout des explications avec les sources PHP
MàJ du 2013-10-25 : Une meilleure alternative ?
MàJ du 2013-10-27 : ajout de tests plus réalistes et étoffement des informations
MàJ du 2013-10-28 : ajout du script de tests pour ne pas trop encombrer la page
Les sources
Rien de mieux que le retour aux sources pour voir les entrailles de ces deux fonctions. Il s'avère qu'elles utilisent la même function en interne, seules les routines de vérification diffèrent.
Ci-dessous, le pseudo code très simplifié du processus de vérification :
function php_stat($fichier)
{
'file_exists'
↳ virtual_file_ex($fichier)
↳ virtual_access($fichier)
'Windows'
↳ tsrm_win32_access($fichier)
↳ return access($fichier)
'Autres systèmes'
↳ return access($fichier)
↳ return _php_stream_stat_path($fichier) == FALSE
'is_file'
↳ _php_stream_stat_path($fichier)
↳ return $fichier.st_mode == S_IFREG
}
Pour les curieux, voici les sources des fonctions :
- php_stat
- virtual_file_ex
- virtual_access
- tsrm_win32_access
- access fait partie de la libc
- _php_stream_stat_path (c'est ici qu'on vérifie le cache)
- et S_IFREG est la constante des fichiers réguliers
file_exists()
nécessite bien plus d'opérations à effectuer. Cependant, _php_stream_stat_path()
est lourde puisqu'elle récupère toutes les informations sur un fichier/dossier (appelé inode) : volume, numéro d'inode, droit d'accès à l'inode, nombre de liens, userid du propriétaire, groupid du propriétaire, type du volume, si le volume est une inode, taille en octets, date de dernier accès (Unix timestamp), date de dernière modification (Unix timestamp), date de dernier changement d'inode (Unix timestamp), taille de bloc et le nombre de blocs de 512 octets alloués.Il faudra attendre de voir le résultat des tests pour se faire une meilleure idée.
Une meilleure alternative ?
Dans ce commentaire, DeyV laisse à penser que stream_resolve_include_path serait une bonne alternative à
file_exists()
. À priori, cette fonction utiliserait aussi le système de cache.Great alternative to file_exists() is stream_resolve_include_path() -- DeyV
function stream_resolve_include_path($fichier)
{
zend_resolve_path($fichier)
↳ php_resolve_path_for_zend($fichier)
↳ php_resolve_path($fichier)
↳ tsrm_realpath($fichier)
↳ return estrdup($fichier)
}
Pour les curieux, le détail de chaque fonction :
- stream_resolve_include_path
- zend_resolve_path
- (resolve_path_function)
- php_resolve_path_for_zend
- php_resolve_path
- tsrm_realpath
- et estrdup
Les tests
Configuration de la machine de test
- Intel Core i5 CPU 750 @ 2.67GHz
- 4 Go de RAM
- disque dur Seagate STM3500418AS 7200 tr/min
- Debian GNU/Linux 7 64 bit
- noyau 3.10-3-amd64
- système de fichiers EXT4
- PHP 5.5.5, options de build : --disable-all --disable-pdo --disable-phar --disable-session --disable-mysqlnd-compression-support --without-pear
Le benchmark
Deux types de tests : un réaliste où on teste la présence de fichiers différents, et un autre moins parlant dans lequel le même fichier est testé 1 000 000 de fois.
Pour le test réaliste, j'ai créé un dossier test qui contient 1 000 000 fichiers (le premier fichier s'appelant 0 et le dernier 999999).
D'après le script qui permet de lancer et chronométrer les tests, le graphique suivant en résulte :
Observations
- 1 :
is_file()
est plus rapide dans tous les cas - 2 :
file_exists()
a un léger avantage surstream_resolve_include_path()
, si ce dernier utilise les chemins absolus - 3 :
stream_resolve_include_path()
est un gouffre avec les chemins relatifs
Historique
MàJ du 2013-10-23 : ajout des explications avec les sources PHP
MàJ du 2013-10-25 : Une meilleure alternative ?
MàJ du 2013-10-27 : ajout de tests plus réalistes et étoffement des informations
MàJ du 2013-10-28 : ajout du script de tests pour ne pas trop encombrer la page