Skip to content

Commit

Permalink
feat: added configs for profilers with mapping to php.ini; added PhpI…
Browse files Browse the repository at this point in the history
…ni config attribute; files observer is created using container now
  • Loading branch information
roxblnfk committed May 31, 2024
1 parent e7e4307 commit c15e5fe
Show file tree
Hide file tree
Showing 12 changed files with 154 additions and 54 deletions.
23 changes: 8 additions & 15 deletions src/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@

namespace Buggregator\Trap;

use Buggregator\Trap\Config\Server\Files\SPX as SPXFileConfig;
use Buggregator\Trap\Config\Server\Files\XDebug as XDebugFileConfig;
use Buggregator\Trap\Config\Server\Files\XHProf as XHProfFileConfig;
use Buggregator\Trap\Config\Server\Frontend as FrontendConfig;
use Buggregator\Trap\Config\Server\SocketServer;
use Buggregator\Trap\Handler\Http\Handler\Websocket;
use Buggregator\Trap\Handler\Http\Middleware;
use Buggregator\Trap\Proto\Buffer;
use Buggregator\Trap\Service\Container;
use Buggregator\Trap\Service\FilesObserver\Filter\XHProf;
use Buggregator\Trap\Socket\Client;
use Buggregator\Trap\Socket\Server;
use Buggregator\Trap\Socket\SocketStream;
Expand Down Expand Up @@ -230,19 +232,10 @@ private function createServer(SocketServer $config, Inspector $inspector): Serve

private function configureFileObserver(): void
{
// todo add
// \ini_get('xdebug.output_dir'),

if (false !== ($path = \ini_get('xhprof.output_dir'))) {
$this->processors[] = new Service\FilesObserver(
$this->logger,
$this->buffer,
new Config\FilesObserver(
path: $path,
converter: XHProf::class,
interval: 2.0,
),
);
}
$this->processors[] = $this->container->make(Service\FilesObserver::class, [
$this->container->get(XHProfFileConfig::class),
$this->container->get(XDebugFileConfig::class),
$this->container->get(SPXFileConfig::class),
]);
}
}
24 changes: 0 additions & 24 deletions src/Config/FilesObserver.php

This file was deleted.

32 changes: 32 additions & 0 deletions src/Config/Server/Files/ObserverConfig.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace Buggregator\Trap\Config\Server\Files;

use Buggregator\Trap\Service\FilesObserver\FrameConverter;

/**
* @internal
*/
abstract class ObserverConfig
{
/* @var non-empty-string|null $path */
public ?string $path = null;

/** @var class-string<FrameConverter>|null $converter */
public ?string $converterClass = null;

/** @var float Scan interval in seconds */
public float $scanInterval = 5.0;

/**
* @psalm-assert-if-true non-empty-string $this->path
* @psalm-assert-if-true class-string<FrameConverter> $this->converterClass
*/
public function isValid(): bool
{
return $this->path !== null && $this->converterClass !== null && $this->path !== ''
&& \is_a($this->converterClass, FrameConverter::class, true) && $this->scanInterval > 0.0;
}
}
17 changes: 17 additions & 0 deletions src/Config/Server/Files/SPX.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Buggregator\Trap\Config\Server\Files;

use Buggregator\Trap\Service\Config\PhpIni;

