Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support call Profiler from Container DI #23

Merged
merged 2 commits into from
Jan 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
});
});