Skip to content

Commit

Permalink
[FEATURE] Add base for profiler
Browse files Browse the repository at this point in the history
  • Loading branch information
Neirda24 committed Mar 26, 2024
1 parent b3a547f commit a60262a
Show file tree
Hide file tree
Showing 14 changed files with 547 additions and 12 deletions.
25 changes: 25 additions & 0 deletions config/debug.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

use Sensiolabs\GotenbergBundle\DataCollector\GotenbergDataCollector;
use Sensiolabs\GotenbergBundle\Debug\TraceableGotenberg;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\DependencyInjection\Reference;
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;

return static function (ContainerConfigurator $container): void {
$services = $container->services();

$services->set('sensiolabs_gotenberg.traceable', TraceableGotenberg::class)
->decorate('sensiolabs_gotenberg')
->args([
'$inner' => new Reference('.inner')
])
;

$services->set('sensiolabs_gotenberg.data_collector', GotenbergDataCollector::class)
->args([
'$traceableGotenberg' => service('sensiolabs_gotenberg'),
])
->tag('data_collector', ['template' => '@SensiolabsGotenberg/WebProfiler/Collector/sensiolabs_gotenberg.html.twig', 'id' => 'sensiolabs_gotenberg'])
;
};
2 changes: 1 addition & 1 deletion config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
use function Symfony\Component\DependencyInjection\Loader\Configurator\tagged_locator;

