├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── .phpcs.xml.dist ├── Api ├── Data │ └── ProfileInterface.php └── ProfileRepositoryInterface.php ├── Console └── Command │ ├── DatabaseProfilerDisableCommand.php │ └── DatabaseProfilerEnableCommand.php ├── Controller ├── Adminhtml │ └── Profiler │ │ └── Config.php ├── Cache.php ├── Cache │ ├── Clean.php │ ├── Disable.php │ ├── Enable.php │ └── Flush.php └── Profiler │ ├── Info.php │ ├── PHPInfo.php │ ├── Purge.php │ ├── Search.php │ └── Toolbar.php ├── Exception └── CollectorNotFoundException.php ├── Helper ├── Config.php ├── Database.php ├── Debug.php ├── File.php ├── Formatter.php ├── Injector.php └── Url.php ├── Logger ├── DataLogger.php ├── Handler.php ├── LoggableInterface.php └── Logger.php ├── Model ├── Collector │ ├── AjaxCollector.php │ ├── CacheCollector.php │ ├── CollectorInterface.php │ ├── ConfigCollector.php │ ├── CustomerCollector.php │ ├── DatabaseCollector.php │ ├── EventCollector.php │ ├── LateCollectorInterface.php │ ├── LayoutCollector.php │ ├── LoggerCollectorInterface.php │ ├── MemoryCollector.php │ ├── ModelCollector.php │ ├── PluginCollector.php │ ├── RequestCollector.php │ ├── TimeCollector.php │ └── TranslationCollector.php ├── Config │ ├── Database │ │ └── ProfilerWriter.php │ └── Source │ │ └── ErrorHandler.php ├── DataCollector.php ├── Indexer │ └── ProfileIndexer.php ├── Info │ ├── CacheInfo.php │ ├── CustomerInfo.php │ ├── DatabaseInfo.php │ ├── ExtensionInfo.php │ ├── LayoutInfo.php │ ├── MagentoInfo.php │ ├── MemoryInfo.php │ ├── PluginInfo.php │ └── RequestInfo.php ├── Profile.php ├── Profile │ └── Criteria.php ├── ProfileRepository.php ├── Profiler.php ├── Profiler │ └── Driver │ │ └── StopwatchDriver.php ├── Serializer │ ├── CollectorSerializer.php │ └── ProfileSerializer.php ├── Session.php ├── Storage │ ├── HttpStorage.php │ ├── ProfileFileStorage.php │ └── ProfileMemoryStorage.php ├── ValueObject │ ├── Block.php │ ├── CacheAction.php │ ├── EventObserver.php │ ├── LayoutNode.php │ ├── LoopModelAction.php │ ├── ModelAction.php │ ├── Plugin.php │ ├── Redirect.php │ ├── SearchResult.php │ └── Translation.php └── View │ ├── Menu.php │ ├── Profiler.php │ ├── Renderer │ ├── LayoutGraphRenderer.php │ ├── LayoutNodeRenderer.php │ ├── ParametersRenderer.php │ ├── QueryListRenderer.php │ ├── QueryParametersRenderer.php │ ├── QueryRenderer.php │ ├── RedirectRenderer.php │ ├── RendererInterface.php │ ├── TableRenderer.php │ ├── TraceCallRenderer.php │ ├── TraceRenderer.php │ └── VarRenderer.php │ ├── Search.php │ ├── Summary.php │ └── Toolbar.php ├── Observer ├── AllowedIP.php ├── BeforeSendResponse.php ├── Collector │ ├── LayoutCollectorAfterToHtml.php │ └── LayoutCollectorBeforeToHtml.php ├── Config │ └── DatabaseProfiler.php ├── DebugHandle.php └── ValidateRedirect.php ├── Plugin ├── Collector │ ├── CacheCollectorPlugin.php │ ├── EventCollectorPlugin.php │ ├── ModelCollectorPlugin.php │ ├── TimeCollectorPlugin.php │ └── TranslationCollectorPlugin.php ├── ErrorHandler │ └── WhoopsPlugin.php ├── PageCache │ └── KernelPlugin.php ├── PreventMessageBlockInitByToolbarPlugin.php ├── ProfileRepository │ └── RequestTimePlugin.php └── UseMagentoBackendThemeOnDebugFrontendViewPlugin.php ├── Readme.md ├── Serializer ├── Serializer.php └── SerializerInterface.php ├── Test └── Unit │ ├── Console │ └── Command │ │ ├── DatabaseProfilerDisableCommandTest.php │ │ └── DatabaseProfilerEnableCommandTest.php │ ├── Controller │ ├── Adminhtml │ │ └── Profiler │ │ │ └── ConfigTest.php │ ├── Cache │ │ ├── CacheControllerTestCase.php │ │ ├── CleanTest.php │ │ ├── DisableTest.php │ │ ├── EnableTest.php │ │ └── FlushTest.php │ └── Profiler │ │ ├── InfoTest.php │ │ ├── PHPInfoTest.php │ │ ├── PurgeTest.php │ │ ├── SearchTest.php │ │ └── ToolbarTest.php │ ├── Helper │ ├── ConfigTest.php │ └── DatabaseTest.php │ ├── Logger │ └── DataLoggerTest.php │ ├── Model │ ├── Storage │ │ ├── HttpStorageTest.php │ │ ├── ProfileFileStorageTest.php │ │ └── ProfileMemoryStorageTest.php │ └── ValueObject │ │ ├── BlockTest.php │ │ ├── CacheActionTest.php │ │ ├── EventObserverTest.php │ │ ├── LayoutNodeTest.php │ │ ├── LoopModelActionTest.php │ │ ├── ModelActionTest.php │ │ ├── PluginTest.php │ │ ├── RedirectTest.php │ │ ├── SearchResultTest.php │ │ └── TranslationTest.php │ ├── Observer │ ├── AllowedIPTest.php │ ├── BeforeSendResponseTest.php │ ├── Collector │ │ ├── LayoutCollectorAfterToHtmlTest.php │ │ └── LayoutCollectorBeforeToHtmlTest.php │ ├── Config │ │ └── DatabaseProfilerTest.php │ ├── DebugHandleTest.php │ └── ValidateRedirectTest.php │ ├── Plugin │ ├── Collector │ │ ├── CacheCollectorPluginTest.php │ │ ├── EventCollectorPluginTest.php │ │ ├── ModelCollectorPluginTest.php │ │ ├── TimeCollectorPluginTest.php │ │ └── TranslationCollectorPluginTest.php │ ├── ErrorHandler │ │ └── WhoopsPluginTest.php │ ├── PageCache │ │ └── KernelPluginTest.php │ └── ProfileRepository │ │ └── RequestTimePluginTest.php │ └── Serializer │ └── SerializerTest.php ├── composer.json ├── etc ├── adminhtml │ ├── di.xml │ ├── events.xml │ ├── routes.xml │ └── system.xml ├── config.xml ├── di.xml ├── events.xml ├── frontend │ ├── di.xml │ └── routes.xml └── module.xml ├── phpmd.xml ├── phpstan.neon.dist ├── phpunit.xml.dist ├── registration.php └── view └── base ├── layout ├── clawrock_debug.xml ├── debug_panel_cache.xml ├── debug_panel_config.xml ├── debug_panel_database.xml ├── debug_panel_event.xml ├── debug_panel_layout.xml ├── debug_panel_model.xml ├── debug_panel_plugin.xml ├── debug_panel_request.xml ├── debug_panel_time.xml ├── debug_panel_translation.xml ├── debug_profiler_info.xml ├── debug_profiler_search.xml └── debug_profiler_toolbar.xml ├── page_layout ├── profiler.xml └── toolbar.xml ├── templates ├── menu │ ├── cache.phtml │ ├── config.phtml │ ├── database.phtml │ ├── event.phtml │ ├── layout.phtml │ ├── model.phtml │ ├── plugin.phtml │ ├── request.phtml │ ├── settings.phtml │ ├── time.phtml │ └── translation.phtml ├── panel │ ├── cache.phtml │ ├── config.phtml │ ├── database.phtml │ ├── event.phtml │ ├── layout.phtml │ ├── model.phtml │ ├── plugin.phtml │ ├── request.phtml │ ├── time.phtml │ └── translation.phtml ├── profiler.phtml ├── profiler │ ├── js.phtml │ ├── summary.phtml │ └── toolbar │ │ └── js.phtml ├── renderer │ ├── layout │ │ ├── graph.phtml │ │ └── node.phtml │ ├── parameters.phtml │ ├── query.phtml │ ├── query │ │ └── list.phtml │ ├── redirect.phtml │ ├── table.phtml │ ├── trace.phtml │ └── trace │ │ └── call.phtml ├── search │ ├── input.phtml │ └── results.phtml ├── toolbar.phtml └── toolbar │ ├── ajax.phtml │ ├── cache.phtml │ ├── config.phtml │ ├── customer.phtml │ ├── database.phtml │ ├── event.phtml │ ├── layout.phtml │ ├── memory.phtml │ ├── model.phtml │ ├── plugin.phtml │ ├── request.phtml │ ├── time.phtml │ └── translation.phtml └── web ├── css ├── profiler.css └── toolbar.css └── images ├── clawrock.svg ├── collector ├── ajax.svg ├── cache.svg ├── config.svg ├── customer.svg ├── database.svg ├── event.svg ├── layout.svg ├── logs.svg ├── memory.svg ├── model.svg ├── models.svg ├── plugin.svg ├── redirect.svg ├── time.svg └── translation.svg ├── icon ├── close.svg ├── menu.svg ├── no.svg ├── search.svg ├── settings.svg └── yes.svg └── magento.svg /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: "Build" 2 | on: ["push", "pull_request"] 3 | jobs: 4 | static-analysis: 5 | name: "Static analysis" 6 | runs-on: "ubuntu-latest" 7 | strategy: 8 | fail-fast: false 9 | matrix: 10 | php: 11 | - "8.1" 12 | - "8.2" 13 | steps: 14 | - name: "Checkout" 15 | uses: "actions/checkout@v3" 16 | 17 | - name: "Setup PHP" 18 | uses: "shivammathur/setup-php@v2" 19 | with: 20 | php-version: ${{ matrix.php }} 21 | extensions: bcmath, gd, intl, soap, sockets, xsl, zip 22 | ini-values: memory_limit=-1 23 | tools: composer:v2 24 | 25 | - name: "Cache Composer dependencies" 26 | uses: "actions/cache@v2" 27 | with: 28 | path: "/tmp/composer-cache" 29 | key: php${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }} 30 | restore-keys: | 31 | php${{ matrix.php }}-composer- 32 | 33 | - name: Create auth.json 34 | run: echo '${{ secrets.COMPOSER_AUTH_JSON }}' > $GITHUB_WORKSPACE/auth.json 35 | 36 | - name: Composer install 37 | run: composer install --no-interaction --prefer-dist 38 | 39 | - name: Remove auth.json 40 | run: rm -f $GITHUB_WORKSPACE/auth.json 41 | 42 | - name: PHPUnit 43 | run: vendor/bin/phpunit -c phpunit.xml.dist Test --no-interaction 44 | 45 | - name: PHPCS 46 | run: vendor/bin/phpcs 47 | 48 | - name: PHPMD 49 | run: vendor/bin/phpmd . text phpmd.xml 50 | 51 | - name: PHPCPD 52 | run: vendor/bin/phpcpd --exclude vendor . 53 | 54 | - name: PHPStan 55 | run: vendor/bin/phpstan analyse --ansi --no-interaction 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | /vendor 3 | .phpunit.result.cache 4 | -------------------------------------------------------------------------------- /.phpcs.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | . 4 | 5 | 6 | *.xml 7 | */vendor/* 8 | */Test/.generated/tmp/generated/code/* 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | *.phtml 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Api/Data/ProfileInterface.php: -------------------------------------------------------------------------------- 1 | profilerWriter = $profilerWriter; 21 | } 22 | 23 | protected function configure(): void 24 | { 25 | parent::configure(); 26 | 27 | $this->setDescription('Disable database profiler required for database collector.'); 28 | } 29 | 30 | /** 31 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 32 | * 33 | * @param \Symfony\Component\Console\Input\InputInterface $input 34 | * @param \Symfony\Component\Console\Output\OutputInterface $output 35 | * @return int 36 | */ 37 | protected function execute(InputInterface $input, OutputInterface $output): int 38 | { 39 | $this->profilerWriter->save(false); 40 | 41 | $output->writeLn('Database profiler disabled!'); 42 | 43 | return Cli::RETURN_SUCCESS; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Console/Command/DatabaseProfilerEnableCommand.php: -------------------------------------------------------------------------------- 1 | profilerWriter = $profilerWriter; 21 | } 22 | 23 | protected function configure(): void 24 | { 25 | parent::configure(); 26 | 27 | $this->setDescription('Enable database profiler required for database collector.'); 28 | } 29 | 30 | /** 31 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 32 | * 33 | * @param \Symfony\Component\Console\Input\InputInterface $input 34 | * @param \Symfony\Component\Console\Output\OutputInterface $output 35 | * @return int 36 | */ 37 | protected function execute(InputInterface $input, OutputInterface $output): int 38 | { 39 | $this->profilerWriter->save(true); 40 | 41 | $output->writeLn('Database profiler enabled!'); 42 | 43 | return Cli::RETURN_SUCCESS; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Profiler/Config.php: -------------------------------------------------------------------------------- 1 | resultRedirectFactory->create(); 22 | 23 | return $resultRedirect->setPath('admin/system_config/edit', [ 24 | 'section' => 'clawrock_debug', 25 | 'key' => $this->_url->getSecretKey('adminhtml', 'system_config', 'edit'), // @phpstan-ignore-line 26 | ]); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Controller/Cache.php: -------------------------------------------------------------------------------- 1 | resultFactory = $resultFactory; 20 | $this->request = $request; 21 | $this->cacheManager = $cacheManager; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Controller/Cache/Clean.php: -------------------------------------------------------------------------------- 1 | resultFactory->create(ResultFactory::TYPE_JSON); 14 | $types = $this->request->getParam('type'); 15 | 16 | if (!$types) { 17 | $types = $this->cacheManager->getAvailableTypes(); 18 | } 19 | 20 | $this->cacheManager->clean((array) $types); 21 | 22 | return $result; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Controller/Cache/Disable.php: -------------------------------------------------------------------------------- 1 | resultFactory->create(ResultFactory::TYPE_JSON); 14 | $types = $this->request->getParam('type'); 15 | 16 | $this->cacheManager->setEnabled((array) $types, false); 17 | 18 | return $result; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Controller/Cache/Enable.php: -------------------------------------------------------------------------------- 1 | resultFactory->create(ResultFactory::TYPE_JSON); 14 | $types = $this->request->getParam('type'); 15 | 16 | $this->cacheManager->setEnabled((array) $types, true); 17 | 18 | return $result; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Controller/Cache/Flush.php: -------------------------------------------------------------------------------- 1 | resultFactory->create(ResultFactory::TYPE_JSON); 15 | $types = $this->request->getParam('type'); 16 | 17 | if (!$types) { 18 | $types = $this->cacheManager->getAvailableTypes(); 19 | } 20 | 21 | $this->cacheManager->flush((array) $types); 22 | 23 | return $result; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Controller/Profiler/PHPInfo.php: -------------------------------------------------------------------------------- 1 | resultFactory = $resultFactory; 18 | } 19 | 20 | public function execute(): ?ResultInterface 21 | { 22 | phpinfo(); 23 | 24 | return $this->resultFactory->create(ResultFactory::TYPE_RAW); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Controller/Profiler/Purge.php: -------------------------------------------------------------------------------- 1 | resultFactory = $resultFactory; 25 | $this->redirect = $redirect; 26 | $this->profileFileStorage = $profileFileStorage; 27 | $this->logger = $logger; 28 | } 29 | 30 | public function execute(): ?ResultInterface 31 | { 32 | try { 33 | $this->profileFileStorage->purge(); 34 | } catch (FileSystemException $e) { 35 | $this->logger->error('ClawRock_Debug: failed to purge file storage', ['exception' => $e]); 36 | } 37 | 38 | /** @var \Magento\Framework\Controller\Result\Redirect $result */ 39 | $result = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); 40 | $result->setUrl($this->redirect->getRefererUrl()); 41 | 42 | return $result; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Controller/Profiler/Toolbar.php: -------------------------------------------------------------------------------- 1 | resultFactory = $resultFactory; 24 | $this->request = $request; 25 | $this->profileMemoryStorage = $profileMemoryStorage; 26 | $this->profileRepository = $profileRepository; 27 | } 28 | 29 | public function execute(): ?\Magento\Framework\Controller\ResultInterface 30 | { 31 | $token = $this->request->getParam(Profiler::URL_TOKEN_PARAMETER); 32 | $profile = $this->profileRepository->getById($token); 33 | $this->profileMemoryStorage->write($profile); 34 | 35 | return $this->resultFactory->create(ResultFactory::TYPE_PAGE); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Exception/CollectorNotFoundException.php: -------------------------------------------------------------------------------- 1 | isBacktraceItemValid($item, $functions)) { 23 | array_shift($backtrace); 24 | $item = reset($backtrace); 25 | } 26 | 27 | $backtrace = array_map(function ($item) { 28 | unset($item['object'], $item['args'], $item['type']); 29 | 30 | return $item; 31 | }, $backtrace); 32 | 33 | return $backtrace; 34 | } 35 | 36 | private function isBacktraceItemValid(array $data, array $functions): bool 37 | { 38 | if (!isset($data['class'], $data['function'])) { 39 | return false; 40 | } 41 | 42 | if (empty($functions)) { 43 | return true; 44 | } 45 | 46 | if (!in_array($data['function'], $functions)) { 47 | return false; 48 | } 49 | 50 | return true; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Helper/File.php: -------------------------------------------------------------------------------- 1 | directoryList = $directoryList; 14 | } 15 | 16 | public function getProfileDirectory(): string 17 | { 18 | return $this->directoryList->getPath('var') . DIRECTORY_SEPARATOR . 'debug'; 19 | } 20 | 21 | public function getProfileIndex(): string 22 | { 23 | return $this->directoryList->getPath('var') . DIRECTORY_SEPARATOR . 'debug' . DIRECTORY_SEPARATOR . 'index.csv'; 24 | } 25 | 26 | public function getProfileTempIndex(): string 27 | { 28 | return $this->directoryList->getPath('var') . DIRECTORY_SEPARATOR . 'debug' . DIRECTORY_SEPARATOR . 'tmp' 29 | . DIRECTORY_SEPARATOR . 'index.csv'; 30 | } 31 | 32 | public function getProfileFilename(string $token): string 33 | { 34 | return $this->getProfileDirectory() . DIRECTORY_SEPARATOR 35 | . substr($token, -2, 2) . DIRECTORY_SEPARATOR 36 | . substr($token, -4, 2) . DIRECTORY_SEPARATOR 37 | . $token; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Helper/Formatter.php: -------------------------------------------------------------------------------- 1 | config = $config; 14 | } 15 | 16 | public function microtime(float $value, ?int $precision = null): string 17 | { 18 | if ($precision === null) { 19 | $precision = $this->config->getTimePrecision(); 20 | } 21 | 22 | return sprintf('%0.' . $precision . 'f', $value * 1000); 23 | } 24 | 25 | public function revertMicrotime(string $value): float 26 | { 27 | return (float) $value / 1000; 28 | } 29 | 30 | public function toMegaBytes(int $value, int $precision = 0): string 31 | { 32 | return sprintf('%0.' . $precision . 'f', $value / 1024 /1024); 33 | } 34 | 35 | public function percentage(float $value, int $precision = 5): string 36 | { 37 | return sprintf('%.' . $precision . 'f%%', $value * 100); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Helper/Url.php: -------------------------------------------------------------------------------- 1 | url = $url; 23 | $this->backendUrl = $backendUrl; 24 | } 25 | 26 | public function getAdminUrl(): string 27 | { 28 | return $this->backendUrl->getRouteUrl(Area::AREA_ADMINHTML); 29 | } 30 | 31 | public function getConfigurationUrl(): string 32 | { 33 | return $this->backendUrl->getUrl(self::CONFIGURATION_URL_PATH); 34 | } 35 | 36 | public function getProfilerUrl(?string $token = null, ?string $panel = null): string 37 | { 38 | $params = []; 39 | if ($token) { 40 | $params[Profiler::URL_TOKEN_PARAMETER] = $token; 41 | } 42 | if ($panel) { 43 | $params[Profiler::URL_PANEL_PARAMETER] = $panel; 44 | } 45 | 46 | return $this->url->getUrl(self::PROFILER_URL_PATH, $params); 47 | } 48 | 49 | public function getToolbarUrl(string $token): string 50 | { 51 | return $this->url->getUrl('_debug/profiler/toolbar', [ 52 | Profiler::URL_TOKEN_PARAMETER => $token, 53 | '_nosid' => true, 54 | ]); 55 | } 56 | 57 | public function getRequestFullActionName(Request $request): string 58 | { 59 | try { 60 | // @phpstan-ignore-next-line 61 | return $request->getRouteName() . '_' . $request->getControllerName() . '_' . $request->getActionName(); 62 | } catch (\Exception $e) { 63 | return 'unknown'; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Logger/DataLogger.php: -------------------------------------------------------------------------------- 1 | data[$value->getId()] = $value; 13 | 14 | return $this; 15 | } 16 | 17 | public function getLogs(): array 18 | { 19 | return $this->data; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Logger/Handler.php: -------------------------------------------------------------------------------- 1 | config = $config; 16 | } 17 | 18 | public function collect(): CollectorInterface 19 | { 20 | // Nothing to collect here 21 | return $this; 22 | } 23 | 24 | /** 25 | * @return bool 26 | */ 27 | public function isEnabled(): bool 28 | { 29 | return $this->config->isAjaxCollectorEnabled(); 30 | } 31 | 32 | public function getData(): array 33 | { 34 | return []; 35 | } 36 | 37 | public function setData(array $data): CollectorInterface 38 | { 39 | return $this; 40 | } 41 | 42 | public function getName(): string 43 | { 44 | return self::NAME; 45 | } 46 | 47 | public function getStatus(): string 48 | { 49 | return self::STATUS_DEFAULT; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Model/Collector/CollectorInterface.php: -------------------------------------------------------------------------------- 1 | configWriter = $configWriter; 18 | } 19 | 20 | public function save(bool $flag): void 21 | { 22 | $configGroup = [ 23 | ConfigOptionsListConstants::CONFIG_PATH_DB => [ 24 | 'connection' => [ 25 | 'default' => [ 26 | 'profiler' => [ 27 | 'class' => Profiler::class, 28 | 'enabled' => $flag, 29 | ], 30 | ], 31 | ], 32 | ], 33 | ]; 34 | 35 | $this->configWriter->saveConfig([ConfigFilePool::APP_ENV => $configGroup]); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Model/Config/Source/ErrorHandler.php: -------------------------------------------------------------------------------- 1 | self::MAGENTO, 'label' => __('Default')], 17 | ['value' => self::WHOOPS, 'label' => __('Whoops')], 18 | ]; 19 | } 20 | 21 | public function toArray(): array 22 | { 23 | return [ 24 | self::MAGENTO => __('Default'), 25 | self::WHOOPS => __('Whoops'), 26 | ]; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Model/DataCollector.php: -------------------------------------------------------------------------------- 1 | $value) { 13 | $this->data[$key] = $value; 14 | } 15 | 16 | return $this; 17 | } 18 | 19 | /** 20 | * @param string $key 21 | * @return mixed 22 | */ 23 | public function getData(string $key = '') 24 | { 25 | if ($key) { 26 | return $this->data[$key] ?? null; 27 | } 28 | 29 | return $this->data; 30 | } 31 | 32 | /** 33 | * @param string $key 34 | * @param mixed $value 35 | * @return $this 36 | */ 37 | public function addData(string $key, $value): DataCollector 38 | { 39 | $this->data[$key] = $value; 40 | 41 | return $this; 42 | } 43 | 44 | /** 45 | * @param string $key 46 | * @param mixed $value 47 | * @return $this 48 | */ 49 | public function appendData(string $key, $value): DataCollector 50 | { 51 | if (!isset($this->data[$key])) { 52 | $this->data[$key] = []; 53 | } 54 | $this->data[$key][] = $value; 55 | 56 | return $this; 57 | } 58 | 59 | public function removeData(string $key): DataCollector 60 | { 61 | unset($this->data[$key]); 62 | 63 | return $this; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Model/Indexer/ProfileIndexer.php: -------------------------------------------------------------------------------- 1 | fileSystem = $fileSystem; 22 | $this->fileWriteFactory = $fileWriteFactory; 23 | $this->logger = $logger; 24 | $this->fileHelper = $fileHelper; 25 | } 26 | 27 | public function index(ProfileInterface $profile): void 28 | { 29 | try { 30 | $tmpIndexPath = $this->fileHelper->getProfileTempIndex(); 31 | $this->fileSystem->createDirectory($this->fileSystem->getParentDirectory($tmpIndexPath)); 32 | $tmpIndex = $this->fileWriteFactory->create($tmpIndexPath, $this->fileSystem, 'w'); 33 | 34 | $tmpIndex->writeCsv($profile->getIndex()); 35 | $index = $tmpIndex->readAll(); 36 | $tmpIndex->close(); 37 | $index .= $this->fileSystem->isExists($this->fileHelper->getProfileIndex()) 38 | ? $this->fileSystem->fileGetContents($this->fileHelper->getProfileIndex()) 39 | : ''; 40 | 41 | $this->fileSystem->filePutContents($this->fileHelper->getProfileIndex(), $index); 42 | } catch (\Exception $e) { 43 | $this->logger->error('ClawRock_Debug: Error during profile indexation', ['exception' => $e]); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Model/Info/CustomerInfo.php: -------------------------------------------------------------------------------- 1 | session = $session; 21 | $this->groupRepository = $groupRepository; 22 | $this->groupInterfaceFactory = $groupInterfaceFactory; 23 | } 24 | 25 | public function isLoggedIn(): bool 26 | { 27 | return $this->session->isLoggedIn(); 28 | } 29 | 30 | public function getCustomer(): Customer 31 | { 32 | return $this->session->getCustomer(); 33 | } 34 | 35 | public function getGroup(): GroupInterface 36 | { 37 | try { 38 | $group = $this->groupRepository->getById($this->getCustomer()->getGroupId()); 39 | } catch (\Exception $e) { 40 | $group = $this->groupInterfaceFactory->create(); 41 | } 42 | 43 | return $group; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Model/Info/ExtensionInfo.php: -------------------------------------------------------------------------------- 1 | layout = $layout; 17 | } 18 | 19 | public function getHandles(): array 20 | { 21 | return $this->layout->getUpdate()->getHandles(); 22 | } 23 | 24 | public function getCreatedBlocks(): array 25 | { 26 | $blocks = []; 27 | foreach ($this->layout->getAllBlocks() as $block) { 28 | $blocks[] = new Block($block); 29 | } 30 | 31 | return $blocks; 32 | } 33 | 34 | public function getNotRenderedBlocks(): array 35 | { 36 | $blocks = []; 37 | foreach ($this->layout->getAllBlocks() as $block) { 38 | /** @var \Magento\Framework\View\Element\AbstractBlock $block */ 39 | if (!$block->getData(LayoutCollector::BLOCK_PROFILER_ID_KEY)) { 40 | $blocks[] = new Block($block); 41 | } 42 | } 43 | 44 | return $blocks; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Model/Info/MemoryInfo.php: -------------------------------------------------------------------------------- 1 | convertToBytes((string) ini_get('memory_limit')); 11 | } 12 | 13 | public function getCurrentPeakMemoryUsage(): int 14 | { 15 | return memory_get_peak_usage(true); 16 | } 17 | 18 | private function convertToBytes(string $memoryLimit): int 19 | { 20 | if ('-1' === $memoryLimit) { 21 | return -1; 22 | } 23 | 24 | $memoryLimit = strtolower($memoryLimit); 25 | $max = $this->readValue($memoryLimit); 26 | 27 | switch (substr($memoryLimit, -1)) { 28 | case 't': 29 | $max *= 1024; 30 | // no break 31 | case 'g': 32 | $max *= 1024; 33 | // no break 34 | case 'm': 35 | $max *= 1024; 36 | // no break 37 | case 'k': 38 | $max *= 1024; 39 | } 40 | 41 | return $max; 42 | } 43 | 44 | private function readValue(string $memoryLimit): int 45 | { 46 | $value = ltrim($memoryLimit, '+'); 47 | if (0 === strpos($value, '0x')) { 48 | // phpcs:ignore Magento2.Functions.DiscouragedFunction.DiscouragedWithAlternative 49 | return intval($value, 16); 50 | } 51 | 52 | if (0 === strpos($value, '0')) { 53 | // phpcs:ignore Magento2.Functions.DiscouragedFunction.DiscouragedWithAlternative 54 | return intval($value, 8); 55 | } 56 | 57 | return (int) $value; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Model/Serializer/CollectorSerializer.php: -------------------------------------------------------------------------------- 1 | objectManager = $objectManager; 20 | $this->logger = $logger; 21 | $this->config = $config; 22 | } 23 | 24 | /** 25 | * @param \ClawRock\Debug\Model\Collector\CollectorInterface[] $collectors 26 | * @return array 27 | */ 28 | public function serialize(array $collectors): array 29 | { 30 | foreach ($collectors as &$collector) { 31 | $collector = $collector->getData(); 32 | } 33 | 34 | return $collectors; 35 | } 36 | 37 | public function unserialize(array $data): array 38 | { 39 | $collectors = []; 40 | foreach ($data as $name => $collector) { 41 | try { 42 | $collectorClass = $this->config->getCollectorClass($name); 43 | $collectors[$name] = $this->objectManager->create($collectorClass)->setData($collector); 44 | } catch (CollectorNotFoundException $e) { 45 | $this->logger->error(sprintf('ClawRock_Debug: collector "%s" not found', $name), ['exception' => $e]); 46 | continue; 47 | } 48 | } 49 | 50 | return $collectors; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Model/Serializer/ProfileSerializer.php: -------------------------------------------------------------------------------- 1 | serializer = $serializer; 20 | $this->collectorSerializer = $collectorSerializer; 21 | $this->profileFactory = $profileFactory; 22 | } 23 | 24 | public function serialize(ProfileInterface $profile): string 25 | { 26 | return $this->serializer->serialize(array_merge( 27 | $profile->getData(), 28 | ['collectors' => $this->collectorSerializer->serialize($profile->getCollectors())] 29 | )); 30 | } 31 | 32 | public function unserialize(string $data): ProfileInterface 33 | { 34 | $profileData = $this->serializer->unserialize($data); 35 | $collectors = $this->collectorSerializer->unserialize($profileData['collectors']); 36 | unset($profileData['collectors']); 37 | 38 | /** @var \ClawRock\Debug\Model\Profile $profile */ 39 | $profile = $this->profileFactory->create(['token' => $profileData['token']])->setData($profileData); 40 | $profile->setCollectors($collectors); 41 | 42 | return $profile; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Model/Session.php: -------------------------------------------------------------------------------- 1 | request; 18 | } 19 | 20 | public function setRequest(Request $request): HttpStorage 21 | { 22 | $this->request = $request; 23 | 24 | return $this; 25 | } 26 | 27 | public function getResponse(): Response 28 | { 29 | return $this->response; 30 | } 31 | 32 | public function setResponse(Response $response): HttpStorage 33 | { 34 | $this->response = $response; 35 | 36 | return $this; 37 | } 38 | 39 | public function markAsFPCRequest(): HttpStorage 40 | { 41 | $this->fpc = true; 42 | 43 | return $this; 44 | } 45 | 46 | public function isFPCRequest(): bool 47 | { 48 | return $this->fpc; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Model/Storage/ProfileMemoryStorage.php: -------------------------------------------------------------------------------- 1 | profile; 15 | } 16 | 17 | public function write(ProfileInterface $profile): void 18 | { 19 | $this->profile = $profile; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Model/ValueObject/Block.php: -------------------------------------------------------------------------------- 1 | id = (string) $block->getData(LayoutCollector::BLOCK_PROFILER_ID_KEY); 23 | $this->name = (string) $block->getNameInLayout(); 24 | $this->class = get_class($block); 25 | $this->module = $block->getModuleName(); 26 | $this->renderTime = (float) $block->getData(LayoutCollector::RENDER_TIME); 27 | $this->template = (string) $block->getTemplate(); 28 | $this->children = $block->getChildNames(); 29 | $this->parentId = $block->getParentBlock() instanceof \Magento\Framework\View\Element\AbstractBlock 30 | ? (string) $block->getParentBlock()->getData(LayoutCollector::BLOCK_PROFILER_ID_KEY) 31 | : ''; 32 | } 33 | 34 | public function getId(): string 35 | { 36 | return $this->id; 37 | } 38 | 39 | public function getName(): string 40 | { 41 | return $this->name; 42 | } 43 | 44 | public function getClass(): string 45 | { 46 | return $this->class; 47 | } 48 | 49 | public function getModule(): string 50 | { 51 | return $this->module; 52 | } 53 | 54 | public function getRenderTime(): float 55 | { 56 | return $this->renderTime; 57 | } 58 | 59 | public function getTemplate(): string 60 | { 61 | return $this->template; 62 | } 63 | 64 | public function getChildren(): array 65 | { 66 | return $this->children; 67 | } 68 | 69 | public function getParentId(): string 70 | { 71 | return $this->parentId; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Model/ValueObject/CacheAction.php: -------------------------------------------------------------------------------- 1 | id = $id; 30 | $this->name = $name; 31 | $this->time = $time; 32 | $this->info = $info; 33 | } 34 | 35 | public function getId(): string 36 | { 37 | return $this->id; 38 | } 39 | 40 | public function getName(): string 41 | { 42 | return $this->name; 43 | } 44 | 45 | public function isLoad(): bool 46 | { 47 | return $this->name === self::LOAD; 48 | } 49 | 50 | public function getTime(): float 51 | { 52 | return $this->time; 53 | } 54 | 55 | public function getInfo(): array 56 | { 57 | return $this->info; 58 | } 59 | 60 | public function isHit(): bool 61 | { 62 | return $this->info[self::CACHE_HIT] ?? false; 63 | } 64 | 65 | public function getTags(): array 66 | { 67 | return $this->info[self::CACHE_TAGS] ?? []; 68 | } 69 | 70 | public function hasTags(): bool 71 | { 72 | return !empty($this->getTags()); 73 | } 74 | 75 | public function getTTL(): int 76 | { 77 | return (int) ($this->info[self::CACHE_TTL] ?? 0); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Model/ValueObject/EventObserver.php: -------------------------------------------------------------------------------- 1 | id = uniqid(); 23 | $this->name = $name; 24 | $this->class = $class; 25 | $this->event = $event; 26 | $this->time = $time; 27 | } 28 | 29 | public function getId(): string 30 | { 31 | return $this->getName() . '_' . $this->id; 32 | } 33 | 34 | public function getName(): string 35 | { 36 | return $this->name; 37 | } 38 | 39 | public function getClass(): string 40 | { 41 | return $this->class; 42 | } 43 | 44 | public function getEvent(): string 45 | { 46 | return $this->event; 47 | } 48 | 49 | public function getTime(): float 50 | { 51 | return $this->time; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Model/ValueObject/LayoutNode.php: -------------------------------------------------------------------------------- 1 | block = $block; 20 | $this->layoutRenderTime = $layoutRenderTime; 21 | $this->prefix = $prefix; 22 | $this->children = $children; 23 | } 24 | 25 | public function getName(): string 26 | { 27 | return $this->block->getName(); 28 | } 29 | 30 | public function getClass(): string 31 | { 32 | return $this->block->getClass(); 33 | } 34 | 35 | public function getModule(): string 36 | { 37 | return $this->block->getModule(); 38 | } 39 | 40 | public function getRenderTime(): float 41 | { 42 | return $this->block->getRenderTime(); 43 | } 44 | 45 | public function getPrefix(): string 46 | { 47 | return (string) $this->prefix; 48 | } 49 | 50 | public function getTemplate(): string 51 | { 52 | return $this->block->getTemplate(); 53 | } 54 | 55 | public function getChildren(): array 56 | { 57 | return $this->children; 58 | } 59 | 60 | public function hasChildren(): bool 61 | { 62 | return !empty($this->children); 63 | } 64 | 65 | public function getParentId(): string 66 | { 67 | return (string) $this->block->getParentId(); 68 | } 69 | 70 | public function getRenderPercent(): float 71 | { 72 | if (empty($this->layoutRenderTime)) { 73 | return 0.0; 74 | } 75 | 76 | return $this->getRenderTime() / $this->layoutRenderTime; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Model/ValueObject/LoopModelAction.php: -------------------------------------------------------------------------------- 1 | modelAction = $modelAction; 15 | $this->time = $time; 16 | $this->count = $count; 17 | } 18 | 19 | public function getModelAction(): ModelAction 20 | { 21 | return $this->modelAction; 22 | } 23 | 24 | public function getName(): string 25 | { 26 | return $this->modelAction->getName(); 27 | } 28 | 29 | public function getModel(): string 30 | { 31 | return $this->modelAction->getModel(); 32 | } 33 | 34 | public function getTrace(): array 35 | { 36 | return $this->modelAction->getTrace(); 37 | } 38 | 39 | public function getTime(): float 40 | { 41 | return $this->time; 42 | } 43 | 44 | public function getCount(): int 45 | { 46 | return $this->count; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Model/ValueObject/ModelAction.php: -------------------------------------------------------------------------------- 1 | id = uniqid(); 24 | $this->name = $name; 25 | $this->model = $model; 26 | $this->time = $time; 27 | $this->trace = $trace; 28 | } 29 | 30 | public function getId(): string 31 | { 32 | return $this->name . '::' . $this->id . '::' . $this->model; 33 | } 34 | 35 | public function getName(): string 36 | { 37 | return $this->name; 38 | } 39 | 40 | public function getModel(): string 41 | { 42 | return $this->model; 43 | } 44 | 45 | public function getTime(): float 46 | { 47 | return $this->time; 48 | } 49 | 50 | public function getTrace(): array 51 | { 52 | return $this->trace; 53 | } 54 | 55 | public function getTraceHash(): string 56 | { 57 | if (empty($this->getTrace())) { 58 | return ''; 59 | } 60 | 61 | // phpcs:ignore Magento2.Security.InsecureFunction.FoundWithAlternative 62 | return md5(serialize($this->getTrace())); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Model/ValueObject/Plugin.php: -------------------------------------------------------------------------------- 1 | class = $class; 17 | $this->name = $name; 18 | $this->sortOrder = $sortOrder; 19 | $this->method = $method; 20 | $this->type = $type; 21 | } 22 | 23 | public function getClass(): string 24 | { 25 | return $this->class; 26 | } 27 | 28 | public function getName(): string 29 | { 30 | return $this->name; 31 | } 32 | 33 | public function getSortOrder(): int 34 | { 35 | return $this->sortOrder; 36 | } 37 | 38 | public function getMethod(): string 39 | { 40 | return $this->method; 41 | } 42 | 43 | public function getType(): string 44 | { 45 | return $this->type; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Model/ValueObject/Translation.php: -------------------------------------------------------------------------------- 1 | phrase = $phrase; 17 | $this->translation = $translation; 18 | $this->defined = $defined; 19 | } 20 | 21 | public function getId(): string 22 | { 23 | return $this->phrase; 24 | } 25 | 26 | public function getPhrase(): string 27 | { 28 | return $this->phrase; 29 | } 30 | 31 | public function getTranslation(): string 32 | { 33 | return $this->translation; 34 | } 35 | 36 | public function isDefined(): bool 37 | { 38 | return $this->defined; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Model/View/Menu.php: -------------------------------------------------------------------------------- 1 | request = $request; 33 | $this->profileMemoryStorage = $profileMemoryStorage; 34 | $this->url = $url; 35 | } 36 | 37 | public function isActive(string $collectorName): bool 38 | { 39 | return $this->getProfile()->hasCollector($collectorName); 40 | } 41 | 42 | public function isCurrentPanel(string $collectorName): bool 43 | { 44 | return $this->request->getParam('panel') === $collectorName; 45 | } 46 | 47 | public function getCollector(string $collectorName): CollectorInterface 48 | { 49 | return $this->getProfile()->getCollector($collectorName); 50 | } 51 | 52 | public function getProfilerUrl(string $collectorName): string 53 | { 54 | return $this->url->getProfilerUrl($this->getProfile()->getToken(), $collectorName); 55 | } 56 | 57 | public function getConfigurationUrl(): string 58 | { 59 | return $this->url->getConfigurationUrl(); 60 | } 61 | 62 | private function getProfile(): ProfileInterface 63 | { 64 | return $this->profileMemoryStorage->read(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Model/View/Renderer/LayoutNodeRenderer.php: -------------------------------------------------------------------------------- 1 | node = $node; 24 | $this->layout = $layout; 25 | $this->layoutNodeRendererFactory = $layoutNodeRendererFactory; 26 | $this->formatter = $formatter; 27 | } 28 | 29 | public function render(): string 30 | { 31 | /** @var \Magento\Framework\View\Element\Template $block */ 32 | $block = $this->layout->createBlock( 33 | Template::class, 34 | '', 35 | [ 36 | 'data' => [ 37 | 'template' => self::TEMPLATE, 38 | 'node' => $this->node, 39 | 'formatter' => $this->formatter, 40 | 'layout_node_renderer' => $this->layoutNodeRendererFactory, 41 | ], 42 | ] 43 | ); 44 | 45 | return $block->toHtml(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Model/View/Renderer/ParametersRenderer.php: -------------------------------------------------------------------------------- 1 | parameters = $parameters; 22 | $this->layout = $layout; 23 | $this->varRenderer = $varRenderer; 24 | } 25 | 26 | public function render(): string 27 | { 28 | /** @var \Magento\Framework\View\Element\Template $block */ 29 | $block = $this->layout->createBlock( 30 | Template::class, 31 | '', 32 | [ 33 | 'data' => [ 34 | 'template' => self::TEMPLATE, 35 | 'parameters' => $this->parameters, 36 | 'var_renderer' => $this->varRenderer, 37 | ], 38 | ] 39 | ); 40 | 41 | return $block->toHtml(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Model/View/Renderer/QueryListRenderer.php: -------------------------------------------------------------------------------- 1 | queries = $queries; 27 | $this->layout = $layout; 28 | $this->mathRandom = $mathRandom; 29 | $this->queryRendererFactory = $queryRendererFactory; 30 | $this->formatter = $formatter; 31 | } 32 | 33 | public function render(): string 34 | { 35 | /** @var \Magento\Framework\View\Element\Template $block */ 36 | $block = $this->layout->createBlock( 37 | Template::class, 38 | '', 39 | [ 40 | 'data' => [ 41 | 'template' => self::TEMPLATE, 42 | 'queries' => $this->queries, 43 | 'query_renderer' => $this->queryRendererFactory, 44 | 'prefix' => $this->mathRandom->getUniqueHash(), 45 | 'formatter' => $this->formatter, 46 | ], 47 | ] 48 | ); 49 | 50 | return $block->toHtml(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Model/View/Renderer/QueryParametersRenderer.php: -------------------------------------------------------------------------------- 1 | query = $query; 18 | $this->parameters = $parameters; 19 | $this->resource = $resource; 20 | } 21 | 22 | public function render(): string 23 | { 24 | $parameters = $this->parameters; 25 | $i = !array_key_exists(0, $parameters) && array_key_exists(1, $parameters) ? 1 : 0; 26 | 27 | $result = preg_replace_callback('/\?|((?resource->getConnection()->quote($value); 35 | $i++; 36 | 37 | return $result; 38 | }, $this->query); 39 | 40 | return (string) $result; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Model/View/Renderer/RedirectRenderer.php: -------------------------------------------------------------------------------- 1 | redirect = $redirect; 33 | $this->layout = $layout; 34 | $this->url = $url; 35 | } 36 | 37 | public function render(): string 38 | { 39 | /** @var \Magento\Framework\View\Element\Template $block */ 40 | $block = $this->layout->createBlock( 41 | Template::class, 42 | '', 43 | [ 44 | 'data' => [ 45 | 'template' => self::TEMPLATE, 46 | ], 47 | ] 48 | ); 49 | 50 | return $block->setProfilerUrl($this->url->getProfilerUrl($this->redirect->getToken())) 51 | ->setRedirect($this->redirect) 52 | ->toHtml(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Model/View/Renderer/RendererInterface.php: -------------------------------------------------------------------------------- 1 | items = $items; 24 | $this->layout = $layout; 25 | $this->varRenderer = $varRenderer; 26 | $this->labels = $labels; 27 | } 28 | 29 | public function render(): string 30 | { 31 | /** @var \Magento\Framework\View\Element\Template $block */ 32 | $block = $this->layout->createBlock( 33 | Template::class, 34 | '', 35 | [ 36 | 'data' => [ 37 | 'template' => self::TEMPLATE, 38 | 'items' => $this->items, 39 | 'labels' => $this->labels, 40 | 'var_renderer' => $this->varRenderer, 41 | 'key_label' => $this->labels[0] ?? 'Key', 42 | 'value_label' => $this->labels[1] ?? 'Value', 43 | ], 44 | ] 45 | ); 46 | 47 | return $block->toHtml(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Model/View/Renderer/TraceCallRenderer.php: -------------------------------------------------------------------------------- 1 | call = $call; 23 | $this->layout = $layout; 24 | $this->directoryList = $directoryList; 25 | } 26 | 27 | public function render(): string 28 | { 29 | /** @var \Magento\Framework\View\Element\Template $block */ 30 | $block = $this->layout->createBlock( 31 | Template::class, 32 | '', 33 | [ 34 | 'data' => [ 35 | 'template' => self::TEMPLATE, 36 | ], 37 | ] 38 | ); 39 | 40 | foreach (self::CALL_INFO as $info) { 41 | if (isset($this->call[$info])) { 42 | $block->setData($info, $this->call[$info]); 43 | } 44 | } 45 | if ($block->hasFile()) { 46 | $block->setFile($this->relativizePath($block->getFile())); 47 | } 48 | 49 | return $block->toHtml(); 50 | } 51 | 52 | private function relativizePath(string $path): string 53 | { 54 | $rootDirectory = $this->directoryList->getRoot(); 55 | 56 | return (string) (strpos($path, $rootDirectory) === 0 ? substr($path, strlen($rootDirectory)) : $path); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Model/View/Renderer/TraceRenderer.php: -------------------------------------------------------------------------------- 1 | id = uniqid(); 23 | $this->trace = $trace; 24 | $this->layout = $layout; 25 | $this->traceCallRendererFactory = $traceCallRendererFactory; 26 | } 27 | 28 | public function render(): string 29 | { 30 | /** @var \Magento\Framework\View\Element\Template $block */ 31 | $block = $this->layout->createBlock( 32 | Template::class, 33 | '', 34 | [ 35 | 'data' => [ 36 | 'template' => self::TEMPLATE, 37 | 'trace' => $this->trace, 38 | 'trace_id' => $this->id, 39 | 'trace_call_renderer' => $this->traceCallRendererFactory, 40 | ], 41 | ] 42 | ); 43 | 44 | return $block->toHtml(); 45 | } 46 | 47 | public function getId(): string 48 | { 49 | return $this->id; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Model/View/Renderer/VarRenderer.php: -------------------------------------------------------------------------------- 1 | cloner = new VarCloner(); 17 | $this->dumper = new HtmlDumper(); 18 | } 19 | 20 | /** 21 | * @param mixed $value 22 | * @return string 23 | */ 24 | public function render($value): string 25 | { 26 | return (string) $this->dumper->dump($this->cloner->cloneVar($value), true); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Model/View/Search.php: -------------------------------------------------------------------------------- 1 | request = $request; 24 | $this->profileMemoryStorage = $profileMemoryStorage; 25 | $this->formatter = $formatter; 26 | $this->url = $url; 27 | } 28 | 29 | public function isParamSelected(string $param, string $expected): bool 30 | { 31 | return $this->request->getParam($param) === $expected; 32 | } 33 | 34 | public function getParam(string $param): ?string 35 | { 36 | return $this->request->getParam($param); 37 | } 38 | 39 | public function getLimits(): array 40 | { 41 | return ['10', '50', '100']; 42 | } 43 | 44 | public function getMethods(): array 45 | { 46 | return ['GET', 'POST', 'DELETE', 'PUT', 'PATCH', 'HEAD']; 47 | } 48 | 49 | public function getToken(): string 50 | { 51 | if ($this->token === null) { 52 | $this->token = $this->profileMemoryStorage->read()->getToken(); 53 | } 54 | 55 | return $this->token; 56 | } 57 | 58 | public function toMegaBytes(int $value): string 59 | { 60 | return $this->formatter->toMegaBytes($value, 2); 61 | } 62 | 63 | public function getProfilerUrl(string $token): string 64 | { 65 | return $this->url->getProfilerUrl($token, RequestCollector::NAME); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Model/View/Summary.php: -------------------------------------------------------------------------------- 1 | profileMemoryStorage = $profileMemoryStorage; 22 | $this->url = $url; 23 | $this->redirectRendererFactory = $redirectRendererFactory; 24 | } 25 | 26 | public function getProfile(): ProfileInterface 27 | { 28 | return $this->profileMemoryStorage->read(); 29 | } 30 | 31 | public function getProfilerUrl(string $token): string 32 | { 33 | return $this->url->getProfilerUrl($token); 34 | } 35 | 36 | public function renderRedirect(Redirect $redirect): string 37 | { 38 | return $this->redirectRendererFactory->create(['redirect' => $redirect])->render(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Observer/AllowedIP.php: -------------------------------------------------------------------------------- 1 | config = $config; 17 | } 18 | 19 | public function execute(Observer $observer) 20 | { 21 | if ($this->config->isAllowedIP()) { 22 | return; 23 | } 24 | 25 | /** @var \Magento\Framework\App\Request\Http $request */ 26 | $request = $observer->getRequest(); 27 | $request->initForward(); 28 | $request->setControllerName('noroute'); 29 | $request->setModuleName('cms'); 30 | $request->setActionName('index'); 31 | $request->setDispatched(false); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Observer/BeforeSendResponse.php: -------------------------------------------------------------------------------- 1 | config = $config; 19 | $this->profiler = $profiler; 20 | } 21 | 22 | public function execute(Observer $observer) 23 | { 24 | $request = $observer->getRequest(); 25 | $response = $observer->getResponse(); 26 | if ($this->isProfilerAction($request) || !$this->config->isEnabled()) { 27 | return; 28 | } 29 | 30 | $this->profiler->run($request, $response); 31 | } 32 | 33 | private function isProfilerAction(\Magento\Framework\HTTP\PhpEnvironment\Request $request): bool 34 | { 35 | return $request->getModuleName() === '_debug' || $request->getModuleName() === 'debug'; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Observer/Collector/LayoutCollectorAfterToHtml.php: -------------------------------------------------------------------------------- 1 | layoutCollector = $layoutCollector; 19 | } 20 | 21 | public function execute(Observer $observer) 22 | { 23 | /** @var \Magento\Framework\View\Element\AbstractBlock $block */ 24 | $block = $observer->getBlock(); 25 | 26 | $renderedTimestamp = microtime(true); 27 | $renderTime = $renderedTimestamp - $block->getData(LayoutCollector::BLOCK_START_RENDER_KEY); 28 | 29 | $block->addData([ 30 | LayoutCollector::BLOCK_STOP_RENDER_KEY => $renderedTimestamp, 31 | LayoutCollector::RENDER_TIME => $renderTime, 32 | ]); 33 | 34 | $this->layoutCollector->log(new Block($block)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Observer/Collector/LayoutCollectorBeforeToHtml.php: -------------------------------------------------------------------------------- 1 | getBlock(); 16 | $id = uniqid(); 17 | $block->addData([ 18 | LayoutCollector::BLOCK_PROFILER_ID_KEY => $id, 19 | LayoutCollector::BLOCK_START_RENDER_KEY => microtime(true), 20 | LayoutCollector::BLOCK_HASH_KEY => spl_object_hash($block), 21 | ]); 22 | 23 | if ($block->getParentBlock() instanceof \Magento\Framework\View\Element\AbstractBlock) { 24 | $block->addData([ 25 | LayoutCollector::BLOCK_PARENT_PROFILER_ID_KEY => $block->getParentBlock() 26 | ->getData(LayoutCollector::BLOCK_PROFILER_ID_KEY), 27 | ]); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Observer/Config/DatabaseProfiler.php: -------------------------------------------------------------------------------- 1 | messageManager = $messageManager; 23 | $this->dbProfilerWriter = $dbProfilerWriter; 24 | $this->config = $config; 25 | } 26 | 27 | public function execute(Observer $observer): void 28 | { 29 | if (!$this->isDBProfilerDependentConfigChanged($observer->getChangedPaths())) { 30 | return; 31 | } 32 | 33 | $flag = $this->config->isDatabaseCollectorEnabled() && $this->config->isActive(); 34 | 35 | try { 36 | $this->dbProfilerWriter->save($flag); 37 | } catch (FileSystemException $e) { 38 | $this->messageManager->addExceptionMessage($e); 39 | } 40 | } 41 | 42 | private function isDBProfilerDependentConfigChanged(array $paths): bool 43 | { 44 | return in_array(Config::CONFIG_COLLECTOR_DATABASE, $paths) || in_array(Config::CONFIG_ENABLED, $paths); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Observer/DebugHandle.php: -------------------------------------------------------------------------------- 1 | config = $config; 18 | } 19 | 20 | public function execute(Observer $observer) 21 | { 22 | if ($this->config->isEnabled()) { 23 | $observer->getLayout()->getUpdate()->addHandle('clawrock_debug'); 24 | } 25 | 26 | if ($observer->getFullActionName() === Profiler::TOOLBAR_FULL_ACTION_NAME) { 27 | $observer->getLayout()->getUpdate()->removeHandle('default'); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Observer/ValidateRedirect.php: -------------------------------------------------------------------------------- 1 | session = $session; 18 | } 19 | 20 | public function execute(Observer $observer) 21 | { 22 | if ($this->session->getData(RequestInfo::REDIRECT_PARAM)) { 23 | $observer->getRequest()->setParam('_redirected', true); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Plugin/Collector/EventCollectorPlugin.php: -------------------------------------------------------------------------------- 1 | eventCollector = $eventCollector; 21 | } 22 | 23 | public function aroundDispatch( 24 | InvokerDefault $subject, 25 | callable $proceed, 26 | array $configuration, 27 | Observer $observer 28 | ): void { 29 | $start = microtime(true); 30 | $proceed($configuration, $observer); 31 | $end = microtime(true); 32 | 33 | $this->eventCollector->log(new EventObserver( 34 | $configuration['name'], 35 | $configuration['instance'], 36 | $observer->getEvent()->getName(), 37 | $end - $start 38 | )); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Plugin/Collector/TimeCollectorPlugin.php: -------------------------------------------------------------------------------- 1 | stopwatchDriver = $stopwatchDriver; 16 | } 17 | 18 | public function beforeLaunch(): void 19 | { 20 | Profiler::reset(); 21 | Profiler::add($this->stopwatchDriver); 22 | Profiler::start($this->stopwatchDriver::ROOT_EVENT); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Plugin/Collector/TranslationCollectorPlugin.php: -------------------------------------------------------------------------------- 1 | translate = $translate; 23 | $this->translationCollector = $translationCollector; 24 | } 25 | 26 | public function beforeRender(Translate $subject, array $source, array $arguments): void 27 | { 28 | /** @var string $text */ 29 | $text = end($source); 30 | $text = str_replace('\"', '"', $text); 31 | $text = str_replace("\\'", "'", $text); 32 | 33 | $data = $this->getTranslations(); 34 | $translation = ''; 35 | 36 | if ($isDefined = array_key_exists($text, $data)) { 37 | $translation = $data[$text]; 38 | } 39 | 40 | $this->translationCollector->log(new Translation(end($source), $translation, $isDefined)); 41 | } 42 | 43 | private function getTranslations(): array 44 | { 45 | if ($this->translations === null) { 46 | $this->translations = $this->translate->getData(); 47 | } 48 | 49 | return $this->translations; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Plugin/ErrorHandler/WhoopsPlugin.php: -------------------------------------------------------------------------------- 1 | config = $config; 22 | $this->whoopsFactory = $whoopsFactory; 23 | $this->prettyPageHandlerFactory = $prettyPageHandlerFactory; 24 | } 25 | 26 | /** 27 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 28 | * 29 | * @param \Magento\Framework\App\Http $subject 30 | * @param \Magento\Framework\App\Bootstrap $bootstrap 31 | * @param \Exception $exception 32 | * @return array 33 | */ 34 | public function beforeCatchException(Http $subject, Bootstrap $bootstrap, \Exception $exception): array 35 | { 36 | if ($this->config->getErrorHandler() === ErrorHandler::WHOOPS) { 37 | $whoops = $this->whoopsFactory->create(); 38 | $whoops->pushHandler($this->prettyPageHandlerFactory->create()); 39 | $whoops->handleException($exception); 40 | } 41 | 42 | return [$bootstrap, $exception]; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Plugin/PageCache/KernelPlugin.php: -------------------------------------------------------------------------------- 1 | httpStorage = $httpStorage; 17 | } 18 | 19 | /** 20 | * @param \Magento\Framework\App\PageCache\Kernel $subject 21 | * @param \Magento\Framework\App\Response\Http|false $result 22 | * @return \Magento\Framework\App\Response\Http|false 23 | */ 24 | public function afterLoad(\Magento\Framework\App\PageCache\Kernel $subject, $result) 25 | { 26 | if ($result !== false) { 27 | $this->httpStorage->markAsFPCRequest(); 28 | } 29 | 30 | return $result; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Plugin/PreventMessageBlockInitByToolbarPlugin.php: -------------------------------------------------------------------------------- 1 | getUpdate()->getHandles(); 19 | if ($handles === ['clawrock_debug']) { 20 | // Block is initialized by layout collector, messages can't be initialized 21 | // because there will be no messages during block rendering 22 | return; 23 | } 24 | 25 | $proceed($messageGroups); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Plugin/ProfileRepository/RequestTimePlugin.php: -------------------------------------------------------------------------------- 1 | getCollector(TimeCollector::NAME); 24 | } catch (\InvalidArgumentException $e) { 25 | return [$profile]; 26 | } 27 | 28 | $profile->setRequestTime($timeCollector->getDuration()); 29 | 30 | return [$profile]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Plugin/UseMagentoBackendThemeOnDebugFrontendViewPlugin.php: -------------------------------------------------------------------------------- 1 | design = $design; 16 | } 17 | 18 | public function beforeExecute(): void 19 | { 20 | $this->design->setArea(Area::AREA_ADMINHTML); 21 | $this->design->setDesignTheme('Magento/backend'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | [![Packagist](https://img.shields.io/packagist/v/clawrock/magento2-debug.svg)](https://packagist.org/packages/clawrock/magento2-debug) 2 | [![Packagist](https://img.shields.io/packagist/dt/clawrock/magento2-debug.svg)](https://packagist.org/packages/clawrock/magento2-debug) 3 | [![Build Status](https://github.com/clawrock/magento2-debug/actions/workflows/ci.yaml/badge.svg)](https://github.com/clawrock/magento2-debug/actions) 4 | 5 | # Magento 2 - Debug module 6 | Module for debugging Magento 2 performance. It works without overwriting any core files and it can be installed with composer. 7 | 8 | ## Installation 9 | 1. Enable developer mode `php bin/magento deploy:mode:set developer` 10 | 2. Install module via composer `composer require clawrock/magento2-debug` 11 | 3. Register module `php bin/magento setup:upgrade` 12 | 4. Enable profiler in configuration: `Stores -> Configuration -> Advanced -> Debug` 13 | 5. Enable DB profiler `php bin/magento debug:db-profiler:enable` 14 | 15 | ## Configuration 16 | All settings have only default scope and config type pool is set to environment for better integration with `php bin/magento app:config:dump` 17 | 18 | ## Compatibility 19 | * Magento >= 2.4.4 20 | * PHP 8.1, 8.2 21 | 22 | ## Profiler collectors 23 | - Ajax 24 | - Cache 25 | - Config 26 | - Customer 27 | - Database 28 | - Events 29 | - Layout 30 | - Memory 31 | - Models 32 | - Plugins 33 | - Request/Response 34 | - Performance 35 | - Translations 36 | 37 | ## Additional features 38 | - [Whoops error handler](http://filp.github.io/whoops/) 39 | 40 | ## Credits 41 | - [Magento 1.x Web Profiler](https://github.com/ecoco/magento_profiler) 42 | - [Symfony WebProfilerBundle](https://github.com/symfony/web-profiler-bundle) 43 | -------------------------------------------------------------------------------- /Serializer/Serializer.php: -------------------------------------------------------------------------------- 1 | profilerWriterMock = $this->getMockBuilder(\ClawRock\Debug\Model\Config\Database\ProfilerWriter::class) 28 | ->disableOriginalConstructor() 29 | ->getMock(); 30 | 31 | $this->application = new Application(); 32 | $this->commandObject = new DatabaseProfilerDisableCommand($this->profilerWriterMock); 33 | $this->application->add($this->commandObject); 34 | $this->command = $this->application->find('debug:db-profiler:disable'); 35 | $this->commandTester = new CommandTester($this->command); 36 | } 37 | 38 | public function testExecute(): void 39 | { 40 | $this->profilerWriterMock->expects($this->once())->method('save')->with(false); 41 | $this->commandTester->execute(['command' => $this->command->getName()]); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Test/Unit/Console/Command/DatabaseProfilerEnableCommandTest.php: -------------------------------------------------------------------------------- 1 | profilerWriterMock = $this->getMockBuilder(\ClawRock\Debug\Model\Config\Database\ProfilerWriter::class) 28 | ->disableOriginalConstructor() 29 | ->getMock(); 30 | 31 | $this->application = new Application(); 32 | $this->commandObject = new DatabaseProfilerEnableCommand($this->profilerWriterMock); 33 | $this->application->add($this->commandObject); 34 | $this->command = $this->application->find('debug:db-profiler:enable'); 35 | $this->commandTester = new CommandTester($this->command); 36 | } 37 | 38 | public function testExecute(): void 39 | { 40 | $this->profilerWriterMock->expects($this->once())->method('save')->with(true); 41 | $this->commandTester->execute(['command' => $this->command->getName()]); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Test/Unit/Controller/Cache/CacheControllerTestCase.php: -------------------------------------------------------------------------------- 1 | resultFactoryMock = $this->createMock(\Magento\Framework\Controller\ResultFactory::class); 22 | $this->resultMock = $this->createMock(\Magento\Framework\Controller\ResultInterface::class); 23 | $this->requestMock = $this->createMock(\Magento\Framework\App\RequestInterface::class); 24 | $this->cacheManagerMock = $this->createMock(\Magento\Framework\App\Cache\Manager::class); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Test/Unit/Controller/Cache/CleanTest.php: -------------------------------------------------------------------------------- 1 | controller = new Clean($this->resultFactoryMock, $this->requestMock, $this->cacheManagerMock); 17 | } 18 | 19 | public function testExecute(): void 20 | { 21 | $this->resultFactoryMock->expects($this->once())->method('create') 22 | ->with(\Magento\Framework\Controller\ResultFactory::TYPE_JSON) 23 | ->willReturn($this->resultMock); 24 | $this->requestMock->expects($this->once())->method('getParam') 25 | ->with('type') 26 | ->willReturn(null); 27 | $this->cacheManagerMock->expects($this->once())->method('getAvailableTypes') 28 | ->willReturn(['cache_type_1', 'cache_type_2']); 29 | $this->cacheManagerMock->expects($this->once())->method('clean')->with(['cache_type_1', 'cache_type_2']); 30 | 31 | $this->assertInstanceOf(\Magento\Framework\Controller\ResultInterface::class, $this->controller->execute()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Test/Unit/Controller/Cache/DisableTest.php: -------------------------------------------------------------------------------- 1 | controller = new Disable($this->resultFactoryMock, $this->requestMock, $this->cacheManagerMock); 17 | } 18 | 19 | public function testExecute(): void 20 | { 21 | $this->resultFactoryMock->expects($this->once())->method('create') 22 | ->with(\Magento\Framework\Controller\ResultFactory::TYPE_JSON) 23 | ->willReturn($this->resultMock); 24 | $this->requestMock->expects($this->once())->method('getParam')->with('type')->willReturn('cache_type'); 25 | $this->cacheManagerMock->expects($this->once())->method('setEnabled')->with(['cache_type'], false); 26 | 27 | $this->assertInstanceOf(\Magento\Framework\Controller\ResultInterface::class, $this->controller->execute()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Test/Unit/Controller/Cache/EnableTest.php: -------------------------------------------------------------------------------- 1 | controller = new Enable($this->resultFactoryMock, $this->requestMock, $this->cacheManagerMock); 17 | } 18 | 19 | public function testExecute(): void 20 | { 21 | $this->resultFactoryMock->expects($this->once())->method('create') 22 | ->with(\Magento\Framework\Controller\ResultFactory::TYPE_JSON) 23 | ->willReturn($this->resultMock); 24 | $this->requestMock->expects($this->once())->method('getParam')->with('type')->willReturn('cache_type'); 25 | $this->cacheManagerMock->expects($this->once())->method('setEnabled')->with(['cache_type'], true); 26 | 27 | $this->assertInstanceOf(\Magento\Framework\Controller\ResultInterface::class, $this->controller->execute()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Test/Unit/Controller/Cache/FlushTest.php: -------------------------------------------------------------------------------- 1 | controller = new Flush($this->resultFactoryMock, $this->requestMock, $this->cacheManagerMock); 17 | } 18 | 19 | public function testExecute(): void 20 | { 21 | $this->resultFactoryMock->expects($this->once())->method('create') 22 | ->with(\Magento\Framework\Controller\ResultFactory::TYPE_JSON) 23 | ->willReturn($this->resultMock); 24 | $this->requestMock->expects($this->once())->method('getParam') 25 | ->with('type') 26 | ->willReturn(null); 27 | $this->cacheManagerMock->expects($this->once())->method('getAvailableTypes') 28 | ->willReturn(['cache_type1', 'cache_type2']); 29 | $this->cacheManagerMock->expects($this->once())->method('flush') 30 | ->with(['cache_type1', 'cache_type2']); 31 | 32 | $this->assertInstanceOf(\Magento\Framework\Controller\ResultInterface::class, $this->controller->execute()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Test/Unit/Controller/Profiler/PHPInfoTest.php: -------------------------------------------------------------------------------- 1 | getMockBuilder(ResultFactory::class) 17 | ->disableOriginalConstructor() 18 | ->getMock(); 19 | 20 | $resultMock = $this->getMockBuilder(Raw::class) 21 | ->disableOriginalConstructor() 22 | ->getMock(); 23 | 24 | $resultFactoryMock->expects($this->once())->method('create') 25 | ->with(ResultFactory::TYPE_RAW) 26 | ->willReturn($resultMock); 27 | 28 | $controller = new PHPInfo($resultFactoryMock); 29 | 30 | ob_start(); 31 | $result = $controller->execute(); 32 | ob_end_clean(); 33 | 34 | $this->assertInstanceOf(ResultInterface::class, $result); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Test/Unit/Logger/DataLoggerTest.php: -------------------------------------------------------------------------------- 1 | loggableMock = $this->getMockForAbstractClass(LoggableInterface::class); 21 | $this->logger = new DataLogger(); 22 | } 23 | 24 | public function testGetLogs(): void 25 | { 26 | $this->assertEquals([], $this->logger->getLogs()); 27 | } 28 | 29 | public function testLog(): void 30 | { 31 | $this->loggableMock->expects($this->once())->method('getId')->willReturn('ID'); 32 | $this->assertInstanceOf(DataLogger::class, $this->logger->log($this->loggableMock)); 33 | $this->assertEquals(['ID' => $this->loggableMock], $this->logger->getLogs()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Test/Unit/Model/Storage/HttpStorageTest.php: -------------------------------------------------------------------------------- 1 | getMockBuilder(\Magento\Framework\HTTP\PhpEnvironment\Request::class) 14 | ->disableOriginalConstructor() 15 | ->getMock(); 16 | 17 | $responseMock = $this->getMockBuilder(\Magento\Framework\HTTP\PhpEnvironment\Response::class) 18 | ->disableOriginalConstructor() 19 | ->getMock(); 20 | 21 | $storage = new HttpStorage(); 22 | $storage->setRequest($requestMock); 23 | $storage->setResponse($responseMock); 24 | $this->assertFalse($storage->isFPCRequest()); 25 | $this->assertTrue($storage->markAsFPCRequest()->isFPCRequest()); 26 | $this->assertEquals($requestMock, $storage->getRequest()); 27 | $this->assertEquals($responseMock, $storage->getResponse()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Test/Unit/Model/Storage/ProfileMemoryStorageTest.php: -------------------------------------------------------------------------------- 1 | getMockForAbstractClass(\ClawRock\Debug\Api\Data\ProfileInterface::class); 14 | 15 | $storage = new ProfileMemoryStorage(); 16 | $storage->write($profileMock); 17 | $this->assertEquals($profileMock, $storage->read()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Test/Unit/Model/ValueObject/CacheActionTest.php: -------------------------------------------------------------------------------- 1 | true, 18 | CacheAction::CACHE_TAGS => ['tag1', 'tag2'], 19 | CacheAction::CACHE_TTL => 3600, 20 | ]; 21 | $cacheAction = new CacheAction($id, $name, $time, $info); 22 | 23 | $this->assertEquals($id, $cacheAction->getId()); 24 | $this->assertEquals($name, $cacheAction->getName()); 25 | $this->assertEquals($time, $cacheAction->getTime()); 26 | $this->assertTrue($cacheAction->isHit()); 27 | $this->assertTrue($cacheAction->isLoad()); 28 | $this->assertEquals($info, $cacheAction->getInfo()); 29 | $this->assertEquals($info[CacheAction::CACHE_TAGS], $cacheAction->getTags()); 30 | $this->assertTrue($cacheAction->hasTags()); 31 | $this->assertEquals($info[CacheAction::CACHE_TTL], $cacheAction->getTTL()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Test/Unit/Model/ValueObject/EventObserverTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($name, $eventObserver->getName()); 21 | $this->assertEquals($class, $eventObserver->getClass()); 22 | $this->assertEquals($event, $eventObserver->getEvent()); 23 | $this->assertEquals($time, $eventObserver->getTime()); 24 | $this->assertStringStartsWith($name, $eventObserver->getId()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Test/Unit/Model/ValueObject/LoopModelActionTest.php: -------------------------------------------------------------------------------- 1 | getMockBuilder(\ClawRock\Debug\Model\ValueObject\ModelAction::class) 19 | ->disableOriginalConstructor() 20 | ->getMock(); 21 | 22 | $modelActionMock->expects($this->once())->method('getName')->willReturn($name); 23 | $modelActionMock->expects($this->once())->method('getModel')->willReturn($model); 24 | $modelActionMock->expects($this->once())->method('getTrace')->willReturn($trace); 25 | 26 | $loopAction = new LoopModelAction($modelActionMock, $time, $count); 27 | $this->assertEquals($modelActionMock, $loopAction->getModelAction()); 28 | $this->assertEquals($name, $loopAction->getName()); 29 | $this->assertEquals($model, $loopAction->getModel()); 30 | $this->assertEquals($trace, $loopAction->getTrace()); 31 | $this->assertEquals($time, $loopAction->getTime()); 32 | $this->assertEquals($count, $loopAction->getCount()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Test/Unit/Model/ValueObject/ModelActionTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($name, $modelAction->getName()); 20 | $this->assertEquals($model, $modelAction->getModel()); 21 | $this->assertEquals($time, $modelAction->getTime()); 22 | $this->assertEquals($trace, $modelAction->getTrace()); 23 | $this->assertNotEmpty($modelAction->getTraceHash()); 24 | $this->assertStringStartsWith($name, $modelAction->getId()); 25 | $this->assertStringEndsWith($model, $modelAction->getId()); 26 | 27 | $modelAction = new ModelAction($name, $model, $time, []); 28 | $this->assertEquals('', $modelAction->getTraceHash()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Test/Unit/Model/ValueObject/PluginTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($class, $plugin->getClass()); 22 | $this->assertEquals($name, $plugin->getName()); 23 | $this->assertEquals($sortOrder, $plugin->getSortOrder()); 24 | $this->assertEquals($method, $plugin->getMethod()); 25 | $this->assertEquals($type, $plugin->getType()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Test/Unit/Model/ValueObject/TranslationTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($phrase, $object->getPhrase()); 20 | $this->assertEquals($phrase, $object->getId()); 21 | $this->assertEquals($translation, $object->getTranslation()); 22 | $this->assertTrue($object->isDefined()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Test/Unit/Observer/Collector/LayoutCollectorBeforeToHtmlTest.php: -------------------------------------------------------------------------------- 1 | blockMock = $this->getMockBuilder(\Magento\Framework\View\Element\AbstractBlock::class) 20 | ->disableOriginalConstructor() 21 | ->getMock(); 22 | 23 | $this->observerMock = $this->getMockBuilder(\Magento\Framework\Event\Observer::class) 24 | ->setMethods(['getBlock']) 25 | ->disableOriginalConstructor() 26 | ->getMock(); 27 | 28 | $this->observer = new LayoutCollectorBeforeToHtml(); 29 | } 30 | 31 | public function testExecute(): void 32 | { 33 | $this->observerMock->expects($this->once())->method('getBlock')->willReturn($this->blockMock); 34 | $this->blockMock->expects($this->exactly(2))->method('addData'); 35 | $this->blockMock->expects($this->exactly(2))->method('getParentBlock')->willReturn($this->blockMock); 36 | 37 | $this->observer->execute($this->observerMock); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Test/Unit/Observer/ValidateRedirectTest.php: -------------------------------------------------------------------------------- 1 | sessionMock = $this->getMockBuilder(\ClawRock\Debug\Model\Session::class) 22 | ->setMethods(['getData']) 23 | ->disableOriginalConstructor() 24 | ->getMock(); 25 | 26 | $this->observerMock = $this->getMockBuilder(\Magento\Framework\Event\Observer::class) 27 | ->setMethods(['getRequest']) 28 | ->disableOriginalConstructor() 29 | ->getMock(); 30 | 31 | $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class) 32 | ->setMethods(['setParam']) 33 | ->getMockForAbstractClass(); 34 | 35 | $this->observer = new ValidateRedirect($this->sessionMock); 36 | } 37 | 38 | public function testExecute(): void 39 | { 40 | $this->sessionMock->expects($this->once())->method('getData')->willReturn('1'); 41 | $this->observerMock->expects($this->once())->method('getRequest')->willReturn($this->requestMock); 42 | $this->requestMock->expects($this->once())->method('setParam'); 43 | 44 | $this->observer->execute($this->observerMock); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Test/Unit/Plugin/Collector/TimeCollectorPluginTest.php: -------------------------------------------------------------------------------- 1 | createMock(\ClawRock\Debug\Model\Profiler\Driver\StopwatchDriver::class); 14 | $plugin = new TimeCollectorPlugin($stopwatchDriverMock); 15 | $plugin->beforeLaunch(); 16 | 17 | $this->assertTrue(\Magento\Framework\Profiler::isEnabled()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Test/Unit/Plugin/Collector/TranslationCollectorPluginTest.php: -------------------------------------------------------------------------------- 1 | getMockBuilder(\Magento\Framework\Phrase\Renderer\Translate::class) 14 | ->disableOriginalConstructor() 15 | ->getMock(); 16 | 17 | $translateMock = $this->getMockForAbstractClass(\Magento\Framework\TranslateInterface::class); 18 | 19 | $translationCollectorMock = $this->getMockBuilder(\ClawRock\Debug\Model\Collector\TranslationCollector::class) 20 | ->disableOriginalConstructor() 21 | ->getMock(); 22 | 23 | $plugin = new TranslationCollectorPlugin($translateMock, $translationCollectorMock); 24 | 25 | $translateMock->expects($this->once())->method('getData')->willReturn(['text' => 'translation']); 26 | 27 | $source = ['text']; 28 | $args = []; 29 | 30 | $plugin->beforeRender($subjectMock, $source, $args); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Test/Unit/Plugin/PageCache/KernelPluginTest.php: -------------------------------------------------------------------------------- 1 | httpStorageMock = $this->getMockBuilder(\ClawRock\Debug\Model\Storage\HttpStorage::class) 20 | ->disableOriginalConstructor() 21 | ->getMock(); 22 | 23 | $this->subjectMock = $this->getMockBuilder(\Magento\Framework\App\PageCache\Kernel::class) 24 | ->disableOriginalConstructor() 25 | ->getMock(); 26 | 27 | $this->plugin = new KernelPlugin($this->httpStorageMock); 28 | } 29 | 30 | public function testAfterLoad(): void 31 | { 32 | $this->httpStorageMock->expects($this->never())->method('markAsFPCRequest'); 33 | $this->assertFalse($this->plugin->afterLoad($this->subjectMock, false)); 34 | } 35 | 36 | public function testAfterLoadFPC(): void 37 | { 38 | $result = $this->createMock(\Magento\Framework\App\Response\Http::class); 39 | $this->httpStorageMock->expects($this->once())->method('markAsFPCRequest'); 40 | $this->assertEquals($result, $this->plugin->afterLoad($this->subjectMock, $result)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Test/Unit/Serializer/SerializerTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($input, $serializer->unserialize($serializer->serialize($input))); 21 | } 22 | 23 | public function serializationProvider(): array 24 | { 25 | return [ 26 | ['1', 2, 3], 27 | [new \stdClass()], 28 | ['
some html
'], 29 | [['foo' => 'bar', new \stdClass(), 'Hello', 'double' => 2.44]], 30 | ]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "clawrock/magento2-debug", 3 | "description": "Magento 2 debug module based on Magento 1 Profiler with some extra features.", 4 | "type": "magento2-module", 5 | "license": [ 6 | "OSL-3.0", 7 | "AFL-3.0" 8 | ], 9 | "require": { 10 | "php": ">=8.1", 11 | "magento/framework": "~103.0", 12 | "magento/module-backend": "~102.0", 13 | "magento/module-developer": "^100.4", 14 | "filp/whoops": "^2.1", 15 | "jdorn/sql-formatter": "^1.2", 16 | "symfony/var-dumper": "*", 17 | "symfony/stopwatch": "^2.8 || ^3.0 || ^4.0 || ^5.0" 18 | }, 19 | "require-dev": { 20 | "bitexpert/phpstan-magento": "^0.30.0", 21 | "dealerdirect/phpcodesniffer-composer-installer": "^0.7.1", 22 | "magento/magento-coding-standard": "*", 23 | "phpcompatibility/php-compatibility": "^9.3", 24 | "phpmd/phpmd": "^2.9.1", 25 | "phpstan/phpstan": "~1.10.0", 26 | "phpunit/phpunit": "~9.5.0", 27 | "sebastian/phpcpd": "^6.0.3", 28 | "slevomat/coding-standard": "~6.4.1", 29 | "squizlabs/php_codesniffer": "~3.7.2", 30 | "phpstan/phpstan-phpunit": "^1.1.1" 31 | }, 32 | "repositories": [ 33 | { 34 | "type": "composer", 35 | "url": "https://repo.magento.com/" 36 | } 37 | ], 38 | "autoload": { 39 | "files": [ 40 | "registration.php" 41 | ], 42 | "psr-4": { 43 | "ClawRock\\Debug\\": "" 44 | } 45 | }, 46 | "config": { 47 | "allow-plugins": { 48 | "magento/composer-dependency-version-audit-plugin": true, 49 | "dealerdirect/phpcodesniffer-composer-installer": true 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /etc/adminhtml/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /etc/adminhtml/events.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /etc/adminhtml/routes.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /etc/events.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /etc/frontend/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /etc/frontend/routes.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /etc/module.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /phpmd.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | Magento 2 Debug module ruleset 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | etc 17 | Test 18 | view 19 | vendor 20 | 21 | -------------------------------------------------------------------------------- /phpstan.neon.dist: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 8 3 | checkMissingIterableValueType: false 4 | checkGenericClassInNonGenericObjectType: false 5 | excludePaths: 6 | - vendor/* 7 | paths: 8 | - . 9 | bootstrapFiles: 10 | - vendor/bitexpert/phpstan-magento/autoload.php 11 | includes: 12 | - vendor/bitexpert/phpstan-magento/extension.neon 13 | - vendor/phpstan/phpstan-phpunit/extension.neon 14 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Console 6 | Controller 7 | Helper 8 | Logger 9 | Model 10 | Observer 11 | Plugin 12 | Serializer 13 | 14 | 15 | Test 16 | 17 | 18 | 19 | 20 | Test/Unit 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /registration.php: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /view/base/layout/debug_panel_cache.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | ClawRock\Debug\Model\View\Profiler 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /view/base/layout/debug_panel_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | ClawRock\Debug\Model\View\Profiler 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /view/base/layout/debug_panel_database.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | ClawRock\Debug\Model\View\Profiler 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /view/base/layout/debug_panel_event.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | ClawRock\Debug\Model\View\Profiler 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /view/base/layout/debug_panel_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | ClawRock\Debug\Model\View\Profiler 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /view/base/layout/debug_panel_model.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | ClawRock\Debug\Model\View\Profiler 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /view/base/layout/debug_panel_plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | ClawRock\Debug\Model\View\Profiler 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /view/base/layout/debug_panel_request.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | ClawRock\Debug\Model\View\Profiler 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /view/base/layout/debug_panel_time.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | ClawRock\Debug\Model\View\Profiler 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /view/base/layout/debug_panel_translation.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | ClawRock\Debug\Model\View\Profiler 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /view/base/layout/debug_profiler_info.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /view/base/layout/debug_profiler_search.xml: -------------------------------------------------------------------------------- 1 | 2 | > 4 | 5 | 6 | 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | ClawRock\Debug\Model\View\Search 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /view/base/page_layout/toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | ClawRock\Debug\Model\View\Toolbar 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /view/base/templates/menu/cache.phtml: -------------------------------------------------------------------------------- 1 | getViewModel(); 10 | ?> 11 | isActive($block->getCollectorName())): ?> 12 |
  • 13 | 14 | 15 | 16 | <?= $escaper->escapeHtmlAttr(__('Cache')); ?> 21 | 22 | escapeHtml(__('Cache')); ?> 23 | 24 | 25 |
  • 26 | 27 | -------------------------------------------------------------------------------- /view/base/templates/menu/config.phtml: -------------------------------------------------------------------------------- 1 | getViewModel(); 10 | ?> 11 | isActive($block->getCollectorName())): ?> 12 |
  • 13 | 14 | 15 | 16 | <?= $escaper->escapeHtmlAttr(__('Config')); ?> 21 | 22 | escapeHtml(__('Config')); ?> 23 | 24 | 25 |
  • 26 | 27 | -------------------------------------------------------------------------------- /view/base/templates/menu/database.phtml: -------------------------------------------------------------------------------- 1 | getViewModel(); 10 | ?> 11 | isActive($block->getCollectorName())): ?> 12 | 13 | getCollector($block->getCollectorName()); ?> 14 |
  • 15 | 16 | 17 | 18 | <?= $escaper->escapeHtmlAttr(__('Database')); ?> 23 | 24 | escapeHtml(__('Database')); ?> 25 | getDuplicatedQueries())): ?> 26 | 27 | getDuplicatedQueries()) ?> 28 | 29 | 30 | 31 | 32 |
  • 33 | 34 | -------------------------------------------------------------------------------- /view/base/templates/menu/event.phtml: -------------------------------------------------------------------------------- 1 | getViewModel(); 10 | ?> 11 | isActive($block->getCollectorName())): ?> 12 |
  • 13 | 14 | 15 | 16 | <?= $escaper->escapeHtmlAttr(__('Events')); ?> 21 | 22 | escapeHtml(__('Events')); ?> 23 | 24 | 25 |
  • 26 | 27 | -------------------------------------------------------------------------------- /view/base/templates/menu/layout.phtml: -------------------------------------------------------------------------------- 1 | getViewModel(); 10 | ?> 11 | isActive($block->getCollectorName())): ?> 12 | 13 | getCollector($block->getCollectorName()); ?> 14 |
  • 15 | 16 | 17 | 18 | <?= $escaper->escapeHtmlAttr(__('Layout')); ?> 23 | 24 | escapeHtml(__('Layout')); ?> 25 | getNotRenderedBlocks())): ?> 26 | 27 | getNotRenderedBlocks()) ?> 28 | 29 | 30 | 31 | 32 |
  • 33 | 34 | -------------------------------------------------------------------------------- /view/base/templates/menu/model.phtml: -------------------------------------------------------------------------------- 1 | getViewModel(); 10 | ?> 11 | isActive($block->getCollectorName())): ?> 12 | 13 | getCollector($block->getCollectorName()); ?> 14 |
  • 15 | 16 | 17 | 18 | <?= $escaper->escapeHtmlAttr(__('Models')); ?> 23 | 24 | escapeHtml(__('Models')); ?> 25 | getLoopLoadMetric()): ?> 26 | 27 | getLoopLoadMetric() ?> 28 | 29 | getLoadMetric() && $collector->isThresholdExceeded()): ?> 30 | 31 | getLoadMetric() ?> 32 | 33 | 34 | 35 | 36 |
  • 37 | 38 | 39 | -------------------------------------------------------------------------------- /view/base/templates/menu/plugin.phtml: -------------------------------------------------------------------------------- 1 | getViewModel(); 10 | ?> 11 | isActive($block->getCollectorName())): ?> 12 | 13 | getCollector($block->getCollectorName()); ?> 14 |
  • 15 | 16 | 17 | 18 | <?= $escaper->escapeHtmlAttr(__('Plugins')); ?> 23 | 24 | escapeHtml(__('Plugins')); ?> 25 | 26 | getPluginsCount() ?> 27 | 28 | 29 | 30 |
  • 31 | 32 | -------------------------------------------------------------------------------- /view/base/templates/menu/request.phtml: -------------------------------------------------------------------------------- 1 | getViewModel(); 10 | ?> 11 | isActive($block->getCollectorName())): ?> 12 |
  • 13 | 14 | 15 | 16 | <?= $escaper->escapeHtmlAttr(__('Request / Response')); ?> 21 | 22 | escapeHtml(__('Request / Response')); ?> 23 | 24 | 25 |
  • 26 | 27 | -------------------------------------------------------------------------------- /view/base/templates/menu/settings.phtml: -------------------------------------------------------------------------------- 1 | getViewModel(); 10 | ?> 11 |
  • 12 | 13 | 14 | 15 | <?= $escaper->escapeHtmlAttr(__('Settings')); ?> 20 | 21 | escapeHtml(__('Settings')); ?> 22 | 23 | 24 |
  • 25 | -------------------------------------------------------------------------------- /view/base/templates/menu/time.phtml: -------------------------------------------------------------------------------- 1 | getViewModel(); 10 | ?> 11 | isActive($block->getCollectorName())): ?> 12 | 13 | getCollector($block->getCollectorName()); ?> 14 |
  • 15 | 16 | 17 | 18 | <?= $escaper->escapeHtmlAttr(__('Performance')); ?> 23 | 24 | escapeHtml(__('Performance')); ?> 25 | 26 | getDuration() ?> ms 27 | 28 | 29 | 30 |
  • 31 | 32 | -------------------------------------------------------------------------------- /view/base/templates/menu/translation.phtml: -------------------------------------------------------------------------------- 1 | getViewModel(); 10 | ?> 11 | isActive($block->getCollectorName())): ?> 12 | 13 | getCollector($block->getCollectorName()); ?> 14 |
  • 15 | 16 | 17 | 18 | <?= $escaper->escapeHtmlAttr(__('Translation')); ?> 23 | 24 | escapeHtml(__('Translation')); ?> 25 | getMissingTranslations())): ?> 26 | 27 | getMissingTranslations()) ?> 28 | 29 | 30 | 31 | 32 |
  • 33 | 34 | -------------------------------------------------------------------------------- /view/base/templates/profiler.phtml: -------------------------------------------------------------------------------- 1 | 9 | 26 |
    27 | getChildHtml('debug_profiler_summary'); ?> 28 |
    29 |
    30 |
    31 |
    32 |
    33 | getChildHtml('debug_profiler_panel'); ?> 34 |
    35 |
    36 | getChildHtml('debug_profiler_sidebar'); ?> 37 |
    38 |
    39 | 40 | -------------------------------------------------------------------------------- /view/base/templates/profiler/summary.phtml: -------------------------------------------------------------------------------- 1 | getViewModel(); 10 | ?> 11 | getProfile()): ?> 12 |
    13 |
    14 |

    15 | getUrl() ?> 16 |

    17 | hasRedirect()): ?> 18 | renderRedirect($profile->getRedirect()); ?> 19 | 20 | 36 |
    37 |
    38 | 39 | -------------------------------------------------------------------------------- /view/base/templates/renderer/layout/graph.phtml: -------------------------------------------------------------------------------- 1 | 8 |
     9 |     
    10 |     getNodes() as $node): ?>
    11 |         getLayoutNodeRenderer()->create(['node' => $node])->render(); ?>
    12 |     
    13 | 
    14 | -------------------------------------------------------------------------------- /view/base/templates/renderer/layout/node.phtml: -------------------------------------------------------------------------------- 1 | getNode(); 9 | ?> 10 |
    11 | getParentId()): ?> 12 | getName()): ?> 13 | getClass() ?> | getTemplate() ?> 14 | 15 | 16 | getName(); ?> 17 | 18 | 19 | 20 | getPrefix() ?>└ 21 | getTemplate()): ?> 22 | 23 | getName() ?> 24 | getClass() ?> | getTemplate() ?> 25 | 26 | 27 | 28 | getName() ?> 29 | getClass() ?> 30 | 31 | 32 | 33 | 34 | 35 | getFormatter()->microtime($node->getRenderTime()) ?> ms 36 | getParentId()): ?> 37 | (getFormatter()->percentage($node->getRenderPercent()) ?>) 38 | 39 | 40 |
    41 | getChildren() as $child): ?> 42 | getLayoutNodeRenderer()->create(['node' => $child])->render(); ?> 43 | 44 | -------------------------------------------------------------------------------- /view/base/templates/renderer/parameters.phtml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | getParameters()): ?> 19 | $value): ?> 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
    escapeHtml(__('Key')); ?>escapeHtml(__('Value')); ?>
    getVarRenderer()->render($value); ?>
    escapeHtml(__('No data')); ?>
    32 | -------------------------------------------------------------------------------- /view/base/templates/renderer/query.phtml: -------------------------------------------------------------------------------- 1 | getQuery(); 10 | ?> 11 | getHighlightedQuery(); ?> 12 | getQueryParams())): ?> 13 |
    14 | escapeHtml(__('Parameters')) ?>: 15 | getVarRenderer()->render($query->getQueryParams()); ?> 16 |
    17 | 18 |
    19 | 22 | escapeHtml(__('View formatted query')) ?> 23 | 24 | 27 | escapeHtml(__('View runnable query')) ?> 28 | 29 |
    30 | 33 | 36 | -------------------------------------------------------------------------------- /view/base/templates/renderer/query/list.phtml: -------------------------------------------------------------------------------- 1 | getPrefix(); 9 | ?> 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | getQueries() as $index => $query): ?> 21 | 22 | 23 | 26 | 27 | 28 | 29 | 30 |
    #escapeHtml(__('Time')) ?>escapeHtml(__('Info')) ?>
    24 | getFormatter()->microtime($query->getElapsedSecs()); ?> ms 25 | getQueryRenderer()->create(['query' => $query])->render(); ?>
    31 | -------------------------------------------------------------------------------- /view/base/templates/renderer/redirect.phtml: -------------------------------------------------------------------------------- 1 | getRedirect(); 10 | ?> 11 |
    12 |
    13 | 14 | getStatusCode() ?> 15 | 16 | escapeHtml(__('Redirect from')) ?> 17 |
    18 |
    19 | getMethod() ?> 20 | 21 | getToken() ?> 22 | 23 |
    24 |
    25 | -------------------------------------------------------------------------------- /view/base/templates/renderer/table.phtml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | getItems() as $key => $value): ?> 18 | 19 | 20 | 21 | 22 | 23 | 24 |
    escapeHtml(__($block->getKeyLabel())); ?>escapeHtml(__($block->getValueLabel())); ?>
    getVarRenderer()->render($value); ?>
    25 | -------------------------------------------------------------------------------- /view/base/templates/renderer/trace.phtml: -------------------------------------------------------------------------------- 1 | getTrace(); 9 | ?> 10 | 11 | 13 | escapeHtml(__('Show trace')) ?> 14 | 15 | 23 | 24 | -------------------------------------------------------------------------------- /view/base/templates/renderer/trace/call.phtml: -------------------------------------------------------------------------------- 1 | 8 |
    9 | hasClass()): ?> 10 | getClass() ?> 11 | 12 | hasClass() && $block->hasFunction()): ?> 13 | :: 14 | 15 | hasFunction()): ?> 16 | getFunction() ?> 17 | 18 |
    19 |
    20 | hasLine()): ?> 21 | #getLine() ?> 22 | 23 | hasFile()): ?> 24 | getFile() ?> 25 | 26 |
    27 | -------------------------------------------------------------------------------- /view/base/templates/toolbar.phtml: -------------------------------------------------------------------------------- 1 | getViewModel(); 10 | $token = $viewModel->getToken(); 11 | ?> 12 |
    13 | 18 | ClawRock 20 | 21 |
    22 |
    23 |
    24 |
    25 | 26 |
    27 | ClawRock 29 |
    30 |
    31 |
    32 | 33 | getCollectorBlocks() as $collectorBlock): ?> 34 | toHtml(); ?> 35 | 36 | 42 | <?= $escaper->escapeHtml(__('Close')) ?> 44 | 45 |
    46 | -------------------------------------------------------------------------------- /view/base/templates/toolbar/ajax.phtml: -------------------------------------------------------------------------------- 1 | getCollector(); 10 | ?> 11 |
    16 |
    17 | <?= $escaper->escapeHtmlAttr(__('Ajax')) ?> 19 | 0 20 |
    21 |
    22 |
    23 | 24 |
    25 |
    26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
    escapeHtml(__('Method')); ?>escapeHtml(__('Status')); ?>escapeHtml(__('URL')); ?>escapeHtml(__('Time')); ?>escapeHtml(__('Profile')); ?>
    38 |
    39 |
    40 |
    41 | -------------------------------------------------------------------------------- /view/base/templates/toolbar/database.phtml: -------------------------------------------------------------------------------- 1 | getCollector(); 10 | ?> 11 |
    12 | 13 |
    14 | <?= $escaper->escapeHtmlAttr(__('Database')) ?> 16 | getQueriesCount() ?> 17 | 18 | escapeHtml(__('in')); ?> 19 | getTotalTime() ?> 20 | ms 21 | 22 |
    23 |
    24 |
    25 |
    26 | escapeHtml(__('Database Queries')); ?> 27 | getQueriesCount() ?> 28 |
    29 |
    30 | escapeHtml(__('Duplicated Queries')); ?> 31 | 32 | getDuplicatedQueries()) ?> 33 | 34 |
    35 |
    36 | escapeHtml(__('Query time')); ?> 37 | getTotalTime() ?> ms 38 |
    39 |
    40 |
    41 | -------------------------------------------------------------------------------- /view/base/templates/toolbar/event.phtml: -------------------------------------------------------------------------------- 1 | getCollector(); 10 | ?> 11 |
    12 | 13 |
    14 | <?= $escaper->escapeHtmlAttr(__('Event')) ?> 16 | getObserversCount() ?> 17 | 18 | escapeHtml(__('in')); ?> 19 | getTime() ?> 20 | ms 21 | 22 |
    23 |
    24 |
    25 |
    26 |
    27 | escapeHtml(__('Events dispatched')); ?> 28 | getEvents()) ?> 29 |
    30 |
    31 | escapeHtml(__('Observers executed')); ?> 32 | getObserversCount() ?> 33 |
    34 |
    35 | escapeHtml(__('Time')); ?> 36 | getTime(); ?> ms 37 |
    38 |
    39 |
    40 |
    41 | -------------------------------------------------------------------------------- /view/base/templates/toolbar/memory.phtml: -------------------------------------------------------------------------------- 1 | getCollector(); 10 | ?> 11 |
    16 |
    17 | <?= $escaper->escapeHtmlAttr(__('Memory')) ?> 19 | getMemoryUsage() ?> 20 | MB 21 |
    22 |
    23 |
    24 | escapeHtml(__('Peak memory usage')); ?> 25 | getMemoryUsage(); ?> MB 26 |
    27 |
    28 | escapeHtml(__('PHP memory limit')); ?> 29 | 30 | hasMemoryLimit() 31 | ? $escaper->escapeHtml(__('Unlimited')) 32 | : /* @noEscape */ $collector->getMemoryLimit(); ?> 33 | 34 |
    35 |
    36 |
    37 | -------------------------------------------------------------------------------- /view/base/templates/toolbar/plugin.phtml: -------------------------------------------------------------------------------- 1 | getCollector(); 10 | ?> 11 |
    12 | 13 |
    14 | <?= $escaper->escapeHtmlAttr(__('Plugin')) ?> 16 | getPluginsCount() ?> 17 |
    18 |
    19 |
    20 |
    21 | escapeHtml(__('Before plugins')); ?> 22 | 23 | getBeforePluginsCount() ?> 24 | 25 |
    26 |
    27 | escapeHtml(__('Around plugins')); ?> 28 | 29 | getAroundPluginsCount() ?> 30 | 31 |
    32 |
    33 | escapeHtml(__('After plugins')); ?>' 34 | 35 | getAfterPluginsCount() ?> 36 | 37 |
    38 |
    39 |
    40 | -------------------------------------------------------------------------------- /view/base/templates/toolbar/time.phtml: -------------------------------------------------------------------------------- 1 | getCollector(); 10 | $token = $block->getToken(); 11 | ?> 12 |
    17 | 18 |
    19 | <?= $escaper->escapeHtmlAttr(__('Time')) ?> 21 | getDuration() ?> 22 | ms 23 |
    24 |
    25 |
    26 | escapeHtml(__('Total Time')); ?> 27 | getDuration() ?> ms 28 |
    29 |
    30 |
    31 |
    32 | -------------------------------------------------------------------------------- /view/base/templates/toolbar/translation.phtml: -------------------------------------------------------------------------------- 1 | getCollector(); 10 | ?> 11 |
    16 | 18 |
    19 | <?= $escaper->escapeHtmlAttr(__('Translation')) ?> 22 | getTotal() ?> 23 |
    24 |
    25 |
    26 |
    27 | escapeHtml(__('Missing messages')); ?> 28 | 32 | getMissingTranslations()); ?> 33 | 34 |
    35 |
    36 | escapeHtml(__('Defined messages')) ?> 37 | getDefinedTranslations()) ?> 38 |
    39 |
    40 |
    41 | -------------------------------------------------------------------------------- /view/base/web/images/collector/ajax.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /view/base/web/images/collector/cache.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /view/base/web/images/collector/config.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /view/base/web/images/collector/customer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /view/base/web/images/collector/database.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /view/base/web/images/collector/event.svg: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | -------------------------------------------------------------------------------- /view/base/web/images/collector/layout.svg: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /view/base/web/images/collector/logs.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /view/base/web/images/collector/memory.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /view/base/web/images/collector/model.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /view/base/web/images/collector/models.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /view/base/web/images/collector/plugin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /view/base/web/images/collector/redirect.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | -------------------------------------------------------------------------------- /view/base/web/images/collector/time.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /view/base/web/images/collector/translation.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | -------------------------------------------------------------------------------- /view/base/web/images/icon/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /view/base/web/images/icon/menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /view/base/web/images/icon/no.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /view/base/web/images/icon/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /view/base/web/images/icon/settings.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 16 | 17 | -------------------------------------------------------------------------------- /view/base/web/images/icon/yes.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /view/base/web/images/magento.svg: -------------------------------------------------------------------------------- 1 | 8 | --------------------------------------------------------------------------------