├── .docker ├── etc │ └── php │ │ ├── entrypoint.sh │ │ ├── php-v1.ini │ │ └── php-v2.ini └── images │ └── php │ ├── .bashrc-v1 │ ├── .bashrc-v2 │ ├── Dockerfile-v1 │ └── Dockerfile-v2 ├── .gitignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── composer.json ├── config └── profiler.php ├── docker-compose.yml ├── phpunit-laravel-52.xml ├── phpunit-laravel-53.xml ├── phpunit-laravel-54.xml ├── phpunit-laravel-55.xml ├── phpunit-laravel-56.xml ├── phpunit-laravel-57.xml ├── phpunit-laravel-58.xml ├── phpunit-laravel-6.xml ├── phpunit-laravel-7.xml ├── phpunit-laravel-8.xml ├── src ├── BaseProfiler.php ├── Console │ ├── ClientCommand.php │ ├── ServerCommand.php │ └── StatusCommand.php ├── Contracts │ ├── DataProcessor.php │ ├── DataTracker.php │ ├── ExecutionContent.php │ ├── ExecutionData.php │ ├── ExecutionRequest.php │ ├── ExecutionResponse.php │ ├── ExecutionRoute.php │ ├── ExecutionServer.php │ ├── ExecutionSession.php │ ├── ExecutionWatcher.php │ ├── LaravelListener.php │ ├── Memory.php │ ├── Processor.php │ ├── Profiler.php │ ├── Timer.php │ └── Tracker.php ├── DisabledProfiler.php ├── Events │ ├── ExceptionHandling.php │ ├── ProfilerBound.php │ ├── ProfilerServerConnectionFailed.php │ ├── ProfilerServerConnectionSuccessful.php │ ├── ResetTrackers.php │ ├── Terminating.php │ └── Tracking.php ├── LaravelDataProcessor.php ├── LaravelDataTracker.php ├── LaravelExecution │ ├── ConsoleFinishedRequest.php │ ├── ConsoleFinishedResponse.php │ ├── ConsoleStartingRequest.php │ ├── ConsoleStartingResponse.php │ ├── ExceptionHandlerFromVersion7.php │ ├── ExceptionHandlerTillVersion6.php │ ├── HttpContent.php │ ├── HttpRequest.php │ ├── HttpResponse.php │ ├── HttpRoute.php │ ├── HttpServer.php │ ├── HttpSession.php │ ├── LaravelExecutionData.php │ ├── NullContent.php │ ├── NullRequest.php │ ├── NullResponse.php │ ├── NullRoute.php │ ├── NullServer.php │ └── NullSession.php ├── LaravelExecutionWatcher.php ├── LaravelListeners │ ├── AuthListener.php │ ├── ConsoleCommandFinishedListener.php │ ├── EventsListener.php │ ├── ExceptionListener.php │ ├── HttpRequestHandledListener.php │ ├── PerformanceListener.php │ ├── QueriesListener.php │ ├── RedisListener.php │ └── ViewsListener.php ├── LaravelProfiler.php ├── Processors │ ├── BroadcastingProcessor.php │ └── StatusCommandProcessor.php ├── ProfilerResolver.php ├── ServiceProvider.php ├── Services │ ├── ConfigService.php │ ├── ConsoleService.php │ ├── GeneratorService.php │ ├── LogService.php │ ├── ParamsService.php │ ├── Performance │ │ ├── MemoryService.php │ │ ├── NullTimerService.php │ │ ├── TimerException.php │ │ └── TimerService.php │ └── helpers.php └── Trackers │ ├── ApplicationTracker.php │ ├── AuthTracker.php │ ├── BaseTracker.php │ ├── BindingsTracker.php │ ├── ConfigTracker.php │ ├── ContentTracker.php │ ├── EventsTracker.php │ ├── ExceptionTracker.php │ ├── PathsTracker.php │ ├── PerformanceTracker.php │ ├── QueriesTracker.php │ ├── RedisTracker.php │ ├── RequestTracker.php │ ├── ResponseTracker.php │ ├── RouteTracker.php │ ├── ServerTracker.php │ ├── ServiceProvidersTracker.php │ ├── SessionTracker.php │ └── ViewsTracker.php └── tests ├── Feature ├── CommandsTest.php ├── LaravelConsoleExecutionTest.php ├── LaravelExecutionTest.php ├── LaravelHttpExecutionTest.php ├── LaravelNullExecutionTest.php ├── PerformanceTest.php ├── PerformanceTrackerTest.php ├── RegisterProfilerTest.php ├── RunningProfilerTest.php ├── TrackersResetTest.php └── TrackersTest.php ├── Support ├── Fixtures │ ├── DummyClassA.php │ ├── DummyClassB.php │ ├── DummyCommand.php │ ├── DummyContractA.php │ ├── DummyContractB.php │ ├── DummyController.php │ ├── DummyEventA.php │ ├── DummyEventB.php │ ├── DummyException.php │ ├── DummyFormRequest.php │ ├── PerformanceProcessor.php │ ├── ProcessorA.php │ ├── ProcessorB.php │ ├── TrackerA.php │ ├── TrackerB.php │ ├── dummy-view-a.blade.php │ └── dummy-view-b.blade.php ├── Framework.php ├── PHPMock.php └── TestListener.php ├── TestCase.php ├── Unit ├── LaravelExecution │ ├── ConsoleFinishedRequestTest.php │ ├── ConsoleStartingRequestTest.php │ └── ConsoleStartingResponseTest.php ├── Services │ ├── ConfigServiceTest.php │ ├── HelpersTest.php │ ├── ParamsServiceTest.php │ └── Performance │ │ └── TimerServiceTest.php └── Trackers │ ├── ApplicationTrackerTest.php │ ├── AuthTrackerTest.php │ ├── BindingsTrackerTest.php │ ├── ConfigTrackerTest.php │ ├── ContentTrackerTest.php │ ├── EventsTrackerTest.php │ ├── ExceptionTrackerTest.php │ ├── PathsTrackerTest.php │ ├── QueriesTrackerTest.php │ ├── RedisTrackerTest.php │ ├── RequestTrackerTest.php │ ├── ResponseTrackerTest.php │ ├── RouteTrackerTest.php │ ├── ServerTrackerTest.php │ ├── ServiceProvidersTrackerTest.php │ ├── SessionTrackerTest.php │ └── ViewsTrackerTest.php └── bootstrap └── phpunit.php /.docker/etc/php/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | redis-server --daemonize yes 4 | 5 | setfacl -dR -m u:www-data:rwX /var/www/html 6 | setfacl -R -m u:www-data:rwX /var/www/html 7 | 8 | docker-php-entrypoint $@ 9 | -------------------------------------------------------------------------------- /.docker/etc/php/php-v1.ini: -------------------------------------------------------------------------------- 1 | memory_limit = 256M 2 | max_execution_time = 600 3 | -------------------------------------------------------------------------------- /.docker/etc/php/php-v2.ini: -------------------------------------------------------------------------------- 1 | memory_limit = 256M 2 | max_execution_time = 600 3 | xdebug.mode=coverage 4 | -------------------------------------------------------------------------------- /.docker/images/php/.bashrc-v1: -------------------------------------------------------------------------------- 1 | alias la52='vendor/bin/phpunit --stop-on-failure -c phpunit-laravel-52.xml' 2 | alias la53='vendor/bin/phpunit --stop-on-failure -c phpunit-laravel-53.xml' 3 | alias la54='vendor/bin/phpunit --stop-on-failure -c phpunit-laravel-54.xml' 4 | alias la55='vendor/bin/phpunit --stop-on-failure -c phpunit-laravel-55.xml' 5 | alias la56='vendor/bin/phpunit --stop-on-failure -c phpunit-laravel-56.xml' 6 | alias la57='vendor/bin/phpunit --stop-on-failure -c phpunit-laravel-57.xml' 7 | alias la58='vendor/bin/phpunit --stop-on-failure -c phpunit-laravel-58.xml' 8 | alias la6='vendor/bin/phpunit --stop-on-failure -c phpunit-laravel-6.xml' 9 | alias la7='vendor/bin/phpunit --stop-on-failure -c phpunit-laravel-7.xml' 10 | 11 | alias phpunit-all='la7 && la6 && la58 && la57 && la56 && la55 && la54 && la53 && la52' 12 | alias phpunit-c='la7 --coverage-html coverage' 13 | -------------------------------------------------------------------------------- /.docker/images/php/.bashrc-v2: -------------------------------------------------------------------------------- 1 | alias la6='vendor/bin/phpunit --stop-on-failure -c phpunit-laravel-6.xml' 2 | alias la7='vendor/bin/phpunit --stop-on-failure -c phpunit-laravel-7.xml' 3 | alias la8='vendor/bin/phpunit --stop-on-failure -c phpunit-laravel-8.xml' 4 | 5 | alias la-all='la8 && la7 && la6' 6 | alias la-c='la8 --coverage-html coverage' 7 | -------------------------------------------------------------------------------- /.docker/images/php/Dockerfile-v1: -------------------------------------------------------------------------------- 1 | FROM php:7.3.10-fpm 2 | 3 | COPY ./.bashrc-v1 /root/.bashrc 4 | 5 | RUN apt-get update > /dev/null && apt-get install -y \ 6 | acl \ 7 | unzip \ 8 | libzip-dev \ 9 | zlib1g-dev \ 10 | libpng-dev \ 11 | libjpeg-dev \ 12 | nodejs \ 13 | redis-server 14 | 15 | RUN docker-php-ext-install zip pdo_mysql bcmath gd > /dev/null 16 | 17 | RUN pecl install xdebug > /dev/null \ 18 | && docker-php-ext-enable xdebug > /dev/null 19 | 20 | RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer > /dev/null 21 | 22 | RUN rm -rf /var/lib/apt/lists/* 23 | -------------------------------------------------------------------------------- /.docker/images/php/Dockerfile-v2: -------------------------------------------------------------------------------- 1 | FROM php:8.0.6-fpm 2 | 3 | COPY ./.bashrc-v2 /root/.bashrc 4 | 5 | RUN apt-get update > /dev/null && apt-get install -y \ 6 | acl \ 7 | unzip \ 8 | libzip-dev \ 9 | zlib1g-dev \ 10 | libpng-dev \ 11 | libjpeg-dev \ 12 | nodejs \ 13 | redis-server 14 | 15 | RUN docker-php-ext-install zip pdo_mysql bcmath gd > /dev/null 16 | 17 | RUN pecl install xdebug > /dev/null \ 18 | && docker-php-ext-enable xdebug > /dev/null 19 | 20 | RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer > /dev/null 21 | 22 | RUN rm -rf /var/lib/apt/lists/* 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /client 2 | /coverage 3 | /frameworks 4 | /vendor 5 | .phpunit.result.cache 6 | composer.lock 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.3.24 5 | - 7.4.0 6 | - 8.0 7 | 8 | services: 9 | - redis-server 10 | 11 | before_script: 12 | - composer install --no-interaction 13 | 14 | script: 15 | - vendor/bin/phpunit -c phpunit-laravel-6.xml --coverage-clover build/logs/clover.xml 16 | - vendor/bin/phpunit -c phpunit-laravel-7.xml 17 | - vendor/bin/phpunit -c phpunit-laravel-8.xml 18 | 19 | after_success: 20 | - travis_retry php vendor/bin/php-coveralls -v 21 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Janusz Kocik 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 6 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation 7 | the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 8 | and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions 11 | of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 14 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 15 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 16 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 17 | DEALINGS IN THE SOFTWARE. 18 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jkocik/laravel-profiler", 3 | "description": "Profiler for Laravel Framework", 4 | "keywords": ["laravel", "profiler", "debugbar"], 5 | "homepage": "https://github.com/jkocik/laravel-profiler", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Janusz Kocik", 10 | "email": "janusz.kocik@gmail.com" 11 | } 12 | ], 13 | "require": { 14 | "php": ">=7.2", 15 | "guzzlehttp/guzzle": "^5.0 || ^6.0 || ^7.0" 16 | }, 17 | "require-dev": { 18 | "mockery/mockery": "^1.0", 19 | "php-mock/php-mock": "^2.0", 20 | "php-coveralls/php-coveralls": "^2.1", 21 | "predis/predis": "^1.1", 22 | "fzaninotto/faker": "^1.5", 23 | "phpunit/phpunit": "^8" 24 | }, 25 | "autoload": { 26 | "psr-4": { 27 | "JKocik\\Laravel\\Profiler\\": "src/" 28 | }, 29 | "files": [ 30 | "src/Services/helpers.php" 31 | ] 32 | }, 33 | "autoload-dev": { 34 | "psr-4": { 35 | "JKocik\\Laravel\\Profiler\\Tests\\": "tests/" 36 | } 37 | }, 38 | "extra": { 39 | "laravel": { 40 | "providers": [ 41 | "JKocik\\Laravel\\Profiler\\ServiceProvider" 42 | ] 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.5' 2 | 3 | services: 4 | profiler_v1: 5 | build: 6 | dockerfile: Dockerfile-v1 7 | context: ./.docker/images/php 8 | container_name: profiler_v1 9 | hostname: profiler_v1 10 | entrypoint: sh /bin/entrypoint.sh php-fpm 11 | volumes: 12 | - ./.docker/etc/php/entrypoint.sh:/bin/entrypoint.sh:ro 13 | - ./.docker/etc/php/php-v1.ini:/usr/local/etc/php/php.ini 14 | - .:/var/www/html 15 | 16 | profiler_v2: 17 | build: 18 | dockerfile: Dockerfile-v2 19 | context: ./.docker/images/php 20 | container_name: profiler_v2 21 | hostname: profiler_v2 22 | entrypoint: sh /bin/entrypoint.sh php-fpm 23 | volumes: 24 | - ./.docker/etc/php/entrypoint.sh:/bin/entrypoint.sh:ro 25 | - ./.docker/etc/php/php-v2.ini:/usr/local/etc/php/php.ini 26 | - .:/var/www/html 27 | -------------------------------------------------------------------------------- /phpunit-laravel-52.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | ./tests/Feature 12 | 13 | 14 | ./tests/Unit 15 | 16 | 17 | 18 | 19 | ./src/ 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /phpunit-laravel-53.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | ./tests/Feature 12 | 13 | 14 | ./tests/Unit 15 | 16 | 17 | 18 | 19 | ./src/ 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /phpunit-laravel-54.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | ./tests/Feature 12 | 13 | 14 | ./tests/Unit 15 | 16 | 17 | 18 | 19 | ./src/ 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /phpunit-laravel-55.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | ./tests/Feature 12 | 13 | 14 | ./tests/Unit 15 | 16 | 17 | 18 | 19 | ./src/ 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /phpunit-laravel-56.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | ./tests/Feature 12 | 13 | 14 | ./tests/Unit 15 | 16 | 17 | 18 | 19 | ./src/ 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /phpunit-laravel-57.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | ./tests/Feature 12 | 13 | 14 | ./tests/Unit 15 | 16 | 17 | 18 | 19 | ./src/ 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /phpunit-laravel-58.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | ./tests/Feature 12 | 13 | 14 | ./tests/Unit 15 | 16 | 17 | 18 | 19 | ./src/ 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /phpunit-laravel-6.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | ./tests/Feature 12 | 13 | 14 | ./tests/Unit 15 | 16 | 17 | 18 | 19 | ./src/ 20 | 21 | ./src/LaravelExecution/ExceptionHandlerFromVersion7.php 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /phpunit-laravel-7.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | ./tests/Feature 12 | 13 | 14 | ./tests/Unit 15 | 16 | 17 | 18 | 19 | ./src/ 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /phpunit-laravel-8.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | ./tests/Feature 12 | 13 | 14 | ./tests/Unit 15 | 16 | 17 | 18 | 19 | ./src/ 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/BaseProfiler.php: -------------------------------------------------------------------------------- 1 | app = $app; 29 | } 30 | 31 | /** 32 | * @return void 33 | */ 34 | public function resetTrackers(): void 35 | { 36 | event(new ResetTrackers()); 37 | } 38 | 39 | /** 40 | * @return void 41 | */ 42 | public function listenForBoot(): void 43 | { 44 | $this->app->beforeBootstrapping(BootProviders::class, function () { 45 | $this->commands(); 46 | $this->boot(); 47 | }); 48 | } 49 | 50 | /** 51 | * @return void 52 | */ 53 | abstract protected function boot(): void; 54 | 55 | /** 56 | * @return void 57 | */ 58 | private function commands(): void 59 | { 60 | Event::listen(ArtisanStarting::class, function (ArtisanStarting $event) { 61 | $event->artisan->resolveCommands([ 62 | StatusCommand::class, 63 | ServerCommand::class, 64 | ClientCommand::class, 65 | ]); 66 | }); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Console/ClientCommand.php: -------------------------------------------------------------------------------- 1 | consoleService = $consoleService; 34 | } 35 | 36 | /** 37 | * @return void 38 | */ 39 | public function handle(): void 40 | { 41 | $this->line('Starting Profiler Client ...'); 42 | 43 | passthru($this->consoleService->profilerClientCmd($this->option('manual'))); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Console/ServerCommand.php: -------------------------------------------------------------------------------- 1 | consoleService = $consoleService; 34 | } 35 | 36 | /** 37 | * @return void 38 | */ 39 | public function handle(): void 40 | { 41 | $this->line('Starting Profiler Server ...'); 42 | 43 | passthru($this->consoleService->profilerServerCmd()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Console/StatusCommand.php: -------------------------------------------------------------------------------- 1 | configService = $configService; 50 | $this->consoleService = $consoleService; 51 | } 52 | 53 | /** 54 | * @return void 55 | */ 56 | public function handle(): void 57 | { 58 | $this->configService->overrideProcessors([ 59 | StatusCommandProcessor::class, 60 | ]); 61 | 62 | $this->printProfilerStatus(); 63 | 64 | if (! $this->configService->isProfilerEnabled()) { 65 | return; 66 | } 67 | 68 | $this->printTrackersStatus(); 69 | 70 | $this->printConnectionStatus(); 71 | } 72 | 73 | /** 74 | * @return void 75 | */ 76 | protected function printProfilerStatus(): void 77 | { 78 | $this->line("1) {$this->consoleService->envInfo()}"); 79 | $this->info($this->consoleService->profilerStatusInfo()); 80 | } 81 | 82 | /** 83 | * @return void 84 | */ 85 | protected function printTrackersStatus(): void 86 | { 87 | $this->line(''); 88 | $this->line("2) {$this->consoleService->trackersStatusInfo()}"); 89 | 90 | $this->configService->trackers()->each(function ($tracker) { 91 | $this->info("- {$tracker}"); 92 | }); 93 | 94 | $this->comment($this->consoleService->trackersCommentLine1()); 95 | $this->comment($this->consoleService->trackersCommentLine2()); 96 | } 97 | 98 | /** 99 | * @return void 100 | */ 101 | protected function printConnectionStatus(): void 102 | { 103 | $this->line(''); 104 | $this->line("3) {$this->consoleService->connectionStatusInfo()}"); 105 | 106 | Event::listen(ProfilerServerConnectionSuccessful::class, function (ProfilerServerConnectionSuccessful $event) { 107 | $this->info($this->consoleService->connectionSuccessfulInfo()); 108 | $this->info($this->consoleService->connectionSuccessfulSocketsInfo($event->socketsPort)); 109 | $this->info($this->consoleService->connectionSuccessfulClientsInfo($event->countClients)); 110 | 111 | $this->connectionStatusIsUnknown = false; 112 | }); 113 | 114 | Event::listen(ProfilerServerConnectionFailed::class, function () { 115 | $this->error($this->error($this->consoleService->connectionFailedInfo())); 116 | 117 | $this->connectionStatusIsUnknown = false; 118 | }); 119 | 120 | app()->terminating(function () { 121 | if (! $this->connectionStatusIsUnknown) { 122 | return; 123 | } 124 | 125 | $this->error($this->consoleService->connectionStatusUnknownInfo()); 126 | }); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/Contracts/DataProcessor.php: -------------------------------------------------------------------------------- 1 | bind(); 17 | } 18 | 19 | /** 20 | * @return void 21 | */ 22 | protected function bind(): void 23 | { 24 | $this->app->singleton(Timer::class, function ($app) { 25 | return $app->make(NullTimerService::class); 26 | }); 27 | 28 | event(ProfilerBound::class); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Events/ExceptionHandling.php: -------------------------------------------------------------------------------- 1 | exception = $exception; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Events/ProfilerBound.php: -------------------------------------------------------------------------------- 1 | socketsPort = $socketsPort; 25 | $this->countClients = $countClients; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Events/ResetTrackers.php: -------------------------------------------------------------------------------- 1 | app = $app; 42 | $this->logService = $logService; 43 | $this->configService = $configService; 44 | } 45 | 46 | /** 47 | * @param DataTracker $dataTracker 48 | * @return void 49 | */ 50 | public function process(DataTracker $dataTracker): void 51 | { 52 | if ($this->shouldNotProcess($dataTracker)) { 53 | return; 54 | } 55 | 56 | $this->configService->processors()->each(function (string $processor) use ($dataTracker) { 57 | try { 58 | $this->make($processor)->process($dataTracker); 59 | } catch (Exception $e) { 60 | $this->logService->error($e); 61 | } 62 | }); 63 | } 64 | 65 | /** 66 | * @param string $processor 67 | * @return Processor 68 | */ 69 | protected function make(string $processor): Processor 70 | { 71 | return $this->app->make($processor); 72 | } 73 | 74 | /** 75 | * @param DataTracker $dataTracker 76 | * @return bool 77 | */ 78 | protected function shouldNotProcess(DataTracker $dataTracker): bool 79 | { 80 | if (! $dataTracker->meta()->has('path')) { 81 | return false; 82 | } 83 | 84 | return $this->configService->pathsToTurnOffProcessors()->map(function ($path) use ($dataTracker) { 85 | return stripos($dataTracker->meta()->get('path'), $path) !== false; 86 | })->contains(true); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/LaravelDataTracker.php: -------------------------------------------------------------------------------- 1 | app = $app; 50 | $this->configService = $configService; 51 | 52 | $this->trackers = new Collection(); 53 | $this->meta = new Collection(); 54 | $this->data = new Collection(); 55 | } 56 | 57 | /** 58 | * @return void 59 | */ 60 | public function track(): void 61 | { 62 | $this->bootTrackers(Collection::make([ 63 | ApplicationTracker::class, 64 | PerformanceTracker::class, 65 | RequestTracker::class, 66 | ResponseTracker::class, 67 | ])); 68 | 69 | $this->bootTrackers($this->configService->trackers()); 70 | } 71 | 72 | /** 73 | * @return void 74 | */ 75 | public function terminate(): void 76 | { 77 | $this->trackers->each(function (Tracker $tracker) { 78 | $tracker->terminate(); 79 | $this->meta = $this->meta->merge($tracker->meta()); 80 | $this->data = $this->data->merge($tracker->data()); 81 | }); 82 | } 83 | 84 | /** 85 | * @return Collection 86 | */ 87 | public function meta(): Collection 88 | { 89 | return $this->meta; 90 | } 91 | 92 | /** 93 | * @return Collection 94 | */ 95 | public function data(): Collection 96 | { 97 | return $this->data; 98 | } 99 | 100 | /** 101 | * @param Collection $trackers 102 | * @return void 103 | */ 104 | protected function bootTrackers(Collection $trackers): void 105 | { 106 | $trackers->each(function (string $tracker) { 107 | $this->trackers->push($this->app->make($tracker)); 108 | }); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/LaravelExecution/ConsoleFinishedRequest.php: -------------------------------------------------------------------------------- 1 | command = $command; 29 | $this->input = $input; 30 | } 31 | 32 | /** 33 | * @return Collection 34 | */ 35 | public function meta(): Collection 36 | { 37 | return Collection::make([ 38 | 'type' => 'command-finished', 39 | 'path' => $this->command, 40 | ]); 41 | } 42 | 43 | /** 44 | * @return Collection 45 | */ 46 | public function data(): Collection 47 | { 48 | return Collection::make([ 49 | 'arguments' => $this->input->getArguments(), 50 | 'options' => $this->input->getOptions(), 51 | ]); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/LaravelExecution/ConsoleFinishedResponse.php: -------------------------------------------------------------------------------- 1 | exitCode = $exitCode; 22 | } 23 | 24 | /** 25 | * @return Collection 26 | */ 27 | public function meta(): Collection 28 | { 29 | return Collection::make([ 30 | 'status' => $this->exitCode, 31 | ]); 32 | } 33 | 34 | /** 35 | * @return Collection 36 | */ 37 | public function data(): Collection 38 | { 39 | return Collection::make(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/LaravelExecution/ConsoleStartingRequest.php: -------------------------------------------------------------------------------- 1 | 'command-starting', 17 | ]); 18 | } 19 | 20 | /** 21 | * @return Collection 22 | */ 23 | public function data(): Collection 24 | { 25 | return Collection::make(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/LaravelExecution/ConsoleStartingResponse.php: -------------------------------------------------------------------------------- 1 | null, 17 | ]); 18 | } 19 | 20 | /** 21 | * @return Collection 22 | */ 23 | public function data(): Collection 24 | { 25 | return Collection::make(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/LaravelExecution/ExceptionHandlerFromVersion7.php: -------------------------------------------------------------------------------- 1 | response = $response; 23 | } 24 | 25 | /** 26 | * @return Collection 27 | */ 28 | public function meta(): Collection 29 | { 30 | return Collection::make(); 31 | } 32 | 33 | /** 34 | * @return Collection 35 | */ 36 | public function data(): Collection 37 | { 38 | return Collection::make([ 39 | 'content' => $this->response->getContent(), 40 | ]); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/LaravelExecution/HttpRequest.php: -------------------------------------------------------------------------------- 1 | request = $request; 24 | } 25 | 26 | /** 27 | * @return Collection 28 | */ 29 | public function meta(): Collection 30 | { 31 | return Collection::make([ 32 | 'type' => 'http', 33 | 'method' => $this->request->method(), 34 | 'path' => $this->request->path(), 35 | 'ajax' => $this->request->ajax(), 36 | 'json' => $this->request->isJson(), 37 | ]); 38 | } 39 | 40 | /** 41 | * @return Collection 42 | */ 43 | public function data(): Collection 44 | { 45 | return Collection::make([ 46 | 'pjax' => $this->request->pjax(), 47 | 'url' => $this->request->url(), 48 | 'query' => $this->request->query(), 49 | 'ip' => $this->request->ip(), 50 | 'header' => $this->request->header(), 51 | 'input' => $this->request->input(), 52 | 'files' => $this->files(), 53 | 'cookie' => $this->request->cookie(), 54 | ]); 55 | } 56 | 57 | /** 58 | * @return Collection 59 | */ 60 | protected function files(): Collection 61 | { 62 | $files = Collection::make($this->request->allFiles()); 63 | 64 | return $this->filesMap($files); 65 | } 66 | 67 | /** 68 | * @param Collection $files 69 | * @return Collection 70 | */ 71 | protected function filesMap(Collection $files): Collection 72 | { 73 | return $files->map(function ($file) { 74 | if (is_array($file)) { 75 | $files = Collection::make($file); 76 | return $this->filesMap($files); 77 | } 78 | 79 | return [get_class($file) => $this->file($file)]; 80 | }); 81 | } 82 | 83 | /** 84 | * @param UploadedFile $file 85 | * @return array 86 | */ 87 | protected function file(UploadedFile $file): array 88 | { 89 | return [ 90 | 'client original name' => $file->getClientOriginalName(), 91 | 'client original extension' => $file->getClientOriginalExtension(), 92 | 'client mime type' => $file->getClientMimeType(), 93 | 'client size' => $this->clientSize($file), 94 | 'path' => $file->path(), 95 | ]; 96 | } 97 | 98 | /** 99 | * @param UploadedFile $file 100 | * @return int 101 | */ 102 | protected function clientSize(UploadedFile $file): int 103 | { 104 | return method_exists($file, 'getClientSize') 105 | ? $file->getClientSize() 106 | : $file->getSize(); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/LaravelExecution/HttpResponse.php: -------------------------------------------------------------------------------- 1 | response = $response; 23 | } 24 | 25 | /** 26 | * @return Collection 27 | */ 28 | public function meta(): Collection 29 | { 30 | return Collection::make([ 31 | 'status' => $this->response->getStatusCode(), 32 | 'status_text' => $this->getStatusText(), 33 | ]); 34 | } 35 | 36 | /** 37 | * @return Collection 38 | */ 39 | public function data(): Collection 40 | { 41 | return Collection::make([ 42 | 'headers' => $this->response->headers->all(), 43 | ]); 44 | } 45 | 46 | /** 47 | * @return string 48 | */ 49 | protected function getStatusText(): string 50 | { 51 | return $this->response::$statusTexts[$this->response->getStatusCode()] ?? 'unknown status'; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/LaravelExecution/HttpRoute.php: -------------------------------------------------------------------------------- 1 | route = $route; 29 | } 30 | 31 | /** 32 | * @return Collection 33 | */ 34 | public function meta(): Collection 35 | { 36 | return Collection::make(); 37 | } 38 | 39 | /** 40 | * @return Collection 41 | */ 42 | public function data(): Collection 43 | { 44 | return Collection::make([ 45 | 'methods' => $this->route->methods(), 46 | 'uri' => $this->route->uri(), 47 | 'name' => $this->route->getName(), 48 | 'middleware' => $this->route->middleware(), 49 | 'parameters' => $this->route->parameters(), 50 | 'prefix' => $this->route->getPrefix(), 51 | 'uses' => $this->uses(), 52 | ]); 53 | } 54 | 55 | /** 56 | * @return array 57 | */ 58 | protected function uses(): array 59 | { 60 | $uses = $this->route->getAction(); 61 | 62 | try { 63 | if ($this->isClosureIn($uses)) { 64 | return $this->closure($uses); 65 | } 66 | 67 | if ($this->isControllerIn($uses)) { 68 | return $this->controller($uses); 69 | } 70 | } catch (ReflectionException $e) {} 71 | 72 | return []; 73 | } 74 | 75 | /** 76 | * @param array $uses 77 | * @return bool 78 | */ 79 | protected function isClosureIn(array $uses): bool 80 | { 81 | return isset($uses['uses']) && $uses['uses'] instanceof Closure; 82 | } 83 | 84 | /** 85 | * @param array $uses 86 | * @return array 87 | */ 88 | protected function closure(array $uses): array 89 | { 90 | $action = new ReflectionFunction($uses['uses']); 91 | 92 | return [ 93 | 'closure' => $action->getFileName() . ':' . $action->getStartLine() . '-' . $action->getEndLine(), 94 | 'form_request' => $this->formRequest($action->getParameters()), 95 | ]; 96 | } 97 | 98 | /** 99 | * @param array $uses 100 | * @return bool 101 | */ 102 | protected function isControllerIn(array $uses): bool 103 | { 104 | return isset($uses['uses']) && count(explode('@', $uses['uses'])) == 2; 105 | } 106 | 107 | /** 108 | * @param array $uses 109 | * @return array 110 | */ 111 | protected function controller(array $uses): array 112 | { 113 | list($controller, $method) = explode('@', $uses['uses']); 114 | 115 | $action = new ReflectionMethod($controller, $method); 116 | 117 | return [ 118 | 'controller' => $uses['uses'] . ':' . $action->getStartLine() . '-' . $action->getEndLine(), 119 | 'form_request' => $this->formRequest($action->getParameters()), 120 | ]; 121 | } 122 | 123 | /** 124 | * @param array $parameters 125 | * @return string 126 | */ 127 | protected function formRequest(array $parameters): string 128 | { 129 | $formRequest = Collection::make($parameters)->filter(function (ReflectionParameter $parameter) { 130 | return $parameter->getType() && is_subclass_of($parameter->getType()->getName(), FormRequest::class); 131 | })->first(); 132 | 133 | return $formRequest ? $formRequest->getType()->getName() : ''; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/LaravelExecution/HttpServer.php: -------------------------------------------------------------------------------- 1 | request = $request; 23 | } 24 | 25 | /** 26 | * @return Collection 27 | */ 28 | public function meta(): Collection 29 | { 30 | return Collection::make(); 31 | } 32 | 33 | /** 34 | * @return Collection 35 | */ 36 | public function data(): Collection 37 | { 38 | return Collection::make( 39 | $this->request->server() 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/LaravelExecution/HttpSession.php: -------------------------------------------------------------------------------- 1 | session = $session; 23 | } 24 | 25 | /** 26 | * @return Collection 27 | */ 28 | public function meta(): Collection 29 | { 30 | return Collection::make(); 31 | } 32 | 33 | /** 34 | * @return Collection 35 | */ 36 | public function data(): Collection 37 | { 38 | return Collection::make($this->session->all()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/LaravelExecution/LaravelExecutionData.php: -------------------------------------------------------------------------------- 1 | setRequest(new NullRequest()); 51 | $this->setRoute(new NullRoute()); 52 | $this->setSession(new NullSession()); 53 | $this->setServer(new NullServer()); 54 | $this->setResponse(new NullResponse()); 55 | $this->setContent(new NullContent()); 56 | } 57 | 58 | /** 59 | * @param ExecutionRequest $request 60 | * @return void 61 | */ 62 | public function setRequest(ExecutionRequest $request): void 63 | { 64 | $this->request = $request; 65 | } 66 | 67 | /** 68 | * @return ExecutionRequest 69 | */ 70 | public function request(): ExecutionRequest 71 | { 72 | return $this->request; 73 | } 74 | 75 | /** 76 | * @param ExecutionRoute $route 77 | * @return void 78 | */ 79 | public function setRoute(ExecutionRoute $route): void 80 | { 81 | $this->route = $route; 82 | } 83 | 84 | /** 85 | * @return ExecutionRoute 86 | */ 87 | public function route(): ExecutionRoute 88 | { 89 | return $this->route; 90 | } 91 | 92 | /** 93 | * @param ExecutionSession $session 94 | * @return void 95 | */ 96 | public function setSession(ExecutionSession $session): void 97 | { 98 | $this->session = $session; 99 | } 100 | 101 | /** 102 | * @return ExecutionSession 103 | */ 104 | public function session(): ExecutionSession 105 | { 106 | return $this->session; 107 | } 108 | 109 | /** 110 | * @param ExecutionServer $server 111 | * @return void 112 | */ 113 | public function setServer(ExecutionServer $server): void 114 | { 115 | $this->server = $server; 116 | } 117 | 118 | /** 119 | * @return ExecutionServer 120 | */ 121 | public function server(): ExecutionServer 122 | { 123 | return $this->server; 124 | } 125 | 126 | /** 127 | * @param ExecutionResponse $response 128 | * @return void 129 | */ 130 | public function setResponse(ExecutionResponse $response): void 131 | { 132 | $this->response = $response; 133 | } 134 | 135 | /** 136 | * @return ExecutionResponse 137 | */ 138 | public function response(): ExecutionResponse 139 | { 140 | return $this->response; 141 | } 142 | 143 | /** 144 | * @param ExecutionContent $content 145 | * @return void 146 | */ 147 | public function setContent(ExecutionContent $content): void 148 | { 149 | $this->content = $content; 150 | } 151 | 152 | /** 153 | * @return ExecutionContent 154 | */ 155 | public function content(): ExecutionContent 156 | { 157 | return $this->content; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/LaravelExecution/NullContent.php: -------------------------------------------------------------------------------- 1 | null, 17 | ]); 18 | } 19 | 20 | /** 21 | * @return Collection 22 | */ 23 | public function data(): Collection 24 | { 25 | return Collection::make(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/LaravelExecution/NullResponse.php: -------------------------------------------------------------------------------- 1 | httpRequestHandledListener = $httpRequestHandledListener; 31 | $this->consoleCommandFinishedListener = $consoleCommandFinishedListener; 32 | } 33 | 34 | /** 35 | * @return void 36 | */ 37 | public function watch(): void 38 | { 39 | $this->httpRequestHandledListener->listen(); 40 | $this->consoleCommandFinishedListener->listen(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/LaravelListeners/AuthListener.php: -------------------------------------------------------------------------------- 1 | user = $logout->user; 25 | }); 26 | } 27 | 28 | /** 29 | * @return User|null 30 | */ 31 | public function user(): ?Model 32 | { 33 | return $this->user ?? Auth::user(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/LaravelListeners/ConsoleCommandFinishedListener.php: -------------------------------------------------------------------------------- 1 | executionData = $executionData; 27 | } 28 | 29 | /** 30 | * @return void 31 | */ 32 | public function listen(): void 33 | { 34 | Event::listen(\Illuminate\Console\Events\ArtisanStarting::class, function ($event) { 35 | $this->executionData->setRequest(new ConsoleStartingRequest()); 36 | $this->executionData->setResponse(new ConsoleStartingResponse()); 37 | }); 38 | 39 | Event::listen(\Illuminate\Console\Events\CommandFinished::class, function ($event) { 40 | $this->executionData->setRequest(new ConsoleFinishedRequest($event->command, $event->input)); 41 | $this->executionData->setResponse(new ConsoleFinishedResponse($event->exitCode)); 42 | }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/LaravelListeners/EventsListener.php: -------------------------------------------------------------------------------- 1 | dispatcher = $dispatcher; 46 | $this->configService = $configService; 47 | } 48 | 49 | /** 50 | * @return void 51 | */ 52 | public function listen(): void 53 | { 54 | $this->listenEvents(); 55 | $this->listenResetTrackers(); 56 | } 57 | 58 | /** 59 | * @return Collection 60 | */ 61 | public function events(): Collection 62 | { 63 | return Collection::make($this->events); 64 | } 65 | 66 | /** 67 | * @return int 68 | */ 69 | public function count(): int 70 | { 71 | return $this->count; 72 | } 73 | 74 | /** 75 | * @return void 76 | */ 77 | protected function listenEvents(): void 78 | { 79 | $this->dispatcher->listen('*', function ($event, $payload = null) { 80 | $name = $this->resolveName($event, $payload); 81 | 82 | if ($this->shouldSkip($name)) { 83 | return; 84 | } 85 | 86 | $this->count++; 87 | 88 | if ($this->shouldGroup($name)) { 89 | return $this->groupToPreviousEvent(); 90 | } 91 | 92 | $this->previousEventName = $name; 93 | 94 | array_push($this->events, $this->resolveEvent($name, $event, $payload)); 95 | }); 96 | } 97 | 98 | /** 99 | * @return void 100 | */ 101 | protected function listenResetTrackers(): void 102 | { 103 | $this->dispatcher->listen(ResetTrackers::class, function () { 104 | $this->events = []; 105 | $this->previousEventName = ''; 106 | $this->count = 0; 107 | }); 108 | } 109 | 110 | /** 111 | * @param string $name 112 | * @param $event 113 | * @param $payload 114 | * @return array 115 | */ 116 | protected function resolveEvent(string $name, $event, $payload): array 117 | { 118 | if ($this->configService->isEventsDataEnabled()) { 119 | return [$event, $payload, $name, 1]; 120 | } 121 | 122 | return [null, null, $name, 1]; 123 | } 124 | 125 | /** 126 | * @param $event 127 | * @param $payload 128 | * @return string 129 | */ 130 | protected function resolveName($event, $payload): string 131 | { 132 | return is_array($payload) ? $event : $this->dispatcher->firing(); 133 | } 134 | 135 | /** 136 | * @param string $name 137 | * @return bool 138 | */ 139 | protected function shouldGroup(string $name): bool 140 | { 141 | return $this->configService->isEventsGroupEnabled() && $name == $this->previousEventName; 142 | } 143 | 144 | /** 145 | * @return void 146 | */ 147 | protected function groupToPreviousEvent(): void 148 | { 149 | $this->events[count($this->events) - 1][3]++; 150 | } 151 | 152 | /** 153 | * @param string $name 154 | * @return bool 155 | */ 156 | protected function shouldSkip(string $name): bool 157 | { 158 | $shouldSkip = Collection::make([ 159 | 'bootstrapped: ' . \Illuminate\Foundation\Bootstrap\BootProviders::class, 160 | \JKocik\Laravel\Profiler\Events\ExceptionHandling::class, 161 | \JKocik\Laravel\Profiler\Events\ProfilerBound::class, 162 | \JKocik\Laravel\Profiler\Events\ResetTrackers::class, 163 | \JKocik\Laravel\Profiler\Events\Terminating::class, 164 | \JKocik\Laravel\Profiler\Events\Tracking::class, 165 | ]); 166 | 167 | return $shouldSkip->contains($name); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/LaravelListeners/ExceptionListener.php: -------------------------------------------------------------------------------- 1 | exception = $exceptionHandling->exception; 24 | }); 25 | } 26 | 27 | public function exception(): ?Throwable 28 | { 29 | return $this->exception; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/LaravelListeners/HttpRequestHandledListener.php: -------------------------------------------------------------------------------- 1 | executionData = $executionData; 33 | } 34 | 35 | /** 36 | * @return void 37 | */ 38 | public function listen(): void 39 | { 40 | /** @codeCoverageIgnoreStart */ 41 | Event::listen('kernel.handled', function (Request $request, Response $response) { 42 | $this->executionData->setRequest(new HttpRequest($request)); 43 | $this->executionData->setRoute($this->routeOf($request)); 44 | $this->executionData->setSession(new HttpSession(session())); 45 | $this->executionData->setServer(new HttpServer($request)); 46 | $this->executionData->setResponse(new HttpResponse($response)); 47 | $this->executionData->setContent(new HttpContent($response)); 48 | }); 49 | /** @codeCoverageIgnoreEnd */ 50 | 51 | Event::listen(\Illuminate\Foundation\Http\Events\RequestHandled::class, function ($event) { 52 | $this->executionData->setRequest(new HttpRequest($event->request)); 53 | $this->executionData->setRoute($this->routeOf($event->request)); 54 | $this->executionData->setSession(new HttpSession(session())); 55 | $this->executionData->setServer(new HttpServer($event->request)); 56 | $this->executionData->setResponse(new HttpResponse($event->response)); 57 | $this->executionData->setContent(new HttpContent($event->response)); 58 | }); 59 | } 60 | 61 | /** 62 | * @param Request $request 63 | * @return ExecutionRoute 64 | */ 65 | protected function routeOf(Request $request): ExecutionRoute 66 | { 67 | return $request->route() ? new HttpRoute($request->route()) : new NullRoute(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/LaravelListeners/PerformanceListener.php: -------------------------------------------------------------------------------- 1 | app = $app; 45 | $this->timer = $timer; 46 | $this->memory = $memory; 47 | } 48 | 49 | /** 50 | * @return void 51 | */ 52 | public function listen(): void 53 | { 54 | $this->listenHttp(); 55 | $this->listenConsole(); 56 | } 57 | 58 | /** 59 | * @return void 60 | */ 61 | protected function listenHttp(): void 62 | { 63 | Event::listen(Tracking::class, function () { 64 | $this->timer->startLaravel(); 65 | }); 66 | 67 | $this->app->booting(function () { 68 | $this->timer->start('boot'); 69 | }); 70 | 71 | $this->app->booted(function () { 72 | $this->timer->finish('boot'); 73 | $this->timer->start($this->resolveRouteName()); 74 | }); 75 | 76 | Event::listen(RouteMatched::class, function () { 77 | $this->timer->finish($this->resolveRouteName()); 78 | $this->timer->start('request'); 79 | }); 80 | 81 | /** @codeCoverageIgnoreStart */ 82 | Event::listen('kernel.handled', function () { 83 | $this->timer->finish('request'); 84 | $this->timer->start('response'); 85 | }); 86 | /** @codeCoverageIgnoreEnd */ 87 | 88 | Event::listen(RequestHandled::class, function () { 89 | $this->timer->finish('request'); 90 | $this->timer->start('response'); 91 | }); 92 | 93 | Event::listen(Terminating::class, function () { 94 | $this->memory->recordPeak(); 95 | $this->timer->finish('response'); 96 | $this->timer->finishLaravel(); 97 | }); 98 | } 99 | 100 | /** 101 | * @return void 102 | */ 103 | protected function listenConsole(): void 104 | { 105 | Event::listen(ArtisanStarting::class, function () { 106 | $this->timer->start('command'); 107 | }); 108 | 109 | Event::listen(Terminating::class, function () { 110 | $this->timer->finish('command'); 111 | }); 112 | } 113 | 114 | /** 115 | * @return string 116 | */ 117 | protected function resolveRouteName(): string 118 | { 119 | if ($this->app->runningUnitTests()) { 120 | return 'setup'; 121 | } 122 | 123 | return 'route'; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/LaravelListeners/QueriesListener.php: -------------------------------------------------------------------------------- 1 | listenQueries(); 32 | $this->listenTransactions(); 33 | $this->listenResetTrackers(); 34 | } 35 | 36 | /** 37 | * @return Collection 38 | */ 39 | public function queries(): Collection 40 | { 41 | return Collection::make($this->queries); 42 | } 43 | 44 | /** 45 | * @return int 46 | */ 47 | public function count(): int 48 | { 49 | return $this->count; 50 | } 51 | 52 | /** 53 | * @return void 54 | */ 55 | protected function listenQueries(): void 56 | { 57 | Event::listen(QueryExecuted::class, function (QueryExecuted $event) { 58 | $this->count++; 59 | 60 | list($bindings, $bindingsQuoted) = $this->formatBindings($event); 61 | 62 | array_push($this->queries, [ 63 | 'query', 64 | $event->sql, 65 | $event->time, 66 | $event->connection->getDatabaseName(), 67 | $event->connectionName, 68 | $bindings, 69 | $bindingsQuoted, 70 | ]); 71 | }); 72 | } 73 | 74 | /** 75 | * @return void 76 | */ 77 | protected function listenTransactions(): void 78 | { 79 | Event::listen(TransactionBeginning::class, function (TransactionBeginning $event) { 80 | array_push($this->queries, [ 81 | 'transaction-begin', 82 | $event->connection->getDatabaseName(), 83 | $event->connectionName, 84 | ]); 85 | }); 86 | 87 | Event::listen(TransactionCommitted::class, function (TransactionCommitted $event) { 88 | array_push($this->queries, [ 89 | 'transaction-commit', 90 | $event->connection->getDatabaseName(), 91 | $event->connectionName, 92 | ]); 93 | }); 94 | 95 | Event::listen(TransactionRolledBack::class, function (TransactionRolledBack $event) { 96 | array_push($this->queries, [ 97 | 'transaction-rollback', 98 | $event->connection->getDatabaseName(), 99 | $event->connectionName, 100 | ]); 101 | }); 102 | } 103 | 104 | /** 105 | * @return void 106 | */ 107 | protected function listenResetTrackers(): void 108 | { 109 | Event::listen(ResetTrackers::class, function () { 110 | $this->queries = []; 111 | $this->count = 0; 112 | }); 113 | } 114 | 115 | /** 116 | * @param QueryExecuted $event 117 | * @return array 118 | */ 119 | protected function formatBindings(QueryExecuted $event): array 120 | { 121 | $preparedBindings = $event->connection->prepareBindings($event->bindings); 122 | 123 | foreach ($preparedBindings as $key => $binding) { 124 | $bindings[$key] = $this->truncate($binding); 125 | $bindingsQuoted[$key] = $this->quote($event, $bindings[$key]); 126 | } 127 | 128 | return [ 129 | $bindings ?? [], 130 | $bindingsQuoted ?? [], 131 | ]; 132 | } 133 | 134 | /** 135 | * @param $binding 136 | * @return mixed 137 | */ 138 | protected function truncate($binding) 139 | { 140 | if (is_string($binding) && strlen($binding) > 255) { 141 | return substr($binding, 0, 255) . '...{truncated}'; 142 | } 143 | 144 | return $binding; 145 | } 146 | 147 | /** 148 | * @param QueryExecuted $event 149 | * @param $binding 150 | * @return mixed 151 | */ 152 | protected function quote(QueryExecuted $event, $binding) 153 | { 154 | if (is_int($binding) || is_float($binding) || is_object($binding)) { 155 | return $binding; 156 | } 157 | 158 | return $event->connection->getPdo()->quote($binding); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/LaravelListeners/RedisListener.php: -------------------------------------------------------------------------------- 1 | listenCommands(); 29 | $this->listenResetTrackers(); 30 | } 31 | 32 | /** 33 | * @return Collection 34 | */ 35 | public function commands(): Collection 36 | { 37 | return Collection::make($this->commands); 38 | } 39 | 40 | /** 41 | * @return int 42 | */ 43 | public function count(): int 44 | { 45 | return $this->count; 46 | } 47 | 48 | /** 49 | * @return void 50 | */ 51 | protected function listenCommands(): void 52 | { 53 | Event::listen(CommandExecuted::class, function (CommandExecuted $event) { 54 | $this->count++; 55 | 56 | array_push($this->commands, [ 57 | $event->command, 58 | $event->time, 59 | $event->connectionName, 60 | $event->parameters, 61 | ]); 62 | }); 63 | } 64 | 65 | /** 66 | * @return void 67 | */ 68 | protected function listenResetTrackers(): void 69 | { 70 | Event::listen(ResetTrackers::class, function () { 71 | $this->commands = []; 72 | $this->count = 0; 73 | }); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/LaravelListeners/ViewsListener.php: -------------------------------------------------------------------------------- 1 | listenViews(); 24 | $this->listenResetTrackers(); 25 | } 26 | 27 | /** 28 | * @return Collection 29 | */ 30 | public function views(): Collection 31 | { 32 | return Collection::make($this->views); 33 | } 34 | 35 | /** 36 | * @return void 37 | */ 38 | protected function listenViews(): void 39 | { 40 | Event::listen('composing:*', function (...$view) { 41 | array_push($this->views, $this->resolveView($view)); 42 | }); 43 | } 44 | 45 | /** 46 | * @return void 47 | */ 48 | protected function listenResetTrackers(): void 49 | { 50 | Event::listen(ResetTrackers::class, function () { 51 | $this->views = []; 52 | }); 53 | } 54 | 55 | /** 56 | * @param array $view 57 | * @return View 58 | */ 59 | protected function resolveView(array $view): View 60 | { 61 | return $view[1][0] ?? $view[0]; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/LaravelProfiler.php: -------------------------------------------------------------------------------- 1 | bind(); 32 | 33 | $this->track(); 34 | 35 | $this->listenForTerminating(); 36 | } 37 | 38 | /** 39 | * @return void 40 | */ 41 | protected function bind(): void 42 | { 43 | $this->app->bind(DataTracker::class, LaravelDataTracker::class); 44 | 45 | $this->app->bind(DataProcessor::class, LaravelDataProcessor::class); 46 | 47 | $this->app->bind(ExecutionWatcher::class, LaravelExecutionWatcher::class); 48 | 49 | $this->app->singleton(ExecutionData::class, function ($app) { 50 | return $app->make(LaravelExecutionData::class); 51 | }); 52 | 53 | $this->app->singleton(Timer::class, function ($app) { 54 | return $app->make(TimerService::class); 55 | }); 56 | 57 | $this->app->singleton(Memory::class, function ($app) { 58 | return $app->make(MemoryService::class); 59 | }); 60 | 61 | event(new ProfilerBound()); 62 | } 63 | 64 | /** 65 | * @return void 66 | */ 67 | protected function track(): void 68 | { 69 | $this->app->make(ExecutionWatcher::class)->watch(); 70 | 71 | $this->dataTracker = $this->app->make(DataTracker::class); 72 | $this->dataTracker->track(); 73 | 74 | event(new Tracking()); 75 | } 76 | 77 | /** 78 | * @return void 79 | */ 80 | protected function listenForTerminating(): void 81 | { 82 | $this->app->afterBootstrapping(BootProviders::class, function () { 83 | $this->registerTerminating(); 84 | }); 85 | } 86 | 87 | /** 88 | * @return void 89 | */ 90 | protected function registerTerminating(): void 91 | { 92 | $this->app->terminating(function () { 93 | event(new Terminating()); 94 | 95 | $this->dataTracker->terminate(); 96 | $this->app->make(DataProcessor::class)->process($this->dataTracker); 97 | }); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/Processors/BroadcastingProcessor.php: -------------------------------------------------------------------------------- 1 | client = $client; 31 | $this->configService = $configService; 32 | } 33 | 34 | /** 35 | * @param DataTracker $dataTracker 36 | * @return void 37 | */ 38 | public function process(DataTracker $dataTracker): void 39 | { 40 | $this->broadcast( 41 | $dataTracker, 42 | $this->configService->serverHttpConnectionUrl() 43 | ); 44 | } 45 | 46 | /** 47 | * @param DataTracker $dataTracker 48 | * @param string $url 49 | * @return Response 50 | * @throws \GuzzleHttp\Exception\GuzzleException 51 | */ 52 | protected function broadcast(DataTracker $dataTracker, string $url): Response 53 | { 54 | return $this->client->request('POST', $url, [ 55 | 'json' => [ 56 | 'meta' => $dataTracker->meta()->toArray(), 57 | 'data' => $dataTracker->data()->toArray(), 58 | ], 59 | ]); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Processors/StatusCommandProcessor.php: -------------------------------------------------------------------------------- 1 | broadcast( 20 | $dataTracker, 21 | $this->configService->serverHttpConnectionUrl() . '/status' 22 | ); 23 | 24 | $body = json_decode($response->getBody()); 25 | 26 | event(new ProfilerServerConnectionSuccessful($body->sockets, $body->clients)); 27 | } catch (ConnectException $e) { 28 | event(new ProfilerServerConnectionFailed()); 29 | throw $e; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/ProfilerResolver.php: -------------------------------------------------------------------------------- 1 | app = $app; 29 | $this->configService = $configService; 30 | } 31 | 32 | /** 33 | * @return Profiler 34 | */ 35 | public function resolve(): Profiler 36 | { 37 | if (! $this->configService->isProfilerEnabled()) { 38 | return $this->app->make(DisabledProfiler::class); 39 | } 40 | 41 | return $this->app->make(LaravelProfiler::class); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | mergeConfigFrom(static::profilerConfigPath(), 'profiler'); 16 | 17 | $this->app->singleton(Profiler::class, function ($app) { 18 | return $app->make(ProfilerResolver::class)->resolve(); 19 | }); 20 | 21 | $this->app->make(Profiler::class)->listenForBoot(); 22 | } 23 | 24 | /** 25 | * @return void 26 | */ 27 | public function boot(): void 28 | { 29 | $this->allowConfigFileToBePublished(); 30 | } 31 | 32 | /** 33 | * @return void 34 | */ 35 | public function allowConfigFileToBePublished(): void 36 | { 37 | $this->publishes([ 38 | static::profilerConfigPath() => config_path('profiler.php'), 39 | ]); 40 | } 41 | 42 | /** 43 | * @return string 44 | */ 45 | public static function profilerConfigPath(): string 46 | { 47 | return __DIR__ . '/../config/profiler.php'; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Services/ConfigService.php: -------------------------------------------------------------------------------- 1 | app = $app; 29 | $this->config = $config; 30 | } 31 | 32 | /** 33 | * @return bool 34 | */ 35 | public function isProfilerEnabled(): bool 36 | { 37 | $enabledOverrides = Collection::make($this->config->get('profiler.enabled_overrides')); 38 | $envToDisable = $enabledOverrides->filter(function ($enabled) { 39 | return ! $enabled; 40 | })->keys(); 41 | 42 | if ($this->app->environment($envToDisable->toArray())) { 43 | return false; 44 | } 45 | 46 | return $this->config->get('profiler.enabled') === true; 47 | } 48 | 49 | /** 50 | * @return Collection 51 | */ 52 | public function trackers(): Collection 53 | { 54 | return Collection::make($this->config->get('profiler.trackers')); 55 | } 56 | 57 | /** 58 | * @return Collection 59 | */ 60 | public function processors(): Collection 61 | { 62 | return Collection::make($this->config->get('profiler.processors')); 63 | } 64 | 65 | /** 66 | * @param array $processors 67 | */ 68 | public function overrideProcessors(array $processors): void 69 | { 70 | $this->config->set('profiler.processors', $processors); 71 | } 72 | 73 | /** 74 | * @return Collection 75 | */ 76 | public function pathsToTurnOffProcessors(): Collection 77 | { 78 | return Collection::make($this->config->get('profiler.turn_off_processors_for_paths')); 79 | } 80 | 81 | /** 82 | * @return string 83 | */ 84 | public function serverHttpConnectionUrl(): string 85 | { 86 | $address = $this->config->get('profiler.server_http.address'); 87 | $port = $this->config->get('profiler.server_http.port'); 88 | 89 | return $address . ':' . $port; 90 | } 91 | 92 | /** 93 | * @return string 94 | */ 95 | public function serverHttpPort(): string 96 | { 97 | return $this->config->get('profiler.server_http.port'); 98 | } 99 | 100 | /** 101 | * @return string 102 | */ 103 | public function serverSocketsPort(): string 104 | { 105 | return $this->config->get('profiler.server_sockets.port'); 106 | } 107 | 108 | /** 109 | * @return bool 110 | */ 111 | public function isViewsDataEnabled(): bool 112 | { 113 | return $this->config->get('profiler.data.views'); 114 | } 115 | 116 | /** 117 | * @return bool 118 | */ 119 | public function isEventsDataEnabled(): bool 120 | { 121 | return $this->config->get('profiler.data.events'); 122 | } 123 | 124 | /** 125 | * @return bool 126 | */ 127 | public function isEventsGroupEnabled(): bool 128 | { 129 | return $this->config->get('profiler.group.events'); 130 | } 131 | 132 | /** 133 | * @param int $level 134 | * @return bool 135 | */ 136 | public function handleExceptions(int $level): bool 137 | { 138 | return $this->config->get('profiler.handle_exceptions') === $level; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/Services/ConsoleService.php: -------------------------------------------------------------------------------- 1 | app = $app; 27 | $this->configService = $configService; 28 | } 29 | 30 | /** 31 | * @return string 32 | */ 33 | public function envInfo(): string 34 | { 35 | return "Your current environment is: {$this->app->environment()}"; 36 | } 37 | 38 | /** 39 | * @return string 40 | */ 41 | public function profilerStatusInfo(): string 42 | { 43 | $status = $this->configService->isProfilerEnabled() ? 'enabled' : 'disabled'; 44 | 45 | return "Laravel Profiler is: {$status}"; 46 | } 47 | 48 | /** 49 | * @return string 50 | */ 51 | public function trackersStatusInfo(): string 52 | { 53 | return "You have {$this->configService->trackers()->count()} tracker(s) turned on"; 54 | } 55 | 56 | /** 57 | * @return string 58 | */ 59 | public function trackersCommentLine1(): string 60 | { 61 | return 'There are 14 trackers available out of the box'; 62 | } 63 | 64 | /** 65 | * @return string 66 | */ 67 | public function trackersCommentLine2(): string 68 | { 69 | return 'turn them on and off in profiler.php configuration file'; 70 | } 71 | 72 | /** 73 | * @return string 74 | */ 75 | public function connectionStatusInfo(): string 76 | { 77 | return "Trying to connect to Profiler Server on {$this->configService->serverHttpConnectionUrl()} ..."; 78 | } 79 | 80 | /** 81 | * @return string 82 | */ 83 | public function connectionSuccessfulInfo(): string 84 | { 85 | return 'Connected successfully'; 86 | } 87 | 88 | /** 89 | * @param int $socketsPort 90 | * @return string 91 | */ 92 | public function connectionSuccessfulSocketsInfo(int $socketsPort): string 93 | { 94 | return "Profiler Server sockets listening on port: {$socketsPort}"; 95 | } 96 | 97 | /** 98 | * @param int $countClients 99 | * @return string 100 | */ 101 | public function connectionSuccessfulClientsInfo(int $countClients): string 102 | { 103 | return "You have {$countClients} Profiler Client(s) connected at the moment"; 104 | } 105 | 106 | /** 107 | * @return string 108 | */ 109 | public function connectionFailedInfo(): string 110 | { 111 | return 'Connection failed'; 112 | } 113 | 114 | /** 115 | * @return string 116 | */ 117 | public function connectionStatusUnknownInfo(): string 118 | { 119 | return 'BroadcastingProcessor did not report connection status, connection status is unknown'; 120 | } 121 | 122 | /** 123 | * @return string 124 | */ 125 | public function profilerServerCmd(): string 126 | { 127 | $http = $this->configService->serverHttpPort(); 128 | $ws = $this->configService->serverSocketsPort(); 129 | 130 | return "node node_modules/laravel-profiler-client/server/server.js http={$http} ws={$ws}"; 131 | } 132 | 133 | /** 134 | * @param bool $manual 135 | * @return string 136 | */ 137 | public function profilerClientCmd(bool $manual): string 138 | { 139 | $options = $manual ? '' : ' -o -s'; 140 | 141 | return "node_modules/.bin/http-server node_modules/laravel-profiler-client/dist/{$options}"; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/Services/GeneratorService.php: -------------------------------------------------------------------------------- 1 | configService = $configService; 25 | } 26 | 27 | /** 28 | * @param Exception $e 29 | * @throws Exception 30 | * @return void 31 | */ 32 | public function error(Exception $e): void 33 | { 34 | if ($this->configService->handleExceptions(self::HANDLE_EXCEPTIONS_THROW)) { 35 | throw $e; 36 | } 37 | 38 | if ($this->configService->handleExceptions(self::HANDLE_EXCEPTIONS_LOG)) { 39 | Log::error($e); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Services/ParamsService.php: -------------------------------------------------------------------------------- 1 | resolveObject($param); 17 | } 18 | 19 | if (is_array($param)) { 20 | return array_map(function ($item) { 21 | return $this->resolve($item); 22 | }, $param); 23 | } 24 | 25 | return $param; 26 | } 27 | 28 | /** 29 | * @param array $params 30 | * @return array 31 | */ 32 | public function resolveFlattenFromArray(array $params): array 33 | { 34 | return array_map(function ($param) { 35 | return $this->resolveFlatten($param); 36 | }, $params); 37 | } 38 | 39 | /** 40 | * @param $param 41 | * @return string 42 | */ 43 | protected function resolveFlatten($param): string 44 | { 45 | if ($param instanceof Collection) { 46 | return get_class($param) . ': ' . $param->count() . ' item(s)'; 47 | } 48 | 49 | if (is_object($param)) { 50 | return get_class($param); 51 | } 52 | 53 | if (is_array($param)) { 54 | return 'array: ' . count($param) . ' item(s)'; 55 | } 56 | 57 | return gettype($param); 58 | } 59 | 60 | /** 61 | * @param $param 62 | * @return array|string 63 | */ 64 | protected function resolveObject($param) 65 | { 66 | if (method_exists($param, 'toArray')) { 67 | return $this->resolve($param->toArray()); 68 | } 69 | 70 | return get_class($param); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Services/Performance/MemoryService.php: -------------------------------------------------------------------------------- 1 | app = $app; 28 | $this->memory = Collection::make(); 29 | } 30 | 31 | /** 32 | * @return void 33 | */ 34 | public function recordPeak(): void 35 | { 36 | $this->memory->put('peak', memory_get_peak_usage()); 37 | } 38 | 39 | /** 40 | * @return Collection 41 | */ 42 | public function all(): Collection 43 | { 44 | return $this->memory; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Services/Performance/NullTimerService.php: -------------------------------------------------------------------------------- 1 | make(Timer::class)->startCustom($name); 13 | } catch (TimerException $e) { 14 | app()->make(LogService::class)->error($e); 15 | } 16 | } 17 | } 18 | 19 | if (! function_exists('profiler_finish')) { 20 | function profiler_finish(string $name): void 21 | { 22 | try { 23 | app()->make(Timer::class)->finishCustom($name); 24 | } catch (TimerException $e) { 25 | app()->make(LogService::class)->error($e); 26 | } 27 | } 28 | } 29 | 30 | if (! function_exists('profiler_reset')) { 31 | function profiler_reset(): void 32 | { 33 | app()->make(Profiler::class)->resetTrackers(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Trackers/ApplicationTracker.php: -------------------------------------------------------------------------------- 1 | generatorService = $generatorService; 26 | } 27 | 28 | /** 29 | * @return void 30 | */ 31 | public function terminate(): void 32 | { 33 | $this->meta->put('execution_at', time()); 34 | $this->meta->put('id', $this->generatorService->unique32CharsId()); 35 | $this->meta->put('laravel_version', $this->app->version()); 36 | $this->meta->put('php_version', phpversion()); 37 | $this->meta->put('env', $this->app->environment()); 38 | $this->meta->put('is_running_in_console', $this->app->runningInConsole()); 39 | 40 | $this->data->put('application', Collection::make([ 41 | 'locale' => $this->app->getLocale(), 42 | 'configuration_is_cached' => $this->app->configurationIsCached(), 43 | 'routes_are_cached' => $this->app->routesAreCached(), 44 | 'is_down_for_maintenance' => $this->app->isDownForMaintenance(), 45 | 'should_skip_middleware' => $this->app->shouldSkipMiddleware(), 46 | ])); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Trackers/AuthTracker.php: -------------------------------------------------------------------------------- 1 | authListener = $authListener; 25 | $this->authListener->listen(); 26 | } 27 | 28 | /** 29 | * @return void 30 | */ 31 | public function terminate(): void 32 | { 33 | $this->data->put('auth', $this->authListener->user()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Trackers/BaseTracker.php: -------------------------------------------------------------------------------- 1 | app = $app; 33 | $this->meta = new Collection(); 34 | $this->data = new Collection(); 35 | } 36 | 37 | /** 38 | * @return Collection 39 | */ 40 | public function meta(): Collection 41 | { 42 | return $this->meta; 43 | } 44 | 45 | /** 46 | * @return Collection 47 | */ 48 | public function data(): Collection 49 | { 50 | return $this->data; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Trackers/BindingsTracker.php: -------------------------------------------------------------------------------- 1 | abstracts()->map(function ($abstract) { 16 | try { 17 | $resolved = $this->resolved($abstract); 18 | } catch (BindingResolutionException $e) {} 19 | 20 | return [ 21 | 'abstract' => $abstract, 22 | 'resolved' => $resolved ?? null, 23 | ]; 24 | }); 25 | 26 | $this->data->put('bindings', $bindings); 27 | } 28 | 29 | /** 30 | * @return Collection 31 | */ 32 | protected function abstracts(): Collection 33 | { 34 | return Collection::make( 35 | array_keys($this->app->getBindings()) 36 | ); 37 | } 38 | 39 | /** 40 | * @param string $abstract 41 | * @return string 42 | * @throws BindingResolutionException 43 | */ 44 | protected function resolved(string $abstract): string 45 | { 46 | if (! $this->app->resolved($abstract)) { 47 | throw new BindingResolutionException(); 48 | } 49 | 50 | $concrete = $this->app->make($abstract); 51 | 52 | if (is_object($concrete)) { 53 | return get_class($concrete); 54 | } 55 | 56 | return gettype($concrete); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Trackers/ConfigTracker.php: -------------------------------------------------------------------------------- 1 | data->put('config', $this->config()); 15 | } 16 | 17 | /** 18 | * @return Collection 19 | */ 20 | protected function config(): Collection 21 | { 22 | return Collection::make( 23 | $this->hideSecretValues( 24 | $this->app->make('config')->all() 25 | ) 26 | ); 27 | } 28 | 29 | /** 30 | * @param array $config 31 | * @return array 32 | */ 33 | protected function hideSecretValues(array $config): array 34 | { 35 | $keys = array_keys($config); 36 | 37 | return array_map(function ($value) use (&$keys) { 38 | $key = array_shift($keys); 39 | 40 | if (is_array($value)) { 41 | return $this->hideSecretValues($value); 42 | } 43 | 44 | if (is_string($value) && preg_match('/^(password|key|secret)$/i', $key)) { 45 | $value = str_repeat('*', strlen($value)); 46 | } 47 | 48 | return $value; 49 | }, $config); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Trackers/ContentTracker.php: -------------------------------------------------------------------------------- 1 | executionData = $executionData; 25 | } 26 | 27 | /** 28 | * @return void 29 | */ 30 | public function terminate(): void 31 | { 32 | $content = $this->executionData->content(); 33 | 34 | $this->meta = $content->meta(); 35 | $this->data->put('content', $content->data()->get('content')); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Trackers/EventsTracker.php: -------------------------------------------------------------------------------- 1 | paramsService = $paramsService; 44 | $this->configService = $configService; 45 | $this->eventsListener = $eventsListener; 46 | $this->eventsListener->listen(); 47 | } 48 | 49 | /** 50 | * @return void 51 | */ 52 | public function terminate(): void 53 | { 54 | $events = $this->eventsListener->events()->map(function ($item) { 55 | list($event, $payload, $name, $count) = $item; 56 | 57 | if ($this->shouldTrackData($count)) { 58 | return [ 59 | 'name' => $name, 60 | 'count' => $count, 61 | 'data' => $this->resolveData($event, $payload), 62 | ]; 63 | } 64 | 65 | return [ 66 | 'name' => $name, 67 | 'count' => $count, 68 | ]; 69 | }); 70 | 71 | $this->meta->put('events_count', $this->eventsListener->count()); 72 | $this->data->put('events', $events); 73 | } 74 | 75 | /** 76 | * @param int $count 77 | * @return bool 78 | */ 79 | protected function shouldTrackData(int $count): bool 80 | { 81 | return $this->configService->isEventsDataEnabled() && $count == 1; 82 | } 83 | 84 | /** 85 | * @param $event 86 | * @param $payload 87 | * @return Collection 88 | */ 89 | protected function resolveData($event, $payload): Collection 90 | { 91 | $event = is_array($payload) ? $payload[0] : $event; 92 | 93 | $class = new \ReflectionClass($event); 94 | 95 | $publicProps = Collection::make( 96 | $class->getProperties(\ReflectionProperty::IS_PUBLIC) 97 | ); 98 | 99 | if (method_exists($publicProps, 'mapWithKeys')) { 100 | return $this->propsMapWithKeys($publicProps, $event); 101 | } 102 | 103 | return $this->propsMap($publicProps, $event); // @codeCoverageIgnore 104 | } 105 | 106 | /** 107 | * @param Collection $publicProps 108 | * @param $event 109 | * @return Collection 110 | */ 111 | protected function propsMapWithKeys(Collection $publicProps, $event): Collection 112 | { 113 | return $publicProps->mapWithKeys(function ($prop) use ($event) { 114 | return [ 115 | $prop->name => $this->paramsService->resolve($event->{$prop->name}), 116 | ]; 117 | }); 118 | } 119 | 120 | /** 121 | * @codeCoverageIgnore 122 | * 123 | * @param Collection $publicProps 124 | * @param $event 125 | * @return Collection 126 | */ 127 | protected function propsMap(Collection $publicProps, $event): Collection 128 | { 129 | return $publicProps->map(function ($prop) use ($event) { 130 | return [ 131 | $prop->name => $this->paramsService->resolve($event->{$prop->name}), 132 | ]; 133 | })->flatten(1); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/Trackers/ExceptionTracker.php: -------------------------------------------------------------------------------- 1 | exceptionListener = $exceptionListener; 29 | $this->exceptionListener->listen(); 30 | 31 | $this->bindExceptionHandler($app); 32 | } 33 | 34 | /** 35 | * @param Application $app 36 | * @return void 37 | */ 38 | protected function bindExceptionHandler(Application $app): void 39 | { 40 | $version = (int) $app->version(); 41 | 42 | $handler = $version < 7 43 | ? ExceptionHandlerTillVersion6::class 44 | : ExceptionHandlerFromVersion7::class; // @codeCoverageIgnore 45 | 46 | $app->singleton(\App\Exceptions\Handler::class, $handler); 47 | } 48 | 49 | /** 50 | * @return void 51 | */ 52 | public function terminate(): void 53 | { 54 | $exception = $this->exceptionListener->exception() ? $this->exception() : null; 55 | 56 | $this->data->put('exception', $exception); 57 | } 58 | 59 | /** 60 | * @return Collection 61 | */ 62 | protected function exception(): Collection 63 | { 64 | $exception = $this->exceptionListener->exception(); 65 | 66 | return Collection::make([ 67 | 'message' => $exception->getMessage(), 68 | 'exception' => get_class($exception), 69 | 'file' => $exception->getFile(), 70 | 'line' => $exception->getLine(), 71 | 'trace' => Collection::make($exception->getTrace())->map(function ($trace) { 72 | return Arr::except($trace, ['args', 'type']); 73 | }), 74 | ]); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Trackers/PathsTracker.php: -------------------------------------------------------------------------------- 1 | paths()->map(function ($path, $name) { 15 | return [ 16 | 'name' => $name, 17 | 'path' => $path, 18 | ]; 19 | })->values(); 20 | 21 | $this->data->put('paths', $paths); 22 | } 23 | 24 | /** 25 | * @return Collection 26 | */ 27 | protected function paths(): Collection 28 | { 29 | return Collection::make([ 30 | 'app_path' => $this->app->path(), 31 | 'base_path' => $this->app->basePath(), 32 | 'lang_path' => $this->app->langPath(), 33 | 'config_path' => $this->app->configPath(), 34 | 'public_path' => $this->app->publicPath(), 35 | 'storage_path' => $this->app->storagePath(), 36 | 'resource_path' => $this->resourcePath(), 37 | 'database_path' => $this->app->databasePath(), 38 | 'bootstrap_path' => $this->app->bootstrapPath(), 39 | 'cached_config_path' => $this->app->getCachedConfigPath(), 40 | 'cached_routes_path' => $this->app->getCachedRoutesPath(), 41 | 'cached_services_path' => $this->app->getCachedServicesPath(), 42 | 'cached_packages_path' => $this->getCachedPackagesPath(), 43 | 'environment_file_path' => $this->app->environmentFilePath(), 44 | ])->filter(function ($item) { 45 | return !! $item; 46 | }); 47 | } 48 | 49 | /** 50 | * @return string 51 | */ 52 | protected function resourcePath(): string 53 | { 54 | return method_exists($this->app, 'resourcePath') 55 | ? $this->app->resourcePath() 56 | : ''; 57 | } 58 | 59 | /** 60 | * @return string 61 | */ 62 | protected function getCachedPackagesPath(): string 63 | { 64 | return method_exists($this->app, 'getCachedPackagesPath') 65 | ? $this->app->getCachedPackagesPath() 66 | : ''; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Trackers/PerformanceTracker.php: -------------------------------------------------------------------------------- 1 | timer = $timer; 39 | $this->memory = $memory; 40 | $performanceListener->listen(); 41 | } 42 | 43 | /** 44 | * @return void 45 | */ 46 | public function terminate(): void 47 | { 48 | $this->data->put('performance', Collection::make([ 49 | 'timer' => $this->timer->all(), 50 | 'memory' => $this->memory->all(), 51 | ])); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Trackers/QueriesTracker.php: -------------------------------------------------------------------------------- 1 | queriesListener = $queriesListener; 25 | $this->queriesListener->listen(); 26 | } 27 | 28 | /** 29 | * @return void 30 | */ 31 | public function terminate(): void 32 | { 33 | $queries = $this->queriesListener->queries()->map(function ($item) { 34 | if ($this->isTransactionType($item[0])) { 35 | return $this->terminateTransaction($item); 36 | } 37 | 38 | return $this->terminateQuery($item); 39 | }); 40 | 41 | $this->meta->put('queries_count', $this->queriesListener->count()); 42 | $this->data->put('queries', $queries); 43 | } 44 | 45 | /** 46 | * @param array $item 47 | * @return array 48 | */ 49 | protected function terminateTransaction(array $item): array 50 | { 51 | list($type, $database, $name) = $item; 52 | 53 | return [ 54 | 'type' => $type, 55 | 'database' => $database, 56 | 'name' => $name, 57 | ]; 58 | } 59 | 60 | /** 61 | * @param array $item 62 | * @return array 63 | */ 64 | protected function terminateQuery(array $item): array 65 | { 66 | list($type, $sql, $time, $database, $name, $bindings, $bindingsQuoted) = $item; 67 | 68 | $formattedSql = $this->formatSql($sql); 69 | 70 | return [ 71 | 'type' => $type, 72 | 'sql' => $formattedSql, 73 | 'bindings' => $bindings, 74 | 'time' => $time, 75 | 'database' => $database, 76 | 'name' => $name, 77 | 'query' => $this->queryWithBindings($bindingsQuoted, $formattedSql), 78 | ]; 79 | } 80 | 81 | /** 82 | * @param string $type 83 | * @return bool 84 | */ 85 | protected function isTransactionType(string $type): bool 86 | { 87 | $transactionTypes = [ 88 | 'transaction-begin', 89 | 'transaction-commit', 90 | 'transaction-rollback', 91 | ]; 92 | 93 | return in_array($type, $transactionTypes); 94 | } 95 | 96 | /** 97 | * @param string $sql 98 | * @return string 99 | */ 100 | protected function formatSql(string $sql): string 101 | { 102 | return preg_replace('/"/', '`', $sql); 103 | } 104 | 105 | /** 106 | * @param array $bindingsQuoted 107 | * @param string $formattedSql 108 | * @return string 109 | */ 110 | protected function queryWithBindings(array $bindingsQuoted, string $formattedSql): string 111 | { 112 | foreach ($bindingsQuoted as $key => $binding) { 113 | $formattedSql = preg_replace($this->bindingRegex($key), $binding, $formattedSql, 1); 114 | } 115 | 116 | return $formattedSql; 117 | } 118 | 119 | /** 120 | * @param $key 121 | * @return string 122 | */ 123 | protected function bindingRegex($key): string 124 | { 125 | return is_int($key) ? "/\?/" : "/:{$key}/"; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/Trackers/RedisTracker.php: -------------------------------------------------------------------------------- 1 | enableRedisEvents(); 30 | 31 | $this->redisListener = $redisListener; 32 | $this->redisListener->listen(); 33 | } 34 | 35 | /** 36 | * @return void 37 | */ 38 | public function terminate(): void 39 | { 40 | $commands = $this->redisListener->commands()->map(function ($item) { 41 | return $this->terminateCommands($item); 42 | }); 43 | 44 | $this->meta->put('redis_count', $this->redisListener->count()); 45 | $this->meta->put('redis_can_be_tracked', $this->redisCanBeTracked); 46 | $this->data->put('redis', $commands); 47 | } 48 | 49 | /** 50 | * @param array $item 51 | * @return array 52 | */ 53 | protected function terminateCommands(array $item): array 54 | { 55 | list($command, $time, $name, $parameters) = $item; 56 | 57 | return [ 58 | 'command' => $command, 59 | 'time' => $time, 60 | 'name' => $name, 61 | 'parameters' => $parameters, 62 | ]; 63 | } 64 | 65 | /** 66 | * @return void 67 | */ 68 | protected function enableRedisEvents(): void 69 | { 70 | $manager = $this->app->make('redis'); 71 | 72 | if (! method_exists($manager, 'enableEvents')) { 73 | return; // @codeCoverageIgnore 74 | } 75 | 76 | $this->redisCanBeTracked = true; 77 | $manager->enableEvents(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Trackers/RequestTracker.php: -------------------------------------------------------------------------------- 1 | executionData = $executionData; 25 | } 26 | 27 | /** 28 | * @return void 29 | */ 30 | public function terminate(): void 31 | { 32 | $request = $this->executionData->request(); 33 | 34 | $this->meta = $request->meta(); 35 | $this->data->put('request', $request->data()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Trackers/ResponseTracker.php: -------------------------------------------------------------------------------- 1 | executionData = $executionData; 25 | } 26 | 27 | /** 28 | * @return void 29 | */ 30 | public function terminate(): void 31 | { 32 | $response = $this->executionData->response(); 33 | 34 | $this->meta = $response->meta(); 35 | $this->data->put('response', $response->data()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Trackers/RouteTracker.php: -------------------------------------------------------------------------------- 1 | executionData = $executionData; 25 | } 26 | 27 | /** 28 | * @return void 29 | */ 30 | public function terminate(): void 31 | { 32 | $route = $this->executionData->route(); 33 | 34 | $this->meta = $route->meta(); 35 | $this->data->put('route', $route->data()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Trackers/ServerTracker.php: -------------------------------------------------------------------------------- 1 | executionData = $executionData; 25 | } 26 | 27 | /** 28 | * @return void 29 | */ 30 | public function terminate(): void 31 | { 32 | $server = $this->executionData->server(); 33 | 34 | $this->meta = $server->meta(); 35 | $this->data->put('server', $server->data()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Trackers/ServiceProvidersTracker.php: -------------------------------------------------------------------------------- 1 | data->put('service_providers', $this->loadedProviders()); 15 | } 16 | 17 | /** 18 | * @return Collection 19 | */ 20 | protected function loadedProviders(): Collection 21 | { 22 | return Collection::make( 23 | array_keys($this->app->getLoadedProviders()) 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Trackers/SessionTracker.php: -------------------------------------------------------------------------------- 1 | executionData = $executionData; 25 | } 26 | 27 | /** 28 | * @return void 29 | */ 30 | public function terminate(): void 31 | { 32 | $session = $this->executionData->session(); 33 | 34 | $this->meta = $session->meta(); 35 | $this->data->put('session', $session->data()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Trackers/ViewsTracker.php: -------------------------------------------------------------------------------- 1 | configService = $configService; 44 | $this->paramsService = $paramsService; 45 | $this->viewsListener = $viewsListener; 46 | $this->viewsListener->listen(); 47 | } 48 | 49 | /** 50 | * @return void 51 | */ 52 | public function terminate(): void 53 | { 54 | $views = $this->viewsListener->views()->map(function (View $view) { 55 | if ($this->configService->isViewsDataEnabled()) { 56 | return [ 57 | 'name' => $view->name(), 58 | 'path' => $view->getPath(), 59 | 'data' => $view->getData(), 60 | ]; 61 | } 62 | 63 | return [ 64 | 'name' => $view->name(), 65 | 'path' => $view->getPath(), 66 | 'params' => $this->paramsService->resolveFlattenFromArray($view->getData()), 67 | ]; 68 | })->values(); 69 | 70 | $this->data->put('views', $views); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tests/Feature/LaravelExecutionTest.php: -------------------------------------------------------------------------------- 1 | turnOffProcessors(); 18 | } 19 | 20 | /** @test */ 21 | function laravel_execution_data_is_singleton() 22 | { 23 | $executionDataA = $this->app->make(ExecutionData::class); 24 | $executionDataB = $this->app->make(ExecutionData::class); 25 | 26 | $this->assertSame($executionDataA, $executionDataB); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/Feature/LaravelNullExecutionTest.php: -------------------------------------------------------------------------------- 1 | turnOffProcessors(); 29 | 30 | $this->executionData = $this->app->make(ExecutionData::class); 31 | } 32 | 33 | 34 | /** @test */ 35 | function has_null_request() 36 | { 37 | $request = $this->executionData->request(); 38 | 39 | $this->assertInstanceOf(NullRequest::class, $request); 40 | } 41 | 42 | /** @test */ 43 | function null_request_has_only_type() 44 | { 45 | $request = $this->executionData->request(); 46 | 47 | $this->assertTrue($request->meta()->has('type')); 48 | $this->assertNull($request->meta()->get('type')); 49 | $this->assertCount(1, $request->meta()); 50 | $this->assertCount(0, $request->data()); 51 | } 52 | 53 | /** @test */ 54 | function has_null_route() 55 | { 56 | $route = $this->executionData->route(); 57 | 58 | $this->assertInstanceOf(NullRoute::class, $route); 59 | } 60 | 61 | /** @test */ 62 | function null_route_is_empty() 63 | { 64 | $route = $this->executionData->route(); 65 | 66 | $this->assertCount(0, $route->meta()); 67 | $this->assertCount(0, $route->data()); 68 | } 69 | 70 | /** @test */ 71 | function has_null_session() 72 | { 73 | $session = $this->executionData->session(); 74 | 75 | $this->assertInstanceOf(NullSession::class, $session); 76 | } 77 | 78 | /** @test */ 79 | function null_session_is_empty() 80 | { 81 | $session = $this->executionData->session(); 82 | 83 | $this->assertCount(0, $session->meta()); 84 | $this->assertCount(0, $session->data()); 85 | } 86 | 87 | /** @test */ 88 | function has_null_server() 89 | { 90 | $server = $this->executionData->server(); 91 | 92 | $this->assertInstanceOf(NullServer::class, $server); 93 | } 94 | 95 | /** @test */ 96 | function null_server_is_empty() 97 | { 98 | $server = $this->executionData->server(); 99 | 100 | $this->assertCount(0, $server->meta()); 101 | $this->assertCount(0, $server->data()); 102 | } 103 | 104 | /** @test */ 105 | function has_null_response() 106 | { 107 | $response = $this->executionData->response(); 108 | 109 | $this->assertInstanceOf(NullResponse::class, $response); 110 | } 111 | 112 | /** @test */ 113 | function null_response_is_empty() 114 | { 115 | $response = $this->executionData->response(); 116 | 117 | $this->assertCount(0, $response->meta()); 118 | $this->assertCount(0, $response->data()); 119 | } 120 | 121 | /** @test */ 122 | function has_null_content() 123 | { 124 | $content = $this->executionData->content(); 125 | 126 | $this->assertInstanceOf(NullContent::class, $content); 127 | } 128 | 129 | /** @test */ 130 | function null_content_is_empty() 131 | { 132 | $content = $this->executionData->content(); 133 | 134 | $this->assertCount(0, $content->meta()); 135 | $this->assertCount(0, $content->data()); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /tests/Feature/PerformanceTest.php: -------------------------------------------------------------------------------- 1 | app->make(Timer::class); 17 | $timerB = $this->app->make(Timer::class); 18 | 19 | $this->assertInstanceOf(TimerService::class, $timerA); 20 | $this->assertSame($timerA, $timerB); 21 | } 22 | 23 | /** @test */ 24 | function disabled_profiler_has_singleton_null_timer() 25 | { 26 | putenv('PROFILER_ENABLED=false'); 27 | $_ENV['PROFILER_ENABLED'] = false; 28 | $this->app = $this->app(); 29 | $timerA = $this->app->make(Timer::class); 30 | $timerB = $this->app->make(Timer::class); 31 | 32 | $this->assertInstanceOf(NullTimerService::class, $timerA); 33 | $this->assertSame($timerA, $timerB); 34 | } 35 | 36 | /** @test */ 37 | function memory_is_singleton() 38 | { 39 | $memoryA = $this->app->make(Memory::class); 40 | $memoryB = $this->app->make(Memory::class); 41 | 42 | $this->assertInstanceOf(Memory::class, $memoryA); 43 | $this->assertSame($memoryA, $memoryB); 44 | } 45 | 46 | /** 47 | * @return void 48 | */ 49 | protected function tearDown(): void 50 | { 51 | parent::tearDown(); 52 | 53 | putenv('PROFILER_ENABLED'); 54 | unset($_ENV['PROFILER_ENABLED']); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/Feature/PerformanceTrackerTest.php: -------------------------------------------------------------------------------- 1 | addFixturePerformanceProcessor(); 24 | } 25 | 26 | /** 27 | * @return void 28 | */ 29 | protected function addFixturePerformanceProcessor(): void 30 | { 31 | $this->app = $this->appWith(function (Application $app) { 32 | $app->make('config')->set('profiler.processors', [ 33 | PerformanceProcessor::class, 34 | ]); 35 | $app->singleton(PerformanceProcessor::class, function () { 36 | return new PerformanceProcessor(); 37 | }); 38 | }); 39 | } 40 | 41 | /** @test */ 42 | function has_laravel_total_execution_time() 43 | { 44 | $this->app->terminate(); 45 | $timer = $this->app->make(Timer::class); 46 | $processor = $this->app->make(PerformanceProcessor::class); 47 | 48 | $this->assertTrue($processor->performance->has('timer')); 49 | $this->assertGreaterThan(0, $timer->milliseconds('laravel')); 50 | $this->assertEquals($timer->milliseconds('laravel'), $processor->performance->get('timer')['laravel']); 51 | } 52 | 53 | /** @test */ 54 | function has_booting_time() 55 | { 56 | $this->app->terminate(); 57 | $timer = $this->app->make(Timer::class); 58 | $processor = $this->app->make(PerformanceProcessor::class); 59 | 60 | $this->assertGreaterThan(0, $timer->milliseconds('boot')); 61 | $this->assertEquals($timer->milliseconds('boot'), $processor->performance->get('timer')['boot']); 62 | } 63 | 64 | /** @test */ 65 | function has_route_time() 66 | { 67 | $this->get('/'); 68 | $timer = $this->app->make(Timer::class); 69 | $processor = $this->app->make(PerformanceProcessor::class); 70 | 71 | $this->assertGreaterThan(0, $timer->milliseconds('route')); 72 | $this->assertEquals($timer->milliseconds('route'), $processor->performance->get('timer')['route']); 73 | } 74 | 75 | /** @test */ 76 | function has_setup_time_instead_of_route_when_testing() 77 | { 78 | $this->tapLaravelVersionTill(6, function () { 79 | putenv('APP_ENV=testing'); 80 | $_ENV['APP_ENV'] = 'testing'; 81 | $this->app = $this->app(); 82 | $this->addFixturePerformanceProcessor(); 83 | 84 | $this->get('/'); 85 | $timer = $this->app->make(Timer::class); 86 | $processor = $this->app->make(PerformanceProcessor::class); 87 | 88 | $this->assertGreaterThan(0, $timer->milliseconds('setup')); 89 | $this->assertEquals($timer->milliseconds('setup'), $processor->performance->get('timer')['setup']); 90 | }); 91 | 92 | $this->tapLaravelVersionFrom(7, function () { 93 | $this->assertTrue(true); 94 | }); 95 | } 96 | 97 | /** @test */ 98 | function has_handle_request_time() 99 | { 100 | $this->get('/'); 101 | $timer = $this->app->make(Timer::class); 102 | $processor = $this->app->make(PerformanceProcessor::class); 103 | 104 | $this->assertGreaterThan(0, $timer->milliseconds('request')); 105 | $this->assertEquals($timer->milliseconds('request'), $processor->performance->get('timer')['request']); 106 | } 107 | 108 | /** @test */ 109 | function has_send_response_and_terminate_time() 110 | { 111 | $this->get('/'); 112 | $timer = $this->app->make(Timer::class); 113 | $processor = $this->app->make(PerformanceProcessor::class); 114 | 115 | $this->assertGreaterThan(0, $timer->milliseconds('response')); 116 | $this->assertEquals($timer->milliseconds('response'), $processor->performance->get('timer')['response']); 117 | } 118 | 119 | /** @test */ 120 | function has_console_time() 121 | { 122 | $this->app->make(Kernel::class)->registerCommand(new DummyCommand(0)); 123 | Artisan::call('dummy-command'); 124 | $this->app->terminate(); 125 | $timer = $this->app->make(Timer::class); 126 | $processor = $this->app->make(PerformanceProcessor::class); 127 | 128 | $this->assertGreaterThan(0, $timer->milliseconds('command')); 129 | $this->assertEquals($timer->milliseconds('command'), $processor->performance->get('timer')['command']); 130 | } 131 | 132 | /** @test */ 133 | function has_memory_peak() 134 | { 135 | $this->app->terminate(); 136 | $processor = $this->app->make(PerformanceProcessor::class); 137 | 138 | $this->assertEquals(PHPMock::MEMORY_USAGE, $processor->performance->get('memory')['peak']); 139 | } 140 | 141 | /** 142 | * @return void 143 | */ 144 | protected function tearDown(): void 145 | { 146 | parent::tearDown(); 147 | 148 | putenv('APP_ENV=local'); 149 | unset($_ENV['APP_ENV']); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /tests/Feature/TrackersResetTest.php: -------------------------------------------------------------------------------- 1 | assertFalse($fired); 22 | 23 | profiler_reset(); 24 | 25 | $this->assertTrue($fired); 26 | } 27 | 28 | /** @test */ 29 | function profiler_reset_function_can_be_executed_even_profiler_is_disabled() 30 | { 31 | $this->app = $this->appWith(function (Application $app) { 32 | $app->make('config')->set('profiler.enabled', false); 33 | }); 34 | 35 | $fired = false; 36 | 37 | Event::listen(ResetTrackers::class, function () use (&$fired) { 38 | $fired = true; 39 | }); 40 | 41 | $this->assertFalse($fired); 42 | 43 | profiler_reset(); 44 | 45 | $this->assertTrue($fired); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/Support/Fixtures/DummyClassA.php: -------------------------------------------------------------------------------- 1 | testExitCode = $testExitCode; 23 | } 24 | 25 | /** 26 | * @var string 27 | */ 28 | protected $signature = 'dummy-command {user?} {--number=}'; 29 | 30 | /** 31 | * @var string 32 | */ 33 | protected $description = 'Display dummy message'; 34 | 35 | /** 36 | * @return void 37 | */ 38 | public function handle() 39 | { 40 | $this->comment('Dummy Command Message'); 41 | 42 | return $this->testExitCode; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/Support/Fixtures/DummyContractA.php: -------------------------------------------------------------------------------- 1 | get('id'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/Support/Fixtures/DummyEventA.php: -------------------------------------------------------------------------------- 1 | user = $user; 61 | $this->usersA = $usersA; 62 | $this->usersB = $usersB; 63 | $this->dummyClasses = $dummyClasses; 64 | $this->dataA = $dataA; 65 | $this->dataB = $dataB; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/Support/Fixtures/DummyException.php: -------------------------------------------------------------------------------- 1 | 'required', 24 | ]; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/Support/Fixtures/PerformanceProcessor.php: -------------------------------------------------------------------------------- 1 | performance = $dataTracker->data()->get('performance'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Support/Fixtures/ProcessorA.php: -------------------------------------------------------------------------------- 1 | meta = new Collection(); 27 | $this->data = new Collection(); 28 | } 29 | 30 | /** 31 | * @param DataTracker $dataTracker 32 | * @return void 33 | */ 34 | public function process(DataTracker $dataTracker): void 35 | { 36 | $this->data = $dataTracker->data(); 37 | $this->meta = $dataTracker->meta(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/Support/Fixtures/ProcessorB.php: -------------------------------------------------------------------------------- 1 | meta->put('meta-key', 'meta-value'); 19 | } 20 | 21 | /** 22 | * @return void 23 | */ 24 | public function terminate(): void 25 | { 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/Support/Fixtures/TrackerB.php: -------------------------------------------------------------------------------- 1 | data->put('data-key', 'data-value'); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/Support/Fixtures/dummy-view-a.blade.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jkocik/laravel-profiler/cda42631f802f32507ba0908cac52c335a494953/tests/Support/Fixtures/dummy-view-a.blade.php -------------------------------------------------------------------------------- /tests/Support/Fixtures/dummy-view-b.blade.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jkocik/laravel-profiler/cda42631f802f32507ba0908cac52c335a494953/tests/Support/Fixtures/dummy-view-b.blade.php -------------------------------------------------------------------------------- /tests/Support/Framework.php: -------------------------------------------------------------------------------- 1 | version()); 23 | } 24 | 25 | /** 26 | * @return string 27 | */ 28 | public function dir(): string 29 | { 30 | return "laravel-{$this->versionWithoutDot()}"; 31 | } 32 | 33 | /** 34 | * @return string 35 | */ 36 | public function composerPackage(): string 37 | { 38 | return "laravel/laravel:{$this->version()}.*"; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/Support/PHPMock.php: -------------------------------------------------------------------------------- 1 | addMock(self::timeMock()->build()); 22 | $phpMock->addMock(self::passthruMock()->build()); 23 | $phpMock->addMock(self::phpVersionMock()->build()); 24 | $phpMock->addMock(self::memoryUsageMock()->build()); 25 | 26 | return $phpMock; 27 | } 28 | 29 | /** 30 | * @return MockBuilder 31 | */ 32 | protected static function timeMock(): MockBuilder 33 | { 34 | return (new MockBuilder()) 35 | ->setNamespace('JKocik\Laravel\Profiler\Trackers') 36 | ->setName('time') 37 | ->setFunctionProvider(new FixedValueFunction(self::TIME)); 38 | } 39 | 40 | /** 41 | * @return MockBuilder 42 | */ 43 | protected static function passthruMock(): MockBuilder 44 | { 45 | return (new MockBuilder()) 46 | ->setNamespace('JKocik\Laravel\Profiler\Console') 47 | ->setName('passthru') 48 | ->setFunctionProvider(new FixedValueFunction('')); 49 | } 50 | 51 | /** 52 | * @return MockBuilder 53 | */ 54 | protected static function phpVersionMock(): MockBuilder 55 | { 56 | return (new MockBuilder()) 57 | ->setNamespace('JKocik\Laravel\Profiler\Trackers') 58 | ->setName('phpversion') 59 | ->setFunctionProvider(new FixedValueFunction(self::PHP_VERSION)); 60 | } 61 | 62 | /** 63 | * @return MockBuilder 64 | */ 65 | protected static function memoryUsageMock(): MockBuilder 66 | { 67 | return (new MockBuilder()) 68 | ->setNamespace('JKocik\Laravel\Profiler\Services\Performance') 69 | ->setName('memory_get_peak_usage') 70 | ->setFunctionProvider(new FixedValueFunction(self::MEMORY_USAGE)); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tests/Support/TestListener.php: -------------------------------------------------------------------------------- 1 | versionPrinted) { 25 | $this->printVersion($test); 26 | } 27 | } 28 | 29 | /** 30 | * @return void 31 | */ 32 | protected function printVersion(Test $test): void 33 | { 34 | fwrite(STDERR, "APP: {$test->appBeforeBootstrap()->version()}\n"); 35 | 36 | $this->versionPrinted = true; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/Unit/LaravelExecution/ConsoleFinishedRequestTest.php: -------------------------------------------------------------------------------- 1 | assertNull($consoleFinishedRequest->meta()->get('path')); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Unit/LaravelExecution/ConsoleStartingRequestTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('command-starting', $consoleStartingRequest->meta()->get('type')); 16 | $this->assertCount(0, $consoleStartingRequest->data()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/Unit/LaravelExecution/ConsoleStartingResponseTest.php: -------------------------------------------------------------------------------- 1 | assertNull($consoleStartingResponse->meta()->get('type')); 16 | $this->assertCount(0, $consoleStartingResponse->data()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/Unit/Services/ConfigServiceTest.php: -------------------------------------------------------------------------------- 1 | app = $this->appWith(function (Application $app) { 20 | $app->make('config')->set('profiler.trackers', [ 21 | TrackerA::class, 22 | TrackerB::class, 23 | ]); 24 | }); 25 | 26 | $trackers = $this->app->make(ConfigService::class)->trackers(); 27 | 28 | $this->assertTrue($trackers->contains(TrackerA::class)); 29 | $this->assertTrue($trackers->contains(TrackerB::class)); 30 | } 31 | 32 | /** @test */ 33 | function returns_processors() 34 | { 35 | $this->app = $this->appWith(function (Application $app) { 36 | $app->make('config')->set('profiler.processors', [ 37 | ProcessorA::class, 38 | ProcessorB::class, 39 | ]); 40 | }); 41 | 42 | $processors = $this->app->make(ConfigService::class)->processors(); 43 | 44 | $this->assertTrue($processors->contains(ProcessorA::class)); 45 | $this->assertTrue($processors->contains(ProcessorB::class)); 46 | } 47 | 48 | /** @test */ 49 | function returns_server_http_connection_url() 50 | { 51 | $url = $this->app->make(ConfigService::class)->serverHttpConnectionUrl(); 52 | 53 | $this->assertEquals('http://localhost:8099', $url); 54 | } 55 | 56 | /** @test */ 57 | function returns_server_http_port() 58 | { 59 | $port = $this->app->make(ConfigService::class)->serverHttpPort(); 60 | 61 | $this->assertEquals('8099', $port); 62 | } 63 | 64 | /** @test */ 65 | function returns_server_sockets_port() 66 | { 67 | $port = $this->app->make(ConfigService::class)->serverSocketsPort(); 68 | 69 | $this->assertEquals('1901', $port); 70 | } 71 | 72 | /** @test */ 73 | function returns_handle_exceptions() 74 | { 75 | $handleDefault = $this->app->make(ConfigService::class)->handleExceptions( 76 | LogService::HANDLE_EXCEPTIONS_LOG 77 | ); 78 | 79 | $this->assertTrue($handleDefault); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /tests/Unit/Services/HelpersTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(true); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/Unit/Services/ParamsServiceTest.php: -------------------------------------------------------------------------------- 1 | app->make(ParamsService::class); 16 | 17 | $this->assertEquals(1, $paramsService->resolve(1)); 18 | $this->assertEquals('something', $paramsService->resolve('something')); 19 | $this->assertEquals(['a', 'b'], $paramsService->resolve(['a', 'b'])); 20 | $this->assertEquals(true, $paramsService->resolve(true)); 21 | $this->assertEquals(false, $paramsService->resolve(false)); 22 | } 23 | 24 | /** @test */ 25 | function returns_array_of_object_param_if_is_available() 26 | { 27 | $paramsService = $this->app->make(ParamsService::class); 28 | $user = $this->user(['email' => 'a@example.com']); 29 | 30 | $this->assertEquals($user->toArray(), $paramsService->resolve($user)); 31 | } 32 | 33 | /** @test */ 34 | function returns_class_name_of_object_param_if_array_is_not_available() 35 | { 36 | $paramsService = $this->app->make(ParamsService::class); 37 | $dummyClassA = new DummyClassA(); 38 | 39 | $this->assertEquals(DummyClassA::class, $paramsService->resolve($dummyClassA)); 40 | } 41 | 42 | /** @test */ 43 | function can_resolve_array_of_objects() 44 | { 45 | $paramsService = $this->app->make(ParamsService::class); 46 | $dummyClassA = new DummyClassA(); 47 | $dummyClassB = new DummyClassB(); 48 | 49 | $this->assertEquals([ 50 | DummyClassA::class, 51 | DummyClassB::class, 52 | ], $paramsService->resolve([$dummyClassA, $dummyClassB])); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests/Unit/Trackers/AuthTrackerTest.php: -------------------------------------------------------------------------------- 1 | app->make(AuthTracker::class); 36 | 37 | $user = $this->factoryUser()->create(['email' => 'user@example.com']); 38 | Auth::login($user); 39 | 40 | $tracker->terminate(); 41 | $auth = $tracker->data()->get('auth'); 42 | 43 | $this->assertEquals($user->email, $auth->email); 44 | } 45 | 46 | /** @test */ 47 | function has_auth_user_even_user_is_logging_out() 48 | { 49 | $tracker = $this->app->make(AuthTracker::class); 50 | 51 | $user = $this->factoryUser()->create([ 52 | 'email' => 'login.me@example.com', 53 | ]); 54 | Auth::login($user); 55 | Auth::logout(); 56 | 57 | $tracker->terminate(); 58 | $auth = $tracker->data()->get('auth'); 59 | 60 | $this->assertEquals($user->email, $auth->email); 61 | } 62 | 63 | /** @test */ 64 | function has_null_auth_user_if_user_is_not_logged_in() 65 | { 66 | $tracker = $this->app->make(AuthTracker::class); 67 | 68 | $tracker->terminate(); 69 | $auth = $tracker->data()->get('auth'); 70 | 71 | $this->assertNull($auth); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /tests/Unit/Trackers/BindingsTrackerTest.php: -------------------------------------------------------------------------------- 1 | app->make(BindingsTracker::class); 18 | 19 | $tracker->terminate(); 20 | $bindings = $tracker->data()->get('bindings'); 21 | 22 | $this->assertNotNull($bindings); 23 | $this->assertCount(count($bindings), $this->app->getBindings()); 24 | $this->assertSame(0, $bindings->keys()[0]); 25 | $this->assertContains($bindings->first()['abstract'], array_keys($this->app->getBindings())); 26 | } 27 | 28 | /** @test */ 29 | function tracks_how_container_resolves_bindings() 30 | { 31 | $this->app->bind(DummyContractA::class, DummyClassA::class); 32 | $this->app->bind(DummyContractB::class, DummyClassB::class); 33 | $this->app->make(DummyContractB::class); 34 | $tracker = $this->app->make(BindingsTracker::class); 35 | 36 | $tracker->terminate(); 37 | $bindings = $tracker->data()->get('bindings'); 38 | 39 | $this->assertNull($bindings->where('abstract', DummyContractA::class)->first()['resolved']); 40 | $this->assertEquals(DummyClassB::class, $bindings->where('abstract', DummyContractB::class)->first()['resolved']); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/Unit/Trackers/ConfigTrackerTest.php: -------------------------------------------------------------------------------- 1 | app->make(ConfigTracker::class); 14 | 15 | $tracker->terminate(); 16 | $config = $tracker->data()->get('config'); 17 | 18 | $this->assertNotNull($config); 19 | $this->assertEquals(collect($this->app->make('config')->all())->keys(), $config->keys()); 20 | } 21 | 22 | /** @test */ 23 | function has_hidden_secret_config_data() 24 | { 25 | config()->set('my-config.password', '1'); 26 | config()->set('my-config.next-config-level.password', '12'); 27 | config()->set('my-config.next-config-level.even-deeper.password', '123'); 28 | config()->set('my-config.PASSWORD', '1234'); 29 | config()->set('my-config.key', '12345'); 30 | config()->set('my-config.secret', '123456'); 31 | config()->set('my-config.value_password', '1234567'); 32 | config()->set('my-config.some_password_value', '12345678'); 33 | config()->set('my-config.password-value', '123456789'); 34 | 35 | $tracker = $this->app->make(ConfigTracker::class); 36 | 37 | $tracker->terminate(); 38 | $config = $tracker->data()->get('config')->toArray(); 39 | 40 | $this->assertEquals('*', $config['my-config']['password']); 41 | $this->assertEquals('**', $config['my-config']['next-config-level']['password']); 42 | $this->assertEquals('***', $config['my-config']['next-config-level']['even-deeper']['password']); 43 | $this->assertEquals('****', $config['my-config']['PASSWORD']); 44 | $this->assertEquals('*****', $config['my-config']['key']); 45 | $this->assertEquals('******', $config['my-config']['secret']); 46 | $this->assertEquals('1234567', $config['my-config']['value_password']); 47 | $this->assertEquals('12345678', $config['my-config']['some_password_value']); 48 | $this->assertEquals('123456789', $config['my-config']['password-value']); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Unit/Trackers/ContentTrackerTest.php: -------------------------------------------------------------------------------- 1 | shouldIgnoreMissing(); 17 | $content->shouldReceive('meta')->once()->andReturn(collect([ 18 | 'key-a' => 'val-a', 19 | 'key-b' => 'val-b', 20 | 'key-c' => 'val-c', 21 | ])); 22 | $content->shouldReceive('data')->andReturn(collect()); 23 | $this->app->make(ExecutionData::class)->setContent($content); 24 | 25 | $tracker = $this->app->make(ContentTracker::class); 26 | $tracker->terminate(); 27 | 28 | $this->assertEquals('val-a', $tracker->meta()->get('key-a')); 29 | $this->assertEquals('val-b', $tracker->meta()->get('key-b')); 30 | $this->assertEquals('val-c', $tracker->meta()->get('key-c')); 31 | } 32 | 33 | /** @test */ 34 | function has_content_data() 35 | { 36 | $content = Mockery::mock(ExecutionContent::class)->shouldIgnoreMissing(); 37 | $content->shouldReceive('data')->once()->andReturn(collect([ 38 | 'content' => '', 39 | ])); 40 | $this->app->make(ExecutionData::class)->setContent($content); 41 | 42 | $tracker = $this->app->make(ContentTracker::class); 43 | $tracker->terminate(); 44 | 45 | $this->assertEquals('', $tracker->data()->get('content')); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/Unit/Trackers/ExceptionTrackerTest.php: -------------------------------------------------------------------------------- 1 | turnOffProcessors(); 30 | } 31 | 32 | /** @test */ 33 | function has_exception() 34 | { 35 | Event::listen(ExceptionHandling::class, function (ExceptionHandling $exceptionHandling) { 36 | $this->exception = $exceptionHandling->exception; 37 | }); 38 | 39 | $tracker = $this->app->make(ExceptionTracker::class); 40 | 41 | $this->get('/i-can-not-find-that-page'); 42 | 43 | $tracker->terminate(); 44 | $exception = $tracker->data()->get('exception'); 45 | 46 | $this->assertSame($this->exception->getMessage(), $exception->get('message')); 47 | $this->assertSame(NotFoundHttpException::class, $exception->get('exception')); 48 | $this->assertSame($this->exception->getFile(), $exception->get('file')); 49 | $this->assertSame($this->exception->getLine(), $exception->get('line')); 50 | $this->assertEquals(collect($this->exception->getTrace())->map(function ($trace) { 51 | return Arr::except($trace, ['args', 'type']); 52 | }), $exception->get('trace')); 53 | } 54 | 55 | /** @test */ 56 | function has_not_exception_on_correct_framework_execution() 57 | { 58 | $tracker = $this->app->make(ExceptionTracker::class); 59 | 60 | $this->get('/'); 61 | 62 | $tracker->terminate(); 63 | $exception = $tracker->data()->get('exception'); 64 | 65 | $this->assertNull($exception); 66 | } 67 | 68 | /** @test */ 69 | function calls_regular_laravel_exception_handler() 70 | { 71 | $this->tapLaravelVersionTill(5.4, function () { 72 | $this->assertTrue(true); 73 | }); 74 | 75 | $this->tapLaravelVersionFrom(5.5, function () { 76 | $mock = Mockery::spy(DummyException::class); 77 | 78 | Route::get('route-ex', function () use ($mock) { 79 | throw $mock; 80 | }); 81 | 82 | $tracker = $this->app->make(ExceptionTracker::class); 83 | 84 | $this->get('/route-ex'); 85 | 86 | $mock->shouldHaveReceived('report')->once(); 87 | }); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /tests/Unit/Trackers/RedisTrackerTest.php: -------------------------------------------------------------------------------- 1 | app->make(RedisTracker::class); 19 | $this->app->make('redis')->set('name', 'Laravel Profiler'); 20 | 21 | $tracker->terminate(); 22 | $redis = $tracker->data()->get('redis'); 23 | 24 | $this->assertNotNull($redis); 25 | 26 | $this->tapLaravelVersionTill(5.6, function () use ($redis) { 27 | $this->assertEquals([], $redis->all()); 28 | }); 29 | $this->tapLaravelVersionFrom(5.7, function () use ($redis) { 30 | $this->assertEquals('set', $redis->first()['command']); 31 | $this->assertEquals('default', $redis->first()['name']); 32 | $this->assertEquals(['name', 'Laravel Profiler'], $redis->first()['parameters']); 33 | $this->assertArrayHasKey('time', $redis->first()); 34 | }); 35 | } 36 | 37 | /** @test */ 38 | function can_count_commands() 39 | { 40 | $tracker = $this->app->make(RedisTracker::class); 41 | $this->app->make('redis')->set('name', 'Laravel Profiler'); 42 | $this->app->make('redis')->set('action', 'testing'); 43 | 44 | $tracker->terminate(); 45 | 46 | $this->tapLaravelVersionTill(5.6, function () use ($tracker) { 47 | $this->assertEquals(0, $tracker->meta()->get('redis_count')); 48 | }); 49 | $this->tapLaravelVersionFrom(5.7, function () use ($tracker) { 50 | $this->assertEquals(2, $tracker->meta()->get('redis_count')); 51 | }); 52 | } 53 | 54 | /** @test */ 55 | function knows_if_laravel_is_able_to_track_redis() 56 | { 57 | $tracker = $this->app->make(RedisTracker::class); 58 | 59 | $tracker->terminate(); 60 | 61 | $this->tapLaravelVersionTill(5.6, function () use ($tracker) { 62 | $this->assertFalse($tracker->meta()->get('redis_can_be_tracked')); 63 | }); 64 | $this->tapLaravelVersionFrom(5.7, function () use ($tracker) { 65 | $this->assertTrue($tracker->meta()->get('redis_can_be_tracked')); 66 | }); 67 | } 68 | 69 | /** @test */ 70 | function can_reset_commands() 71 | { 72 | $tracker = $this->app->make(RedisTracker::class); 73 | $this->app->make('redis')->set('name', 'Laravel Profiler'); 74 | $this->app->make('redis')->set('name', 'Laravel Profiler'); 75 | $this->app->make('redis')->set('name', 'Laravel Profiler'); 76 | 77 | profiler_reset(); 78 | 79 | $this->app->make('redis')->set('name', 'Laravel Profiler'); 80 | 81 | $tracker->terminate(); 82 | 83 | $this->tapLaravelVersionTill(5.6, function () { 84 | $this->assertTrue(true); 85 | }); 86 | $this->tapLaravelVersionFrom(5.7, function () use ($tracker) { 87 | $this->assertEquals(1, $tracker->meta()->get('redis_count')); 88 | $this->assertCount(1, $tracker->data()->get('redis')); 89 | }); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tests/Unit/Trackers/RequestTrackerTest.php: -------------------------------------------------------------------------------- 1 | shouldIgnoreMissing(); 17 | $request->shouldReceive('meta')->once()->andReturn(collect([ 18 | 'key-a' => 'val-a', 19 | 'key-b' => 'val-b', 20 | 'key-c' => 'val-c', 21 | ])); 22 | $this->app->make(ExecutionData::class)->setRequest($request); 23 | 24 | $tracker = $this->app->make(RequestTracker::class); 25 | $tracker->terminate(); 26 | 27 | $this->assertEquals('val-a', $tracker->meta()->get('key-a')); 28 | $this->assertEquals('val-b', $tracker->meta()->get('key-b')); 29 | $this->assertEquals('val-c', $tracker->meta()->get('key-c')); 30 | } 31 | 32 | /** @test */ 33 | function has_request_data() 34 | { 35 | $request = Mockery::mock(ExecutionRequest::class)->shouldIgnoreMissing(); 36 | $request->shouldReceive('data')->once()->andReturn(collect([ 37 | ['name' => 'key-x', 'value' => 'val-x'], 38 | ['name' => 'key-y', 'value' => 'val-y'], 39 | ['name' => 'key-z', 'value' => 'val-z'], 40 | ])); 41 | $this->app->make(ExecutionData::class)->setRequest($request); 42 | 43 | $tracker = $this->app->make(RequestTracker::class); 44 | $tracker->terminate(); 45 | 46 | $this->assertEquals('val-x', $tracker->data()->get('request')->where('name', 'key-x')->first()['value']); 47 | $this->assertEquals('val-y', $tracker->data()->get('request')->where('name', 'key-y')->first()['value']); 48 | $this->assertEquals('val-z', $tracker->data()->get('request')->where('name', 'key-z')->first()['value']); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Unit/Trackers/ResponseTrackerTest.php: -------------------------------------------------------------------------------- 1 | shouldIgnoreMissing(); 17 | $response->shouldReceive('meta')->once()->andReturn(collect([ 18 | 'key-a' => 'val-a', 19 | 'key-b' => 'val-b', 20 | 'key-c' => 'val-c', 21 | ])); 22 | $this->app->make(ExecutionData::class)->setResponse($response); 23 | 24 | $tracker = $this->app->make(ResponseTracker::class); 25 | $tracker->terminate(); 26 | 27 | $this->assertEquals('val-a', $tracker->meta()->get('key-a')); 28 | $this->assertEquals('val-b', $tracker->meta()->get('key-b')); 29 | $this->assertEquals('val-c', $tracker->meta()->get('key-c')); 30 | } 31 | 32 | /** @test */ 33 | function has_response_data() 34 | { 35 | $response = Mockery::mock(ExecutionResponse::class)->shouldIgnoreMissing(); 36 | $response->shouldReceive('data')->once()->andReturn(collect([ 37 | ['name' => 'key-x', 'value' => 'val-x'], 38 | ['name' => 'key-y', 'value' => 'val-y'], 39 | ['name' => 'key-z', 'value' => 'val-z'], 40 | ])); 41 | $this->app->make(ExecutionData::class)->setResponse($response); 42 | 43 | $tracker = $this->app->make(ResponseTracker::class); 44 | $tracker->terminate(); 45 | 46 | $this->assertEquals('val-x', $tracker->data()->get('response')->where('name', 'key-x')->first()['value']); 47 | $this->assertEquals('val-y', $tracker->data()->get('response')->where('name', 'key-y')->first()['value']); 48 | $this->assertEquals('val-z', $tracker->data()->get('response')->where('name', 'key-z')->first()['value']); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Unit/Trackers/RouteTrackerTest.php: -------------------------------------------------------------------------------- 1 | shouldIgnoreMissing(); 17 | $route->shouldReceive('meta')->once()->andReturn(collect([ 18 | 'key-a' => 'val-a', 19 | 'key-b' => 'val-b', 20 | 'key-c' => 'val-c', 21 | ])); 22 | $this->app->make(ExecutionData::class)->setRoute($route); 23 | 24 | $tracker = $this->app->make(RouteTracker::class); 25 | $tracker->terminate(); 26 | 27 | $this->assertEquals('val-a', $tracker->meta()->get('key-a')); 28 | $this->assertEquals('val-b', $tracker->meta()->get('key-b')); 29 | $this->assertEquals('val-c', $tracker->meta()->get('key-c')); 30 | } 31 | 32 | /** @test */ 33 | function has_route_data() 34 | { 35 | $route = Mockery::mock(ExecutionRoute::class)->shouldIgnoreMissing(); 36 | $route->shouldReceive('data')->once()->andReturn(collect([ 37 | ['name' => 'key-x', 'value' => 'val-x'], 38 | ['name' => 'key-y', 'value' => 'val-y'], 39 | ['name' => 'key-z', 'value' => 'val-z'], 40 | ])); 41 | $this->app->make(ExecutionData::class)->setRoute($route); 42 | 43 | $tracker = $this->app->make(RouteTracker::class); 44 | $tracker->terminate(); 45 | 46 | $this->assertEquals('val-x', $tracker->data()->get('route')->where('name', 'key-x')->first()['value']); 47 | $this->assertEquals('val-y', $tracker->data()->get('route')->where('name', 'key-y')->first()['value']); 48 | $this->assertEquals('val-z', $tracker->data()->get('route')->where('name', 'key-z')->first()['value']); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Unit/Trackers/ServerTrackerTest.php: -------------------------------------------------------------------------------- 1 | shouldIgnoreMissing(); 17 | $server->shouldReceive('meta')->once()->andReturn(collect([ 18 | 'key-a' => 'val-a', 19 | 'key-b' => 'val-b', 20 | 'key-c' => 'val-c', 21 | ])); 22 | $this->app->make(ExecutionData::class)->setServer($server); 23 | 24 | $tracker = $this->app->make(ServerTracker::class); 25 | $tracker->terminate(); 26 | 27 | $this->assertEquals('val-a', $tracker->meta()->get('key-a')); 28 | $this->assertEquals('val-b', $tracker->meta()->get('key-b')); 29 | $this->assertEquals('val-c', $tracker->meta()->get('key-c')); 30 | } 31 | 32 | /** @test */ 33 | function has_server_data() 34 | { 35 | $server = Mockery::mock(ExecutionServer::class)->shouldIgnoreMissing(); 36 | $server->shouldReceive('data')->once()->andReturn(collect([ 37 | ['name' => 'key-x', 'value' => 'val-x'], 38 | ['name' => 'key-y', 'value' => 'val-y'], 39 | ['name' => 'key-z', 'value' => 'val-z'], 40 | ])); 41 | $this->app->make(ExecutionData::class)->setServer($server); 42 | 43 | $tracker = $this->app->make(ServerTracker::class); 44 | $tracker->terminate(); 45 | 46 | $this->assertEquals('val-x', $tracker->data()->get('server')->where('name', 'key-x')->first()['value']); 47 | $this->assertEquals('val-y', $tracker->data()->get('server')->where('name', 'key-y')->first()['value']); 48 | $this->assertEquals('val-z', $tracker->data()->get('server')->where('name', 'key-z')->first()['value']); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Unit/Trackers/ServiceProvidersTrackerTest.php: -------------------------------------------------------------------------------- 1 | app) extends ServiceProvider { 15 | public function register() {} 16 | }; 17 | $this->app->register($provider); 18 | $tracker = $this->app->make(ServiceProvidersTracker::class); 19 | 20 | $tracker->terminate(); 21 | $serviceProviders = $tracker->data()->get('service_providers'); 22 | 23 | $this->assertNotNull($serviceProviders); 24 | $this->assertTrue($serviceProviders->contains(get_class($provider))); 25 | $this->assertCount(count($serviceProviders), $this->app->getLoadedProviders()); 26 | $this->assertSame(0, $serviceProviders->keys()[0]); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/Unit/Trackers/SessionTrackerTest.php: -------------------------------------------------------------------------------- 1 | shouldIgnoreMissing(); 17 | $session->shouldReceive('meta')->once()->andReturn(collect([ 18 | 'key-a' => 'val-a', 19 | 'key-b' => 'val-b', 20 | 'key-c' => 'val-c', 21 | ])); 22 | $this->app->make(ExecutionData::class)->setSession($session); 23 | 24 | $tracker = $this->app->make(SessionTracker::class); 25 | $tracker->terminate(); 26 | 27 | $this->assertEquals('val-a', $tracker->meta()->get('key-a')); 28 | $this->assertEquals('val-b', $tracker->meta()->get('key-b')); 29 | $this->assertEquals('val-c', $tracker->meta()->get('key-c')); 30 | } 31 | 32 | /** @test */ 33 | function has_session_data() 34 | { 35 | $session = Mockery::mock(ExecutionSession::class)->shouldIgnoreMissing(); 36 | $session->shouldReceive('data')->once()->andReturn(collect([ 37 | ['name' => 'key-x', 'value' => 'val-x'], 38 | ['name' => 'key-y', 'value' => 'val-y'], 39 | ['name' => 'key-z', 'value' => 'val-z'], 40 | ])); 41 | $this->app->make(ExecutionData::class)->setSession($session); 42 | 43 | $tracker = $this->app->make(SessionTracker::class); 44 | $tracker->terminate(); 45 | 46 | $this->assertEquals('val-x', $tracker->data()->get('session')->where('name', 'key-x')->first()['value']); 47 | $this->assertEquals('val-y', $tracker->data()->get('session')->where('name', 'key-y')->first()['value']); 48 | $this->assertEquals('val-z', $tracker->data()->get('session')->where('name', 'key-z')->first()['value']); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Unit/Trackers/ViewsTrackerTest.php: -------------------------------------------------------------------------------- 1 | app['view']->addNamespace('tests', __DIR__ . '/../../Support/Fixtures'); 18 | } 19 | 20 | /** @test */ 21 | function has_views() 22 | { 23 | $tracker = $this->app->make(ViewsTracker::class); 24 | 25 | view('tests::dummy-view-a')->render(); 26 | view('tests::dummy-view-b')->render(); 27 | 28 | $tracker->terminate(); 29 | $views = $tracker->data()->get('views'); 30 | 31 | $this->assertNotNull($views); 32 | $this->assertCount(2, $views); 33 | } 34 | 35 | /** @test */ 36 | function has_view_name() 37 | { 38 | $tracker = $this->app->make(ViewsTracker::class); 39 | 40 | view('tests::dummy-view-a')->render(); 41 | 42 | $tracker->terminate(); 43 | $views = $tracker->data()->get('views'); 44 | 45 | $this->assertEquals('tests::dummy-view-a', $views->first()['name']); 46 | } 47 | 48 | /** @test */ 49 | function has_view_path() 50 | { 51 | $tracker = $this->app->make(ViewsTracker::class); 52 | 53 | view('tests::dummy-view-a')->render(); 54 | 55 | $tracker->terminate(); 56 | $views = $tracker->data()->get('views'); 57 | 58 | $this->assertEquals(__DIR__ . '/../../Support/Fixtures/dummy-view-a.blade.php', $views->first()['path']); 59 | } 60 | 61 | /** @test */ 62 | function has_view_data() 63 | { 64 | $this->app->make('config')->set('profiler.data.views', true); 65 | 66 | $tracker = $this->app->make(ViewsTracker::class); 67 | 68 | $user = ['name' => 'Joe']; 69 | view('tests::dummy-view-a', compact('user'))->render(); 70 | 71 | $tracker->terminate(); 72 | $views = $tracker->data()->get('views'); 73 | 74 | $this->assertEquals(['name' => 'Joe'], $views->first()['data']['user']); 75 | $this->assertArrayNotHasKey('params', $views->first()); 76 | } 77 | 78 | /** @test */ 79 | function has_not_data_of_views_if_data_tracking_is_disabled_in_config() 80 | { 81 | $tracker = $this->app->make(ViewsTracker::class); 82 | 83 | $user = ['name' => 'Joe']; 84 | view('tests::dummy-view-a', compact('user'))->render(); 85 | 86 | $tracker->terminate(); 87 | $views = $tracker->data()->get('views'); 88 | 89 | $this->assertArrayNotHasKey('data', $views->first()); 90 | } 91 | 92 | /** @test */ 93 | function has_view_params_if_data_tracking_is_disabled_in_config() 94 | { 95 | $tracker = $this->app->make(ViewsTracker::class); 96 | 97 | $name = 'Joe'; 98 | $visits = 125; 99 | $price = 5.89; 100 | $active = true; 101 | $address = null; 102 | $related = $this->user(['email' => 'a@example.com']); 103 | $roles = collect(['publisher', 'viewer']); 104 | $tags = ['a']; 105 | $comments = []; 106 | view('tests::dummy-view-a', compact( 107 | 'name', 108 | 'visits', 109 | 'price', 110 | 'active', 111 | 'address', 112 | 'related', 113 | 'roles', 114 | 'tags', 115 | 'comments' 116 | ))->render(); 117 | 118 | $tracker->terminate(); 119 | $views = $tracker->data()->get('views'); 120 | 121 | $this->assertArrayNotHasKey('data', $views->first()); 122 | $this->assertEquals([ 123 | 'name' => 'string', 124 | 'visits' => 'integer', 125 | 'price' => 'double', 126 | 'active' => 'boolean', 127 | 'address' => 'NULL', 128 | 'related' => $this->userClass(), 129 | 'roles' => 'Illuminate\Support\Collection: 2 item(s)', 130 | 'tags' => 'array: 1 item(s)', 131 | 'comments' => 'array: 0 item(s)', 132 | ], $views->first()['params']); 133 | } 134 | 135 | /** @test */ 136 | function can_reset_views() 137 | { 138 | $tracker = $this->app->make(ViewsTracker::class); 139 | view('tests::dummy-view-a')->render(); 140 | view('tests::dummy-view-a')->render(); 141 | view('tests::dummy-view-a')->render(); 142 | 143 | profiler_reset(); 144 | 145 | view('tests::dummy-view-a')->render(); 146 | 147 | $tracker->terminate(); 148 | 149 | $this->assertCount(1, $tracker->data()->get('views')); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /tests/bootstrap/phpunit.php: -------------------------------------------------------------------------------- 1 | dir() . '/vendor/autoload.php'; 18 | 19 | if (! file_exists($frameworkAutoloadFile)) { 20 | shell_exec('composer create-project --no-dev ' . $framework->composerPackage() . ' ./frameworks/' . $framework->dir()); 21 | } 22 | 23 | return $frameworkAutoloadFile; 24 | } 25 | 26 | /** 27 | * @param \JKocik\Laravel\Profiler\Tests\Support\Framework $framework 28 | * @return void 29 | */ 30 | function bootstrapFrameworkEnv(\JKocik\Laravel\Profiler\Tests\Support\Framework $framework): void 31 | { 32 | $frameworkPath = __DIR__ . '/../../frameworks/' . $framework->dir(); 33 | 34 | if (! file_exists($frameworkPath . '/.env')) { 35 | copy($frameworkPath . '/.env.example', $frameworkPath . '/.env'); 36 | } 37 | } 38 | --------------------------------------------------------------------------------