PHP Phar creation slow on a powerful PC, how to speed up (loading/reading ~3000 files)?

performance, phar, php, symfony

Solution

Try using Phar::addFile($file) instead of Phar::addFromString(file_get_contents($file))

i.e.

function addFile(Phar $phar, SplFileInfo $file)
{
    $root = realpath(__DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR);
    $path = strtr(str_replace($root, '', $file->getRealPath()), '\\', '/');
    //$phar->addFromString($path, file_get_contents($file));
    $phar->addFile($file,$path);
}

Problem

I'm trying to pack my web application (Symfony 2 project) with Phar. I've successfully packed Silex, a micro framework with hundred of files in a reasonable time (1-2 minutes). The problem is on my development machine (i7 4770k, 16GB, SSD Raid 0, project on a RAM disk) creating the archive is really slow, it takes ~1 sec for each file. I really need to find out a way to speed up things. Single iteration of reading/loading the file is slow. I'm adding files using: ``` function addFile(Phar $phar, SplFileInfo $file) { $root = realpath(__DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR); $path = strtr(str_replace($root, '', $file->getRealPath()), '\\', '/'); $phar->addFromString($path, file_get_contents($file)); } $phar = new Phar(/* ... */); $phar->startBuffering(); // ... foreach ($files as $file) { addFile($phar, $file); } // ... $phar->setStub(/* ... */); $phar->stopBuffering(); ``` How can I speed up reading/adding files? Could be my OS (Windows) the problem? EDIT: disabling buffering didn't solve the problem. Same speed adding from strings: ``` // This is VERY fast (~ 1 sec to read all 3000+ files) $strings = array(); foreach ($files as $file) { $root = realpath(__DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR); $path = strtr(str_replace($root, '', $file->getRealPath()), '\\', '/'); $strings[$path] = file_get_contents($file->getRealPath()); } // This is SLOW foreach ($strings as $local => $content) { $phar->addFromString($local, $content); } ``` EDIT: full quick&dirty script (may help) `app/build`: ``` #!/usr/bin/env php <?php set_time_limit(0); require __DIR__.'/../vendor/autoload.php'; use Symfony\Component\Finder\Finder; use Symfony\Component\Console\Input\ArgvInput; $input = new ArgvInput(); $env = $input->getParameterOption(array('--env', '-e'), 'dev'); function addFile(Phar $phar, SplFileInfo $file) { $root = realpath(__DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR); $path = strtr(str_replace($root, '', $file->getRealPath()), '\\', '/'); $phar->addFromString($path, file_get_contents($file)); } $phar = new Phar(__DIR__ . "/../symfony.phar", 0, "symfony.phar"); $phar->startBuffering(); $envexclude = array_diff(array('dev', 'prod', 'test'), array($env)); // App $app = (new Finder()) ->files() ->notPath('/cache/') ->notPath('/logs/') ->notName('build') ->notname('/._('.implode('|', $envexclude).')\.yml$/') ->in(__DIR__); // Vendor $vendor = (new Finder()) ->files() ->ignoreVCS(true) ->name('*.{php,twig,xlf,xsd,xml}') ->notPath('/tests/i') ->notPath('/docs/i') ->in(__DIR__.'/../vendor'); // Src $src = (new Finder()) ->files() ->notPath('/tests/i') ->in(__DIR__.'/../src'); // Web $web = (new Finder()) ->files() ->in(__DIR__.'/../web') ->notname('/._('.implode('|', $envexclude).')\.php$/'); $all = array_merge( iterator_to_array($app), iterator_to_array($src), iterator_to_array($vendor), iterator_to_array($web) ); $c = count($all); $i = 1; $strings = array(); foreach ($all as $file) { addFile($phar, $file); echo "Done $i/$c\r\n"; $i++; } $stub = <<<'STUB' Phar::webPhar(null, "/web/app_phar.php", null, array(), function ($path) { return '/web/app_phar.php'.$path; }); __HALT_COMPILER(); STUB; $phar->setStub($stub); $phar->stopBuffering(); ```

Original source

Related problems