Skip to content

Commit 6214d1a

Browse files
fulloclaude
andcommitted
Add phar distribution and update all docs with phar examples
- Add bin/build-phar.php: builds self-contained sci-profiler.phar (41KB) with built-in PSR-4 autoloader, zero host dependencies - Add bin/sci-profiler.phar: pre-built phar ready to use - Update README, configuration, WordPress, Laravel, Symfony, extending docs to show phar usage alongside source-based setup - Add .claude/CLAUDE.md with pre-push checklist (tests + phar build) - Phar config resolution: env var > local file next to phar > env vars > bundled defaults Usage: php -d auto_prepend_file=bin/sci-profiler.phar -S localhost:8000 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 4218af4 commit 6214d1a

10 files changed

Lines changed: 365 additions & 36 deletions

.claude/CLAUDE.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# SCI Profiler PHP — Development Guidelines
2+
3+
## Project Overview
4+
5+
Non-invasive Software Carbon Intensity (SCI) profiler for PHP applications. Uses `auto_prepend_file` to measure carbon footprint per HTTP request or CLI execution.
6+
7+
## Key Commands
8+
9+
```bash
10+
composer test # Run PHPUnit test suite
11+
composer analyse # PHPStan static analysis
12+
composer cs-fix # Fix PSR-12 coding style
13+
```
14+
15+
## Pre-Push Checklist
16+
17+
Before every push to the repository:
18+
19+
1. **Run the test suite**: `composer test` — all tests must pass
20+
2. **Run static analysis**: `composer analyse` — no errors allowed
21+
3. **Build the phar**: `php -d phar.readonly=0 bin/build-phar.php` — must succeed and produce `bin/sci-profiler.phar`
22+
4. **Quick phar smoke test**: verify the phar works with a simple script:
23+
```bash
24+
php -d auto_prepend_file=bin/sci-profiler.phar -r "echo 'OK';"
25+
tail -1 /tmp/sci-profiler/sci-profiler.jsonl | jq .sci.sci_mgco2eq
26+
```
27+
28+
## Architecture
29+
30+
- `src/bootstrap.php` — entry point for `auto_prepend_file` (source mode)
31+
- `bin/sci-profiler.phar` — self-contained phar with built-in autoloader (no composer needed on host)
32+
- `bin/build-phar.php` — phar build script
33+
- `src/Config.php` — immutable config value object; defaults are class constants (`DEFAULT_*`)
34+
- `src/SciCalculator.php` — SCI formula; constants for magic numbers
35+
- `src/Collector/``CollectorInterface` + TimeCollector, MemoryCollector, RequestCollector
36+
- `src/Reporter/``ReporterInterface` + JsonReporter, LogReporter, HtmlReporter + `EnsuresOutputDirectory` trait
37+
- `config/sci-profiler.php` — default config template (bundled in phar)
38+
- `analisi/` — local-only test scripts (in .gitignore)
39+
40+
## Coding Standards
41+
42+
- PSR-12 coding style (enforced by php-cs-fixer)
43+
- PSR-4 autoloading under `SciProfiler\` namespace
44+
- All classes are `final` with `declare(strict_types=1)`
45+
- No dependencies in production code (only dev: phpunit, phpstan, php-cs-fixer)
46+
- Reporter errors are always caught silently — never break the host application
47+
48+
## The phar
49+
50+
The phar bundles all source files + default config into a single 41KB file. It includes its own PSR-4 autoloader in the stub, so it requires zero dependencies on the host machine.
51+
52+
Config resolution in phar mode:
53+
1. `SCI_PROFILER_CONFIG_FILE` env var
54+
2. `sci-profiler.local.php` next to the phar
55+
3. `SCI_PROFILER_*` env vars
56+
4. Bundled default config
57+
5. Built-in class defaults

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
.DS_Store
66
*.swp
77
*.swo
8-
.claude/
8+
.claude/settings.local.json
9+
.claude/skills/
910
/analisi/

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ The **functional unit** is a complete user-facing operation (e.g., loading a Wor
4141
### 1. Install
4242

4343
```bash
44+
# Option A: Phar (zero dependencies, ~41KB)
45+
wget -O /opt/sci-profiler.phar https://github.com/fullo/sci-profiler-php/releases/latest/download/sci-profiler.phar
46+
47+
# Option B: Clone + Composer
4448
git clone https://github.com/fullo/sci-profiler-php.git /opt/sci-profiler-php
4549
cd /opt/sci-profiler-php && composer install --no-dev
4650
```
@@ -50,13 +54,18 @@ cd /opt/sci-profiler-php && composer install --no-dev
5054
Add to your `php.ini`, PHP-FPM pool, or virtualhost config:
5155

5256
```ini
57+
; Using phar (no dependencies):
58+
auto_prepend_file = /opt/sci-profiler.phar
59+
; Or using source (requires composer install):
5360
auto_prepend_file = /opt/sci-profiler-php/src/bootstrap.php
5461
```
5562

5663
Or with PHP's built-in server:
5764

5865
```bash
59-
php -d auto_prepend_file=/opt/sci-profiler-php/src/bootstrap.php -S localhost:8000 -t public/
66+
php -d auto_prepend_file=/opt/sci-profiler.phar -S localhost:8000 -t public/
67+
# Or with source:
68+
# php -d auto_prepend_file=/opt/sci-profiler-php/src/bootstrap.php -S localhost:8000 -t public/
6069
```
6170

6271
### 3. Configure (optional)
@@ -104,6 +113,13 @@ composer analyse # PHPStan static analysis
104113
composer cs-fix # PSR-12 coding style
105114
```
106115

