From af185c2791f9f58bdd8b36940ade4cb018ac7448 Mon Sep 17 00:00:00 2001 From: kEERill Date: Mon, 23 Dec 2024 18:13:06 +0300 Subject: [PATCH 1/2] [add] Added the ability to use Profiler in your classes --- composer.json | 1 + src/Factories/OptionalProfilerFactory.php | 42 ++++++++++ src/Factories/ProfilerFactory.php | 34 ++++++++ .../XhprofProfiler.php | 9 +-- src/XhprofServiceProvider.php | 67 ++++------------ tests/ProviderTest.php | 78 ++++++++++++------- 6 files changed, 150 insertions(+), 81 deletions(-) create mode 100644 src/Factories/OptionalProfilerFactory.php create mode 100644 src/Factories/ProfilerFactory.php rename src/{middleware => Middleware}/XhprofProfiler.php (82%) diff --git a/composer.json b/composer.json index ce4f4da..a4618a3 100644 --- a/composer.json +++ b/composer.json @@ -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": { diff --git a/src/Factories/OptionalProfilerFactory.php b/src/Factories/OptionalProfilerFactory.php new file mode 100644 index 0000000..e090098 --- /dev/null +++ b/src/Factories/OptionalProfilerFactory.php @@ -0,0 +1,42 @@ +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'); + } +} diff --git a/src/Factories/ProfilerFactory.php b/src/Factories/ProfilerFactory.php new file mode 100644 index 0000000..6e976f4 --- /dev/null +++ b/src/Factories/ProfilerFactory.php @@ -0,0 +1,34 @@ +makeStorage(), + DriverFactory::createXhrofDriver(), + $this->configRepository->get('app.name') + ); + } + + private function makeStorage(): StorageInterface + { + return new WebStorage( + new CurlHttpClient, + $this->configRepository->get('xhprof.endpoint'), + ); + } +} diff --git a/src/middleware/XhprofProfiler.php b/src/Middleware/XhprofProfiler.php similarity index 82% rename from src/middleware/XhprofProfiler.php rename to src/Middleware/XhprofProfiler.php index e86c388..7ba54a1 100644 --- a/src/middleware/XhprofProfiler.php +++ b/src/Middleware/XhprofProfiler.php @@ -1,6 +1,6 @@ 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 * @@ -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'); } } diff --git a/tests/ProviderTest.php b/tests/ProviderTest.php index d316eb8..8f0f64b 100644 --- a/tests/ProviderTest.php +++ b/tests/ProviderTest.php @@ -1,16 +1,23 @@ 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()); @@ -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); }); }); From 41ab6543929e1d98ebbddfc5ea46d6afbe3f5d9d Mon Sep 17 00:00:00 2001 From: kEERill Date: Mon, 23 Dec 2024 18:24:50 +0300 Subject: [PATCH 2/2] [add] Added example usage in readme file --- README.md | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6c3ccc4..f56b532 100644 --- a/README.md +++ b/README.md @@ -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: