diff --git a/README.md b/README.md index d552407..87bf3b7 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ This package generates a [PHP 7.4 preloading](https://www.php.net/manual/en/opca * [Compilation](#compilation) + [`writeTo()`](#writeto) + [`getList()`](#getlist) +- [Safe Preloader](#safe-preloader) - [Example](#example) - [Security](#security) - [License](#license) @@ -112,7 +113,7 @@ This is handy if you can combine the condition with your own application logic, #### `whenOneIn()` -This is method is just a helper to allows you to quickly make generate a Preloader list in one of a given number of random chances. +This is method is just a helper to allows you to quickly generate a Preloader script in one of a given number of random chances. ```php getList(); This may become handy if you have your own script, or you just want to tinker around it. +## Safe Preloader + +This packages comes with a handy Safe Preloader, located in `helpers/safe_preloader.php`. + +What it does is very simple: it registers a shutdown function for PHP that is executed after the preload script finishes, and registers any error the script may have returned so you can debug it. + +To use it, copy the file into an accessible path for PHP, and along with the real preloader script, reference it in your `php.ini`: + +```ini +opcache.preload=/www/app/safe_preloader.php +``` + +```php +sendToBrowser(); Preloader::make() ->whenOneIn(100) - ->memory(64) + ->memoryLimit(64) ->writeTo(PHP_LOCALSTATEDIR . '/preload.php'); // put it in /var.; ``` diff --git a/composer.json b/composer.json index 41935a0..f362278 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ }, "require-dev": { "phpunit/phpunit": "^9.0", - "symfony/finder": "^5.0.5" + "symfony/finder": "^4.3||^5.0.5" }, "autoload": { "psr-4": { diff --git a/src/Preloader.php b/src/Preloader.php index 173eeb2..2467dd0 100644 --- a/src/Preloader.php +++ b/src/Preloader.php @@ -172,6 +172,8 @@ protected function prepareCompiler(string $path) $this->compiler->contents = file_get_contents(static::STUB_LOCATION); $this->compiler->opcacheConfig = $this->getOpcacheConfig(); $this->compiler->preloaderConfig = $this->getPreloaderConfig(); + $this->compiler->useRequire = $this->useRequire; + $this->compiler->autoloader = $this->autoloader; $this->compiler->writeTo = $path; return $this->compiler; diff --git a/src/PreloaderCompiler.php b/src/PreloaderCompiler.php index 08be25c..d80443f 100644 --- a/src/PreloaderCompiler.php +++ b/src/PreloaderCompiler.php @@ -32,7 +32,7 @@ class PreloaderCompiler * * @var bool */ - public bool $useRequire = true; + public bool $useRequire; /** * The file list to include. diff --git a/tests/PreloaderTest.php b/tests/PreloaderTest.php index 4105770..1007d45 100644 --- a/tests/PreloaderTest.php +++ b/tests/PreloaderTest.php @@ -33,7 +33,7 @@ protected function setUp() : void $this->preloaderPath = $this->workdir . DIRECTORY_SEPARATOR . 'preloader.php'; } - public function test_when_condition_receives_callable_and_resolves_on_generation() + protected function mockOpcache($list = null) { $this->opcache->method('isEnabled') ->willReturn(true); @@ -55,7 +55,12 @@ public function test_when_condition_receives_callable_and_resolves_on_generation ], ]); $this->opcache->method('getScripts') - ->willReturn($this->list); + ->willReturn($list ?? $this->list); + } + + public function test_when_condition_receives_callable_and_resolves_on_generation() + { + $this->mockOpcache(); $preloader = new Preloader(new PreloaderCompiler, new PreloaderLister, $this->opcache); @@ -74,27 +79,7 @@ public function test_when_condition_receives_callable_and_resolves_on_generation public function test_when_one_in_condition_receives_callable_and_resolves_on_generation() { - $this->opcache->method('isEnabled') - ->willReturn(true); - $this->opcache->method('getNumberCachedScripts') - ->willReturn(1000); - $this->opcache->method('getHits') - ->willReturn(1001); - $this->opcache->method('getStatus') - ->willReturn([ - 'memory_usage' => [ - 'used_memory' => $usedMemory = rand(1000, 999999), - 'free_memory' => $freeMemory = rand(1000, 999999), - 'wasted_memory' => $wastedMemory = rand(1000, 999999), - ], - 'opcache_statistics' => [ - 'num_cached_scripts' => $cachedScripts = rand(1000, 999999), - 'opcache_hit_rate' => $hitRate = rand(100, 9999)/100, - 'misses' => $misses = rand(1000, 999999), - ], - ]); - $this->opcache->method('getScripts') - ->willReturn($this->list); + $this->mockOpcache(); $preloader = new Preloader(new PreloaderCompiler, new PreloaderLister, $this->opcache); @@ -112,27 +97,7 @@ public function test_when_one_in_condition_receives_callable_and_resolves_on_gen public function test_append_files_as_array() { - $this->opcache->method('isEnabled') - ->willReturn(true); - $this->opcache->method('getNumberCachedScripts') - ->willReturn(1000); - $this->opcache->method('getHits') - ->willReturn(1001); - $this->opcache->method('getStatus') - ->willReturn([ - 'memory_usage' => [ - 'used_memory' => $usedMemory = rand(1000, 999999), - 'free_memory' => $freeMemory = rand(1000, 999999), - 'wasted_memory' => $wastedMemory = rand(1000, 999999), - ], - 'opcache_statistics' => [ - 'num_cached_scripts' => $cachedScripts = rand(1000, 999999), - 'opcache_hit_rate' => $hitRate = rand(100, 9999)/100, - 'misses' => $misses = rand(1000, 999999), - ], - ]); - $this->opcache->method('getScripts') - ->willReturn($this->list); + $this->mockOpcache(); $preloader = new Preloader(new PreloaderCompiler, new PreloaderLister, $this->opcache); @@ -163,27 +128,7 @@ public function test_append_files_as_array() public function test_append_files_as_callable() { - $this->opcache->method('isEnabled') - ->willReturn(true); - $this->opcache->method('getNumberCachedScripts') - ->willReturn(1000); - $this->opcache->method('getHits') - ->willReturn(1001); - $this->opcache->method('getStatus') - ->willReturn([ - 'memory_usage' => [ - 'used_memory' => $usedMemory = rand(1000, 999999), - 'free_memory' => $freeMemory = rand(1000, 999999), - 'wasted_memory' => $wastedMemory = rand(1000, 999999), - ], - 'opcache_statistics' => [ - 'num_cached_scripts' => $cachedScripts = rand(1000, 999999), - 'opcache_hit_rate' => $hitRate = rand(100, 9999)/100, - 'misses' => $misses = rand(1000, 999999), - ], - ]); - $this->opcache->method('getScripts') - ->willReturn($this->list); + $this->mockOpcache(); $preloader = new Preloader(new PreloaderCompiler, new PreloaderLister, $this->opcache); @@ -214,43 +159,23 @@ public function test_append_files_as_callable() public function test_exclude_files_as_array() { - $this->opcache->method('isEnabled') - ->willReturn(true); - $this->opcache->method('getNumberCachedScripts') - ->willReturn(1000); - $this->opcache->method('getHits') - ->willReturn(1001); - $this->opcache->method('getStatus') - ->willReturn([ - 'memory_usage' => [ - 'used_memory' => $usedMemory = rand(1000, 999999), - 'free_memory' => $freeMemory = rand(1000, 999999), - 'wasted_memory' => $wastedMemory = rand(1000, 999999), - ], - 'opcache_statistics' => [ - 'num_cached_scripts' => $cachedScripts = rand(1000, 999999), - 'opcache_hit_rate' => $hitRate = rand(100, 9999)/100, - 'misses' => $misses = rand(1000, 999999), - ], - ]); - $this->opcache->method('getScripts') - ->willReturn([ - implode(DIRECTORY_SEPARATOR, [$this->workdir, 'examples', 'test_a', 'foo.php']) => [ // 3 - 'hits' => 10, - 'memory_consumption' => 1 * (1024 ** 2), - 'last_used_timestamp' => 1400000000 - ], - implode(DIRECTORY_SEPARATOR, [$this->workdir, 'examples', 'test_a', 'bar.php']) => [ // 1 - 'hits' => 20, - 'memory_consumption' => 3 * (1024 ** 2), - 'last_used_timestamp' => 1400000002 - ], - implode(DIRECTORY_SEPARATOR, [$this->workdir, 'examples', 'test_b', 'qux.php']) => [ // 1 - 'hits' => 20, - 'memory_consumption' => 3 * (1024 ** 2), - 'last_used_timestamp' => 1400000002 - ], - ]); + $this->mockOpcache([ + implode(DIRECTORY_SEPARATOR, [$this->workdir, 'examples', 'test_a', 'foo.php']) => [ // 3 + 'hits' => 10, + 'memory_consumption' => 1 * (1024 ** 2), + 'last_used_timestamp' => 1400000000 + ], + implode(DIRECTORY_SEPARATOR, [$this->workdir, 'examples', 'test_a', 'bar.php']) => [ // 1 + 'hits' => 20, + 'memory_consumption' => 3 * (1024 ** 2), + 'last_used_timestamp' => 1400000002 + ], + implode(DIRECTORY_SEPARATOR, [$this->workdir, 'examples', 'test_b', 'qux.php']) => [ // 1 + 'hits' => 20, + 'memory_consumption' => 3 * (1024 ** 2), + 'last_used_timestamp' => 1400000002 + ], + ]); $preloader = new Preloader(new PreloaderCompiler, new PreloaderLister, $this->opcache); @@ -277,43 +202,23 @@ public function test_exclude_files_as_array() public function test_exclude_files_as_closure() { - $this->opcache->method('isEnabled') - ->willReturn(true); - $this->opcache->method('getNumberCachedScripts') - ->willReturn(1000); - $this->opcache->method('getHits') - ->willReturn(1001); - $this->opcache->method('getStatus') - ->willReturn([ - 'memory_usage' => [ - 'used_memory' => $usedMemory = rand(1000, 999999), - 'free_memory' => $freeMemory = rand(1000, 999999), - 'wasted_memory' => $wastedMemory = rand(1000, 999999), - ], - 'opcache_statistics' => [ - 'num_cached_scripts' => $cachedScripts = rand(1000, 999999), - 'opcache_hit_rate' => $hitRate = rand(100, 9999)/100, - 'misses' => $misses = rand(1000, 999999), - ], - ]); - $this->opcache->method('getScripts') - ->willReturn([ - implode(DIRECTORY_SEPARATOR, [$this->workdir, 'examples', 'test_a', 'foo.php']) => [ // 3 - 'hits' => 10, - 'memory_consumption' => 1 * (1024 ** 2), - 'last_used_timestamp' => 1400000000 - ], - implode(DIRECTORY_SEPARATOR, [$this->workdir, 'examples', 'test_a', 'bar.php']) => [ // 1 - 'hits' => 20, - 'memory_consumption' => 3 * (1024 ** 2), - 'last_used_timestamp' => 1400000002 - ], - implode(DIRECTORY_SEPARATOR, [$this->workdir, 'examples', 'test_b', 'qux.php']) => [ // 1 - 'hits' => 20, - 'memory_consumption' => 3 * (1024 ** 2), - 'last_used_timestamp' => 1400000002 - ], - ]); + $this->mockOpcache([ + implode(DIRECTORY_SEPARATOR, [$this->workdir, 'examples', 'test_a', 'foo.php']) => [ // 3 + 'hits' => 10, + 'memory_consumption' => 1 * (1024 ** 2), + 'last_used_timestamp' => 1400000000 + ], + implode(DIRECTORY_SEPARATOR, [$this->workdir, 'examples', 'test_a', 'bar.php']) => [ // 1 + 'hits' => 20, + 'memory_consumption' => 3 * (1024 ** 2), + 'last_used_timestamp' => 1400000002 + ], + implode(DIRECTORY_SEPARATOR, [$this->workdir, 'examples', 'test_b', 'qux.php']) => [ // 1 + 'hits' => 20, + 'memory_consumption' => 3 * (1024 ** 2), + 'last_used_timestamp' => 1400000002 + ], + ]); $preloader = new Preloader(new PreloaderCompiler, new PreloaderLister, $this->opcache); @@ -340,58 +245,38 @@ public function test_exclude_files_as_closure() public function test_self_excludes_from_list() { - $this->opcache->method('isEnabled') - ->willReturn(true); - $this->opcache->method('getNumberCachedScripts') - ->willReturn(1000); - $this->opcache->method('getHits') - ->willReturn(1001); - $this->opcache->method('getStatus') - ->willReturn([ - 'memory_usage' => [ - 'used_memory' => $usedMemory = rand(1000, 999999), - 'free_memory' => $freeMemory = rand(1000, 999999), - 'wasted_memory' => $wastedMemory = rand(1000, 999999), - ], - 'opcache_statistics' => [ - 'num_cached_scripts' => $cachedScripts = rand(1000, 999999), - 'opcache_hit_rate' => $hitRate = rand(100, 9999)/100, - 'misses' => $misses = rand(1000, 999999), - ], - ]); - $this->opcache->method('getScripts') - ->willReturn([ - implode(DIRECTORY_SEPARATOR, [$this->workdir, 'examples', 'test_a', 'foo.php']) => [ // 3 - 'hits' => 10, - 'memory_consumption' => 1 * (1024 ** 2), - 'last_used_timestamp' => 1400000000 - ], - implode(DIRECTORY_SEPARATOR, [$this->workdir, 'examples', 'test_a', 'bar.php']) => [ // 1 - 'hits' => 20, - 'memory_consumption' => 3 * (1024 ** 2), - 'last_used_timestamp' => 1400000002 - ], - implode(DIRECTORY_SEPARATOR, [$this->workdir, 'examples', 'test_b', 'qux.php']) => [ // 1 - 'hits' => 20, - 'memory_consumption' => 3 * (1024 ** 2), - 'last_used_timestamp' => 1400000002 - ], - implode(DIRECTORY_SEPARATOR, [$this->workdir, 'examples', 'test_b', 'quz.php']) => [ // 1 - 'hits' => 20, - 'memory_consumption' => 3 * (1024 ** 2), - 'last_used_timestamp' => 1400000002 - ], - realpath(implode(DIRECTORY_SEPARATOR, [__DIR__, '..', 'src', 'Opcache.php'])) => [ // 1 - 'hits' => 20, - 'memory_consumption' => 3 * (1024 ** 2), - 'last_used_timestamp' => 1400000002 - ], - realpath(implode(DIRECTORY_SEPARATOR, [__DIR__, '..', 'src', 'Preloader.php'])) => [ // 1 - 'hits' => 20, - 'memory_consumption' => 3 * (1024 ** 2), - 'last_used_timestamp' => 1400000002 - ], - ]); + $this->mockOpcache([ + implode(DIRECTORY_SEPARATOR, [$this->workdir, 'examples', 'test_a', 'foo.php']) => [ // 3 + 'hits' => 10, + 'memory_consumption' => 1 * (1024 ** 2), + 'last_used_timestamp' => 1400000000 + ], + implode(DIRECTORY_SEPARATOR, [$this->workdir, 'examples', 'test_a', 'bar.php']) => [ // 1 + 'hits' => 20, + 'memory_consumption' => 3 * (1024 ** 2), + 'last_used_timestamp' => 1400000002 + ], + implode(DIRECTORY_SEPARATOR, [$this->workdir, 'examples', 'test_b', 'qux.php']) => [ // 1 + 'hits' => 20, + 'memory_consumption' => 3 * (1024 ** 2), + 'last_used_timestamp' => 1400000002 + ], + implode(DIRECTORY_SEPARATOR, [$this->workdir, 'examples', 'test_b', 'quz.php']) => [ // 1 + 'hits' => 20, + 'memory_consumption' => 3 * (1024 ** 2), + 'last_used_timestamp' => 1400000002 + ], + realpath(implode(DIRECTORY_SEPARATOR, [__DIR__, '..', 'src', 'Opcache.php'])) => [ // 1 + 'hits' => 20, + 'memory_consumption' => 3 * (1024 ** 2), + 'last_used_timestamp' => 1400000002 + ], + realpath(implode(DIRECTORY_SEPARATOR, [__DIR__, '..', 'src', 'Preloader.php'])) => [ // 1 + 'hits' => 20, + 'memory_consumption' => 3 * (1024 ** 2), + 'last_used_timestamp' => 1400000002 + ], + ]); $preloader = new Preloader(new PreloaderCompiler, new PreloaderLister, $this->opcache); @@ -428,27 +313,7 @@ public function test_self_excludes_from_list() public function test_limits_memory_list() { - $this->opcache->method('isEnabled') - ->willReturn(true); - $this->opcache->method('getNumberCachedScripts') - ->willReturn(1000); - $this->opcache->method('getHits') - ->willReturn(1001); - $this->opcache->method('getStatus') - ->willReturn([ - 'memory_usage' => [ - 'used_memory' => $usedMemory = rand(1000, 999999), - 'free_memory' => $freeMemory = rand(1000, 999999), - 'wasted_memory' => $wastedMemory = rand(1000, 999999), - ], - 'opcache_statistics' => [ - 'num_cached_scripts' => $cachedScripts = rand(1000, 999999), - 'opcache_hit_rate' => $hitRate = rand(100, 9999)/100, - 'misses' => $misses = rand(1000, 999999), - ], - ]); - $this->opcache->method('getScripts') - ->willReturn($this->list); + $this->mockOpcache(); $preloader = new Preloader(new PreloaderCompiler, new PreloaderLister, $this->opcache); @@ -466,27 +331,7 @@ public function test_limits_memory_list() public function test_memory_limit_disabled_lists_all_files() { - $this->opcache->method('isEnabled') - ->willReturn(true); - $this->opcache->method('getNumberCachedScripts') - ->willReturn(1000); - $this->opcache->method('getHits') - ->willReturn(1001); - $this->opcache->method('getStatus') - ->willReturn([ - 'memory_usage' => [ - 'used_memory' => $usedMemory = rand(1000, 999999), - 'free_memory' => $freeMemory = rand(1000, 999999), - 'wasted_memory' => $wastedMemory = rand(1000, 999999), - ], - 'opcache_statistics' => [ - 'num_cached_scripts' => $cachedScripts = rand(1000, 999999), - 'opcache_hit_rate' => $hitRate = rand(100, 9999)/100, - 'misses' => $misses = rand(1000, 999999), - ], - ]); - $this->opcache->method('getScripts') - ->willReturn($this->list); + $this->mockOpcache(); $preloader = new Preloader(new PreloaderCompiler, new PreloaderLister, $this->opcache); @@ -502,29 +347,23 @@ public function test_memory_limit_disabled_lists_all_files() $this->assertStringContainsString('baz.php', $contents); } + public function test_uses_compile_by_default() + { + $this->mockOpcache(); + + $preloader = new Preloader(new PreloaderCompiler, new PreloaderLister, $this->opcache); + + $preloader->writeTo($this->preloaderPath); + + $contents = file_get_contents($this->preloaderPath); + + $this->assertStringContainsString('opcache_compile_file($file)', $contents); + $this->assertStringNotContainsString('autoload', $contents); + } + public function test_uses_require_instead_of_compile() { - $this->opcache->method('isEnabled') - ->willReturn(true); - $this->opcache->method('getNumberCachedScripts') - ->willReturn(1000); - $this->opcache->method('getHits') - ->willReturn(1001); - $this->opcache->method('getStatus') - ->willReturn([ - 'memory_usage' => [ - 'used_memory' => $usedMemory = rand(1000, 999999), - 'free_memory' => $freeMemory = rand(1000, 999999), - 'wasted_memory' => $wastedMemory = rand(1000, 999999), - ], - 'opcache_statistics' => [ - 'num_cached_scripts' => $cachedScripts = rand(1000, 999999), - 'opcache_hit_rate' => $hitRate = rand(100, 9999)/100, - 'misses' => $misses = rand(1000, 999999), - ], - ]); - $this->opcache->method('getScripts') - ->willReturn($this->list); + $this->mockOpcache(); $preloader = new Preloader(new PreloaderCompiler, new PreloaderLister, $this->opcache); @@ -534,10 +373,10 @@ public function test_uses_require_instead_of_compile() $contents = file_get_contents($this->preloaderPath); - $this->assertStringContainsString('require_once ' . implode(DIRECTORY_SEPARATOR, [ + $this->assertStringContainsString('require_once ' . realpath(implode(DIRECTORY_SEPARATOR, [ $this->workdir, 'autoload.php' - ]), $contents); - $this->assertStringContainsString('require_once ', $contents); + ])), $contents); + $this->assertStringContainsString('require_once $file', $contents); } public function test_exception_when_autoload_doesnt_exists() @@ -545,27 +384,7 @@ public function test_exception_when_autoload_doesnt_exists() $this->expectException(LogicException::class); $this->expectExceptionMessage('Cannot proceed without a Composer Autoload.'); - $this->opcache->method('isEnabled') - ->willReturn(true); - $this->opcache->method('getNumberCachedScripts') - ->willReturn(1000); - $this->opcache->method('getHits') - ->willReturn(1001); - $this->opcache->method('getStatus') - ->willReturn([ - 'memory_usage' => [ - 'used_memory' => $usedMemory = rand(1000, 999999), - 'free_memory' => $freeMemory = rand(1000, 999999), - 'wasted_memory' => $wastedMemory = rand(1000, 999999), - ], - 'opcache_statistics' => [ - 'num_cached_scripts' => $cachedScripts = rand(1000, 999999), - 'opcache_hit_rate' => $hitRate = rand(100, 9999)/100, - 'misses' => $misses = rand(1000, 999999), - ], - ]); - $this->opcache->method('getScripts') - ->willReturn($this->list); + $this->mockOpcache(); $preloader = new Preloader(new PreloaderCompiler, new PreloaderLister, $this->opcache); @@ -576,27 +395,7 @@ public function test_exception_when_autoload_doesnt_exists() public function test_raw_list() { - $this->opcache->method('isEnabled') - ->willReturn(true); - $this->opcache->method('getNumberCachedScripts') - ->willReturn(1000); - $this->opcache->method('getHits') - ->willReturn(1001); - $this->opcache->method('getStatus') - ->willReturn([ - 'memory_usage' => [ - 'used_memory' => $usedMemory = rand(1000, 999999), - 'free_memory' => $freeMemory = rand(1000, 999999), - 'wasted_memory' => $wastedMemory = rand(1000, 999999), - ], - 'opcache_statistics' => [ - 'num_cached_scripts' => $cachedScripts = rand(1000, 999999), - 'opcache_hit_rate' => $hitRate = rand(100, 9999)/100, - 'misses' => $misses = rand(1000, 999999), - ], - ]); - $this->opcache->method('getScripts') - ->willReturn($this->list); + $this->mockOpcache(); $preloader = new Preloader(new PreloaderCompiler, new PreloaderLister, $this->opcache);