diff --git a/CHANGELOG.md b/CHANGELOG.md index b9c115816..8292e1ef6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * Added search option `same_as` to Flex Objects * Added PHP 8 compatible `function_exists()`: `Utils::functionExists()` * New sites have `compatibility` features turned off by default, upgrading from older versions will keep the settings on + * Updated Clockwork to v5.0 1. [](#improved) * Updated bundled JQuery to latest version `3.5.1` * Forward a `sid` to GPM when downloading a premium package via CLI diff --git a/composer.json b/composer.json index 02c12c3de..9f92f2157 100644 --- a/composer.json +++ b/composer.json @@ -55,7 +55,7 @@ "dragonmantank/cron-expression": "^1.2", "phive/twig-extensions-deferred": "^1.0", "willdurand/negotiation": "2.x-dev", - "itsgoingd/clockwork": "^4.1", + "itsgoingd/clockwork": "^5.0", "enshrined/svg-sanitize": "~0.13", "symfony/http-client": "^4.4" }, diff --git a/composer.lock b/composer.lock index 10716d312..c0c1241cc 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "2bef5ff955b35b0f8f2e7f05956a18a0", + "content-hash": "5018a33d28d9548d37318a86986c8d07", "packages": [ { "name": "antoligy/dom-string-iterators", @@ -730,21 +730,21 @@ }, { "name": "itsgoingd/clockwork", - "version": "v4.1.8", + "version": "v5.0.1", "source": { "type": "git", "url": "https://github.com/itsgoingd/clockwork.git", - "reference": "0dc2e5be39de88b18719bb4f3cdacfa605ad2146" + "reference": "a6665903018ea5ac2d0219ccb6e9e03ed1339c3f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/itsgoingd/clockwork/zipball/0dc2e5be39de88b18719bb4f3cdacfa605ad2146", - "reference": "0dc2e5be39de88b18719bb4f3cdacfa605ad2146", + "url": "https://api.github.com/repos/itsgoingd/clockwork/zipball/a6665903018ea5ac2d0219ccb6e9e03ed1339c3f", + "reference": "a6665903018ea5ac2d0219ccb6e9e03ed1339c3f", "shasum": "" }, "require": { "ext-json": "*", - "php": ">=5.5", + "php": ">=5.6", "psr/log": "1.*" }, "type": "library", @@ -774,7 +774,7 @@ "homepage": "https://twitter.com/itsgoingd" } ], - "description": "php dev tools integrated to your browser", + "description": "php dev tools in your browser", "homepage": "https://underground.works/clockwork", "keywords": [ "Devtools", @@ -785,11 +785,7 @@ "profiling", "slim" ], - "support": { - "issues": "https://github.com/itsgoingd/clockwork/issues", - "source": "https://github.com/itsgoingd/clockwork/tree/v4.1.8" - }, - "time": "2020-09-17T19:21:25+00:00" + "time": "2020-11-22T00:14:53+00:00" }, { "name": "kodus/psr7-server", diff --git a/system/src/Grav/Common/Debugger.php b/system/src/Grav/Common/Debugger.php index ad4008628..5d45f91bc 100644 --- a/system/src/Grav/Common/Debugger.php +++ b/system/src/Grav/Common/Debugger.php @@ -38,6 +38,7 @@ use Psr\Http\Message\ServerRequestInterface; use ReflectionObject; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Throwable; +use Twig\Environment; use Twig\Template; use Twig\TemplateWrapper; use function array_slice; @@ -162,16 +163,17 @@ class Debugger $clockwork->addDataSource(new MonologDataSource($log)); } - $clockwork->addDataSource(new TwigClockworkDataSource()); - - $timeLine = $clockwork->getTimeline(); + $timeline = $clockwork->timeline(); if ($this->requestTime !== GRAV_REQUEST_TIME) { - $timeLine->addEvent('server', 'Server', $this->requestTime, GRAV_REQUEST_TIME); + $event = $timeline->event('Server'); + $event->finalize($this->requestTime, GRAV_REQUEST_TIME); } if ($this->currentTime !== GRAV_REQUEST_TIME) { - $timeLine->addEvent('loading', 'Loading', GRAV_REQUEST_TIME, $this->currentTime); + $event = $timeline->event('Loading'); + $event->finalize(GRAV_REQUEST_TIME, $this->currentTime); } - $timeLine->addEvent('setup', 'Site Setup', $this->currentTime, microtime(true)); + $event = $timeline->event('Site Setup'); + $event->finalize($this->currentTime, microtime(true)); } if ($this->censored) { @@ -253,7 +255,7 @@ class Debugger $this->finalize(); - $clockwork->getTimeline()->finalize($request->getAttribute('request_time')); + $clockwork->timeline()->finalize($request->getAttribute('request_time')); if ($this->censored) { $censored = 'CENSORED'; @@ -344,21 +346,20 @@ class Debugger } $nowTime = microtime(true); - $clkTimeLine = $this->clockwork ? $this->clockwork->getTimeline() : null; + $clkTimeLine = $this->clockwork ? $this->clockwork->timeline() : null; $debTimeLine = $this->debugbar ? $this->debugbar['time'] : null; foreach ($this->timers as $name => $data) { $description = $data[0]; $startTime = $data[1] ?? null; $endTime = $data[2] ?? $nowTime; - if ($endTime - $startTime < 0.001) { - continue; - } - if ($clkTimeLine) { - $clkTimeLine->addEvent($name, $description ?? $name, $startTime, $endTime); - } + $event = $clkTimeLine->event($description); + $event->finalize($startTime, $endTime); + } elseif ($debTimeLine) { + if ($endTime - $startTime < 0.001) { + continue; + } - if ($debTimeLine) { $debTimeLine->addMeasure($description ?? $name, $startTime, $endTime); } } @@ -412,7 +413,7 @@ class Debugger $this->renderer = $this->debugbar->getJavascriptRenderer(); $this->renderer->setIncludeVendors(false); - list($css_files, $js_files) = $this->renderer->getAssets(null, JavascriptRenderer::RELATIVE_URL); + [$css_files, $js_files] = $this->renderer->getAssets(null, JavascriptRenderer::RELATIVE_URL); foreach ((array)$css_files as $css) { $assets->addCss($css); @@ -545,6 +546,16 @@ class Debugger return $response; } + public function addTwigProfiler(Environment $twig): void + { + $clockwork = $this->getClockwork(); + if ($clockwork) { + $source = new TwigClockworkDataSource($twig); + $source->listenToEvents(); + $clockwork->addDataSource($source); + } + } + /** * Start profiling code. * @@ -756,20 +767,18 @@ class Debugger */ public function addEvent(string $name, $event, EventDispatcherInterface $dispatcher) { - if ($this->enabled) { - if ($this->clockwork) { - $data = null; - if ($event && method_exists($event, '__debugInfo')) { - $data = $event; - } - - $listeners = []; - foreach ($dispatcher->getListeners($name) as $listener) { - $listeners[] = $this->resolveCallable($listener); - } - - $this->clockwork->addEvent($name, $data, microtime(true), ['listeners' => $listeners]); + if ($this->enabled && $this->clockwork) { + $data = null; + if ($event && method_exists($event, '__debugInfo')) { + $data = $event; } + + $listeners = []; + foreach ($dispatcher->getListeners($name) as $listener) { + $listeners[] = $this->resolveCallable($listener); + } + + $this->clockwork->addEvent($name, $data, microtime(true), ['listeners' => $listeners]); } return $this; diff --git a/system/src/Grav/Common/Grav.php b/system/src/Grav/Common/Grav.php index 9f0c6ece9..b0d0f6416 100644 --- a/system/src/Grav/Common/Grav.php +++ b/system/src/Grav/Common/Grav.php @@ -461,10 +461,11 @@ class Grav extends Container { /** @var EventDispatcherInterface $events */ $events = $this['events']; + $eventName = get_class($event); /** @var Debugger $debugger */ $debugger = $this['debugger']; - $debugger->addEvent(get_class($event), $event, $events); + $debugger->addEvent($eventName, $event, $events); return $events->dispatch($event); } diff --git a/system/src/Grav/Common/Twig/Twig.php b/system/src/Grav/Common/Twig/Twig.php index 3802e73d4..30531806e 100644 --- a/system/src/Grav/Common/Twig/Twig.php +++ b/system/src/Grav/Common/Twig/Twig.php @@ -9,6 +9,7 @@ namespace Grav\Common\Twig; +use Grav\Common\Debugger; use Grav\Common\Grav; use Grav\Common\Config\Config; use Grav\Common\Language\Language; @@ -24,7 +25,6 @@ use Twig\Environment; use Twig\Error\LoaderError; use Twig\Extension\CoreExtension; use Twig\Extension\DebugExtension; -use Twig\Extension\ProfilerExtension; use Twig\Extension\StringLoaderExtension; use Twig\Loader\ArrayLoader; use Twig\Loader\ChainLoader; @@ -192,8 +192,9 @@ class Twig $this->twig->addExtension(new DeferredExtension()); $this->twig->addExtension(new StringLoaderExtension()); - $this->profile = new Profile(); - $this->twig->addExtension(new ProfilerExtension($this->profile)); + /** @var Debugger $debugger */ + $debugger = $this->grav['debugger']; + $debugger->addTwigProfiler($this->twig); $this->grav->fireEvent('onTwigExtensions'); diff --git a/system/src/Grav/Common/Twig/TwigClockworkDataSource.php b/system/src/Grav/Common/Twig/TwigClockworkDataSource.php index 8747b92cf..b8eb0eea1 100644 --- a/system/src/Grav/Common/Twig/TwigClockworkDataSource.php +++ b/system/src/Grav/Common/Twig/TwigClockworkDataSource.php @@ -11,8 +11,9 @@ namespace Grav\Common\Twig; use Clockwork\DataSource\DataSource; use Clockwork\Request\Request; -use Clockwork\Request\Timeline; -use Grav\Common\Grav; +use Twig\Environment; +use Twig\Extension\ProfilerExtension; +use Twig\Profiler\Profile; /** * Class TwigClockworkDataSource @@ -20,33 +21,37 @@ use Grav\Common\Grav; */ class TwigClockworkDataSource extends DataSource { - /** @var Timeline Views data structure */ - protected $views; + /** @var Environment */ + protected $twig; - /** - * TwigClockworkDataSource constructor. - */ - public function __construct() + /** @var Profile */ + protected $profile; + + // Create a new data source, takes Twig instance as an argument + public function __construct(Environment $twig) { - $this->views = new Timeline(); + $this->twig = $twig; } /** - * Resolves and adds the Twig profiler data to the request + * Register the Twig profiler extension + */ + public function listenToEvents(): void + { + $this->twig->addExtension(new ProfilerExtension($this->profile = new Profile())); + } + + /** + * Adds rendered views to the request * * @param Request $request * @return Request */ public function resolve(Request $request) { - $profile = Grav::instance()['twig']->profile(); + $timeline = (new TwigClockworkDumper())->dump($this->profile); - if ($profile) { - $processor = new TwigProfileProcessor(); - - $processor->process($profile, $this->views); - $request->viewsData = $this->views->finalize(); - } + $request->viewsData = array_merge($request->viewsData, $timeline->finalize()); return $request; } diff --git a/system/src/Grav/Common/Twig/TwigClockworkDumper.php b/system/src/Grav/Common/Twig/TwigClockworkDumper.php new file mode 100644 index 000000000..a63cba81b --- /dev/null +++ b/system/src/Grav/Common/Twig/TwigClockworkDumper.php @@ -0,0 +1,72 @@ +dumpProfile($profile, $timeline); + + return $timeline; + } + + /** + * @param Profile $profile + * @param Timeline $timeline + * @param null $parent + */ + public function dumpProfile(Profile $profile, Timeline $timeline, $parent = null) + { + $id = $this->lastId++; + + if ($profile->isRoot()) { + $name = $profile->getName(); + } elseif ($profile->isTemplate()) { + $name = $profile->getTemplate(); + } else { + $name = $profile->getTemplate() . '::' . $profile->getType() . '(' . $profile->getName() . ')'; + } + + foreach ($profile as $p) { + $this->dumpProfile($p, $timeline, $id); + } + + $data = $profile->__serialize(); + + $timeline->event($name, [ + 'name' => $id, + 'start' => $data[3]['wt'] ?? null, + 'end' => $data[4]['wt'] ?? null, + 'data' => [ + 'data' => [], + 'memoryUsage' => $data[4]['mu'] ?? null, + 'parent' => $parent + ] + ]); + } +} diff --git a/system/src/Grav/Common/Twig/TwigProfileProcessor.php b/system/src/Grav/Common/Twig/TwigProfileProcessor.php deleted file mode 100644 index 30767fabe..000000000 --- a/system/src/Grav/Common/Twig/TwigProfileProcessor.php +++ /dev/null @@ -1,102 +0,0 @@ -isRoot()) { - $this->root = $profile->getDuration(); - $name = $profile->getName(); - } else { - if ($profile->isTemplate()) { - $name = $this->formatTemplate($profile, $prefix); - } else { - $name = $this->formatNonTemplate($profile, $prefix); - } - $prefix .= '⎯⎯'; - } - - $percent = $this->root ? $profile->getDuration() / $this->root * 100 : 0; - - $data = [ - 'tm' => $this->formatTime($profile, $percent), - 'mu' => Utils::prettySize($profile->getMemoryUsage()) - ]; - - if ($profile->isRoot()) { - $data += ['pmu' => Utils::prettySize($profile->getPeakMemoryUsage())]; - } - - - $views->addEvent( - $counter, - $profile->getTemplate(), - 0, - $profile->getDuration(), - [ 'name' => $name, 'data' => $data ] - ); - - $nCount = count($profile->getProfiles()); - foreach ($profile as $i => $p) { - $this->process($p, $views, ++$counter, $prefix, $i + 1 !== $nCount); - } - } - - /** - * @param Profile $profile - * @param string $prefix - * @return string - */ - protected function formatTemplate(Profile $profile, $prefix) - { - return sprintf('%s⤍ %s', $prefix, $profile->getTemplate()); - } - - /** - * @param Profile $profile - * @param string $prefix - * @return string - */ - protected function formatNonTemplate(Profile $profile, $prefix) - { - return sprintf('%s⤍ %s::%s(%s)', $prefix, $profile->getTemplate(), $profile->getType(), $profile->getName()); - } - - /** - * @param Profile $profile - * @param float $percent - * @return string - */ - protected function formatTime(Profile $profile, $percent) - { - return sprintf('%.2fms/%.0f%%', $profile->getDuration() * 1000, $percent); - } -}