PHP: file_exists vs stream_resolve_include_path - Что лучше?

Похоже, что в последнее время разработчики php довольно удивлялись тому, лучше ли использовать file_exists() или stream_resolve_include_path() при проверке наличия или отсутствия файла (будь то включение их, системы кэширования и т.д.).

Мне стало интересно, кто-нибудь там проверил тестирование тестов, какой из них является лучшим вариантом, для времени загрузки страницы, производительности сервера и использования памяти.

Я ничего не мог найти здесь, в SO, чтобы решить эту проблему, поэтому подумал, что пришло время для этого.

Ответ 1

Я сделал небольшой тест, но перед результатами посмотрю, как работают эти функции. Вы можете прочитать исходный код PHP здесь. Существует французская версия этого ответа, написанная ранее на этой неделе, хорошее время;).

Я также расскажу о is_file(), так как он определен в одну и ту же основную функцию в источнике. По основной функции я говорю источник C, не доступный из языка PHP в ваши скрипты.

Из того, что я понимаю, file_exists() и is_file() являются дочерними элементами основной функции php_stat(). Это очень упрощенный псевдокод процесса:

function php_stat($file)
{
    'file_exists'
        ↳ virtual_file_ex($file)
            ↳ virtual_access($file)
                'Windows'
                    ↳ tsrm_win32_access($file)
                        ↳ return access($file)
                'Other systems'
                    ↳ return access($file)
    'is_file'
        ↳ return $file.st_mode == S_IFREG
}

И псевдокод процесса stream_resolve_include_path():

function stream_resolve_include_path($file)
{
    zend_resolve_path($file)
        ↳ php_resolve_path_for_zend($file)
            ↳ php_resolve_path($file)
                ↳ tsrm_realpath($file)
                    ↳ return estrdup($file)
}

Отсюда, без числового результата теста, вы можете увидеть, как одна из функций стоит дорого в ресурсе.


Код для эталона:

function bench_file($file) {
    $res = array();
    $max = 1000000;

    // is_file()
    $res[] = microtime(1);
    for ( $i = 0; $i < $max; ++$i ) {
        if ( is_file($file) ) {
            //
        }
    }
    $res[] = microtime(1);

    clearstatcache();

    // file_exists()
    $res[] = microtime(1);
    for ( $i = 0; $i < $max; ++$i ) {
        if ( file_exists($file) ) {
            //
        }
    }
    $res[] = microtime(1);

    clearstatcache();

    // stream_resolve_include_path()
    $res[] = microtime(1);
    for ( $i = 0; $i < $max; ++$i ) {
        if ( stream_resolve_include_path($file) !== false ) {
            //
        }
    }
    $res[] = microtime(1);

    printf(
        'is_file = %f, file_exists = %f, stream_resolve_include_path = %f',
        $res[1] - $res[0], $res[3] - $res[2], $res[5] - $res[4]
    );

}

Пусть тест с файлом existante (1) и неустойчивым (2):

1 : is_file = 0.218582, file_exists = 0.742195, stream_resolve_include_path = 1.626521
2 : is_file = 0.458983, file_exists = 0.644638, stream_resolve_include_path = 5.623289

Результаты говорят сами за себя;)


Benchmark v2 - просто более простой способ добавить новые функции для тестирования.

function micro($func, $file) {
    $max = 1000000;
    $start = microtime(1);
    for ( $i = 0; $i < $max; ++$i ) {
        if ( $func($file) ) {
            //
        }
    }
    $end = microtime(1);
    clearstatcache();
    return $end - $start;
}

function bench_file($file) {
    $res = array(
        'is_file' => micro('is_file', $file),
        'file_exists' => micro('file_exists', $file),
        'stream_resolve_include_path' => micro('stream_resolve_include_path', $file)
    );
    $ret = '';
    foreach ( $res as $key => $value ) {
        $ret .= sprintf('%s = %f, ', $key, $value);
    }
    return trim($ret, ', ');
}

echo '<pre>', bench_file('file-ok'), "\n", bench_file('file-ko'), '</pre>';

Результаты:

is_file = 0.295752, file_exists = 0.852082, stream_resolve_include_path = 1.759607
is_file = 0.527770, file_exists = 0.724793, stream_resolve_include_path = 5.916151

Существует немного затрат на вызов $funct(), это объясняет несколько более высокие цифры.