dvictorjhg / braidphp
An attribute-driven PHP framework with a router and lightweight single-process TCP HTTP runtime.
Requires
- php: ^8.4
- dvictorjhg/php-injector: ^1.0.1
- psr/http-message: ^2.0
Requires (Dev)
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0
- squizlabs/php_codesniffer: ^3.0
- zalas/phpunit-globals: ^3.0
Suggests
None
Provides
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-11 18:11:17 UTC
README
BraidPHP is an attribute-driven PHP framework with module composition, routing,
PSR HTTP messages, and a lightweight single-process TCP HTTP runtime.
Dependency injection and provider storage are delegated to the standalone
dvictorjhg/php-injector package.
- Package requirement: PHP
^8.4 - Runtime model: one blocking process accepts, parses, routes, invokes, and writes each request in sequence
- Main source areas:
src/Coreandsrc/Router - Checked-in sample app:
Example/
Documentation
- Full guide: GitHub Pages documentation
- Local static entry point:
docs/index.html - First application walkthrough: GitHub Pages first-app guide
- Modules and dependency injection: GitHub Pages modules guide
- Routing reference: GitHub Pages routing guide
- Executable recipes: JSON API and request handling
- HTTP messages and runtime: GitHub Pages HTTP guide, runtime guide, operations guide
- Development and release workflow: GitHub Pages development guide
Requirements
- PHP ^8.4
- Composer
Installation
composer require dvictorjhg/braidphp
Verified Quick Start
Create one module, one provider, one controller, then start the TCP server:
<?php declare(strict_types=1); require_once __DIR__ . '/vendor/autoload.php'; use dvictorjhg\braidphp\Core\App; use dvictorjhg\braidphp\Core\Attributes\Module; use dvictorjhg\braidphp\Router\Attributes\Get; use dvictorjhg\braidphp\Router\Attributes\Route; use dvictorjhg\braidphp\Router\Http\Request; use dvictorjhg\braidphp\Router\HttpModule; final class Greeter { public function greeting(string $name): string { return "Hello $name!"; } } #[Route(path: '/api')] final class GreetingController { public function __construct(private Greeter $greeter) { } #[Get('/hello/:name', pathMatch: 'full')] public function hello(Request $request): string { return $this->greeter->greeting($request->getRouteParam('name') ?? ''); } } #[Module( imports: [HttpModule::class], providers: [Greeter::class], controllers: [GreetingController::class], )] final class AppModule { } $app = new App(); $app->bootstrapModule(new AppModule()); $app->listen(address: '0.0.0.0', port: '8000');
Then request the route:
GET http://127.0.0.1:8000/api/hello/Ada
Hello Ada!
For the full empty-directory walkthrough, including composer.json, src/, and
index.php, see the GitHub Pages first-app guide.
What The Package Includes
#[Module]application composition withimports,providers,controllers, andbootstrap- Optional initial providers passed to
new App([...])before module bootstrap - Attribute-driven routing with class prefixes and method-level HTTP route attributes
- Supported shortcut attributes:
#[Get],#[Head],#[Post],#[Put],#[Delete],#[Connect],#[Options],#[Trace],#[Patch] - Programmatic routes via
dvictorjhg\braidphp\Router\Classes\Route - Path parameter capture through
Request::getRouteParam()andRequest::getRouteParams() - Query-string parsing through
Request::getQueryParams() - PSR HTTP message implementations for
Request,Response,Uri, andStream - A minimal TCP listener through
App::listen()
Runtime Caveats
App::listen()is a blocking single-process server loop.- The example front controller reads
SERVER_ADDRESSandSERVER_PORTfrom the environment and defaults to0.0.0.0:8000. - There is no worker pool, middleware pipeline, or FastCGI/SAPI integration in this repository.
- Scale by running multiple processes behind a process manager, reverse proxy, or container platform.
- The active PHPInjector is process-global, not request scoped. Do not cache request-specific state in shared providers or bootstrap independent apps in one process and expect separate containers.
handleRequest()propagates controller exceptions. Onlylisten()catches them, and its 500 response exposes the exception message. Catch and sanitize failures in application code before exposing the listener publicly.- Arrays are not converted to JSON automatically. Return a
Responsewith an explicit JSON content type andjson_encode(..., JSON_THROW_ON_ERROR)body.
Test Requests Without A Server
This complete example uses the checked-in application, so run it from a repository
clone after composer install, not from a package-only installation:
<?php declare(strict_types=1); require __DIR__ . '/vendor/autoload.php'; use dvictorjhg\braidphp\Core\App; use dvictorjhg\braidphp\Example\AppModule; use dvictorjhg\braidphp\Router\Http\HttpMethod; use dvictorjhg\braidphp\Router\Http\Request; $app = new App(); $app->bootstrapModule(new AppModule()); foreach (['/api/hello/Ada', '/health/status', '/missing'] as $uri) { $response = $app->handleRequest(new Request(method: HttpMethod::GET, uri: $uri)); echo $response->getStatusCode() . ' ' . (string) $response->getBody() . PHP_EOL; }
Expected output:
200 Hello Ada!
200 ok
404 Not Found
See the complete JSON recipe for decoded path parameters, query validation, dependency injection, strict path matching, and exception handling.
Run The Example Application
From the repository root:
composer install php Example/index.php
In a second terminal:
curl http://127.0.0.1:8000/api/hello/Ada # Hello Ada! curl 'http://127.0.0.1:8000/api/hello?name=Ada' # Hello Ada! curl -X POST http://127.0.0.1:8000/api/hi/Ada # Hi Ada! curl http://127.0.0.1:8000/health/status # ok
Development Commands
php tools/check-docs.php
composer validate --strict
composer check-platform-reqs
composer analyse
composer check-style
composer test
composer test:coverage
composer coverage:check
- CI runs the suite in
.github/workflows/ci.ymlon PHP 8.4 and 8.5. composer coverage:checkenforces a minimum 80% statement coverage from the generated Clover XML.codecov.ymlseparately configures Codecov project and patch targets at 50%.php tools/check-docs.phprequires PHP DOM andproc_open. It executes marked HTML examples and non-server README examples in separate PHP processes and compares their expected output. Xdebug is disabled only for these child runs.
Containers
The repository includes a PHP image definition in
docker/php/php.Dockerfile, a compose file in
docker-compose.yml, and helper launchers in
bin/podman-run.sh and
bin/podman-run.ps1.
Typical local Podman flow:
./bin/podman-run.sh --environment development --detach podman exec braidphp-development composer test ./bin/podman-run.sh --action down
The default .env values expose SERVER_PORT=8000 and set the base PHP image
tag to php-8.5.10-cli-trixie. The Podman launcher appends the environment
name, so its default development image is
localhost/dvictorjhg/braidphp:php-8.5.10-cli-trixie-development.
Changelog
See CHANGELOG.md.
Contributing
See CONTRIBUTING.md, CODE_OF_CONDUCT.md, and AI_USE_POLICY.md.
Security
See SECURITY.md. Report vulnerabilities privately instead of opening a public issue.
Release / Publishing
For a release, update CHANGELOG.md, run the validation commands
above, create and push an annotated tag such as 1.0.2, publish a GitHub
Release, and refresh the package on Packagist.
License And Attribution
BraidPHP is released under the Apache License 2.0. Preserve the license text, repository copyright, and attribution notices. See NOTICE and CITATION.cff for attribution details.