return function (ContainerConfigurator $container): void {
return static function (ContainerConfigurator $container): void {
$services = $container->services();

$services->set('sensiolabs_gotenberg.client', GotenbergClient::class)
Expand Down
84 changes: 84 additions & 0 deletions src/DataCollector/GotenbergDataCollector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

declare(strict_types=1);

namespace Sensiolabs\GotenbergBundle\DataCollector;

use Sensiolabs\GotenbergBundle\Debug\Builder\TraceablePdfBuilder;
use Sensiolabs\GotenbergBundle\Debug\TraceableGotenberg;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Cloner\Stub;
use function array_map;
use function array_merge;
use function count;

final class GotenbergDataCollector extends DataCollector implements LateDataCollectorInterface
{
public function __construct(
private readonly TraceableGotenberg $traceableGotenberg,
) {
}

public function collect(Request $request, Response $response, ?\Throwable $exception = null): void
{
}

public function getName(): string
{
return 'sensiolabs_gotenberg';
}

public function lateCollect(): void
{
$this->data['builders'] = [];
$this->data['request_count'] = 0;

/**
* @var string $id
* @var TraceablePdfBuilder $builder
*/
foreach ($this->traceableGotenberg->getBuilders() as [$id, $builder]) {
$this->data['builders'][$id] ??= [
'class' => $builder->getInner()::class,
'default_options' => [],
'pdfs' => array_map(function (array $request): array {
$request['calls'] = array_map(function (array $call): array {
return array_merge($call, ['stub' => $this->cloneVar($call['stub'])]);
}, $request['calls']);

return $request;
}, $builder->getPdfs()),
];
$this->data['request_count'] += count($builder->getPdfs());
}
}

/**
* @return array<string, array{
* 'class': string,
* 'default_options': array<mixed>,
* 'pdfs': list<array{
* 'time': float,
* 'fileName': string,
* 'calls': list<array{
* 'method': string,
* 'arguments': array<mixed>,
* 'stub': Data
* }>
* }>
* }>
*/
public function getBuilders(): array
{
return $this->data['builders'] ?? [];
}

public function getRequestCount(): int
{
return $this->data['request_count'] ?? 0;
}
}
89 changes: 89 additions & 0 deletions src/Debug/Builder/TraceablePdfBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

declare(strict_types=1);

namespace Sensiolabs\GotenbergBundle\Debug\Builder;

use Sensiolabs\GotenbergBundle\Builder\PdfBuilderInterface;
use Sensiolabs\GotenbergBundle\Client\PdfResponse;
use Symfony\Component\VarDumper\Caster\ArgsStub;
use Symfony\Component\VarDumper\Cloner\Stub;
use function microtime;

final class TraceablePdfBuilder implements PdfBuilderInterface
{
/**
* @var list<array{'time': float, 'fileName': string, 'calls': list<array{'method': string, 'arguments': array<mixed>, 'stub': Stub}>}>
*/
private array $pdfs = [];

/**
* @var list<array{'method': string, 'arguments': array<mixed>, 'stub': Stub}>
*/
private array $calls = [];

private int $totalGenerated = 0;

public function __construct(
private readonly PdfBuilderInterface $inner,
) {
}

public function generate(): PdfResponse
{
$start = microtime(true);
$response = $this->inner->generate();
$end = microtime(true);

$fileName = 'Unknown.pdf';
if ($response->headers->has('Content-Disposition')) {
$matches = [];

preg_match('#[^;]*;\sfilename="?(?P<fileName>[^"]*)"?#', $response->headers->get('Content-Disposition', ''), $matches);
$fileName = $matches['fileName'];
}

$this->pdfs[] = [
'calls' => $this->calls,
'time' => $end - $start,
'fileName' => $fileName,
];

++$this->totalGenerated;

return $response;
}

/**
* @param array<mixed> $arguments
*/
public function __call(string $name, array $arguments): mixed
{
$result = $this->inner->$name(...$arguments);

$this->calls[] = [
'method' => $name,
'arguments' => $arguments,
'stub' => new ArgsStub($arguments, $name, $this->inner::class),
];

if ($result === $this->inner) {
return $this;
}

return $result;
}

/**
* @return list<array{'time': float, 'fileName': string, 'calls': list<array{'method': string, 'arguments': array<mixed>, 'stub': Stub}>}>
*/
public function getPdfs(): array
{
return $this->pdfs;
}

public function getInner(): PdfBuilderInterface
{
return $this->inner;
}
}
114 changes: 114 additions & 0 deletions src/Debug/TraceableGotenberg.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<?php

declare(strict_types=1);

namespace Sensiolabs\GotenbergBundle\Debug;

use Sensiolabs\GotenbergBundle\Builder\HtmlPdfBuilder;
use Sensiolabs\GotenbergBundle\Builder\LibreOfficePdfBuilder;
use Sensiolabs\GotenbergBundle\Builder\MarkdownPdfBuilder;
use Sensiolabs\GotenbergBundle\Builder\PdfBuilderInterface;
use Sensiolabs\GotenbergBundle\Builder\UrlPdfBuilder;
use Sensiolabs\GotenbergBundle\Debug\Builder\TraceablePdfBuilder;
use Sensiolabs\GotenbergBundle\Pdf\GotenbergInterface;

final class TraceableGotenberg implements GotenbergInterface
{
/**
* @var list<array{string, TraceablePdfBuilder}>
*/
private array $builders = [];

public function __construct(private readonly GotenbergInterface $inner)
{
}

public function get(string $builder): PdfBuilderInterface
{
$traceableBuilder = $this->inner->get($builder);

if (!$traceableBuilder instanceof TraceablePdfBuilder) {
return $traceableBuilder;
}

$this->builders[] = [$builder, $traceableBuilder];

return $traceableBuilder;
}

/**
* @return HtmlPdfBuilder|TraceablePdfBuilder
*/
public function html(): PdfBuilderInterface
{
/** @var HtmlPdfBuilder|TraceablePdfBuilder $traceableBuilder */
$traceableBuilder = $this->inner->html();

if (!$traceableBuilder instanceof TraceablePdfBuilder) {
return $traceableBuilder;
}

$this->builders[] = ['html', $traceableBuilder];

return $traceableBuilder;
}

/**
* @return UrlPdfBuilder|TraceablePdfBuilder
*/
public function url(): PdfBuilderInterface
{
/** @var UrlPdfBuilder|TraceablePdfBuilder $traceableBuilder */
$traceableBuilder = $this->inner->url();

if (!$traceableBuilder instanceof TraceablePdfBuilder) {
return $traceableBuilder;
}

$this->builders[] = ['url', $traceableBuilder];

return $traceableBuilder;
}

/**
* @return LibreOfficePdfBuilder|TraceablePdfBuilder
*/
public function office(): PdfBuilderInterface
{
/** @var LibreOfficePdfBuilder|TraceablePdfBuilder $traceableBuilder */
$traceableBuilder = $this->inner->office();

if (!$traceableBuilder instanceof TraceablePdfBuilder) {
return $traceableBuilder;
}

$this->builders[] = ['office', $traceableBuilder];

return $traceableBuilder;
}

/**
* @return MarkdownPdfBuilder|TraceablePdfBuilder
*/
public function markdown(): PdfBuilderInterface
{
/** @var MarkdownPdfBuilder|TraceablePdfBuilder $traceableBuilder */
$traceableBuilder = $this->inner->markdown();

if (!$traceableBuilder instanceof TraceablePdfBuilder) {
return $traceableBuilder;
}

$this->builders[] = ['markdown', $traceableBuilder];

return $traceableBuilder;
}

/**
* @return list<array{string, TraceablePdfBuilder}>
*/
public function getBuilders(): array
{
return $this->builders;
}
}
29 changes: 29 additions & 0 deletions src/DependencyInjection/CompilerPass/GotenbergPass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace Sensiolabs\GotenbergBundle\DependencyInjection\CompilerPass;

use Sensiolabs\GotenbergBundle\Debug\Builder\TraceablePdfBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

final class GotenbergPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
if (!$container->has('sensiolabs_gotenberg.data_collector')) {
return;
}

foreach ($container->findTaggedServiceIds('sensiolabs_gotenberg.builder') as $serviceId => $tags) {
$container->register('debug.'.$serviceId, TraceablePdfBuilder::class)
->setDecoratedService($serviceId)
->setArguments([
'$inner' => new Reference('.inner'),
])
;
}
}
}
4 changes: 4 additions & 0 deletions src/DependencyInjection/SensiolabsGotenbergExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ public function load(array $configs, ContainerBuilder $container): void
$loader = new PhpFileLoader($container, new FileLocator(__DIR__.'/../../config'));
$loader->load('services.php');

if ($container->getParameter('kernel.debug') === true) {
$loader->load('debug.php');
}

$configuration = new Configuration();

/** @var array{base_uri: string, base_directory: string, default_options: array{html: array<string, mixed>, url: array<string, mixed>, markdown: array<string, mixed>, office: array<string, mixed>}} $config */
Expand Down
8 changes: 4 additions & 4 deletions src/Pdf/Gotenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,22 +37,22 @@ private function getInternal(string $key): PdfBuilderInterface
return $this->get(".sensiolabs_gotenberg.builder.{$key}");
}

public function html(): HtmlPdfBuilder
public function html(): PdfBuilderInterface
{
return $this->getInternal('html');
}

public function url(): UrlPdfBuilder
public function url(): PdfBuilderInterface
{
return $this->getInternal('url');
}

public function office(): LibreOfficePdfBuilder
public function office(): PdfBuilderInterface
{
return $this->getInternal('office');
}

public function markdown(): MarkdownPdfBuilder
public function markdown(): PdfBuilderInterface
{
return $this->getInternal('markdown');
}
Expand Down
Loading

0 comments on commit a60262a

Please sign in to comment.