phug-php/renderer

View on GitHub
Renderer/Profiler/ProfilerModule.php

Summary

Maintainability
C
7 hrs
Test Coverage
File `ProfilerModule.php` has 296 lines of code (exceeds 250 allowed). Consider refactoring.
<?php
 
namespace Phug\Renderer\Profiler;
 
use Phug\Compiler\Event\CompileEvent;
use Phug\Compiler\Event\ElementEvent;
use Phug\Compiler\Event\NodeEvent;
use Phug\Compiler\Event\OutputEvent;
use Phug\CompilerEvent;
use Phug\Event;
use Phug\Formatter;
use Phug\Formatter\Event\DependencyStorageEvent;
use Phug\Formatter\Event\FormatEvent;
use Phug\FormatterEvent;
use Phug\Lexer\Event\EndLexEvent;
use Phug\Lexer\Event\LexEvent;
use Phug\Lexer\Event\TokenEvent;
use Phug\LexerEvent;
use Phug\Parser\Event\NodeEvent as ParserNodeEvent;
use Phug\Parser\Event\ParseEvent;
use Phug\Parser\NodeInterface;
use Phug\ParserEvent;
use Phug\Renderer\Event\HtmlEvent;
use Phug\Renderer\Event\RenderEvent;
use Phug\RendererEvent;
use Phug\Util\AbstractModule;
use Phug\Util\ModuleContainerInterface;
use Phug\Util\SandBox;
use SplObjectStorage;
 
