Skip to content

Commit 16779ea

Browse files
committed
build(phar): replace humbug/box with native bin/build-phar.php script
Box 4.x has a known PHP 8.4 incompatibility (chdir() errno 20 in endBuffering()). Replace it with bin/build-phar.php — a native PHP Phar builder with no external dependencies. Changes: - composer.json: scripts.build → php -d phar.readonly=0 bin/build-phar.php - Makefile: remove BOX var and _require-box guard; build target uses PHAR_BUILDER = bin/build-phar.php; VERSION simplified to git tag only; check-env shows PHAR builder status instead of box version - README.md: remove humbug/box from requirements and quick build - docs/BUILDING.md: rewrite around bin/build-phar.php; document why native builder was chosen; remove all box.json references
1 parent e43937d commit 16779ea

4 files changed

Lines changed: 52 additions & 104 deletions

File tree

Makefile

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
# make build Compile kcode.phar
1010
#
1111
# Requirements:
12-
# PHP ≥ 8.4 · Composer 2.x · humbug/box 4.x (for PHAR builds)
12+
# PHP ≥ 8.4 · Composer 2.x
1313
#
1414

1515
.PHONY: help install install-prod build verify self-test \
@@ -23,16 +23,14 @@ SHELL := /bin/bash
2323

2424
PHP ?= php
2525
COMPOSER ?= composer
26-
BOX ?= box
2726
KCODE := vendor/bin/kcode
27+
PHAR_BUILDER := bin/build-phar.php
2828

2929
BUILD_DIR := build
3030
PHAR := $(BUILD_DIR)/kcode.phar
3131

32-
# Version: prefer git tag, fall back to box.json metadata, then 'dev'
33-
VERSION := $(shell git describe --tags --abbrev=0 2>/dev/null \
34-
|| $(PHP) -r "echo json_decode(file_get_contents('box.json'),true)['metadata']['version'] ?? 'dev';" 2>/dev/null \
35-
|| echo "dev")
32+
# Version: prefer git tag, then 'dev'
33+
VERSION := $(shell git describe --tags --abbrev=0 2>/dev/null || echo "dev")
3634
COMMIT := $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
3735
TIMESTAMP := $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
3836

@@ -140,11 +138,11 @@ lint: cs-check analyse ## Lint: code style check + static analysis (no fixes app
140138
# Build (PHAR)
141139
# ══════════════════════════════════════════════════════════════
142140

143-
build: | _require-box _require-vendor ## Compile kcode.phar via humbug/box
141+
build: | _require-vendor ## Compile kcode.phar via bin/build-phar.php
144142
$(call _header,Building kcode.phar v$(VERSION))
145143
@mkdir -p $(BUILD_DIR)
146144
@START=$$(date +%s%N); \
147-
$(PHP) -d phar.readonly=0 $$(command -v $(BOX)) compile --config=box.json && \
145+
$(PHP) -d phar.readonly=0 $(PHAR_BUILDER) && \
148146
END=$$(date +%s%N); \
149147
ELAPSED=$$(( (END - START) / 1000000 )); \
150148
SECS=$$(( ELAPSED / 1000 )); \
@@ -230,23 +228,17 @@ check-env: ## Show build environment info (PHP, Composer, Box, Git, phar.readonl
230228
$(call _header,Build Environment)
231229
@printf " $(_BOLD)PHP$(_RESET) $$($(PHP) -v 2>/dev/null | head -1 || echo 'not found')\n"
232230
@printf " $(_BOLD)Composer$(_RESET) "; $(COMPOSER) --version 2>/dev/null | head -1 || printf "not found\n"
233-
@printf " $(_BOLD)Box$(_RESET) "; $(BOX) --version 2>/dev/null | head -1 || printf "not found (install: https://github.com/box-project/box)\n"
231+
@printf " $(_BOLD)PHAR builder$(_RESET) "; test -f $(PHAR_BUILDER) && printf "bin/build-phar.php (OK)\n" || printf "not found\n"
234232
@printf " $(_BOLD)kcode$(_RESET) "; test -f $(KCODE) && $(PHP) $(KCODE) --version 2>/dev/null || printf "not found — run make install\n"
235233
@printf " $(_BOLD)Git tag$(_RESET) $(VERSION) ($(COMMIT))\n"
236234
@printf " $(_BOLD)phar.readonly$(_RESET) $$($(PHP) -r 'echo ini_get("phar.readonly") ? "On (WARN: use php -d phar.readonly=0 for builds)" : "Off (OK)";' 2>/dev/null)\n"
237235
@printf "\n"
238236

239237
# ── Guards ─────────────────────────────────────────────────
240238

