diff --git a/src/Command/AbstractCommand.php b/src/Command/AbstractCommand.php index bac5935..d7d0e2e 100644 --- a/src/Command/AbstractCommand.php +++ b/src/Command/AbstractCommand.php @@ -11,6 +11,7 @@ namespace CacheTool\Command; +use CacheTool\CacheTool; use Symfony\Component\Console\Command\Command; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; diff --git a/src/Command/OpcacheStatusScriptsCommand.php b/src/Command/OpcacheStatusScriptsCommand.php index d95d4a7..10680f5 100644 --- a/src/Command/OpcacheStatusScriptsCommand.php +++ b/src/Command/OpcacheStatusScriptsCommand.php @@ -14,6 +14,7 @@ use CacheTool\Util\Formatter; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; class OpcacheStatusScriptsCommand extends AbstractOpcacheCommand @@ -26,7 +27,13 @@ protected function configure() $this ->setName('opcache:status:scripts') ->setDescription('Show scripts in the opcode cache') - ->setHelp(''); + ->setHelp('') + ->addOption( + 'exclude', + 'e', + InputOption::VALUE_OPTIONAL, + 'Exclude scripts that match this regex. Example: `.*vendor.*`. Delimiters are not needed.' + ); } /** @@ -39,6 +46,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int $info = $this->getCacheTool()->opcache_get_status(true); $this->ensureSuccessfulOpcacheCall($info); + $exclude = $input->getOption('exclude') ?? null; + $table = new Table($output); $table ->setHeaders([ @@ -46,7 +55,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int 'Memory', 'Filename' ]) - ->setRows($this->processFilelist($info['scripts'])) + ->setRows($this->processFilelist($info['scripts'], $exclude)) ; $table->render(); @@ -54,11 +63,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int return 0; } - protected function processFileList(array $cacheList) - { + protected function processFileList( + array $cacheList, + string $exclude = null + ) { $list = []; - foreach ($cacheList as $item) { + $filteredList = $exclude ? $this->excludeFiles($cacheList, $exclude) : $cacheList; + foreach ($filteredList as $item) { $list[] = [ number_format($item['hits']), Formatter::bytes($item['memory_consumption']), @@ -68,4 +80,9 @@ protected function processFileList(array $cacheList) return $list; } + + protected function excludeFiles(array $cacheList, string $exclude = null): array + { + return array_intersect_key($cacheList, array_flip(preg_grep("({$exclude})", array_keys($cacheList), \PREG_GREP_INVERT))); + } } diff --git a/tests/Command/CommandTest.php b/tests/Command/CommandTest.php index 35c933d..89f1ced 100644 --- a/tests/Command/CommandTest.php +++ b/tests/Command/CommandTest.php @@ -2,16 +2,57 @@ namespace CacheTool\Command; +use CacheTool\CacheTool; +use CacheTool\Code; use CacheTool\Console\Application; use CacheTool\Console\Config; +use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\StringInput; use Symfony\Component\Console\Output\BufferedOutput; abstract class CommandTest extends \PHPUnit\Framework\TestCase { - public function runCommand($cmd) + public function runCommand($cmd, $mockData = null) { - $app = new Application(new Config(['adapter' => 'cli'])); + $app = new class($mockData, new Config(['adapter' => 'cli'])) extends Application { + protected $mockData; + public function __construct($mockData, Config $config) + { + parent::__construct($config); + + $this->mockData = $mockData; + } + + public function buildContainer(InputInterface $input) + { + $container = parent::buildContainer($input); + + $cacheTool = CacheTool::factory( + new class($this->mockData) extends \CacheTool\Adapter\Cli { + public function __construct(protected $mockData) + {} + + public function doRun(Code $code) + { + if ($this->mockData) { + $wrappedMockData = [ + 'errors' => null, + 'result' => $this->mockData, + ]; + return serialize($wrappedMockData); + } + + return parent::doRun($code); + } + }, + $this->config['temp_dir'], + $this->logger + ); + $container->set('cachetool', $cacheTool); + + return $container; + } + }; $app->setAutoExit(false); $input = new StringInput($cmd); diff --git a/tests/Command/OpcacheStatusScriptsCommandTest.php b/tests/Command/OpcacheStatusScriptsCommandTest.php index b32ea6e..990e607 100644 --- a/tests/Command/OpcacheStatusScriptsCommandTest.php +++ b/tests/Command/OpcacheStatusScriptsCommandTest.php @@ -15,4 +15,74 @@ public function testCommand() $this->assertStringContainsString('Memory', $result); $this->assertStringContainsString('Filename', $result); } + + public function testExcludingScriptsWorksAsExpected() + { + $this->assertHasOpcache(); + + $scriptsMock = [ + '/vendor/somefile.php' => [ + 'full_path' => '/vendor/somefile.php', + 'hits' => 1, + 'memory_consumption' => 1024, + ], + '/src/my/path/to/somefile.php' => [ + 'full_path' => '/src/my/path/to/somefile.php', + 'hits' => 2, + 'memory_consumption' => 1024, + ], + '/src/my/other/path/to/somefile.php' => [ + 'full_path' => '/src/my/other/path/to/somefile.php', + 'hits' => 3, + 'memory_consumption' => 1024, + ], + '/vendor/someotherfile.php' => [ + 'full_path' => '/vendor/someotherfile.php', + 'hits' => 4, + 'memory_consumption' => 1024, + ], + ]; + + $result = $this->runCommand('opcache:status:scripts -v -e vendor', ['scripts' => $scriptsMock]); + + $this->assertStringContainsString('opcache_get_status(true)', $result); + $this->assertStringContainsString('/src/my/path/to/somefile.php', $result); + $this->assertStringNotContainsString('vendor', $result); // No findings of "vendor" expected! + } + + public function testNoScriptsAreExcludedByDefault() + { + $this->assertHasOpcache(); + + $scriptsMock = [ + '/vendor/somefile.php' => [ + 'full_path' => '/vendor/somefile.php', + 'hits' => 1, + 'memory_consumption' => 1024, + ], + '/src/my/path/to/somefile.php' => [ + 'full_path' => '/src/my/path/to/somefile.php', + 'hits' => 2, + 'memory_consumption' => 1024, + ], + '/src/my/other/path/to/somefile.php' => [ + 'full_path' => '/src/my/other/path/to/somefile.php', + 'hits' => 3, + 'memory_consumption' => 1024, + ], + '/vendor/someotherfile.php' => [ + 'full_path' => '/vendor/someotherfile.php', + 'hits' => 4, + 'memory_consumption' => 1024, + ], + ]; + + $result = $this->runCommand('opcache:status:scripts -v', ['scripts' => $scriptsMock]); + + $this->assertStringContainsString('opcache_get_status(true)', $result); + $this->assertStringContainsString('/vendor/somefile.php', $result); + $this->assertStringContainsString('/src/my/path/to/somefile.php', $result); + $this->assertStringContainsString('/src/my/other/path/to/somefile.php', $result); + $this->assertStringContainsString('/vendor/someotherfile.php', $result); + } }