Skip to content

Commit

Permalink
Added support call Profiler from Container DI (#23)
Browse files Browse the repository at this point in the history
* [add] Added the ability to use Profiler in your classes

* [add] Added example usage in readme file

---------

Co-authored-by: kEERill <[email protected]>
  • Loading branch information
kEERill and kEERill authored Jan 19, 2025
1 parent bbdc85b commit 1eb0c52
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 82 deletions.
42 changes: 41 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,47 @@ Disabled values: `false` `0` `off` `no`

This feature works great with a browser extension like [ModHeader](https://modheader.com/). It lets you switch profiling on and off right from your browser.

## Usage with Sail
### Usage Profiler

By default, the profiler is turned on and off with each HTTP request.
However, you may have other points where your\application starts. For example, it can be queues, commands, and so on.

In such cases, you can configure the profiler to run in the desired location yourself:

```php

use SpiralPackages\Profiler\Profiler;

class RegisterUserActionJob
{
public function __construct(
public string $name,
public string $password
) {
}

/**
* Get Profiler object from Container DI
*
* @param Profiler $profiler
* @return void
*/
public function handle(Profiler $profiler): void
{
try {
$profiler->start();

// code for register new user
}
finally {
$profiler->end();
}
}
}

```

### Usage with Sail

Add the buggregator service to your docker-compose file:

Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"require-dev": {
"laravel/pint": "^1.0",
"orchestra/testbench": "^8.8|^9.0",
"larastan/larastan": "^2.9",
"pestphp/pest": "^2.20"
},
"autoload": {
Expand Down
42 changes: 42 additions & 0 deletions src/Factories/OptionalProfilerFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace Maantje\XhprofBuggregatorLaravel\Factories;

use Illuminate\Foundation\Application;
use Maantje\XhprofBuggregatorLaravel\Middleware\XhprofProfiler;
use SpiralPackages\Profiler\Driver\NullDriver;
use SpiralPackages\Profiler\Profiler;
use SpiralPackages\Profiler\Storage\NullStorage;

final class OptionalProfilerFactory
{
protected ProfilerFactory $profilerFactory;

public function __construct(
protected Application $app
) {
$this->profilerFactory = new ProfilerFactory($this->app['config']);
}

public function create(): Profiler
{
if ($this->isEnabled()) {
return $this->profilerFactory->create();
}

return new Profiler(
storage: new NullStorage,
driver: new NullDriver,
appName: $this->app['config']->get('app.name'),
);
}

private function isEnabled(): bool
{
if ($this->app['request']->hasHeader(XhprofProfiler::HEADER)) {
return filter_var($this->app['request']->header(XhprofProfiler::HEADER), FILTER_VALIDATE_BOOLEAN);
}

return $this->app['config']->get('xhprof.enabled');
}
}
34 changes: 34 additions & 0 deletions src/Factories/ProfilerFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace Maantje\XhprofBuggregatorLaravel\Factories;

use Illuminate\Config\Repository;
use SpiralPackages\Profiler\DriverFactory;
use SpiralPackages\Profiler\Profiler;
use SpiralPackages\Profiler\Storage\StorageInterface;
use SpiralPackages\Profiler\Storage\WebStorage;
use Symfony\Component\HttpClient\CurlHttpClient;

final class ProfilerFactory
{
public function __construct(
protected Repository $configRepository
) {}

public function create(): Profiler
{
return new Profiler(
$this->makeStorage(),
DriverFactory::createXhrofDriver(),
$this->configRepository->get('app.name')
);
}

private function makeStorage(): StorageInterface
{
return new WebStorage(
new CurlHttpClient,
$this->configRepository->get('xhprof.endpoint'),
);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace Maantje\XhprofBuggregatorLaravel\middleware;
namespace Maantje\XhprofBuggregatorLaravel\Middleware;

use Closure;
use Illuminate\Http\Request;
Expand All @@ -11,10 +11,9 @@ class XhprofProfiler
{
public const HEADER = 'X-Xhprof-Enabled';

public function __construct(private readonly Profiler $profiler)
{
//
}
public function __construct(
private readonly Profiler $profiler
) {}

/**
* Handle an incoming request.
Expand Down
67 changes: 17 additions & 50 deletions src/XhprofServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@
namespace Maantje\XhprofBuggregatorLaravel;

use Illuminate\Contracts\Http\Kernel;
use Illuminate\Foundation\Application;
use Illuminate\Support\ServiceProvider;
use Maantje\XhprofBuggregatorLaravel\middleware\XhprofProfiler;
use SpiralPackages\Profiler\DriverFactory;
use Maantje\XhprofBuggregatorLaravel\Factories\OptionalProfilerFactory;
use Maantje\XhprofBuggregatorLaravel\Middleware\XhprofProfiler;
use SpiralPackages\Profiler\Profiler;
use SpiralPackages\Profiler\Storage\WebStorage;
use Symfony\Component\HttpClient\CurlHttpClient;
use Throwable;

class XhprofServiceProvider extends ServiceProvider
Expand All @@ -17,28 +16,26 @@ public function register(): void
{
$this->mergeConfigFrom(__DIR__.'/../config/xhprof.php', 'xhprof');

if (! $this->isEnabled()) {
return;
}

if ($this->shouldRegisterMiddleware()) {
$this->registerMiddleware();
}

$this->app->bind(Profiler::class, function () {
$storage = new WebStorage(
new CurlHttpClient(),
config('xhprof.endpoint'),
);

return new Profiler(
$storage,
DriverFactory::createXhrofDriver(),
config('app.name')
);
$this->app->scoped(Profiler::class, function (Application $application) {
return (new OptionalProfilerFactory($application))
->create();
});
}

/**
* Bootstrap any package services.
*/
public function boot(): void
{
$this->publishes([
__DIR__.'/../config/xhprof.php' => config_path('xhprof.php'),
]);
}

/**
* Registers the XhprofProfiler middleware
*
Expand All @@ -59,38 +56,8 @@ protected function registerMiddleware(): void
$kernel->prependMiddleware(XhprofProfiler::class);
}

/**
* Bootstrap any package services.
*/
public function boot(): void
{
$this->publishes([
__DIR__.'/../config/xhprof.php' => config_path('xhprof.php'),
]);
}

/**
* Checks if the profiler should be enabled
*/
private function isEnabled(): bool
{
if (request()->hasHeader(XhprofProfiler::HEADER)) {
return filter_var(request()->header(XhprofProfiler::HEADER), FILTER_VALIDATE_BOOLEAN);
}

try {
return config()->get('xhprof.enabled');
} catch (Throwable) {
return false;
}
}

private function shouldRegisterMiddleware(): bool
{
try {
return config()->get('xhprof.register_middleware');
} catch (Throwable) {
return true;
}
return config('xhprof.register_middleware');
}
}
78 changes: 52 additions & 26 deletions tests/ProviderTest.php
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
<?php

use Illuminate\Contracts\Http\Kernel;
use Maantje\XhprofBuggregatorLaravel\middleware\XhprofProfiler;
use Maantje\XhprofBuggregatorLaravel\Middleware\XhprofProfiler;
use Maantje\XhprofBuggregatorLaravel\XhprofServiceProvider;
use SpiralPackages\Profiler\Driver\NullDriver;
use SpiralPackages\Profiler\Driver\XhprofDriver;
use SpiralPackages\Profiler\Profiler;
use SpiralPackages\Profiler\Storage\NullStorage;
use SpiralPackages\Profiler\Storage\WebStorage;

describe('enabled', function () {
describe('xhprof enabled', function () {
beforeEach(function () {
config()->set('xhprof.enabled', true);
});

it('does not register middleware when header is given', function () {
setXhprofEnabledHeader('false');
it('always registers middleware', function (?string $enabledFromHeader) {
if (! is_null($enabledFromHeader)) {
setXhprofEnabledHeader($enabledFromHeader);
}

$provider = new XhprofServiceProvider(app());

Expand All @@ -21,53 +28,72 @@

expect(
$kernel->hasMiddleware(XhprofProfiler::class)
)->toBeFalse();
)->toBeTrue();
})->with([null, 'true', 'false']);

it('get profiler with storage and xhprof driver', function () {
$provider = new XhprofServiceProvider(app());

$provider->register();

/** @var Profiler $profiler */
$profiler = app(Profiler::class);

expect((fn () => $this->storage)->call($profiler))
->toBeInstanceOf(WebStorage::class)
->and((fn () => $this->driver)->call($profiler))
->toBeInstanceOf(XhprofDriver::class);
});

it('registers middleware', function () {
it('get profiler nullable with storage and xhprof driver when header is given', function () {
setXhprofEnabledHeader('false');

$provider = new XhprofServiceProvider(app());

$provider->register();

/** @var \Illuminate\Foundation\Http\Kernel $kernel */
$kernel = app(Kernel::class);
/** @var Profiler $profiler */
$profiler = app(Profiler::class);

expect(
$kernel->hasMiddleware(XhprofProfiler::class)
)->toBeTrue();
expect((fn () => $this->storage)->call($profiler))
->toBeInstanceOf(NullStorage::class)
->and((fn () => $this->driver)->call($profiler))
->toBeInstanceOf(NullDriver::class);
});
});

describe('disabled', function () {
describe('xhprof disabled', function () {
beforeEach(function () {
config()->set('xhprof.enabled', false);
});

it('registers middleware when header is given', function () {
setXhprofEnabledHeader('true');

it('get profiler nullable with storage and xhprof driver', function () {
$provider = new XhprofServiceProvider(app());

$provider->register();

/** @var \Illuminate\Foundation\Http\Kernel $kernel */
$kernel = app(Kernel::class);
/** @var Profiler $profiler */
$profiler = app(Profiler::class);

expect(
$kernel->hasMiddleware(XhprofProfiler::class)
)->toBeTrue();
expect((fn () => $this->storage)->call($profiler))
->toBeInstanceOf(NullStorage::class)
->and((fn () => $this->driver)->call($profiler))
->toBeInstanceOf(NullDriver::class);
});

it('does not register middleware', function () {
it('get profiler with storage and xhprof driver when header is given', function () {
setXhprofEnabledHeader('true');

$provider = new XhprofServiceProvider(app());

$provider->register();

/** @var \Illuminate\Foundation\Http\Kernel $kernel */
$kernel = app(Kernel::class);
/** @var Profiler $profiler */
$profiler = app(Profiler::class);

expect(
$kernel->hasMiddleware(XhprofProfiler::class)
)->toBeFalse();
expect((fn () => $this->storage)->call($profiler))
->toBeInstanceOf(WebStorage::class)
->and((fn () => $this->driver)->call($profiler))
->toBeInstanceOf(XhprofDriver::class);
});
});

0 comments on commit 1eb0c52

Please sign in to comment.