/**
* @internal
*/
final class SPX extends ObserverConfig
{
/** @var non-empty-string|null Path to SPX files */
#[PhpIni('spx.data_dir')]
public ?string $path = null;

Check failure on line 16 in src/Config/Server/Files/SPX.php

View workflow job for this annotation

GitHub Actions / static-analysis (ubuntu-latest, 8.2, locked)

NonInvariantDocblockPropertyType

src/Config/Server/Files/SPX.php:16:20: NonInvariantDocblockPropertyType: Property Buggregator\Trap\Config\Server\Files\SPX::$path has type non-empty-string|null, not invariant with Buggregator\Trap\Config\Server\Files\ObserverConfig::$path of type null|string (see https://psalm.dev/267)

Check failure on line 16 in src/Config/Server/Files/SPX.php

View workflow job for this annotation

GitHub Actions / static-analysis (ubuntu-latest, 8.2, locked)

NonInvariantDocblockPropertyType

src/Config/Server/Files/SPX.php:16:20: NonInvariantDocblockPropertyType: Property Buggregator\Trap\Config\Server\Files\SPX::$path has type non-empty-string|null, not invariant with Buggregator\Trap\Config\Server\Files\ObserverConfig::$path of type null|string (see https://psalm.dev/267)
}
17 changes: 17 additions & 0 deletions src/Config/Server/Files/XDebug.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Buggregator\Trap\Config\Server\Files;

use Buggregator\Trap\Service\Config\PhpIni;

/**
* @internal
*/
final class XDebug extends ObserverConfig
{
/** @var non-empty-string|null Path to XDebug files */
#[PhpIni('xdebug.output_dir')]
public ?string $path = null;

Check failure on line 16 in src/Config/Server/Files/XDebug.php

View workflow job for this annotation

GitHub Actions / static-analysis (ubuntu-latest, 8.2, locked)

NonInvariantDocblockPropertyType

src/Config/Server/Files/XDebug.php:16:20: NonInvariantDocblockPropertyType: Property Buggregator\Trap\Config\Server\Files\XDebug::$path has type non-empty-string|null, not invariant with Buggregator\Trap\Config\Server\Files\ObserverConfig::$path of type null|string (see https://psalm.dev/267)

Check failure on line 16 in src/Config/Server/Files/XDebug.php

View workflow job for this annotation

GitHub Actions / static-analysis (ubuntu-latest, 8.2, locked)

NonInvariantDocblockPropertyType

src/Config/Server/Files/XDebug.php:16:20: NonInvariantDocblockPropertyType: Property Buggregator\Trap\Config\Server\Files\XDebug::$path has type non-empty-string|null, not invariant with Buggregator\Trap\Config\Server\Files\ObserverConfig::$path of type null|string (see https://psalm.dev/267)
}
20 changes: 20 additions & 0 deletions src/Config/Server/Files/XHProf.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace Buggregator\Trap\Config\Server\Files;

use Buggregator\Trap\Service\Config\PhpIni;
use Buggregator\Trap\Service\FilesObserver\Filter\XHProf as Converter;

/**
* @internal
*/
final class XHProf extends ObserverConfig
{
/** @var non-empty-string|null Path to XHProf files */
#[PhpIni('xhprof.output_dir')]
public ?string $path = null;

Check failure on line 17 in src/Config/Server/Files/XHProf.php

View workflow job for this annotation

GitHub Actions / static-analysis (ubuntu-latest, 8.2, locked)

NonInvariantDocblockPropertyType

src/Config/Server/Files/XHProf.php:17:20: NonInvariantDocblockPropertyType: Property Buggregator\Trap\Config\Server\Files\XHProf::$path has type non-empty-string|null, not invariant with Buggregator\Trap\Config\Server\Files\ObserverConfig::$path of type null|string (see https://psalm.dev/267)

Check failure on line 17 in src/Config/Server/Files/XHProf.php

View workflow job for this annotation

GitHub Actions / static-analysis (ubuntu-latest, 8.2, locked)

NonInvariantDocblockPropertyType

src/Config/Server/Files/XHProf.php:17:20: NonInvariantDocblockPropertyType: Property Buggregator\Trap\Config\Server\Files\XHProf::$path has type non-empty-string|null, not invariant with Buggregator\Trap\Config\Server\Files\ObserverConfig::$path of type null|string (see https://psalm.dev/267)

public ?string $converterClass = Converter::class;

Check failure on line 19 in src/Config/Server/Files/XHProf.php

View workflow job for this annotation

GitHub Actions / static-analysis (ubuntu-latest, 8.2, locked)

NonInvariantDocblockPropertyType

src/Config/Server/Files/XHProf.php:19:20: NonInvariantDocblockPropertyType: Property Buggregator\Trap\Config\Server\Files\XHProf::$converterClass has type null|string, not invariant with Buggregator\Trap\Config\Server\Files\ObserverConfig::$converterClass of type class-string<Buggregator\Trap\Service\FilesObserver\FrameConverter>|null (see https://psalm.dev/267)

Check failure on line 19 in src/Config/Server/Files/XHProf.php

View workflow job for this annotation

GitHub Actions / static-analysis (ubuntu-latest, 8.2, locked)

NonInvariantDocblockPropertyType

src/Config/Server/Files/XHProf.php:19:20: NonInvariantDocblockPropertyType: Property Buggregator\Trap\Config\Server\Files\XHProf::$converterClass has type null|string, not invariant with Buggregator\Trap\Config\Server\Files\ObserverConfig::$converterClass of type class-string<Buggregator\Trap\Service\FilesObserver\FrameConverter>|null (see https://psalm.dev/267)
}
5 changes: 5 additions & 0 deletions src/Service/Config/ConfigLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ private function injectValue(object $config, \ReflectionProperty $property, arra
$attribute instanceof Env => $this->env[$attribute->name] ?? null,
$attribute instanceof InputOption => $this->inputOptions[$attribute->name] ?? null,
$attribute instanceof InputArgument => $this->inputArguments[$attribute->name] ?? null,
$attribute instanceof PhpIni => (static fn(string|false $value): ?string => match ($value) {
// Option does not exist or set to null
'', false => null,
default => $value,
})(\ini_get($attribute->option)),
default => null,
};

Expand Down
16 changes: 16 additions & 0 deletions src/Service/Config/PhpIni.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Buggregator\Trap\Service\Config;

/**
* @internal
*/
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::IS_REPEATABLE)]
final class PhpIni implements ConfigAttribute
{
public function __construct(
public string $option,
) {}
}
5 changes: 4 additions & 1 deletion src/Service/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ public function make(string $class, array $arguments = []): object
try {
$result = $this->injector->make($class, \array_merge((array) $binding, $arguments));
} catch (\Throwable $e) {
throw new class(previous: $e) extends \RuntimeException implements NotFoundExceptionInterface {};
throw new class(
"Unable to create object of class $class.",
previous: $e,
) extends \RuntimeException implements NotFoundExceptionInterface {};
}
}

Expand Down
14 changes: 12 additions & 2 deletions src/Service/FilesObserver.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@
namespace Buggregator\Trap\Service;

use Buggregator\Trap\Cancellable;
use Buggregator\Trap\Config\FilesObserver as Config;
use Buggregator\Trap\Config\Server\Files\ObserverConfig as Config;
use Buggregator\Trap\Logger;
use Buggregator\Trap\Processable;
use Buggregator\Trap\Proto\Buffer;
use Buggregator\Trap\Proto\Frame;
use Buggregator\Trap\Service\FilesObserver\Handler;
use Yiisoft\Injector\Injector;

/**
* The service orchestrates the process of scanning files in directories.
*
* There are {@see Handler} instances for each configuration that are executed in fibers.
*
* @internal
*/
final class FilesObserver implements Processable, Cancellable
Expand All @@ -23,13 +28,18 @@ final class FilesObserver implements Processable, Cancellable
private array $fibers = [];

public function __construct(
private readonly Container $container,
private readonly Logger $logger,
private readonly Buffer $buffer,
Config ...$configs,
) {
foreach ($configs as $config) {
if (!$config->isValid()) {
continue;
}

$this->fibers[] = new \Fiber(function () use ($config): void {
foreach (Handler::generate($config, $this->logger) as $frame) {
foreach ($this->container->make(Handler::class, [$config]) as $frame) {
$this->propagateFrame($frame);
}
});
Expand Down
2 changes: 1 addition & 1 deletion src/Service/FilesObserver/FrameConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ interface FrameConverter
public function validate(FileInfo $file): bool;

/**
* @return iterable<ProfilerFrame>
* @return iterable<int, ProfilerFrame>
*/
public function convert(FileInfo $file): iterable;
}
33 changes: 22 additions & 11 deletions src/Service/FilesObserver/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,57 @@

namespace Buggregator\Trap\Service\FilesObserver;

use Buggregator\Trap\Config\FilesObserver as Config;
use Buggregator\Trap\Config\Server\Files\ObserverConfig as Config;
use Buggregator\Trap\Logger;
use Buggregator\Trap\Proto\Frame;
use Buggregator\Trap\Service\Container;
use Buggregator\Trap\Support\Timer;

/**
* The handler is responsible for scanning files in a directory and converting them into frames.
* It does it in a loop with a given interval.
*
* @see Config
*
* @internal
*
* @implements \IteratorAggregate<int, Frame>
*/
final class Handler
final class Handler implements \IteratorAggregate
{
private readonly Timer $timer;

/** @var array<non-empty-string, FileInfo> */
private array $cache = [];

/** @var non-empty-string */
private readonly string $path;

private FrameConverter $converter;

private function __construct(
public function __construct(
Config $config,
private readonly Logger $logger,
Container $container,
) {
$config->isValid() or throw new \InvalidArgumentException('Invalid configuration.');

$this->path = $config->path;
$this->timer = new Timer($config->interval);
$this->converter = new ($config->converter)();
$this->timer = new Timer($config->scanInterval);
$this->converter = $container->make($config->converterClass, [$config]);
}

/**
* @return \Generator<int, Frame, mixed, void>
* @return \Traversable<int, Frame>
*/
public static function generate(Config $config, Logger $logger): \Generator
public function getIterator(): \Traversable
{
$self = new self($config, $logger);
do {
foreach ($self->syncFiles() as $info) {
yield from $self->converter->convert($info);
foreach ($this->syncFiles() as $info) {
yield from $this->converter->convert($info);
}

$self->timer->wait()->reset();
$this->timer->wait()->reset();
} while (true);
}

Expand Down

0 comments on commit c15e5fe

Please sign in to comment.