`ProfilerModule` has 23 functions (exceeds 20 allowed). Consider refactoring.
class ProfilerModule extends AbstractModule
{
/**
* @var int
*/
private $startTime = 0;
 
/**
* @var int
*/
private $initialMemoryUsage = 0;
 
/**
* @var EventList
*/
private $events = null;
 
/**
* @var SplObjectStorage
*/
private $nodesRegister = null;
 
/**
* @var callable
*/
private $eventDump = null;
 
/**
* @var array
*/
private static $profilers = [];
 
/**
* @var int
*/
private static $profilersIndex = 0;
 
public function __construct(EventList $events, ModuleContainerInterface $container)
{
parent::__construct($container);
 
$this->events = $events;
$this->initialize();
}
 
public function initialize()
{
$this->startTime = microtime(true);
$this->initialMemoryUsage = memory_get_usage();
$this->nodesRegister = new SplObjectStorage();
 
if (!$this->getContainer()->hasOption('profiler.dump_event')) {
$this->getContainer()->setOption('profiler.dump_event', [$this, 'getEventDump']);
}
 
$this->eventDump = $this->getContainer()->getOption('profiler.dump_event');
 
if (in_array($this->eventDump, ['var_dump', 'print_r'], true)) {
$function = $this->eventDump;
$this->eventDump = function ($value) use ($function) {
return $this->getFunctionDump($value, $function);
};
}
}
 
public function reset()
{
$this->initialize();
$this->events->reset();
}
 
public function kill()
{
$this->events->lock();
}
 
public function isAlive()
{
return !$this->events->isLocked();
}
 
/**
* @return EventList
*/
public function getEvents()
{
return $this->events;
}
 
/**
* Catch output of a dump function and return it as string.
*
* @param mixed $value
* @param callable $function
*
* @return string
*/
public function getFunctionDump($value, $function = 'var_dump')
{
$sandBox = new SandBox(function () use ($function, $value) {
$function($value);
});
 
return $sandBox->getBuffer();
}
 
/**
* @param Event $event
*
* @return string
*/
public function getEventDump(Event $event)
{
$dump = new Dump($event);
 
return $dump->dump();
}
 
public function getDebugId($nodeId)
{
$index = static::$profilersIndex++;
static::$profilers[$index] = [$this, $nodeId];
 
return $index;
}
 
/**
* @param int|null $nodeId
*
* @throws ProfilerException
* @throws ProfilerLocatedException
*/
public function recordDisplayEvent($nodeId)
{
if (!$this->isAlive()) {
return;
}
 
/** @var Formatter $formatter */
$formatter = $this->getContainer();
 
if ($formatter->debugIdExists($nodeId)) {
$event = new Event('display');
$this->appendNode($event, $formatter->getNodeFromDebugId($nodeId));
$this->record($event);
}
}
 
/**
* @param int|null $debugId
*
* @throws ProfilerException
* @throws ProfilerLocatedException
*/
public static function recordProfilerDisplayEvent($debugId)
{
if (isset(static::$profilers[$debugId])) {
/** @var ProfilerModule $profiler */
list($profiler, $nodeId) = static::$profilers[$debugId];
$profiler->recordDisplayEvent($nodeId);
}
}
 
private function cleanupProfilerNodes()
{
static::$profilers = array_filter(static::$profilers, function ($params) {
return $params[0] !== $this;
});
}
 
public function attachEvents()
{
parent::attachEvents();
$formatter = $this->getContainer();
if ($formatter instanceof Formatter) {
$formatter->setOption('patterns.debug_comment', function ($nodeId) {
return "\n".(
$nodeId !== ''
? '\\'.static::class.'::recordProfilerDisplayEvent('.
var_export($this->getDebugId($nodeId), true).
");\n"
: ''
)."// PUG_DEBUG:$nodeId\n";
});
}
}
 
Method `getEventListeners` has 30 lines of code (exceeds 25 allowed). Consider refactoring.
public function getEventListeners()
{
$eventListeners = array_map(function (callable $eventListener) {
return function (Event $event) use ($eventListener) {
if ($this->isAlive() && $eventListener($event) !== false) {
$this->record($event);
}
};
}, array_merge(
[
RendererEvent::RENDER => function (RenderEvent $event) {
$this->appendParam($event, '__link', $event);
},
],
$this->getCompilerEventListeners(),
$this->getFormatterEventListeners(),
$this->getParserEventListeners(),
$this->getLexerEventListeners()
));
 
$eventListeners[RendererEvent::HTML] = function (HtmlEvent $event) {
$this->appendParam($event, '__link', $event->getRenderEvent());
if ($this->isAlive()) {
$this->record($event);
}
 
if ($event->getBuffer()) {
$event->setBuffer($this->renderProfile().$event->getBuffer());
}
 
if (is_string($event->getResult())) {
$event->setResult($this->renderProfile().$event->getResult());
}
};
 
return $eventListeners;
}
 
private function appendParam(Event $event, $key, $value)
{
$event->setParams(array_merge($event->getParams(), [
$key => $value,
]));
}
 
private function appendNode(Event $event, $node)
{
if ($node instanceof NodeInterface) {
$this->appendParam($event, '__location', $node->getSourceLocation());
$this->appendParam($event, '__link', $node->getToken() ?: $node);
}
}
 
private function getException(Event $event, $message)
{
$params = $event->getParams();
$this->kill();
 
return isset($params['__location'])
? new ProfilerLocatedException($params['__location'], $message)
: new ProfilerException($message);
}
 
/**
* @param Event $event
*
* @throws ProfilerException
* @throws ProfilerLocatedException
*/
Function `record` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.
private function record(Event $event)
{
$time = microtime(true) - $this->startTime;
$container = $this->getContainer();
$maxTime = $container->getOption('execution_max_time');
 
if ($maxTime > -1 && $time * 1000 > $maxTime) {
throw $this->getException($event, 'execution_max_time of '.$maxTime.'ms exceeded.');
}
 
$memoryLimit = $container->getOption('memory_limit');
 
if ($memoryLimit > -1 && memory_get_usage() - $this->initialMemoryUsage > $memoryLimit) {
throw $this->getException($event, 'memory_limit of '.$memoryLimit.'B exceeded.');
}
 
$this->appendParam($event, '__time', $time);
 
if ($container->getOption('profiler.display') || $container->getOption('profiler.log')) {
$this->events[] = $event;
}
}
 
private function renderProfile()
{
$display = $this->getContainer()->getOption('profiler.display');
$log = $this->getContainer()->getOption('profiler.log');
if (!$display && !$log) {
return '';
}
 
$profile = new Profile(
$this->events,
$this->nodesRegister,
$this->startTime,
$this->initialMemoryUsage,
$this->eventDump
);
 
$profile->compose(
$this->getContainer()->getOption('profiler.time_precision'),
$this->getContainer()->getOption('profiler.line_height')
);
 
$this->kill();
 
$render = $profile->render();
 
$this->cleanupProfilerNodes();
 
if ($log) {
file_put_contents($log, $render);
}
 
return $display ? $render : '';
}
 
private function getCompilerEventListeners()
{
return [
CompilerEvent::COMPILE => function (CompileEvent $event) {
$this->appendParam($event, '__link', $event);
},
CompilerEvent::ELEMENT => function (ElementEvent $event) {
$this->appendNode($event, $event->getElement()->getOriginNode());
},
CompilerEvent::NODE => function (NodeEvent $event) {
$this->appendNode($event, $event->getNode());
},
CompilerEvent::OUTPUT => function (OutputEvent $event) {
$this->appendParam($event, '__link', $event->getCompileEvent());
},
];
}
 
private function getFormatterEventListeners()
{
return [
FormatterEvent::DEPENDENCY_STORAGE => function (DependencyStorageEvent $event) {
$this->appendParam($event, '__link', $event->getDependencyStorage());
},
FormatterEvent::FORMAT => function (FormatEvent $event) {
$this->appendNode($event, $event->getElement()->getOriginNode());
},
];
}
 
private function getParserEventListeners()
{
return [
ParserEvent::PARSE => function (ParseEvent $event) {
$this->appendParam($event, '__link', $event);
},
ParserEvent::DOCUMENT => function (ParserNodeEvent $event) {
$this->appendNode($event, $event->getNode());
},
ParserEvent::STATE_ENTER => function (ParserNodeEvent $event) {
$this->appendNode($event, $event->getNode());
},
ParserEvent::STATE_LEAVE => function (ParserNodeEvent $event) {
$this->appendNode($event, $event->getNode());
},
ParserEvent::STATE_STORE => function (ParserNodeEvent $event) {
$this->appendNode($event, $event->getNode());
},
];
}
 
private function getLexerEventListeners()
{
return [
LexerEvent::LEX => function (LexEvent $event) {
$this->appendParam($event, '__link', $event);
},
LexerEvent::END_LEX => function (EndLexEvent $event) {
$this->appendParam($event, '__link', $event->getLexEvent());
},
LexerEvent::TOKEN => function (TokenEvent $event) {
$token = $event->getToken();
$this->appendParam($event, '__location', $token->getSourceLocation());
$this->appendParam($event, '__link', $token);
},
];
}
}