116+
### Building the phar
117+
118+
```bash
119+
php bin/build-phar.php
120+
# Creates bin/sci-profiler.phar (~41KB, zero dependencies)
121+
```
122+
107123
## Related
108124

109125
- [sci-profiler](https://github.com/fullo/sci-profiler) — Original TypeScript SCI profiler

bin/build-phar.php

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Builds sci-profiler.phar from the src/ directory.
7+
*
8+
* Usage:
9+
* php -d phar.readonly=0 bin/build-phar.php
10+
*
11+
* The resulting phar can be used directly with auto_prepend_file:
12+
* php -d auto_prepend_file=bin/sci-profiler.phar -S localhost:8000
13+
*/
14+
15+
$projectRoot = dirname(__DIR__);
16+
$pharFile = $projectRoot . '/bin/sci-profiler.phar';
17+
$srcDir = $projectRoot . '/src';
18+
$configDir = $projectRoot . '/config';
19+
20+
// Clean previous build
21+
if (file_exists($pharFile)) {
22+
unlink($pharFile);
23+
echo "Removed previous build.\n";
24+
}
25+
26+
echo "Building sci-profiler.phar...\n";
27+
28+
try {
29+
$phar = new Phar($pharFile, 0, 'sci-profiler.phar');
30+
$phar->startBuffering();
31+
32+
// Add all src/ files
33+
$srcIterator = new RecursiveIteratorIterator(
34+
new RecursiveDirectoryIterator($srcDir, FilesystemIterator::SKIP_DOTS),
35+
);
36+
37+
$count = 0;
38+
foreach ($srcIterator as $file) {
39+
if ($file->getExtension() !== 'php') {
40+
continue;
41+
}
42+
43+
$relativePath = 'src/' . ltrim(
44+
str_replace($srcDir, '', $file->getPathname()),
45+
DIRECTORY_SEPARATOR,
46+
);
47+
48+
$phar->addFile($file->getPathname(), $relativePath);
49+
echo " + {$relativePath}\n";
50+
$count++;
51+
}
52+
53+
// Add default config
54+
$phar->addFile($configDir . '/sci-profiler.php', 'config/sci-profiler.php');
55+
echo " + config/sci-profiler.php\n";
56+
$count++;
57+
58+
// Create the stub: when used as auto_prepend_file, it includes the bootstrap
59+
$stub = <<<'STUB'
60+
<?php
61+
/**
62+
* SCI Profiler PHP — phar stub.
63+
*
64+
* When loaded via auto_prepend_file, this registers the phar's autoloader
65+
* and executes the bootstrap to start profiling.
66+
*/
67+
Phar::mapPhar('sci-profiler.phar');
68+
69+
// Register PSR-4 autoloader for classes inside the phar
70+
spl_autoload_register(static function (string $class): void {
71+
$prefix = 'SciProfiler\\';
72+
if (strncmp($class, $prefix, strlen($prefix)) !== 0) {
73+
return;
74+
}
75+
76+
$relativeClass = substr($class, strlen($prefix));
77+
$file = 'phar://sci-profiler.phar/src/' . str_replace('\\', '/', $relativeClass) . '.php';
78+
79+
if (file_exists($file)) {
80+
require $file;
81+
}
82+
});
83+
84+
// Load configuration (same logic as bootstrap.php, adapted for phar paths)
85+
$__sciConfig = (static function (): \SciProfiler\Config {
86+
// 1. Explicit config file via env
87+
$configFile = getenv('SCI_PROFILER_CONFIG_FILE');
88+
if ($configFile !== false && is_file($configFile)) {
89+
return \SciProfiler\Config::fromFile($configFile);
90+
}
91+
92+
// 2. Config file next to the phar (bin/sci-profiler.local.php)
93+
$pharDir = dirname(Phar::running(false));
94+
$localConfig = $pharDir . '/sci-profiler.local.php';
95+
if ($pharDir !== '' && is_file($localConfig)) {
96+
return \SciProfiler\Config::fromFile($localConfig);
97+
}
98+
99+
// 3. Environment variables
100+
if (getenv('SCI_PROFILER_ENABLED') !== false) {
101+
return \SciProfiler\Config::fromEnvironment();
102+
}
103+
104+
// 4. Default config bundled in the phar
105+
$pharConfig = 'phar://sci-profiler.phar/config/sci-profiler.php';
106+
if (file_exists($pharConfig)) {
107+
return \SciProfiler\Config::fromFile($pharConfig);
108+
}
109+
110+
// 5. Built-in defaults
111+
return new \SciProfiler\Config();
112+
})();
113+
114+
if (!$__sciConfig->isEnabled()) {
115+
return;
116+
}
117+
118+
// Build and start profiler
119+
$__sciProfiler = new \SciProfiler\SciProfiler($__sciConfig);
120+
$__sciProfiler->addCollector(new \SciProfiler\Collector\TimeCollector());
121+
$__sciProfiler->addCollector(new \SciProfiler\Collector\MemoryCollector());
122+
$__sciProfiler->addCollector(new \SciProfiler\Collector\RequestCollector());
123+
124+
$__sciReporterMap = [
125+
'json' => static fn () => new \SciProfiler\Reporter\JsonReporter(),
126+
'log' => static fn () => new \SciProfiler\Reporter\LogReporter(),
127+
'html' => static fn () => new \SciProfiler\Reporter\HtmlReporter(),
128+
];
129+
foreach ($__sciConfig->getReporters() as $__rName) {
130+
$__rName = trim($__rName);
131+
if (isset($__sciReporterMap[$__rName])) {
132+
$__sciProfiler->addReporter($__sciReporterMap[$__rName]());
133+
}
134+
}
135+
136+
$__sciProfiler->start();
137+
138+
register_shutdown_function(static function () use ($__sciProfiler): void {
139+
if ($__sciProfiler->isStarted()) {
140+
$__sciProfiler->stop();
141+
}
142+
});
143+
144+
// Cleanup variables from global scope
145+
unset($__sciConfig, $__sciReporterMap, $__rName);
146+
147+
__HALT_COMPILER();
148+
STUB;
149+
150+
$phar->setStub($stub);
151+
$phar->stopBuffering();
152+
153+
// Make executable
154+
chmod($pharFile, 0755);
155+
156+
$size = filesize($pharFile);
157+
$sizeKb = round($size / 1024, 1);
158+
159+
echo "\nBuild complete:\n";
160+
echo " File: {$pharFile}\n";
161+
echo " Size: {$sizeKb} KB\n";
162+
echo " Files: {$count}\n";
163+
echo "\nUsage:\n";
164+
echo " php -d auto_prepend_file={$pharFile} -S localhost:8000 -t public/\n";
165+
} catch (Exception $e) {
166+
echo "ERROR: " . $e->getMessage() . "\n";
167+
exit(1);
168+
}

bin/sci-profiler.phar

41.3 KB
Binary file not shown.

doc/configuration.md

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ The `grid_carbon_intensity` parameter has the biggest impact on SCI scores. Use
106106
### php.ini (global)
107107

108108
```ini
109+
; Using phar (no dependencies):
110+
auto_prepend_file = /opt/sci-profiler.phar
111+
; Or using source (requires composer install):
109112
auto_prepend_file = /opt/sci-profiler-php/src/bootstrap.php
110113
```
111114

@@ -115,22 +118,31 @@ auto_prepend_file = /opt/sci-profiler-php/src/bootstrap.php
115118
<VirtualHost *:80>
116119
ServerName staging.example.com
117120
DocumentRoot /var/www/myapp/public
118-
php_value auto_prepend_file "/opt/sci-profiler-php/src/bootstrap.php"
121+
# Using phar:
122+
php_value auto_prepend_file "/opt/sci-profiler.phar"
123+
# Or using source:
124+
# php_value auto_prepend_file "/opt/sci-profiler-php/src/bootstrap.php"
119125
</VirtualHost>
120126
```
121127

122128
### PHP-FPM pool
123129

124130
```ini
125131
[staging]
126-
php_value[auto_prepend_file] = /opt/sci-profiler-php/src/bootstrap.php
132+
; Using phar:
133+
php_value[auto_prepend_file] = /opt/sci-profiler.phar
134+
; Or using source:
135+
; php_value[auto_prepend_file] = /opt/sci-profiler-php/src/bootstrap.php
127136
```
128137

129138
### Nginx + PHP-FPM
130139

131140
```nginx
132141
location ~ \.php$ {
133-
fastcgi_param PHP_VALUE "auto_prepend_file=/opt/sci-profiler-php/src/bootstrap.php";
142+
# Using phar:
143+
fastcgi_param PHP_VALUE "auto_prepend_file=/opt/sci-profiler.phar";
144+
# Or using source:
145+
# fastcgi_param PHP_VALUE "auto_prepend_file=/opt/sci-profiler-php/src/bootstrap.php";
134146
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
135147
include fastcgi_params;
136148
}
@@ -140,21 +152,33 @@ location ~ \.php$ {
140152

141153
```ini
142154
; public/.user.ini
143-
auto_prepend_file = /opt/sci-profiler-php/src/bootstrap.php
155+
; Using phar:
156+
auto_prepend_file = /opt/sci-profiler.phar
157+
; Or using source:
158+
; auto_prepend_file = /opt/sci-profiler-php/src/bootstrap.php
144159
```
145160

146161
### Docker
147162

148163
```dockerfile
149-
COPY sci-profiler-php /opt/sci-profiler-php
150-
RUN echo "auto_prepend_file=/opt/sci-profiler-php/src/bootstrap.php" \
164+
# Using phar:
165+
COPY sci-profiler.phar /opt/sci-profiler.phar
166+
RUN echo "auto_prepend_file=/opt/sci-profiler.phar" \
151167
>> /usr/local/etc/php/conf.d/sci-profiler.ini
168+
169+
# Or using source:
170+
# COPY sci-profiler-php /opt/sci-profiler-php
171+
# RUN echo "auto_prepend_file=/opt/sci-profiler-php/src/bootstrap.php" \
172+
# >> /usr/local/etc/php/conf.d/sci-profiler.ini
152173
```
153174

154175
### PHP built-in server
155176

156177
```bash
157-
php -d auto_prepend_file=/opt/sci-profiler-php/src/bootstrap.php -S localhost:8000 -t public/
178+
# Using phar:
179+
php -d auto_prepend_file=/opt/sci-profiler.phar -S localhost:8000 -t public/
180+
# Or using source:
181+
# php -d auto_prepend_file=/opt/sci-profiler-php/src/bootstrap.php -S localhost:8000 -t public/
158182
```
159183

160184
### Disable without removing

0 commit comments

Comments
 (0)