241-
_require-box:
242-
@command -v $(BOX) > /dev/null 2>&1 || { \
243-
printf "\n"; \
244-
printf " $(_RED)$(_RESET) $(_BOLD)humbug/box$(_RESET) not found\n"; \
245-
printf "\n"; \
246-
printf " Install standalone:\n"; \
247-
printf " $(_CYAN)wget -O box https://github.com/box-project/box/releases/latest/download/box.phar$(_RESET)\n"; \
248-
printf " $(_CYAN)chmod +x box && sudo mv box /usr/local/bin/box$(_RESET)\n"; \
249-
printf "\n"; \
239+
_require-phar-builder:
240+
@test -f $(PHAR_BUILDER) || { \
241+
printf "\n $(_RED)$(_RESET) $(_BOLD)$(PHAR_BUILDER)$(_RESET) not found. Expected at bin/build-phar.php.\n\n"; \
250242
exit 1; \
251243
}
252244

README.md

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -506,15 +506,10 @@ Unidirectional. No cycles. Commands depend on the Devkit facade. Runners and gen
506506

507507
- PHP 8.4+ with `phar.readonly=0`
508508
- Composer 2.x
509-
- [humbug/box](https://github.com/box-project/box) 4.x
510509

511510
### Quick Build
512511

513512
```bash
514-
# Install box standalone (recommended)
515-
wget -O box https://github.com/box-project/box/releases/latest/download/box.phar
516-
chmod +x box && sudo mv box /usr/local/bin/box
517-
518513
# Full release pipeline: quality → build → verify
519514
make release
520515
```
@@ -525,8 +520,8 @@ make release
525520
# 1. Install dependencies
526521
composer install
527522

528-
# 2. Compile PHAR (~15-20 MB with GZ compression)
529-
php -d phar.readonly=0 box compile --config=box.json
523+
# 2. Compile PHAR
524+
php -d phar.readonly=0 bin/build-phar.php
530525

531526
# 3. Verify
532527
php build/kcode.phar --version
@@ -548,7 +543,7 @@ git push --tags
548543

549544
The CI compiles `kcode.phar`, verifies it, and attaches it to the GitHub Release automatically.
550545

551-
See [docs/BUILDING.md](docs/BUILDING.md) for full build documentation, troubleshooting, and box.json details.
546+
See [docs/BUILDING.md](docs/BUILDING.md) for full build documentation and troubleshooting.
552547

553548
## Contributing
554549

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
"kcode:cs-check": "bin/kcode cs:fix --check",
5555
"kcode:quality": "bin/kcode quality",
5656
"kcode:migrate": "bin/kcode migrate",
57-
"build": "php -d phar.readonly=0 box compile --config=box.json"
57+
"build": "php -d phar.readonly=0 bin/build-phar.php"
5858
},
5959
"config": {
6060
"sort-packages": true,

docs/BUILDING.md

Lines changed: 38 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -8,37 +8,13 @@ This document covers how to compile `kcode.phar` from source, verify the artifac
88
|---|---|---|
99
| PHP | 8.4+ | Runtime and PHAR compilation |
1010
| Composer | 2.x | Dependency installation |
11-
| humbug/box | 4.x | PHAR compiler |
12-
13-
### Installing humbug/box
14-
15-
Option 1 — Composer global:
16-
17-
```bash
18-
composer global require humbug/box
19-
```
20-
21-
Option 2 — Standalone PHAR:
22-
23-
```bash
24-
wget -O box https://github.com/box-project/box/releases/latest/download/box.phar
25-
chmod +x box
26-
sudo mv box /usr/local/bin/box
27-
```
28-
29-
Verify installation:
30-
31-
```bash
32-
box --version
33-
# humbug/box 4.x.x
34-
```
3511

3612
### PHP Configuration
3713

38-
PHAR compilation requires `phar.readonly=0`. The Makefile passes this automatically via `-d phar.readonly=0`. For manual builds:
14+
PHAR compilation requires `phar.readonly=0`. The Makefile and `bin/build-phar.php` pass this automatically. For manual builds:
3915

4016
```bash
41-
php -d phar.readonly=0 box compile
17+
php -d phar.readonly=0 bin/build-phar.php
4218
```
4319

4420
Alternatively, set it in `php.ini`:
@@ -65,22 +41,48 @@ make self-test # Run against this project
6541
### Manual Build
6642

6743
```bash
68-
# 1. Install dependencies (with dev — quality tools are bundled in the PHAR)
44+
# 1. Install dependencies (dev tools are bundled in the PHAR)
6945
composer install --no-interaction --prefer-dist --optimize-autoloader --no-scripts
7046

71-
# 2. Compile PHAR
72-
php -d phar.readonly=0 box compile --config=box.json
47+
# 2. Compile PHAR using the native builder
48+
php -d phar.readonly=0 bin/build-phar.php
7349

7450
# 3. Verify
7551
php build/kcode.phar --version
7652
php build/kcode.phar --help
7753
```
7854

55+
## PHAR Builder — `bin/build-phar.php`
56+
57+
The project uses a native PHP PHAR builder (`bin/build-phar.php`) instead of `humbug/box`.
58+
59+
**Why:** Box 4.x has a known compatibility issue with PHP 8.4 (`chdir(): Not a directory (errno 20)` during `endBuffering()`). The native builder avoids this bug entirely with no external dependency.
60+
61+
**What it does:**
62+
1. Collects all `src/` PHP files (38 files)
63+
2. Collects `vendor/` PHP + JSON files, excluding test/doc directories
64+
3. Adds `LICENSE`
65+
4. Sets `bin/kcode` as the entry-point stub
66+
5. GZ-compresses the archive
67+
6. Sets permissions to `0755`
68+
69+
```bash
70+
# Output
71+
📦 Building kcode.phar...
72+
+ src/: 38 PHP files
73+
+ vendor/: <N> files
74+
+ LICENSE
75+
+ stub (bin/kcode entry point)
76+
77+
✅ Built: build/kcode.phar (X.XX MB)
78+
Files: <total>
79+
```
80+
7981
## Build Output
8082

8183
```
8284
build/
83-
└── kcode.phar # ~15-20 MB (GZ compressed)
85+
└── kcode.phar # GZ compressed PHAR
8486
```
8587

8688
The PHAR includes:
@@ -97,34 +99,6 @@ The PHAR includes:
9799
| Autoloader | `vendor/autoload.php` + `vendor/composer/` |
98100
| License | `LICENSE` |
99101

100-
Test files, documentation, and examples are excluded from the PHAR via the `exclude` and `blacklist` directives in `box.json`.
101-
102-
## box.json Configuration
103-
104-
Key settings:
105-
106-
```json
107-
{
108-
"main": "bin/kcode", // Entry point
109-
"output": "build/kcode.phar", // Output path
110-
"compression": "GZ", // GZ compression (~40% size reduction)
111-
"chmod": "0755", // Executable permission
112-
"stub": true, // Auto-generated stub with shebang
113-
"alias": "kcode.phar", // Internal PHAR alias for Phar::running()
114-
115-
"directories": ["src"], // Devkit source
116-
"finder": [{ // Vendor dependencies
117-
"name": "*.php",
118-
"in": ["vendor"],
119-
"exclude": ["Tests", "tests", "test", "doc", "docs", "examples"]
120-
}],
121-
122-
"compactors": [ // Strip comments/whitespace from PHP files
123-
"KevinGH\\Box\\Compactor\\Php"
124-
]
125-
}
126-
```
127-
128102
## Verification
129103

130104
After building, verify the PHAR works correctly:
@@ -144,9 +118,7 @@ php /path/to/kcode.phar init
144118
php /path/to/kcode.phar quality
145119
```
146120

147-
### PHAR Signature Verification
148-
149-
Box signs the PHAR with SHA-256 by default. Verify:
121+
### PHAR Signature
150122

151123
```bash
152124
php -r "echo (new Phar('build/kcode.phar'))->getSignature()['hash'];"
@@ -182,29 +154,19 @@ Creating a phar archive is disabled by the php.ini setting phar.readonly
182154

183155
**Fix:** Pass `-d phar.readonly=0` to PHP or set `phar.readonly = Off` in php.ini.
184156

185-
### Box not found
186-
187-
```
188-
✗ humbug/box not found.
189-
```
190-
191-
**Fix:** Install box globally (see Prerequisites above).
192-
193157
### PHAR too large
194158

195159
If the PHAR exceeds 30 MB:
196160

197-
1. Verify `exclude` in box.json filters out test files.
198-
2. Check that `compression: "GZ"` is set.
199-
3. Run `box info build/kcode.phar` to inspect contents.
161+
1. Check that GZ compression is enabled in `bin/build-phar.php` (`Phar::GZ`).
162+
2. Verify test/doc directories are excluded by the builder's `$excludeDirs` list.
200163

201164
### Binary not found inside PHAR
202165

203166
If `kcode.phar test` reports "Binary not found for phpunit":
204167

205168
1. Verify dependencies were installed before building: `composer install`
206-
2. Check that `vendor/bin/phpunit` exists in the project before compilation.
207-
3. Run `box info --list build/kcode.phar | grep phpunit` to verify inclusion.
169+
2. Check that `vendor/bin/phpunit` exists before compilation.
208170

209171
### Platform-specific issues
210172

@@ -216,9 +178,8 @@ php kcode.phar quality # Explicit PHP invocation
216178

217179
## Version Bumping
218180

219-
The version is stored in two places — keep them in sync:
181+
The version is stored in one place:
220182

221183
1. `src/Core/Devkit.php``private const string VERSION = '1.0.0';`
222-
2. `box.json``metadata.version`
223184

224-
The Makefile resolves the version via `git describe --tags --abbrev=0` first, falling back to `box.json` metadata, then `'dev'`. Always tag releases with `git tag vX.Y.Z` before running `make release`.
185+
The Makefile resolves the version via `git describe --tags --abbrev=0`, falling back to `'dev'`. Always tag releases with `git tag vX.Y.Z` before running `make release`.

0 commit comments

Comments
 (0)