├── .gitignore ├── Logger ├── Logger.php ├── LoggableInterface.php ├── Handler.php └── DataLogger.php ├── view └── base │ ├── web │ ├── images │ │ ├── readme │ │ │ ├── ajax.png │ │ │ ├── cache.png │ │ │ ├── hyva.png │ │ │ ├── config.png │ │ │ ├── database.png │ │ │ ├── events.png │ │ │ ├── homepage.png │ │ │ ├── memory.png │ │ │ ├── models.png │ │ │ ├── plugins.png │ │ │ ├── request.png │ │ │ ├── callmap-main.png │ │ │ ├── callmap-popup.png │ │ │ ├── layout-render.png │ │ │ ├── performance.png │ │ │ ├── translation.png │ │ │ └── layout-block-cache.png │ │ ├── jquery-ui │ │ │ ├── ui-icons_444444_256x240.png │ │ │ ├── ui-icons_555555_256x240.png │ │ │ ├── ui-icons_777620_256x240.png │ │ │ ├── ui-icons_777777_256x240.png │ │ │ ├── ui-icons_cc0000_256x240.png │ │ │ └── ui-icons_ffffff_256x240.png │ │ ├── collector │ │ │ ├── customer.svg │ │ │ ├── layout.svg │ │ │ ├── memory.svg │ │ │ ├── config.svg │ │ │ ├── time.svg │ │ │ ├── model.svg │ │ │ ├── models.svg │ │ │ ├── ajax.svg │ │ │ ├── cache.svg │ │ │ ├── database.svg │ │ │ ├── logs.svg │ │ │ ├── redirect.svg │ │ │ ├── event.svg │ │ │ ├── plugin.svg │ │ │ └── translation.svg │ │ ├── icon │ │ │ ├── no.svg │ │ │ ├── yes.svg │ │ │ ├── close.svg │ │ │ ├── menu.svg │ │ │ ├── search.svg │ │ │ └── settings.svg │ │ └── magento.svg │ └── js │ │ └── callmap │ │ └── extra-data.js │ ├── layout │ ├── clawrock_debug.xml │ ├── debug_panel_cache.xml │ ├── debug_panel_event.xml │ ├── debug_panel_model.xml │ ├── debug_panel_time.xml │ ├── debug_panel_config.xml │ ├── debug_panel_database.xml │ ├── debug_panel_layout.xml │ ├── debug_panel_plugin.xml │ ├── debug_panel_request.xml │ ├── debug_panel_translation.xml │ ├── debug_profiler_info.xml │ ├── debug_xhprof_detail.xml │ ├── debug_panel_callmap.xml │ └── debug_profiler_search.xml │ ├── requirejs-config.js │ ├── templates │ ├── renderer │ │ ├── layout │ │ │ ├── graph.phtml │ │ │ └── node.phtml │ │ ├── redirect.phtml │ │ ├── table.phtml │ │ ├── trace │ │ │ └── call.phtml │ │ ├── trace.phtml │ │ ├── parameters.phtml │ │ ├── query │ │ │ └── list.phtml │ │ └── query.phtml │ ├── menu │ │ ├── settings.phtml │ │ ├── cache.phtml │ │ ├── config.phtml │ │ ├── callmap.phtml │ │ ├── request.phtml │ │ ├── event.phtml │ │ ├── time.phtml │ │ ├── plugin.phtml │ │ ├── layout.phtml │ │ ├── database.phtml │ │ ├── translation.phtml │ │ └── model.phtml │ ├── toolbar.phtml │ ├── profiler.phtml │ ├── toolbar │ │ ├── time.phtml │ │ ├── callmap.phtml │ │ ├── memory.phtml │ │ ├── translation.phtml │ │ ├── ajax.phtml │ │ ├── plugin.phtml │ │ ├── event.phtml │ │ ├── database.phtml │ │ ├── layout.phtml │ │ └── model.phtml │ └── profiler │ │ └── summary.phtml │ └── page_layout │ └── toolbar.xml ├── Model ├── Session.php ├── View │ ├── Renderer │ │ ├── RendererInterface.php │ │ ├── VarRenderer.php │ │ ├── RedirectRenderer.php │ │ ├── ParametersRenderer.php │ │ ├── TableRenderer.php │ │ ├── QueryParametersRenderer.php │ │ ├── TraceRenderer.php │ │ ├── LayoutNodeRenderer.php │ │ ├── TraceCallRenderer.php │ │ ├── QueryListRenderer.php │ │ └── QueryRenderer.php │ ├── Summary.php │ ├── Menu.php │ ├── Xhprof.php │ └── Search.php ├── Collector │ ├── LateCollectorInterface.php │ ├── LoggerCollectorInterface.php │ ├── CollectorInterface.php │ └── AjaxCollector.php ├── Url │ └── ScopeResolver.php ├── Info │ ├── CallmapInfo.php │ ├── ExtensionInfo.php │ ├── LayoutInfo.php │ ├── MemoryInfo.php │ ├── CustomerInfo.php │ └── MagentoInfo.php ├── Storage │ ├── ProfileMemoryStorage.php │ └── HttpStorage.php ├── Config │ ├── Source │ │ ├── ErrorHandler.php │ │ └── XhprofFlags.php │ └── Database │ │ └── ProfilerWriter.php ├── DataCollector.php ├── ValueObject │ ├── Translation.php │ ├── LoopModelAction.php │ ├── EventObserver.php │ ├── ModelAction.php │ └── Plugin.php ├── Serializer │ ├── CollectorSerializer.php │ └── ProfileSerializer.php └── Indexer │ └── ProfileIndexer.php ├── registration.php ├── App ├── Action │ ├── Context.php │ └── Frontend │ │ └── Context.php ├── AbstractAction.php ├── ConfigInterface.php ├── DefaultPath.php ├── Request │ └── PathInfoProcessor.php ├── Config.php ├── Router │ └── NoRouteHandler.php └── Router.php ├── Exception └── CollectorNotFoundException.php ├── Serializer ├── SerializerInterface.php └── Serializer.php ├── etc ├── debug │ ├── routes.xml │ └── di.xml ├── frontend │ └── routes.xml ├── adminhtml │ ├── routes.xml │ ├── di.xml │ └── events.xml ├── module.xml └── events.xml ├── Controller ├── Debug │ ├── Profiler │ │ ├── PHPInfo.php │ │ ├── Toolbar.php │ │ ├── Purge.php │ │ └── Search.php │ └── Cache │ │ ├── Enable.php │ │ ├── Disable.php │ │ ├── Clean.php │ │ └── Flush.php └── Adminhtml │ └── Profiler │ └── Config.php ├── Observer ├── ValidateRedirect.php ├── DebugHandle.php ├── AllowedIP.php ├── Collector │ ├── LayoutCollectorBeforeToHtml.php │ └── LayoutCollectorAfterToHtml.php ├── BeforeSendResponse.php └── Config │ └── DatabaseProfiler.php ├── Plugin ├── Collector │ ├── TimeCollectorPlugin.php │ ├── EventCollectorPlugin.php │ ├── TranslationCollectorPlugin.php │ └── CacheCollectorPlugin.php ├── PageCache │ └── KernelPlugin.php ├── ProfileRepository │ └── RequestTimePlugin.php └── ErrorHandler │ └── WhoopsPlugin.php ├── Interception ├── Code │ └── Generator │ │ ├── Interceptor.php │ │ └── SetupInterceptor.php ├── PluginDataCollector.php └── CliProfiler.php ├── phpmd.xml ├── Console └── Command │ ├── DatabaseProfilerEnableCommand.php │ └── DatabaseProfilerDisableCommand.php ├── Helper ├── Debug.php ├── Formatter.php ├── File.php └── Injector.php ├── Api ├── Data │ └── ProfileInterface.php └── ProfileRepositoryInterface.php ├── composer.json └── .travis.yml /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | composer.lock 3 | /vendor 4 | -------------------------------------------------------------------------------- /Logger/Logger.php: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /App/AbstractAction.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /view/base/requirejs-config.js: -------------------------------------------------------------------------------- 1 | var config = { 2 | map: { 3 | '*': { 4 | extraData: 'Daseraf_Debug/js/callmap/extra-data', 5 | tablesorter: 'Daseraf_Debug/js/tablesorter', 6 | } 7 | }, 8 | shim: { 9 | 'tablesorter': { 10 | 'deps': ['jquery', 'jquery-ui'] 11 | } 12 | } 13 | 14 | }; -------------------------------------------------------------------------------- /etc/frontend/routes.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /view/base/templates/renderer/layout/graph.phtml: -------------------------------------------------------------------------------- 1 | 2 |
3 |     
4 |     getNodes() as $node) : ?>
5 |         getLayoutNodeRenderer()->create(['node' => $node])->render(); ?>
6 |     
7 | 
8 | -------------------------------------------------------------------------------- /view/base/web/images/collector/customer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Serializer/Serializer.php: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Logger/DataLogger.php: -------------------------------------------------------------------------------- 1 | data[$value->getId()] = $value; 12 | 13 | return $this; 14 | } 15 | 16 | public function getLogs() 17 | { 18 | return $this->data; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /etc/adminhtml/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Magento\Framework\Url 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Model/Info/CallmapInfo.php: -------------------------------------------------------------------------------- 1 | runData = $data; 15 | } 16 | 17 | public function getRunData(): array 18 | { 19 | return $this->runData; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /etc/adminhtml/events.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /etc/module.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Controller/Debug/Profiler/PHPInfo.php: -------------------------------------------------------------------------------- 1 | resultFactory->create(ResultFactory::TYPE_RAW); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /view/base/web/images/icon/no.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /view/base/web/images/icon/yes.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Model/Storage/ProfileMemoryStorage.php: -------------------------------------------------------------------------------- 1 | profile; 17 | } 18 | 19 | public function write(ProfileInterface $profile) 20 | { 21 | $this->profile = $profile; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /view/base/web/images/icon/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /view/base/web/images/collector/layout.svg: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /view/base/web/images/collector/memory.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /view/base/web/images/collector/config.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Model/View/Renderer/VarRenderer.php: -------------------------------------------------------------------------------- 1 | variable = $variable; 14 | } 15 | 16 | /** 17 | * @SuppressWarnings(PHPMD.StaticAccess) 18 | * @return string 19 | */ 20 | public function render(): string 21 | { 22 | return (string) VarDumper::dump($this->variable); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /view/base/web/images/collector/time.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/magento.svg: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /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/database.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /view/base/page_layout/toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | Daseraf\Debug\Model\View\Toolbar 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Model/Collector/CollectorInterface.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | getRedirect(); ?> 4 |
5 |
6 | 7 | getStatusCode() ?> 8 | 9 | escapeHtml(__('Redirect from')) ?> 10 |
11 |
12 | getMethod() ?> 13 | 14 | getToken() ?> 15 | 16 |
17 |
18 | -------------------------------------------------------------------------------- /Model/Config/Source/ErrorHandler.php: -------------------------------------------------------------------------------- 1 | self::MAGENTO, 'label' => __('Default')], 16 | ['value' => self::WHOOPS, 'label' => __('Whoops')], 17 | ]; 18 | } 19 | 20 | public function toArray() 21 | { 22 | return [ 23 | self::MAGENTO => __('Default'), 24 | self::WHOOPS => __('Whoops'), 25 | ]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /view/base/web/images/icon/menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /view/base/web/images/icon/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /view/base/templates/menu/settings.phtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | getViewModel(); ?> 4 |
  • 5 | 6 | 7 | 8 | <?= $block->escapeHtmlAttr(__('Settings')); ?> 10 | 11 | escapeHtml(__('Settings')); ?> 12 | 13 | 14 |
  • 15 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Profiler/Config.php: -------------------------------------------------------------------------------- 1 | resultRedirectFactory->create(); 20 | 21 | return $resultRedirect->setPath('admin/system_config/edit', [ 22 | 'section' => 'daseraf_debug', 23 | 'key' => $this->_url->getSecretKey('adminhtml', 'system_config', 'edit'), 24 | ]); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /view/base/templates/renderer/table.phtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | getItems() as $key => $value): ?> 11 | 12 | 13 | 14 | 15 | 16 | 17 |
    escapeHtml(__($block->getKeyLabel())); ?>escapeHtml(__($block->getValueLabel())); ?>
    getVarRenderer()->create(['variable' => $value])->render(); ?>
    -------------------------------------------------------------------------------- /view/base/web/images/collector/logs.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /view/base/layout/debug_panel_cache.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | Daseraf\Debug\Model\View\Profiler 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /view/base/layout/debug_panel_event.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | Daseraf\Debug\Model\View\Profiler 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /view/base/layout/debug_panel_model.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | Daseraf\Debug\Model\View\Profiler 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /view/base/layout/debug_panel_time.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | Daseraf\Debug\Model\View\Profiler 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /view/base/layout/debug_panel_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | Daseraf\Debug\Model\View\Profiler 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /view/base/layout/debug_panel_database.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | Daseraf\Debug\Model\View\Profiler 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /view/base/layout/debug_panel_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | Daseraf\Debug\Model\View\Profiler 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /view/base/layout/debug_panel_plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | Daseraf\Debug\Model\View\Profiler 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /view/base/layout/debug_panel_request.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | Daseraf\Debug\Model\View\Profiler 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Observer/ValidateRedirect.php: -------------------------------------------------------------------------------- 1 | session = $session; 20 | } 21 | 22 | public function execute(Observer $observer) 23 | { 24 | if ($this->session->getData(RequestInfo::REDIRECT_PARAM)) { 25 | $observer->getRequest()->setParam('_redirected', true); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /view/base/layout/debug_panel_translation.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | Daseraf\Debug\Model\View\Profiler 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /view/base/layout/debug_profiler_info.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 32 | -------------------------------------------------------------------------------- /Model/Collector/AjaxCollector.php: -------------------------------------------------------------------------------- 1 | config = $config; 18 | } 19 | 20 | public function collect(): CollectorInterface 21 | { 22 | // Nothing to collect here 23 | return $this; 24 | } 25 | 26 | /** 27 | * @return bool 28 | */ 29 | public function isEnabled(): bool 30 | { 31 | return $this->config->isAjaxCollectorEnabled(); 32 | } 33 | 34 | public function getData(): array 35 | { 36 | return []; 37 | } 38 | 39 | public function setData(array $data): CollectorInterface 40 | { 41 | return $this; 42 | } 43 | 44 | public function getName(): string 45 | { 46 | return self::NAME; 47 | } 48 | 49 | public function getStatus(): string 50 | { 51 | return self::STATUS_DEFAULT; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Plugin/Collector/EventCollectorPlugin.php: -------------------------------------------------------------------------------- 1 | eventCollector = $eventCollector; 23 | } 24 | 25 | public function aroundDispatch(InvokerDefault $subject, callable $proceed, array $configuration, Observer $observer) 26 | { 27 | $start = microtime(true); 28 | $proceed($configuration, $observer); 29 | $end = microtime(true); 30 | 31 | $this->eventCollector->log(new EventObserver( 32 | $configuration['name'], 33 | $configuration['instance'], 34 | $observer->getEvent()->getName(), 35 | $end - $start 36 | )); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /view/base/layout/debug_profiler_search.xml: -------------------------------------------------------------------------------- 1 | 2 | > 4 | 5 | 6 | 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | Daseraf\Debug\Model\View\Search 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Model/DataCollector.php: -------------------------------------------------------------------------------- 1 | $value) { 15 | $this->data[$key] = $value; 16 | } 17 | 18 | return $this; 19 | } 20 | 21 | public function getData(string $key = '') 22 | { 23 | if ($key) { 24 | return $this->data[$key] ?? null; 25 | } 26 | 27 | return $this->data; 28 | } 29 | 30 | public function addData(string $key, $value): DataCollector 31 | { 32 | $this->data[$key] = $value; 33 | 34 | return $this; 35 | } 36 | 37 | public function appendData(string $key, $value): DataCollector 38 | { 39 | if (!isset($this->data[$key])) { 40 | $this->data[$key] = []; 41 | } 42 | $this->data[$key][] = $value; 43 | 44 | return $this; 45 | } 46 | 47 | public function removeData(string $key): DataCollector 48 | { 49 | unset($this->data[$key]); 50 | 51 | return $this; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /view/base/templates/toolbar/time.phtml: -------------------------------------------------------------------------------- 1 | getCollector(); 5 | $token = $block->getToken(); 6 | ?> 7 | 23 | -------------------------------------------------------------------------------- /Console/Command/DatabaseProfilerEnableCommand.php: -------------------------------------------------------------------------------- 1 | profilerWriter = $profilerWriter; 22 | } 23 | 24 | protected function configure() 25 | { 26 | parent::configure(); 27 | 28 | $this->setDescription('Enable database profiler required for database collector.'); 29 | } 30 | 31 | /** 32 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 33 | */ 34 | protected function execute(InputInterface $input, OutputInterface $output) 35 | { 36 | $this->profilerWriter->save(true); 37 | 38 | $output->writeLn('Database profiler enabled!'); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Console/Command/DatabaseProfilerDisableCommand.php: -------------------------------------------------------------------------------- 1 | profilerWriter = $profilerWriter; 22 | } 23 | 24 | protected function configure() 25 | { 26 | parent::configure(); 27 | 28 | $this->setDescription('Disable database profiler required for database collector.'); 29 | } 30 | 31 | /** 32 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 33 | */ 34 | protected function execute(InputInterface $input, OutputInterface $output) 35 | { 36 | $this->profilerWriter->save(false); 37 | 38 | $output->writeLn('Database profiler disabled!'); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Model/ValueObject/Translation.php: -------------------------------------------------------------------------------- 1 | phrase = $phrase; 27 | $this->translation = $translation; 28 | $this->defined = $defined; 29 | } 30 | 31 | public function getId() 32 | { 33 | return $this->phrase; 34 | } 35 | 36 | /** 37 | * @return string 38 | */ 39 | public function getPhrase(): string 40 | { 41 | return $this->phrase; 42 | } 43 | 44 | /** 45 | * @return string 46 | */ 47 | public function getTranslation(): string 48 | { 49 | return $this->translation; 50 | } 51 | 52 | /** 53 | * @return bool 54 | */ 55 | public function isDefined(): bool 56 | { 57 | return $this->defined; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Model/View/Renderer/RedirectRenderer.php: -------------------------------------------------------------------------------- 1 | redirect = $redirect; 32 | $this->layout = $layout; 33 | $this->url = $url; 34 | } 35 | 36 | public function render(): string 37 | { 38 | return $this->layout->createBlock(Template::class) 39 | ->setTemplate(self::TEMPLATE) 40 | ->setProfilerUrl($this->url->getProfilerUrl($this->redirect->getToken())) 41 | ->setRedirect($this->redirect) 42 | ->toHtml(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /view/base/templates/toolbar/callmap.phtml: -------------------------------------------------------------------------------- 1 | getCollector(); 5 | $xhporfViewModel = $block->getXhporfViewModel(); 6 | $xhporfProfile = $xhporfViewModel->getXhprofProfile($collector->getData()); 7 | $fallbackData = $xhporfProfile->sort('ewt'); 8 | $totalFunctions = number_format($xhporfProfile->getFunctionCount()); 9 | ?> 10 |
    11 | 12 |
    13 | <?= $block->escapeHtmlAttr(__('Call Map')) ?> 15 | escapeHtmlAttr(__('Call Map')) ?> 16 |
    17 |
    18 |
    19 |
    20 | escapeHtml(__('Number of function calls')); ?> 21 | 22 |
    23 |
    24 |
    25 | -------------------------------------------------------------------------------- /view/base/web/images/collector/event.svg: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | -------------------------------------------------------------------------------- /Observer/Collector/LayoutCollectorAfterToHtml.php: -------------------------------------------------------------------------------- 1 | layoutCollector = $layoutCollector; 21 | } 22 | 23 | public function execute(Observer $observer) 24 | { 25 | /** @var \Magento\Framework\View\Element\AbstractBlock $block */ 26 | $block = $observer->getBlock(); 27 | 28 | $renderedTimestamp = microtime(true); 29 | $renderTime = $renderedTimestamp - $block->getData(LayoutCollector::BLOCK_START_RENDER_KEY); 30 | 31 | $block->addData([ 32 | LayoutCollector::BLOCK_STOP_RENDER_KEY => $renderedTimestamp, 33 | LayoutCollector::RENDER_TIME => $renderTime, 34 | ]); 35 | 36 | $this->layoutCollector->log(new Block($block)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Model/Config/Database/ProfilerWriter.php: -------------------------------------------------------------------------------- 1 | configWriter = $configWriter; 20 | } 21 | 22 | /** 23 | * @param bool $flag 24 | * @throws \Magento\Framework\Exception\FileSystemException 25 | */ 26 | public function save(bool $flag) 27 | { 28 | $configGroup = [ 29 | ConfigOptionsListConstants::CONFIG_PATH_DB => [ 30 | 'connection' => [ 31 | 'default' => [ 32 | 'profiler' => [ 33 | 'class' => Profiler::class, 34 | 'enabled' => $flag, 35 | ], 36 | ], 37 | ], 38 | ], 39 | ]; 40 | 41 | $this->configWriter->saveConfig([ConfigFilePool::APP_ENV => $configGroup]); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Model/Info/LayoutInfo.php: -------------------------------------------------------------------------------- 1 | layout = $layout; 19 | } 20 | 21 | public function getHandles(): array 22 | { 23 | return $this->layout->getUpdate()->getHandles(); 24 | } 25 | 26 | public function getCreatedBlocks(): array 27 | { 28 | $blocks = []; 29 | foreach ($this->layout->getAllBlocks() as $block) { 30 | $blocks[] = new Block($block); 31 | } 32 | 33 | return $blocks; 34 | } 35 | 36 | public function getNotRenderedBlocks(): array 37 | { 38 | $blocks = []; 39 | foreach ($this->layout->getAllBlocks() as $block) { 40 | /** @var \Magento\Framework\View\Element\AbstractBlock $block */ 41 | if (!$block->getData(LayoutCollector::BLOCK_PROFILER_ID_KEY)) { 42 | $blocks[] = new Block($block); 43 | } 44 | } 45 | 46 | return $blocks; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Controller/Debug/Profiler/Toolbar.php: -------------------------------------------------------------------------------- 1 | profileMemoryStorage = $profileMemoryStorage; 28 | $this->profileRepository = $profileRepository; 29 | } 30 | 31 | public function execute() 32 | { 33 | $token = $this->getRequest()->getParam(Profiler::URL_TOKEN_PARAMETER); 34 | $profile = $this->profileRepository->getById($token); 35 | $this->profileMemoryStorage->write($profile); 36 | 37 | return $this->resultFactory->create(ResultFactory::TYPE_PAGE); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /view/base/web/images/collector/plugin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /view/base/templates/menu/event.phtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | getViewModel(); ?> 4 | 5 | isActive($block->getCollectorName())): ?> 6 | 7 | getCollector($block->getCollectorName()); ?> 8 |
  • 9 | 10 | 11 | 12 | <?= $block->escapeHtmlAttr(__('Events')); ?> 14 | 15 | escapeHtml(__('Events')); ?> 16 | 17 | getObserversCount() ?> 18 | 19 | 20 | 21 |
  • 22 | 23 | -------------------------------------------------------------------------------- /view/base/templates/menu/time.phtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | getViewModel(); ?> 4 | 5 | isActive($block->getCollectorName())): ?> 6 | 7 | getCollector($block->getCollectorName()); ?> 8 |
  • 9 | 10 | 11 | 12 | <?= $block->escapeHtmlAttr(__('Performance')); ?> 14 | 15 | escapeHtml(__('Performance')); ?> 16 | 17 | getDuration() ?> ms 18 | 19 | 20 | 21 |
  • 22 | 23 | -------------------------------------------------------------------------------- /Helper/Debug.php: -------------------------------------------------------------------------------- 1 | isBacktraceItemValid($item, $functions)) { 22 | array_shift($backtrace); 23 | $item = reset($backtrace); 24 | } 25 | 26 | return array_map(function ($item) { 27 | unset($item['object'], $item['args'], $item['type']); 28 | 29 | return $item; 30 | }, $backtrace); 31 | } 32 | 33 | private function isBacktraceItemValid(array $data, array $functions): bool 34 | { 35 | if (!isset($data['class'], $data['function'])) { 36 | return false; 37 | } 38 | 39 | if (empty($functions)) { 40 | return true; 41 | } 42 | 43 | if (!in_array($data['function'], $functions)) { 44 | return false; 45 | } 46 | 47 | return true; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Api/Data/ProfileInterface.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | getViewModel(); ?> 4 | 5 | isActive($block->getCollectorName())): ?> 6 | 7 | getCollector($block->getCollectorName()); ?> 8 |
  • 9 | 10 | 11 | 12 | <?= $block->escapeHtmlAttr(__('Plugins')); ?> 14 | 15 | escapeHtml(__('Plugins')); ?> 16 | 17 | getPluginsCount() ?> 18 | 19 | 20 | 21 |
  • 22 | 23 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Controller/Debug/Profiler/Purge.php: -------------------------------------------------------------------------------- 1 | profileFileStorage = $profileFileStorage; 28 | $this->logger = $logger; 29 | } 30 | 31 | public function execute() 32 | { 33 | try { 34 | $this->profileFileStorage->purge(); 35 | } catch (FileSystemException $e) { 36 | $this->logger->critical($e); 37 | } 38 | 39 | /** @var $resultRedirect */ 40 | return $this->resultFactory->create(ResultFactory::TYPE_REDIRECT) 41 | ->setUrl($this->_redirect->getRefererUrl()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Model/View/Renderer/ParametersRenderer.php: -------------------------------------------------------------------------------- 1 | parameters = $parameters; 32 | $this->layout = $layout; 33 | $this->varRendererFactory = $varRendererFactory; 34 | } 35 | 36 | public function render(): string 37 | { 38 | return $this->layout->createBlock(Template::class) 39 | ->setTemplate(self::TEMPLATE) 40 | ->setData('parameters', $this->parameters) 41 | ->setData('var_renderer', $this->varRendererFactory) 42 | ->toHtml(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /App/Request/PathInfoProcessor.php: -------------------------------------------------------------------------------- 1 | subject = $subject; 26 | $this->frontname = $frontNameResolver; 27 | } 28 | 29 | /** 30 | * Process path info 31 | * 32 | * @param \Magento\Framework\App\RequestInterface $request 33 | * @param string $pathInfo 34 | * @return string 35 | */ 36 | public function process(\Magento\Framework\App\RequestInterface $request, $pathInfo) 37 | { 38 | $pathParts = explode('/', ltrim($pathInfo, '/'), 2); 39 | $firstPart = $pathParts[0]; 40 | 41 | if ($firstPart != $this->frontNameResolver->getFrontName()) { 42 | return $this->subject->process($request, $pathInfo); 43 | } 44 | return $pathInfo; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /view/base/templates/menu/layout.phtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | getViewModel(); ?> 4 | 5 | isActive($block->getCollectorName())): ?> 6 | 7 | getCollector($block->getCollectorName()); ?> 8 |
  • 9 | 10 | 11 | 12 | <?= $block->escapeHtmlAttr(__('Layout')); ?> 14 | 15 | escapeHtml(__('Layout')); ?> 16 | getNotRenderedBlocks())): ?> 17 | 18 | getNotRenderedBlocks()) ?> 19 | 20 | 21 | 22 | 23 |
  • 24 | 25 | -------------------------------------------------------------------------------- /view/base/templates/menu/database.phtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | getViewModel(); ?> 4 | 5 | isActive($block->getCollectorName())): ?> 6 | 7 | getCollector($block->getCollectorName()); ?> 8 |
  • 9 | 10 | 11 | 12 | <?= $block->escapeHtmlAttr(__('Database')); ?> 14 | 15 | escapeHtml(__('Database')); ?> 16 | getDuplicatedQueries())): ?> 17 | 18 | getDuplicatedQueries()) ?> 19 | 20 | 21 | 22 | 23 |
  • 24 | 25 | -------------------------------------------------------------------------------- /view/base/templates/menu/translation.phtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | getViewModel(); ?> 4 | 5 | isActive($block->getCollectorName())): ?> 6 | 7 | getCollector($block->getCollectorName()); ?> 8 |
  • 9 | 10 | 11 | 12 | <?= $block->escapeHtmlAttr(__('Translation')); ?> 14 | 15 | escapeHtml(__('Translation')); ?> 16 | getMissingTranslations())): ?> 17 | 18 | getMissingTranslations()) ?> 19 | 20 | 21 | 22 | 23 |
  • 24 | 25 | -------------------------------------------------------------------------------- /Helper/Formatter.php: -------------------------------------------------------------------------------- 1 | config = $config; 16 | } 17 | 18 | public function microtime(float $value, int $precision = null) 19 | { 20 | if ($precision === null) { 21 | $precision = $this->config->getTimePrecision(); 22 | } 23 | 24 | return sprintf('%0.' . $precision . 'f', $value * 1000); 25 | } 26 | 27 | public function revertMicrotime(string $value): float 28 | { 29 | return (float) $value / 1000; 30 | } 31 | 32 | public function toMegaBytes(int $value, int $precision = 0) 33 | { 34 | return sprintf('%0.' . $precision . 'f', $value / 1024 / 1024); 35 | } 36 | 37 | public function formatBytes($bytes, $to, $decimal_places = 3) 38 | { 39 | $formulas = [ 40 | 'K' => number_format($bytes / 1024, $decimal_places), 41 | 'M' => number_format($bytes / 1048576, $decimal_places), 42 | 'G' => number_format($bytes / 1073741824, $decimal_places), 43 | ]; 44 | 45 | return $formulas[$to] ?? 0; 46 | } 47 | 48 | public function percentage(float $value, int $precision = 5) 49 | { 50 | return sprintf('%.' . $precision . 'f%%', $value * 100); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Model/ValueObject/LoopModelAction.php: -------------------------------------------------------------------------------- 1 | modelAction = $modelAction; 25 | $this->time = $time; 26 | $this->count = $count; 27 | } 28 | 29 | /** 30 | * @return \Daseraf\Debug\Model\ValueObject\ModelAction 31 | */ 32 | public function getModelAction(): ModelAction 33 | { 34 | return $this->modelAction; 35 | } 36 | 37 | public function getName(): string 38 | { 39 | return $this->modelAction->getName(); 40 | } 41 | 42 | public function getModel(): string 43 | { 44 | return $this->modelAction->getModel(); 45 | } 46 | 47 | public function getTrace(): array 48 | { 49 | return $this->modelAction->getTrace(); 50 | } 51 | 52 | /** 53 | * @return float 54 | */ 55 | public function getTime(): float 56 | { 57 | return $this->time; 58 | } 59 | 60 | /** 61 | * @return int 62 | */ 63 | public function getCount(): int 64 | { 65 | return $this->count; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /view/base/templates/toolbar/memory.phtml: -------------------------------------------------------------------------------- 1 | getCollector(); 5 | ?> 6 |
    7 |
    8 | <?= $block->escapeHtmlAttr(__('Memory')) ?> 10 | 11 |
    12 |
    13 |
    14 | escapeHtml(__('Peak memory usage')); ?> 15 | getTotalMemoryUsage(); ?> MB 16 |
    17 |
    18 | escapeHtml(__('Real memory usage')); ?> 19 | getRealMemoryUsage(); ?> MB 20 |
    21 |
    22 | escapeHtml(__('PHP memory limit')); ?> 23 | hasMemoryLimit() ? $block->escapeHtml(__('Unlimited')) : $collector->getMemoryLimit(); ?> 24 |
    25 |
    26 |
    27 | -------------------------------------------------------------------------------- /Observer/BeforeSendResponse.php: -------------------------------------------------------------------------------- 1 | config = $config; 25 | $this->profiler = $profiler; 26 | } 27 | 28 | public function execute(Observer $observer) 29 | { 30 | $request = $observer->getEvent()->getRequest(); 31 | $response = $observer->getEvent()->getResponse(); 32 | if ($this->isProfilerAction($request) || !$this->config->isEnabled()) { 33 | return; 34 | } 35 | 36 | $this->profiler->run($request, $response); 37 | } 38 | 39 | private function isProfilerAction(\Magento\Framework\HTTP\PhpEnvironment\Request $request) 40 | { 41 | if (preg_match('/\/debug\/profiler*/s', $request->getPathInfo())) { 42 | return true; 43 | } 44 | if (preg_match('/\/debug\/xhprof*/s', $request->getPathInfo())) { 45 | return true; 46 | } 47 | 48 | return $request->getModuleName() === 'debug'; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Plugin/ErrorHandler/WhoopsPlugin.php: -------------------------------------------------------------------------------- 1 | config = $config; 35 | $this->whoopsFactory = $whoopsFactory; 36 | $this->prettyPageHandlerFactory = $prettyPageHandlerFactory; 37 | } 38 | 39 | public function beforeCatchException(Http $subject, Bootstrap $bootstrap, \Exception $exception) 40 | { 41 | if ($this->config->getErrorHandler() === ErrorHandler::WHOOPS) { 42 | $whoops = $this->whoopsFactory->create(); 43 | $whoops->pushHandler($this->prettyPageHandlerFactory->create()); 44 | $whoops->handleException($exception); 45 | } 46 | 47 | return [$bootstrap, $exception]; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /view/base/templates/toolbar/translation.phtml: -------------------------------------------------------------------------------- 1 | getCollector(); 5 | ?> 6 |
    7 | 9 |
    10 | <?= $block->escapeHtmlAttr(__('Translation')) ?> 12 | getTotal() ?> 13 |
    14 |
    15 |
    16 |
    17 | escapeHtml(__('Missing messages')); ?> 18 | 19 | getMissingTranslations()); ?> 20 | 21 |
    22 |
    23 | escapeHtml(__('Defined messages')) ?> 24 | getDefinedTranslations()) ?> 25 |
    26 |
    27 |
    28 | -------------------------------------------------------------------------------- /view/base/templates/renderer/query/list.phtml: -------------------------------------------------------------------------------- 1 | getPrefix(); 4 | ?> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | getQueries() as $index => $data): ?> 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 34 |
    #escapeHtml(__('Query Count')) ?>escapeHtml(__('Time')) ?>escapeHtml(__('Info')) ?>
    28 | getFormatter()->microtime($query->getElapsedSecs()); ?> ms 29 | getQueryRenderer()->create(['query' => $query])->render(); ?>
    35 | -------------------------------------------------------------------------------- /view/base/templates/toolbar/ajax.phtml: -------------------------------------------------------------------------------- 1 | getCollector(); 5 | ?> 6 |
    7 |
    8 | <?= $block->escapeHtmlAttr(__('Ajax')) ?> 10 | 0 11 |
    12 |
    13 |
    14 | 15 |
    16 |
    17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
    escapeHtml(__('Method')); ?>escapeHtml(__('Status')); ?>escapeHtml(__('URL')); ?>escapeHtml(__('Time')); ?>escapeHtml(__('Profile')); ?>
    29 |
    30 |
    31 |
    32 | -------------------------------------------------------------------------------- /Model/Info/MemoryInfo.php: -------------------------------------------------------------------------------- 1 | convertToBytes(ini_get('memory_limit')); 10 | } 11 | 12 | public function getRealMemoryUsage() 13 | { 14 | return memory_get_peak_usage(true); 15 | } 16 | 17 | public function getMemoryUsage() 18 | { 19 | return memory_get_peak_usage(); 20 | } 21 | 22 | private function convertToBytes($memoryLimit) 23 | { 24 | if ('-1' === $memoryLimit) { 25 | return -1; 26 | } 27 | 28 | $memoryLimit = strtolower($memoryLimit); 29 | $max = $this->readValue($memoryLimit); 30 | 31 | switch (substr($memoryLimit, -1)) { 32 | case 't': 33 | $max *= 1024; 34 | // no break 35 | case 'g': 36 | $max *= 1024; 37 | // no break 38 | case 'm': 39 | $max *= 1024; 40 | // no break 41 | case 'k': 42 | $max *= 1024; 43 | } 44 | 45 | return $max; 46 | } 47 | 48 | private function readValue($memoryLimit): int 49 | { 50 | $value = ltrim($memoryLimit, '+'); 51 | if (0 === strpos($value, '0x')) { 52 | return intval($value, 16); 53 | } 54 | 55 | if (0 === strpos($value, '0')) { 56 | return intval($value, 8); 57 | } 58 | 59 | return (int) $value; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /view/base/web/images/collector/translation.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | -------------------------------------------------------------------------------- /Model/View/Summary.php: -------------------------------------------------------------------------------- 1 | profileMemoryStorage = $profileMemoryStorage; 32 | $this->url = $url; 33 | $this->redirectRendererFactory = $redirectRendererFactory; 34 | } 35 | 36 | public function getProfile(): ProfileInterface 37 | { 38 | return $this->profileMemoryStorage->read(); 39 | } 40 | 41 | public function getProfilerUrl($token): string 42 | { 43 | return $this->url->getProfilerUrl($token); 44 | } 45 | 46 | public function renderRedirect(Redirect $redirect): string 47 | { 48 | return $this->redirectRendererFactory->create(['redirect' => $redirect])->render(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Model/Info/CustomerInfo.php: -------------------------------------------------------------------------------- 1 | session = $session; 31 | $this->groupRepository = $groupRepository; 32 | $this->groupInterfaceFactory = $groupInterfaceFactory; 33 | } 34 | 35 | public function isLoggedIn(): bool 36 | { 37 | return $this->session->isLoggedIn(); 38 | } 39 | 40 | public function getCustomer(): Customer 41 | { 42 | return $this->session->getCustomer(); 43 | } 44 | 45 | public function getGroup(): GroupInterface 46 | { 47 | try { 48 | $group = $this->groupRepository->getById($this->getCustomer()->getGroupId()); 49 | } catch (\Exception $e) { 50 | $group = $this->groupInterfaceFactory->create(); 51 | } 52 | 53 | return $group; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /App/Config.php: -------------------------------------------------------------------------------- 1 | appConfig = $appConfig; 27 | } 28 | 29 | /** 30 | * @inheritdoc 31 | */ 32 | public function getValue($path) 33 | { 34 | if (isset($this->data[$path])) { 35 | return $this->data[$path]; 36 | } 37 | 38 | $configPath = ScopeConfigInterface::SCOPE_TYPE_DEFAULT; 39 | if ($path) { 40 | $configPath .= '/' . $path; 41 | } 42 | return $this->appConfig->get(System::CONFIG_TYPE, $configPath); 43 | } 44 | 45 | /** 46 | * @inheritdoc 47 | */ 48 | public function setValue($path, $value) 49 | { 50 | $this->data[$path] = $value; 51 | } 52 | 53 | /** 54 | * @inheritdoc 55 | */ 56 | public function isSetFlag($path) 57 | { 58 | $configPath = ScopeConfigInterface::SCOPE_TYPE_DEFAULT; 59 | if ($path) { 60 | $configPath .= '/' . $path; 61 | } 62 | return (bool) $this->appConfig->get(System::CONFIG_TYPE, $configPath); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Model/View/Renderer/TableRenderer.php: -------------------------------------------------------------------------------- 1 | items = $items; 38 | $this->layout = $layout; 39 | $this->varRendererFactory = $varRendererFactory; 40 | $this->labels = $labels; 41 | } 42 | 43 | public function render(): string 44 | { 45 | return $this->layout->createBlock(Template::class) 46 | ->setTemplate(self::TEMPLATE) 47 | ->setData('items', $this->items) 48 | ->setData('labels', $this->labels) 49 | ->setData('var_renderer', $this->varRendererFactory) 50 | ->setData('key_label', $this->labels[0] ?? 'Key') 51 | ->setData('value_label', $this->labels[1] ?? 'Value') 52 | ->toHtml(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Model/View/Renderer/QueryParametersRenderer.php: -------------------------------------------------------------------------------- 1 | query = $query; 30 | $this->parameters = $parameters; 31 | $this->resource = $resource; 32 | } 33 | 34 | public function render(): string 35 | { 36 | $parameters = $this->parameters; 37 | $i = !array_key_exists(0, $parameters) && array_key_exists(1, $parameters) ? 1 : 0; 38 | 39 | return preg_replace_callback('/\?|((?resource->getConnection(ResourceConnection::DEFAULT_CONNECTION)->quote($value); 46 | $i++; 47 | 48 | return $result; 49 | }, $this->query); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Model/View/Renderer/TraceRenderer.php: -------------------------------------------------------------------------------- 1 | id = uniqid(); 37 | $this->trace = $trace; 38 | $this->layout = $layout; 39 | $this->traceCallRendererFactory = $traceCallRendererFactory; 40 | } 41 | 42 | public function render(): string 43 | { 44 | return $this->layout->createBlock(Template::class) 45 | ->setTemplate(self::TEMPLATE) 46 | ->setData('trace', $this->trace) 47 | ->setData('trace_id', $this->id) 48 | ->setData('trace_call_renderer', $this->traceCallRendererFactory) 49 | ->toHtml(); 50 | } 51 | 52 | /** 53 | * @return string 54 | */ 55 | public function getId(): string 56 | { 57 | return $this->id; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Model/ValueObject/EventObserver.php: -------------------------------------------------------------------------------- 1 | id = uniqid(); 41 | $this->name = $name; 42 | $this->class = $class; 43 | $this->event = $event; 44 | $this->time = $time; 45 | } 46 | 47 | public function getId() 48 | { 49 | return $this->getName() . '_' . $this->id; 50 | } 51 | 52 | /** 53 | * @return string 54 | */ 55 | public function getName(): string 56 | { 57 | return $this->name; 58 | } 59 | 60 | /** 61 | * @return string 62 | */ 63 | public function getClass(): string 64 | { 65 | return $this->class; 66 | } 67 | 68 | /** 69 | * @return string 70 | */ 71 | public function getEvent(): string 72 | { 73 | return $this->event; 74 | } 75 | 76 | /** 77 | * @return float 78 | */ 79 | public function getTime(): float 80 | { 81 | return $this->time; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "daseraf/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 | "magento/framework": "~101.0|~102.0|~103.0", 11 | "magento/module-backend": "~100.2|~101.0|~102.0", 12 | "magento/module-developer": "^100.2", 13 | "filp/whoops": "^2.1", 14 | "jdorn/sql-formatter": "^1.2", 15 | "symfony/var-dumper": "*", 16 | "symfony/stopwatch": "^2.8 || ^3.0 || ^4.0 || ^5.0 || ^6.0", 17 | "daseraf/magento2-debug-theme": "^1.0" 18 | }, 19 | "require-dev": { 20 | "squizlabs/php_codesniffer": "^3.3", 21 | "phpmd/phpmd": "^2.6", 22 | "sebastian/phpcpd": "^3.0", 23 | "php-coveralls/php-coveralls": "^2.1" 24 | }, 25 | "config": { 26 | "preferred-install": "source" 27 | }, 28 | "repositories": { 29 | "magento": { 30 | "type": "composer", 31 | "url": "https://repo.magento.com/" 32 | }, 33 | "dbg-theme": { 34 | "type": "git", 35 | "url": "https://github.com/daseraf/magento2-debug-theme.git" 36 | } 37 | }, 38 | "suggested": { 39 | "daseraf/magento2-debug-creatuity": "A required module if you use the interceptor generator from Creatuity in your project" 40 | }, 41 | "autoload": { 42 | "files": [ 43 | "registration.php" 44 | ], 45 | "psr-4": { 46 | "Daseraf\\Debug\\": "" 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Api/ProfileRepositoryInterface.php: -------------------------------------------------------------------------------- 1 | directoryList = $directoryList; 16 | } 17 | 18 | /** 19 | * @throws \Magento\Framework\Exception\FileSystemException 20 | * @return string 21 | */ 22 | public function getProfileDirectory() 23 | { 24 | return $this->directoryList->getPath('var') . DIRECTORY_SEPARATOR . 'debug'; 25 | } 26 | 27 | /** 28 | * @throws \Magento\Framework\Exception\FileSystemException 29 | * @return string 30 | */ 31 | public function getProfileIndex() 32 | { 33 | return $this->directoryList->getPath('var') . DIRECTORY_SEPARATOR . 'debug' . DIRECTORY_SEPARATOR . 'index.csv'; 34 | } 35 | 36 | public function getProfileTempIndex() 37 | { 38 | return $this->directoryList->getPath('var') . DIRECTORY_SEPARATOR . 'debug' . DIRECTORY_SEPARATOR . 'tmp' 39 | . DIRECTORY_SEPARATOR . 'index.csv'; 40 | } 41 | 42 | /** 43 | * @param $token 44 | * @throws \Magento\Framework\Exception\FileSystemException 45 | * @return string 46 | */ 47 | public function getProfileFilename($token) 48 | { 49 | return $this->getProfileDirectory() . DIRECTORY_SEPARATOR 50 | . substr($token, -2, 2) . DIRECTORY_SEPARATOR 51 | . substr($token, -4, 2) . DIRECTORY_SEPARATOR 52 | . $token; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /view/base/templates/toolbar/plugin.phtml: -------------------------------------------------------------------------------- 1 | getCollector(); 5 | ?> 6 |
    7 | 8 |
    9 | <?= $block->escapeHtmlAttr(__('Plugin')) ?> 11 | getPluginsCount() ?> 12 |
    13 |
    14 |
    15 |
    16 | escapeHtml(__('Before plugins')); ?> 17 | 18 | getBeforePluginsCount() ?> 19 | 20 |
    21 |
    22 | escapeHtml(__('Around plugins')); ?> 23 | 24 | getAroundPluginsCount() ?> 25 | 26 |
    27 |
    28 | escapeHtml(__('After plugins')); ?> 29 | 30 | getAfterPluginsCount() ?> 31 | 32 |
    33 |
    34 |
    35 | -------------------------------------------------------------------------------- /view/base/templates/menu/model.phtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | getViewModel(); ?> 4 | 5 | isActive($block->getCollectorName())): ?> 6 | 7 | getCollector($block->getCollectorName()); ?> 8 |
  • 9 | 10 | 11 | 12 | <?= $block->escapeHtmlAttr(__('Models')); ?> 14 | 15 | escapeHtml(__('Models')); ?> 16 | getLoopLoadMetric()): ?> 17 | 18 | getLoopLoadMetric() ?> 19 | 20 | getLoadMetric() && $collector->isThresholdExceeded()): ?> 21 | 22 | getLoadMetric() ?> 23 | 24 | 25 | 26 | 27 |
  • 28 | 29 | 30 | -------------------------------------------------------------------------------- /Model/View/Renderer/LayoutNodeRenderer.php: -------------------------------------------------------------------------------- 1 | node = $node; 38 | $this->layout = $layout; 39 | $this->layoutNodeRendererFactory = $layoutNodeRendererFactory; 40 | $this->formatter = $formatter; 41 | } 42 | 43 | public function render(): string 44 | { 45 | return $this->layout->createBlock(Template::class) 46 | ->setTemplate(self::TEMPLATE) 47 | ->setData([ 48 | 'node' => $this->node, 49 | 'formatter' => $this->formatter, 50 | 'layout_node_renderer' => $this->layoutNodeRendererFactory, 51 | ]) 52 | ->toHtml(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /App/Router/NoRouteHandler.php: -------------------------------------------------------------------------------- 1 | frontNameResolver = $frontNameResolver; 25 | $this->routeConfig = $routeConfig; 26 | } 27 | 28 | /** 29 | * Check and process no route request 30 | * 31 | * @param \Magento\Framework\App\RequestInterface $request 32 | * @return bool 33 | */ 34 | public function process(\Magento\Framework\App\RequestInterface $request) 35 | { 36 | $requestPathParams = explode('/', trim($request->getPathInfo(), '/')); 37 | $areaFrontName = array_shift($requestPathParams); 38 | 39 | if ($areaFrontName === $this->frontNameResolver->getFrontName(true)) { 40 | $moduleName = $this->routeConfig->getRouteFrontName('debug'); 41 | $actionNamespace = 'noroute'; 42 | $actionName = 'index'; 43 | $request->setModuleName($moduleName)->setControllerName($actionNamespace)->setActionName($actionName); 44 | return true; 45 | } 46 | return false; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Model/Storage/HttpStorage.php: -------------------------------------------------------------------------------- 1 | request; 31 | } 32 | 33 | /** 34 | * @param \Magento\Framework\HTTP\PhpEnvironment\Request $request 35 | * @return HttpStorage 36 | */ 37 | public function setRequest(Request $request): HttpStorage 38 | { 39 | $this->request = $request; 40 | 41 | return $this; 42 | } 43 | 44 | /** 45 | * @return \Magento\Framework\HTTP\PhpEnvironment\Response 46 | */ 47 | public function getResponse(): ?Response 48 | { 49 | return $this->response; 50 | } 51 | 52 | /** 53 | * @param \Magento\Framework\HTTP\PhpEnvironment\Response $response 54 | * @return HttpStorage 55 | */ 56 | public function setResponse(Response $response): HttpStorage 57 | { 58 | $this->response = $response; 59 | 60 | return $this; 61 | } 62 | 63 | public function markAsFPCRequest(): HttpStorage 64 | { 65 | $this->fpc = true; 66 | 67 | return $this; 68 | } 69 | 70 | public function isFPCRequest(): bool 71 | { 72 | return $this->fpc; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /view/base/templates/renderer/query.phtml: -------------------------------------------------------------------------------- 1 | getQuery(); 5 | ?> 6 | getHighlightedQuery(); ?> 7 | getQueryParams())) : ?> 8 |
    9 | escapeHtml(__('Parameters')) ?>: 10 | getVarRenderer()->create(['variable' => $query->getQueryParams()])->render(); ?> 11 |
    12 | 13 | 14 | 15 | getUniqId() ?> 16 | escapeHtml(__('Hide formatted query')) ?> 17 | escapeHtml(__('View formatted query')) ?> 18 | 19 | 20 | getUniqId() ?> 21 | escapeHtml(__('Hide runnable query')) ?> 22 | escapeHtml(__('View runnable query')) ?> 23 | 24 | 25 | getUniqId() ?> 26 | getFormattedQuery(); ?> 27 | 28 | getUniqId() ?> 29 | getRunnableQuery(); ?> 30 | 31 | -------------------------------------------------------------------------------- /Plugin/Collector/TranslationCollectorPlugin.php: -------------------------------------------------------------------------------- 1 | translate = $translate; 33 | $this->translationCollector = $translationCollector; 34 | } 35 | 36 | public function beforeRender(Translate $subject, array $source, array $arguments) 37 | { 38 | $text = end($source); 39 | $text = str_replace('\"', '"', $text); 40 | $text = str_replace("\\'", "'", $text); 41 | 42 | $data = $this->getTranslations(); 43 | $translation = ''; 44 | 45 | if ($isDefined = array_key_exists($text, $data)) { 46 | $translation = $data[$text]; 47 | } 48 | 49 | $this->translationCollector->log(new Translation(end($source), $translation, $isDefined)); 50 | 51 | return null; 52 | } 53 | 54 | private function getTranslations(): array 55 | { 56 | if ($this->translations === null) { 57 | $this->translations = $this->translate->getData(); 58 | } 59 | 60 | return $this->translations; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /view/base/templates/profiler/summary.phtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | getViewModel(); ?> 4 | 5 | getProfile()): ?> 6 |
    7 |
    8 |

    9 | getUrl() ?> 10 |

    11 | hasRedirect()): ?> 12 | renderRedirect($profile->getRedirect()); ?> 13 | 14 | 30 |
    31 |
    32 | 33 | -------------------------------------------------------------------------------- /Interception/CliProfiler.php: -------------------------------------------------------------------------------- 1 | logger = ObjectManager::getInstance()->get(LoggerInterface::class); 34 | $this->profiler = ObjectManager::getInstance()->get(Profiler::class); 35 | } 36 | 37 | /** 38 | * @inheritdoc 39 | * 40 | * @throws \Exception The exception in case of unexpected error 41 | */ 42 | public function doRun(InputInterface $input, OutputInterface $output) 43 | { 44 | $exitCode = null; 45 | try { 46 | $exitCode = parent::doRun($input, $output); 47 | } catch (\Exception $e) { 48 | $errorMessage = $e->getMessage() . PHP_EOL . $e->getTraceAsString(); 49 | $this->logger->error($errorMessage); 50 | $this->initException = $e; 51 | } 52 | 53 | if ($this->initException) { 54 | throw $this->initException; 55 | } 56 | 57 | $name = $this->getCommandName($input); 58 | $this->profiler->collectCli($exitCode, $name); 59 | 60 | return $exitCode; 61 | } 62 | } -------------------------------------------------------------------------------- /Model/Serializer/CollectorSerializer.php: -------------------------------------------------------------------------------- 1 | objectManager = $objectManager; 31 | $this->logger = $logger; 32 | $this->config = $config; 33 | } 34 | 35 | /** 36 | * @param CollectorInterface[] $collectors 37 | * @return array 38 | */ 39 | public function serialize(array $collectors): array 40 | { 41 | foreach ($collectors as &$collector) { 42 | $collector = $collector->getData(); 43 | } 44 | 45 | return $collectors; 46 | } 47 | 48 | public function unserialize(array $data): array 49 | { 50 | $collectors = []; 51 | foreach ($data as $name => $collector) { 52 | try { 53 | $collectorClass = $this->config->getCollectorClass($name); 54 | $collectors[$name] = $this->objectManager->create($collectorClass)->setData($collector); 55 | } catch (CollectorNotFoundException $e) { 56 | $this->logger->critical($e); 57 | continue; 58 | } 59 | } 60 | 61 | return $collectors; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Model/Serializer/ProfileSerializer.php: -------------------------------------------------------------------------------- 1 | serializer = $serializer; 30 | $this->collectorSerializer = $collectorSerializer; 31 | $this->profileFactory = $profileFactory; 32 | } 33 | 34 | public function serialize(ProfileInterface $profile): string 35 | { 36 | return $this->serializer->serialize(array_merge( 37 | $profile->getData(), 38 | ['collectors' => $this->collectorSerializer->serialize($profile->getCollectors())] 39 | )); 40 | } 41 | 42 | public function unserialize(string $data): ProfileInterface 43 | { 44 | $profileData = $this->serializer->unserialize($data); 45 | $collectors = $this->collectorSerializer->unserialize($profileData['collectors']); 46 | unset($profileData['collectors']); 47 | 48 | /** @var \Daseraf\Debug\Model\Profile $profile */ 49 | $profile = $this->profileFactory->create(['token' => $profileData['token']])->setData($profileData); 50 | $profile->setCollectors($collectors); 51 | 52 | return $profile; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Model/View/Renderer/TraceCallRenderer.php: -------------------------------------------------------------------------------- 1 | call = $call; 34 | $this->layout = $layout; 35 | $this->directoryList = $directoryList; 36 | } 37 | 38 | public function render(): string 39 | { 40 | /** @var \Magento\Framework\View\Element\Template $block */ 41 | $block = $this->layout->createBlock(Template::class); 42 | 43 | foreach (self::CALL_INFO as $info) { 44 | if (isset($this->call[$info])) { 45 | $block->setData($info, $this->call[$info]); 46 | } 47 | } 48 | if ($block->hasFile()) { 49 | $block->setFile($this->relativizePath($block->getFile())); 50 | } 51 | 52 | return $block->setTemplate(self::TEMPLATE)->toHtml(); 53 | } 54 | 55 | private function relativizePath(string $path): string 56 | { 57 | $rootDirectory = $this->directoryList->getRoot(); 58 | 59 | return (string) strpos($path, $rootDirectory) === 0 ? substr($path, strlen($rootDirectory)) : $path; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Helper/Injector.php: -------------------------------------------------------------------------------- 1 | layout = $layout; 26 | $this->viewModel = $viewModel; 27 | } 28 | 29 | public function inject(Request $request, Response $response, $token = null) 30 | { 31 | $content = $response->getBody(); 32 | $pos = strripos($content, ''); 33 | 34 | if (false !== $pos) { 35 | /** @var Toolbar $toolbarBlock */ 36 | $toolbarBlock = $this->layout->createBlock(Template::class, 'debug.toolbar'); 37 | $toolbarBlock->setTemplate('Daseraf_Debug::profiler/toolbar/js.phtml')->setData([ 38 | 'view_model' => $this->viewModel, 39 | 'token' => $token, 40 | 'request' => $request, 41 | ]); 42 | 43 | /** @var Template $jsBlock */ 44 | $jsBlock = $this->layout->createBlock(Template::class, 'debug.profiler.js'); 45 | $jsBlock->setTemplate('Daseraf_Debug::profiler/js.phtml'); 46 | 47 | $toolbarBlock->setChild('debug_profiler_js', $jsBlock); 48 | 49 | $toolbar = "\n" . str_replace("\n", '', $toolbarBlock->toHtml()) . "\n"; 50 | $content = substr($content, 0, $pos) . $toolbar . substr($content, $pos); 51 | $response->setBody($content); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Model/View/Renderer/QueryListRenderer.php: -------------------------------------------------------------------------------- 1 | queries = $queries; 44 | $this->layout = $layout; 45 | $this->mathRandom = $mathRandom; 46 | $this->queryRendererFactory = $queryRendererFactory; 47 | $this->formatter = $formatter; 48 | } 49 | 50 | public function render(): string 51 | { 52 | return $this->layout->createBlock(Template::class) 53 | ->setTemplate(self::TEMPLATE) 54 | ->setData([ 55 | 'queries' => $this->queries, 56 | 'query_renderer' => $this->queryRendererFactory, 57 | 'prefix' => $this->mathRandom->getUniqueHash(), 58 | 'formatter' => $this->formatter, 59 | ]) 60 | ->toHtml(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Observer/Config/DatabaseProfiler.php: -------------------------------------------------------------------------------- 1 | messageManager = $messageManager; 33 | $this->dbProfilerWriter = $dbProfilerWriter; 34 | $this->config = $config; 35 | } 36 | 37 | /** 38 | * @param \Magento\Framework\Event\Observer $observer 39 | * @return void 40 | */ 41 | public function execute(Observer $observer) 42 | { 43 | if (!$this->isDBProfilerDependentConfigChanged($observer->getChangedPaths())) { 44 | return; 45 | } 46 | 47 | $flag = $this->config->isDatabaseCollectorEnabled() && $this->config->isActive(); 48 | 49 | try { 50 | $this->dbProfilerWriter->save($flag); 51 | } catch (FileSystemException $e) { 52 | $this->messageManager->addExceptionMessage($e); 53 | } 54 | } 55 | 56 | private function isDBProfilerDependentConfigChanged(array $paths): bool 57 | { 58 | return in_array(Config::CONFIG_COLLECTOR_DATABASE, $paths) || in_array(Config::CONFIG_ENABLED, $paths); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Model/View/Menu.php: -------------------------------------------------------------------------------- 1 | request = $request; 32 | $this->profileMemoryStorage = $profileMemoryStorage; 33 | $this->url = $url; 34 | } 35 | 36 | public function isActive(string $collectorName): bool 37 | { 38 | return $this->getProfile()->hasCollector($collectorName); 39 | } 40 | 41 | public function isCurrentPanel(string $collectorName): bool 42 | { 43 | return $this->request->getParam('panel') === $collectorName; 44 | } 45 | 46 | public function getCollector(string $collectorName): CollectorInterface 47 | { 48 | return $this->getProfile()->getCollector($collectorName); 49 | } 50 | 51 | public function getProfilerUrl(string $collectorName): string 52 | { 53 | return $this->url->getProfilerUrl($this->getProfile()->getToken(), $collectorName); 54 | } 55 | 56 | public function getConfigurationUrl(): string 57 | { 58 | return $this->url->getConfigurationUrl(); 59 | } 60 | 61 | private function getProfile(): ProfileInterface 62 | { 63 | return $this->profileMemoryStorage->read(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /App/Router.php: -------------------------------------------------------------------------------- 1 | getPathInfo(), '/'); 56 | $path = FrontNameResolver::AREA_CODE . '/' . $path; 57 | 58 | $params = explode('/', $path ? $path : $this->pathConfig->getDefaultPath()); 59 | foreach ($this->_requiredParams as $paramName) { 60 | $output[$paramName] = array_shift($params); 61 | } 62 | //$output['moduleFrontName'] = 'debug'; 63 | for ($i = 0, $l = sizeof($params); $i < $l; $i += 2) { 64 | $output['variables'][$params[$i]] = isset($params[$i + 1]) ? urldecode($params[$i + 1]) : ''; 65 | } 66 | return $output; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /view/base/web/images/icon/settings.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 16 | 17 | -------------------------------------------------------------------------------- /view/base/templates/toolbar/event.phtml: -------------------------------------------------------------------------------- 1 | getCollector(); 5 | ?> 6 |
    7 | 8 |
    9 | <?= $block->escapeHtmlAttr(__('Event')) ?> 11 | getObserversCount() ?> 12 | 13 | escapeHtml(__('in')); ?> 14 | getTime() ?> 15 | ms 16 | 17 |
    18 |
    19 |
    20 |
    21 |
    22 | escapeHtml(__('Events dispatched')); ?> 23 | getEvents()) ?> 24 |
    25 |
    26 | escapeHtml(__('Observers executed')); ?> 27 | getObserversCount() ?> 28 |
    29 |
    30 | escapeHtml(__('Time')); ?> 31 | getTime(); ?> ms 32 |
    33 |
    34 |
    35 |
    36 | -------------------------------------------------------------------------------- /view/base/templates/toolbar/database.phtml: -------------------------------------------------------------------------------- 1 | getCollector(); 5 | ?> 6 |
    7 | 8 |
    9 | <?= $block->escapeHtmlAttr(__('Database')) ?> 11 | getQueriesCount() ?> 12 | 13 | escapeHtml(__('in')); ?> 14 | getTotalTime() ?> 15 | ms 16 | 17 |
    18 |
    19 |
    20 |
    21 | escapeHtml(__('Database Queries')); ?> 22 | getQueriesCount() ?> 23 |
    24 |
    25 | escapeHtml(__('Duplicated Queries')); ?> 26 | 27 | getDuplicatedQueries()) ?> 28 | 29 |
    30 |
    31 | escapeHtml(__('Query time')); ?> 32 | getTotalTime() ?> ms 33 |
    34 |
    35 |
    36 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.0 5 | - 7.1 6 | - 7.2 7 | 8 | install: 9 | - echo "{\"http-basic\":{\"repo.magento.com\":{\"username\":\"${MAGENTO_USERNAME}\",\"password\":\"${MAGENTO_PASSWORD}\"}}}" > auth.json 10 | - composer install --prefer-dist 11 | 12 | script: 13 | - php vendor/bin/phpcs --standard=PSR2 --extensions=php --ignore=etc,view,vendor . 14 | - php vendor/bin/phpmd . text phpmd.xml --suffixes=php 15 | - php vendor/bin/phpcpd . --exclude=etc --exclude=Test --exclude=view --exclude=vendor 16 | - php vendor/bin/phpunit --coverage-clover build/logs/clover.xml 17 | 18 | after_script: 19 | - php vendor/bin/php-coveralls 20 | 21 | env: 22 | global: 23 | - secure: VSM4hmQPJNRN6TcD4k1dbUJblG3gtEOGR08MuQA8AzKejHVcMLbz5auUJkHcSR+zKUimsINUFPRPn1A2lFTSfYttmiYZfpIIf0oQcs+wqp/v0UaO56G9nEzTmzRbxr9gz65eNzFJakfsg95CS1RGN6wVQK4LNOUYyHeT/saJ+69D9WdMH3qkWksWAyCYJoMd9zJFf5ojg7DtcwMzA+FC2AZcXMcwWBTY1yXInGaO87hz3VhodrJT2MapvEoB3PHul/2zAnHSfSJyQmioRa3QGdK6oXsckuZMafSpcVhWNN9bzMS7Z+nYTPJEeM085pAK6XUHKadCYdHDXEQEiG7OmJP5wMNEmw9aXRKS4wOc9IsuXHoGiO5jzeGFuXcj66yqPYBkotQ/uEUrCzZ758G9XvxqR4dI01uUcme35eSZg44PofXOtD34sxkW8Zkat/3RKJUHyqxeyms+0aPRtbeRXo3agw75Sr89n4AqapCPMnhVrKKDXOXbAV16+OCHIEalX8WrXnzDoBg/CK4EBI7AtRWV1PAdGXEVdXRrfVG4fEilwyIMHpCEXTZN3KPjnZsNKBwG4KGVAffIgm+jmR+hQQWCHo0c5roDUNY5ekG+FL54jwFB3e7Jq2x37iXBUDW/jLaRVab53T/ugAC3IZmJWILHPQ4VCzs1Jjt+6ojoLGM= 24 | - secure: aYHyyZwkkwcIgT0Drf4rncibv7DWUH74tpe6LNEyYnu11nPKv9sQD7xj3G9c2Z+rp3QSbN+QPyYtY0BY8KzGoLLoTHrmECOBi5O/kDyzFumo/p2Vt6YrFpOSn9GByuys8eL5o8LCtpKVWlvi0B5X3k693eLaHEy12VqpK2igB1HOUZC+2FNzO8U7gGH9aomOLxr08pW0uCk14KNj3KPCi/PixX1JP6KBt0OGyM7+nFBJzet9nDDdy16PDBTdcIYGnIAcE/JKtYdQek4OpvacKaCkfZ++wZ6u1c8oyO5SEPHZW8yiN1rjosXw8gE/dOJarrNeYo8gidAJHX6Tia7F+t23c2JcvIkSS5xInalWyvvrUntJ6eiAy8RZfxWTJzXrJ0aFhtbcmKhG8fR4QH83bfoF3nBHOT8551eadh7pjTz5CZ3NMzV5mQ3/+kXWctXOTgsTTlBG/9JZDszRIkPm8i3aTLt0JGVtQRnxuwBO8F5LZhGWVii4wwQBZj9vshlQ3lDcU9FF285aBwhQBfid7PjlyCKme0DK9/d4TjFWcTE7QQ4yVmbw9Q5pNigPNalNFI3N+xo4XOkyWWUk0E02C2i5oWdIHkrqyBm3lxYQyldYcwMnJk+DzPQB7UeHiuXknjZNyJjW7E4lkriRu/6aTisIvuC5G6QY2KCYMBNVH90= 25 | -------------------------------------------------------------------------------- /Controller/Debug/Profiler/Search.php: -------------------------------------------------------------------------------- 1 | layout = $layout; 30 | $this->profileRepository = $profileRepository; 31 | } 32 | 33 | /** 34 | * @SuppressWarnings(PHPMD.StaticAccess) 35 | * @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface 36 | */ 37 | public function execute() 38 | { 39 | $request = $this->getRequest(); 40 | 41 | if (!empty($token = $request->getParam('_token'))) { 42 | return $this->_redirect('debug/profiler/info', [Profiler::URL_TOKEN_PARAMETER => $token]); 43 | } 44 | 45 | /** @var \Magento\Framework\View\Result\Page $page */ 46 | $page = $this->resultFactory->create(ResultFactory::TYPE_PAGE); 47 | 48 | $page->addPageLayoutHandles([ 49 | 'profiler' => 'info', 50 | ], 'debug'); 51 | 52 | $criteria = Criteria::createFromRequest($request); 53 | 54 | $this->layout->getBlock('debug.profiler.panel.content')->addData([ 55 | 'results' => $this->profileRepository->find($criteria), 56 | 'criteria' => $criteria, 57 | ]); 58 | 59 | return $page; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /view/base/templates/toolbar/layout.phtml: -------------------------------------------------------------------------------- 1 | getCollector(); 5 | ?> 6 |
    7 | 8 |
    9 | <?= $block->escapeHtmlAttr(__('Layout')) ?> 11 | 12 | getRenderTime() ?> ms 13 | 14 |
    15 |
    16 |
    17 |
    18 |
    19 | escapeHtml(__('Layout handles')); ?> 20 | getHandles() as $handle): ?> 21 |
    22 | 23 |
    24 |
    25 |
    26 |
    27 | escapeHtml(__('Render Time')); ?> 28 | getRenderTime(); ?> ms 29 |
    30 |
    31 | escapeHtml(__('Block created')); ?> 32 | getCreatedBlocks()) ?> 33 |
    34 |
    35 | escapeHtml(__('Block rendered')); ?> 36 | getRenderedBlocks()) ?> 37 |
    38 |
    39 |
    40 |
    41 | -------------------------------------------------------------------------------- /Model/Indexer/ProfileIndexer.php: -------------------------------------------------------------------------------- 1 | fileSystem = $fileSystem; 37 | $this->fileWriteFactory = $fileWriteFactory; 38 | $this->logger = $logger; 39 | $this->fileHelper = $fileHelper; 40 | } 41 | 42 | public function index(ProfileInterface $profile): ProfileIndexer 43 | { 44 | try { 45 | $tmpIndexPath = $this->fileHelper->getProfileTempIndex(); 46 | $this->fileSystem->createDirectory($this->fileSystem->getParentDirectory($tmpIndexPath)); 47 | $tmpIndex = $this->fileWriteFactory->create($tmpIndexPath, $this->fileSystem, 'w'); 48 | 49 | $tmpIndex->writeCsv($profile->getIndex()); 50 | $index = $tmpIndex->readAll(); 51 | $tmpIndex->close(); 52 | 53 | try { 54 | $index .= $this->fileSystem->fileGetContents($this->fileHelper->getProfileIndex()); 55 | } catch (FileSystemException $e) { 56 | $this->logger->info($e); 57 | } 58 | 59 | $this->fileSystem->filePutContents($this->fileHelper->getProfileIndex(), $index); 60 | } catch (\Exception $e) { 61 | $this->logger->critical($e); 62 | } 63 | 64 | return $this; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /view/base/templates/toolbar/model.phtml: -------------------------------------------------------------------------------- 1 | getCollector(); 5 | ?> 6 |
    7 | 8 |
    9 | <?= $block->escapeHtmlAttr(__('Model')) ?> 11 | getTotalActionsMetric() ?> 12 |
    13 |
    14 |
    15 |
    16 | escapeHtml(__('Model loads')); ?> 17 | 18 | getLoadMetric(); ?> 19 | 20 |
    21 |
    22 | escapeHtml(__('Loads in loops')); ?> 23 | 24 | getLoopLoadMetric() ?> 25 | 26 |
    27 |
    28 | escapeHtml(__('Model save')); ?> 29 | 30 | getSaveMetric(); ?> 31 | 32 |
    33 |
    34 | escapeHtml(__('Model delete')); ?> 35 | 36 | getDeleteMetric(); ?> 37 | 38 |
    39 |
    40 |
    41 | -------------------------------------------------------------------------------- /Model/Info/MagentoInfo.php: -------------------------------------------------------------------------------- 1 | appState = $appState; 39 | $this->cache = $cache; 40 | $this->productMetadata = $productMetadata; 41 | $this->moduleList = $moduleList; 42 | } 43 | 44 | public function isDeveloperMode(): bool 45 | { 46 | return $this->appState->getMode() === State::MODE_DEVELOPER; 47 | } 48 | 49 | public function getVersion(): string 50 | { 51 | if ($this->cache->test(self::VERSION_CACHE_ID)) { 52 | return $this->cache->load(self::VERSION_CACHE_ID); 53 | } 54 | 55 | $version = $this->productMetadata->getVersion() . ' ' . $this->productMetadata->getEdition(); 56 | $this->cache->save($version, self::VERSION_CACHE_ID); 57 | 58 | return $version; 59 | } 60 | 61 | public function getModules(): array 62 | { 63 | if ($this->cache->test(self::MODULES_CACHE_ID)) { 64 | return unserialize($this->cache->load(self::MODULES_CACHE_ID)); 65 | } 66 | 67 | $modules = $this->moduleList->getAll(); 68 | $this->cache->save(serialize($modules), self::MODULES_CACHE_ID); 69 | 70 | return $modules; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Model/ValueObject/ModelAction.php: -------------------------------------------------------------------------------- 1 | id = uniqid(); 43 | $this->name = $name; 44 | $this->model = $model; 45 | $this->time = $time; 46 | $this->trace = $trace; 47 | } 48 | 49 | /** 50 | * @return string 51 | */ 52 | public function getId(): string 53 | { 54 | return $this->name . '::' . $this->id . '::' . $this->model; 55 | } 56 | 57 | /** 58 | * @return string 59 | */ 60 | public function getName(): string 61 | { 62 | return $this->name; 63 | } 64 | 65 | /** 66 | * @return string 67 | */ 68 | public function getModel(): string 69 | { 70 | return $this->model; 71 | } 72 | 73 | /** 74 | * @return float 75 | */ 76 | public function getTime(): float 77 | { 78 | return $this->time; 79 | } 80 | 81 | /** 82 | * @return array 83 | */ 84 | public function getTrace(): array 85 | { 86 | return $this->trace; 87 | } 88 | 89 | /** 90 | * @return string 91 | */ 92 | public function getTraceHash(): string 93 | { 94 | if (empty($this->getTrace())) { 95 | return ''; 96 | } 97 | 98 | return md5(serialize($this->getTrace())); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Model/View/Xhprof.php: -------------------------------------------------------------------------------- 1 | xhprofProfileFactory = $xhprofProfileFactory; 44 | $this->formatter = $formatter; 45 | $this->request = $request; 46 | } 47 | 48 | public function getXhprofProfile($runData) 49 | { 50 | if ($this->calculatedProfile) { 51 | return $this->calculatedProfile; 52 | } 53 | 54 | $profileData['profile'] = $runData; 55 | /** @var XhprofProfile $profile */ 56 | $profile = $this->xhprofProfileFactory->create($profileData); 57 | $profile->calculateSelf(); 58 | 59 | $this->calculatedProfile = $profile; 60 | 61 | return $this->calculatedProfile; 62 | } 63 | 64 | public function getRelatives(XhprofProfile $profile) 65 | { 66 | $functionName = $this->request->getParam('function'); 67 | 68 | return $profile->getRelatives($functionName, null, 1); 69 | } 70 | 71 | public function formatTime($value) 72 | { 73 | return $this->formatter->revertMicrotime($value); 74 | } 75 | 76 | public function formatBytes($value) 77 | { 78 | return $this->formatter->formatBytes(abs($value), 'M'); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Model/View/Search.php: -------------------------------------------------------------------------------- 1 | request = $request; 42 | $this->profileMemoryStorage = $profileMemoryStorage; 43 | $this->formatter = $formatter; 44 | $this->url = $url; 45 | } 46 | 47 | public function isParamSelected($param, $expected): bool 48 | { 49 | return $this->request->getParam($param) === $expected; 50 | } 51 | 52 | public function getParam($param) 53 | { 54 | return $this->request->getParam($param); 55 | } 56 | 57 | public function getLimits() 58 | { 59 | return ['10', '50', '100']; 60 | } 61 | 62 | public function getMethods() 63 | { 64 | return ['GET', 'POST', 'DELETE', 'PUT', 'PATCH', 'HEAD']; 65 | } 66 | 67 | public function getToken() 68 | { 69 | if ($this->token === null) { 70 | $this->token = $this->profileMemoryStorage->read()->getToken(); 71 | } 72 | 73 | return $this->token; 74 | } 75 | 76 | public function toMegaBytes(int $value) 77 | { 78 | return $this->formatter->toMegaBytes($value, 2); 79 | } 80 | 81 | public function getProfilerUrl(string $token): string 82 | { 83 | return $this->url->getProfilerUrl($token, RequestCollector::NAME); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Plugin/Collector/CacheCollectorPlugin.php: -------------------------------------------------------------------------------- 1 | cacheCollector = $cacheCollector; 22 | } 23 | 24 | public function aroundLoad(Cache $subject, callable $proceed, $identifier) 25 | { 26 | $start = microtime(true); 27 | $result = $proceed($identifier); 28 | $time = microtime(true) - $start; 29 | $this->cacheCollector->log(new CacheAction($identifier, CacheAction::LOAD, $time, [ 30 | CacheAction::CACHE_HIT => ($result !== false), 31 | ])); 32 | 33 | return $result; 34 | } 35 | 36 | public function aroundSave(Cache $subject, callable $proceed, $data, $identifier, $tags = [], $lifeTime = null) 37 | { 38 | $start = microtime(true); 39 | $result = $proceed($data, $identifier, $tags, $lifeTime); 40 | $time = microtime(true) - $start; 41 | $this->cacheCollector->log(new CacheAction($identifier, CacheAction::SAVE, $time, [ 42 | CacheAction::CACHE_TAGS => $tags, 43 | CacheAction::CACHE_TTL => $lifeTime, 44 | ])); 45 | 46 | return $result; 47 | } 48 | 49 | public function aroundRemove(Cache $subject, callable $proceed, $identifier) 50 | { 51 | $start = microtime(true); 52 | $result = $proceed($identifier); 53 | $time = microtime(true) - $start; 54 | $this->cacheCollector->log(new CacheAction($identifier, CacheAction::REMOVE, $time)); 55 | 56 | return $result; 57 | } 58 | 59 | public function aroundClean(Cache $subject, callable $proceed, $tags = []) 60 | { 61 | $start = microtime(true); 62 | $result = $proceed($tags); 63 | $time = microtime(true) - $start; 64 | $this->cacheCollector->log(new CacheAction('', CacheAction::CLEAN, $time)); 65 | 66 | return $result; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Model/View/Renderer/QueryRenderer.php: -------------------------------------------------------------------------------- 1 | query = $query; 44 | $this->layout = $layout; 45 | $this->mathRandom = $mathRandom; 46 | $this->varRendererFactory = $varRendererFactory; 47 | $this->databaseHelper = $databaseHelper; 48 | } 49 | 50 | public function render(): string 51 | { 52 | return $this->layout->createBlock(Template::class) 53 | ->setTemplate(self::TEMPLATE) 54 | ->setData([ 55 | 'query' => $this->query, 56 | 'highlighted_query' => $this->databaseHelper->highlightQuery($this->query->getQuery()), 57 | 'formatted_query' => $this->databaseHelper->formatQuery($this->query->getQuery()), 58 | 'runnable_query' => $this->databaseHelper->highlightQuery( 59 | $this->databaseHelper->replaceQueryParameters( 60 | $this->query->getQuery(), 61 | $this->query->getQueryParams() 62 | ) 63 | ), 64 | 'var_renderer' => $this->varRendererFactory, 65 | 'uniq_id' => $this->mathRandom->getUniqueHash(), 66 | ]) 67 | ->toHtml(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /view/base/templates/renderer/layout/node.phtml: -------------------------------------------------------------------------------- 1 | getNode(); 5 | $nodeTemplate = $node->getTemplate(); 6 | $cacheStatus = $node->getCacheStatus(); 7 | $isCached = $cacheStatus === 'load'; 8 | $cacheStatusColor = ''; 9 | switch ($cacheStatus) { 10 | case 'load': 11 | $cacheStatusColor = '#dfd;'; 12 | break; 13 | case 'save': 14 | $cacheStatusColor = '#ffd;'; 15 | break; 16 | case 'not cached': 17 | $cacheStatusColor = '#fdd;'; 18 | break; 19 | default: 20 | $cacheStatusColor = '#ddf'; 21 | } 22 | ?> 23 |
    24 | getParentId()): ?> 25 | 26 | getName() ?> | 27 | getClass() ?> 28 | | 29 | 30 | 31 | 32 | 33 | 34 | getPrefix() ?>└ 35 | 36 | getName() ?> | 37 | getClass() ?> 38 | | 39 | 40 | 41 | 42 | 43 | 44 | | 45 | getFormatter()->microtime($node->getRenderTime()) ?> ms 46 | getParentId()): ?> 47 | | (getFormatter()->percentage($node->getRenderPercent()) ?>) 48 | 49 | 50 | getChildren()): ?> 51 |
    52 | getChildren() as $child): ?> 53 | getLayoutNodeRenderer()->create(['node' => $child])->render(); ?> 54 | 55 |
    56 | 57 |
    -------------------------------------------------------------------------------- /Model/ValueObject/Plugin.php: -------------------------------------------------------------------------------- 1 | class = $class; 52 | $this->name = $name; 53 | $this->sortOrder = $sortOrder; 54 | $this->method = $method; 55 | $this->type = $type; 56 | $this->executionTime = $executionTime; 57 | $this->executionCount = $executionCount; 58 | } 59 | 60 | /** 61 | * @return string 62 | */ 63 | public function getClass(): string 64 | { 65 | return $this->class; 66 | } 67 | 68 | /** 69 | * @return string 70 | */ 71 | public function getName(): string 72 | { 73 | return $this->name; 74 | } 75 | 76 | /** 77 | * @return int 78 | */ 79 | public function getSortOrder(): int 80 | { 81 | return $this->sortOrder; 82 | } 83 | 84 | /** 85 | * @return string 86 | */ 87 | public function getMethod(): string 88 | { 89 | return $this->method; 90 | } 91 | 92 | /** 93 | * @return string 94 | */ 95 | public function getType(): string 96 | { 97 | return $this->type; 98 | } 99 | 100 | /** 101 | * @return float 102 | */ 103 | public function getExecutionTime(): float 104 | { 105 | return $this->executionTime; 106 | } 107 | 108 | /** 109 | * @return int 110 | */ 111 | public function getExecutionCount(): int 112 | { 113 | return $this->executionCount; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /etc/debug/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Daseraf\Debug\App\Router 11 | false 12 | 10 13 | 14 | 15 | Magento\Framework\App\Router\DefaultRouter 16 | false 17 | 100 18 | 19 | 20 | 21 | 22 | 23 | 24 | debug 25 | 26 | 27 | 28 | 29 | 30 | 31 | Daseraf\Debug\App\Router\NoRouteHandler 32 | 60 33 | 34 | 35 | 36 | 37 | 38 | 39 | debug 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | debug 48 | 49 | 50 | 51 | 52 | 53 | 54 | DebugTheme/debug 55 | 56 | 57 | 58 | 59 | --------------------------------------------------------------------------------