Skip to content

Commit fb85f5f

Browse files
committed
Merge branch 'develop'
* develop: (39 commits) add missing calls to each section make sure all sections render without an error add type coverage badge remove unused dependency remove old documentation add documentation mention the package contains a middleware transform the package into a library add tests add named constructor to prefix routes for when used locally in an app make kernel constructor private fix display of contents remove old data prefix services to avoid collisions in user apps add dedicated route to display each section to avoid loading all profile data in a single page add sections loader extract the creation of the service used to load the profile add profile sections the call graph data is json, not svg add page to show a profile ...
2 parents 0813ecc + acf2440 commit fb85f5f

File tree

135 files changed

+3003
-4591
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

135 files changed

+3003
-4591
lines changed

.github/workflows/ci.yml

+25-25
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
strategy:
99
matrix:
1010
os: [ubuntu-latest, macOS-latest]
11-
php-version: ['7.4', '8.0']
11+
php-version: ['8.1', '8.2']
1212
name: 'PHPUnit'
1313
steps:
1414
- name: Checkout
@@ -20,27 +20,36 @@ jobs:
2020
extensions: mbstring, intl
2121
coverage: xdebug
2222
ini-values: xdebug.max_nesting_level=2048
23-
- name: Get Composer Cache Directory
24-
id: composer-cache
25-
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
26-
- name: Cache dependencies
27-
uses: actions/cache@v2
28-
with:
29-
path: ${{ steps.composer-cache.outputs.dir }}
30-
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
31-
restore-keys: ${{ runner.os }}-composer-
32-
- name: Install Dependencies
33-
run: composer install --no-progress
23+
- name: Composer
24+
uses: "ramsey/composer-install@v2"
3425
- name: PHPUnit
3526
run: vendor/bin/phpunit --coverage-clover=coverage.clover
3627
- uses: codecov/codecov-action@v1
3728
with:
3829
token: ${{ secrets.CODECOV_TOKEN }}
30+
psalm:
31+
runs-on: ubuntu-latest
32+
strategy:
33+
matrix:
34+
php-version: ['8.1', '8.2']
35+
name: 'Psalm'
36+
steps:
37+
- name: Checkout
38+
uses: actions/checkout@v2
39+
- name: Setup PHP
40+
uses: shivammathur/setup-php@v2
41+
with:
42+
php-version: ${{ matrix.php-version }}
43+
extensions: mbstring, intl
44+
- name: Composer
45+
uses: "ramsey/composer-install@v2"
46+
- name: Psalm
47+
run: vendor/bin/psalm --shepherd
3948
cs:
4049
runs-on: ubuntu-latest
4150
strategy:
4251
matrix:
43-
php-version: ['7.4']
52+
php-version: ['8.1']
4453
name: 'CS'
4554
steps:
4655
- name: Checkout
@@ -50,16 +59,7 @@ jobs:
5059
with:
5160
php-version: ${{ matrix.php-version }}
5261
extensions: mbstring, intl
53-
- name: Get Composer Cache Directory
54-
id: composer-cache
55-
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
56-
- name: Cache dependencies
57-
uses: actions/cache@v2
58-
with:
59-
path: ${{ steps.composer-cache.outputs.dir }}
60-
key: ${{ runner.os }}-${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.json') }}
61-
restore-keys: ${{ runner.os }}-${{ matrix.php-version }}-composer-
62-
- name: Install Dependencies
63-
run: composer install --no-progress
62+
- name: Composer
63+
uses: "ramsey/composer-install@v2"
6464
- name: CS
65-
run: vendor/bin/php-cs-fixer fix --diff --dry-run --diff-format udiff
65+
run: vendor/bin/php-cs-fixer fix --diff --dry-run
File renamed without changes.

README.md

+9-23
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,16 @@
22

33
[![Build Status](https://github.com/Innmind/Profiler/workflows/CI/badge.svg?branch=master)](https://github.com/Innmind/Profiler/actions?query=workflow%3ACI)
44
[![codecov](https://codecov.io/gh/Innmind/Profiler/branch/develop/graph/badge.svg)](https://codecov.io/gh/Innmind/Profiler)
5+
[![Type Coverage](https://shepherd.dev/github/innmind/profiler/coverage.svg)](https://shepherd.dev/github/innmind/profiler)
56

6-
(Framework agnostic) App profiler to help profile any kind of applications (http or cli).
7+
App profiler to help profile any kind of applications (http or cli).
78

8-
## Why
9-
10-
For my side projects I decided to no longer use the Symfony framework to explore different ways to build apps. However Symfony ship with a builtin profiler to help debug the application, thus I lost this tool in my side projects.
11-
12-
The problem with the current state of frameworks is that they either provide a profiler coupled to the framework or do not provide any. Since I needed to build a profiler for my needs I decided to also address the problem that many applications can't have access to a profiler by building one decoupled from any framework.
13-
14-
The decoupling is guaranteed by 2 things:
15-
16-
- it's a standalone project (so the application is not altered by much)
17-
- profiles are created via http, so we deal with raw values
9+
This package can be integrated in an existing app (using [`innmind/framework`](https://packagist.org/packages/innmind/framework)) or run as a [standalone app](docs/standalone.md)
1810

1911
## Installation
2012

2113
```sh
22-
composer create-project innmind/profiler
14+
composer require innmind/profiler
2315
```
2416

2517
## Overview
@@ -28,14 +20,16 @@ The profiler contains 2 types of entities: a profile and sections.
2820

2921
A profile contains a name (usually the http path called or the cli), the point in time the profile started, the status (succeeded, failed or pending) and an exit message.
3022

31-
A section is a part of a profile. By default there are 7 sections:
23+
A section is a part of a profile. By default there are 9 sections:
3224
- Http: the request and response (if the app didn't crash) the app received
3325
- Exception: the stack trace represented as graph (see [`innmind/stack-trace`](https://packagist.org/packages/innmind/stack-trace))
3426
- App graph: the object graph representing the application (see [`innmind/object-graph`](https://packagist.org/packages/innmind/object-graph))
27+
- Call graph: a flamechart
3528
- Environment: the list of environment variables
3629
- Processes: the list of commands run on the machine
3730
- Remote / Http: all the http requests issued by the application
3831
- Remote / Processes: all the commands run on a distant machine
32+
- Remote / Sql: all the SQL queries issued to a database
3933

4034
![](overview/index.png)
4135
![](overview/http.png)
@@ -46,14 +40,6 @@ A section is a part of a profile. By default there are 7 sections:
4640
![](overview/remote_processes.png)
4741
![](overview/remote_http.png)
4842

49-
## Usage
50-
51-
```sh
52-
cd profiler-directory/public && php -S localhost:8000
53-
```
54-
55-
This will start the php builtin webserver to expose the profiler on the local machine on the port `8000`. (You can change the port or use a real webserver such a nginx, the use of php webserver is for simplicity)
56-
57-
Then open `http://localhost:8000` in your browser to access the profiler.
43+
## Documentation
5844

59-
To create new profiles you need to create them via http, you can use the [`innmind/rest-client`](https://packagist.org/packages/innmind/rest-client) to simplify the task. All the capabilities can be accessed via the url `OPTIONS http://localhost:8000/*`
45+
All the documentation can be found in the [`docs`](docs/) folder.

composer.json

+16-16
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,33 @@
11
{
22
"name": "innmind/profiler",
3-
"type": "project",
3+
"type": "library",
44
"description": "App profiler",
55
"keywords": ["profiler", "http", "cli"],
66
"homepage": "http://github.com/Innmind/Profiler",
77
"license": "MIT",
88
"authors": [
99
{
1010
"name": "Baptiste Langlade",
11-
"email": "langlade.baptiste@gmail.com"
11+
"email": "baptiste.langlade@hey.com"
1212
}
1313
],
1414
"support": {
1515
"issues": "http://github.com/Innmind/Profiler/issues"
1616
},
1717
"require": {
18-
"php": "~7.4|~8.0",
19-
"innmind/immutable": "~3.5",
20-
"ramsey/uuid": "^3.8",
21-
"innmind/operating-system": "~2.0",
22-
"innmind/rest-server": "~8.0",
23-
"innmind/http-framework": "~2.1",
24-
"innmind/http-server": "~2.0",
25-
"innmind/templating": "~2.0"
18+
"php": "~8.1",
19+
"innmind/immutable": "~4.13",
20+
"ramsey/uuid": "~4.7",
21+
"innmind/operating-system": "~3.7",
22+
"innmind/framework": "~1.1",
23+
"innmind/json": "^1.3",
24+
"innmind/html": "^6.1.1",
25+
"innmind/url-template": "^3.0"
2626
},
2727
"autoload": {
2828
"psr-4": {
2929
"Innmind\\Profiler\\": "src/"
30-
},
31-
"files": [
32-
"src/Domain/bootstrap.php",
33-
"src/Web/bootstrap.php"
34-
]
30+
}
3531
},
3632
"autoload-dev": {
3733
"psr-4": {
@@ -40,6 +36,10 @@
4036
},
4137
"require-dev": {
4238
"phpunit/phpunit": "~9.0",
43-
"innmind/coding-standard": "^1.1"
39+
"vimeo/psalm": "~5.6",
40+
"innmind/coding-standard": "~2.0"
41+
},
42+
"provide": {
43+
"innmind/framework-middlewares": "1.0"
4444
}
4545
}

docs/README.md

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Getting started
2+
3+
## Installation
4+
5+
```sh
6+
composer require innmind/profiler
7+
```
8+
9+
## Use cases
10+
11+
- [Add the profiler to an existing app](in-app.md)
12+
- [Record profiles](record.md)
13+
- [Run as standalone app](standalone.md)

docs/in-app.md

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Add the profiler to an existing app
2+
3+
For this use cas you must [`innmind/framework`](https://packagist.org/packages/innmind/framework).
4+
5+
```php
6+
use Innmind\Framework\{
7+
Application,
8+
Main\Http,
9+
};
10+
use Innmind\Profiler\Web\Kernel;
11+
use Innmind\Url\Path;
12+
13+
new class extends Http {
14+
protected function configure(Application $app): Application
15+
{
16+
return $app->map(Kernel::inApp(Path::of('/tmp/')));
17+
}
18+
};
19+
```
20+
21+
This example will expose the profiler under the `/_profiler/` route and the profiles will be stored in the `/tmp/` folder. Using the tmp folder means the profiles will be lost when rebooting your machine, you can use a local folder if you want to keep them.
22+
23+
> **Note** this example add the middleware in the entrypoint of your app but you can add it in your own middleware.

docs/record.md

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Record profiles
2+
3+
By default the profiler record nothing, it's up to you to record the data that will be shown in the profiler.
4+
5+
The examples show how to record data when the profiler is [in the current app](in-app.md).
6+
7+
```php
8+
use Innmind\Framework\{
9+
Application,
10+
Middleware,
11+
Http\RequestHandler,
12+
};
13+
use Innmind\Profiler\{
14+
Web\Kernel,
15+
Profiler,
16+
};
17+
use Innmind\Http\Message\{
18+
ServerRequest,
19+
Response,
20+
};
21+
use Innmind\Url\Path;
22+
23+
final class YourApp implements Middleware
24+
{
25+
public function __invoke(Application $app): Application
26+
{
27+
return $app
28+
->map(Kernel::inApp(Path::of('/tmp/')))
29+
->mapRequestHandler(
30+
static fn($handler, $get) => new class($handler, $get('innmind/profiler')) implements RequestHandler {
31+
public function __construct(
32+
private RequestHandler $inner,
33+
private Profiler $profiler,
34+
) {
35+
}
36+
37+
public function __invoke(ServerRequest $request): Response
38+
{
39+
$profile = $this->profiler->start($request->url()->path()->toString());
40+
$this->profiler->mutate(
41+
$profile,
42+
static function($mutation) {
43+
$mutation->sections(); // call any method here to record sections data
44+
},
45+
);
46+
47+
$response = ($this->inner)($request);
48+
$this->profiler->mutate(
49+
$profile,
50+
static fn($mutation) => match ($response->statusCode()->successful()) {
51+
true => $mutation->succeed($response->statusCode()->toString()),
52+
false => $mutation->fail($response->statusCode()->toString()),
53+
},
54+
);
55+
56+
return $response;
57+
}
58+
},
59+
);
60+
}
61+
}
62+
```

docs/standalone.md

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Run as standalone app
2+
3+
The first step is to add an entrypoint that will be exposed by an HTTP server.
4+
5+
```php
6+
<?php
7+
declare(strict_types=1);
8+
9+
require 'path/to/vendor/autoload.php';
10+
11+
use Innmind\Framework\{
12+
Application,
13+
Main\Http,
14+
};
15+
use Innmind\Profiler\Web\Kernel;
16+
use Innmind\Url\Path;
17+
18+
new class extends Http {
19+
protected function configure(Application $app): Application
20+
{
21+
return $app
22+
->map(Kernel::standalone(Path::of('/tmp/')))
23+
->map(new YourMiddleware);
24+
}
25+
}
26+
```
27+
28+
This will expose the profiler via the `/` route.
29+
30+
Because the profiler doesn't come with its own HTTP api to record profiles you will need to implements these yourself via `YourMiddleware`.

psalm.xml

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0"?>
2+
<psalm
3+
errorLevel="1"
4+
findUnusedBaselineEntry="true"
5+
findUnusedCode="false"
6+
resolveFromConfigFile="true"
7+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
8+
xmlns="https://getpsalm.org/schema/config"
9+
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
10+
>
11+
<projectFiles>
12+
<directory name="src" />
13+
<directory name="public" />
14+
<ignoreFiles>
15+
<directory name="vendor" />
16+
</ignoreFiles>
17+
</projectFiles>
18+
</psalm>

0 commit comments

Comments
 (0)