Search by

dvictorjhg / braidphp

dvictorjhg

An attribute-driven PHP framework with a router and lightweight single-process TCP HTTP runtime.

1.0.2 2026-08-31 11:43 UTC

This package is auto-updated.

Last update: 2026-10-11 18:11:17 UTC


README

BraidPHP

Latest Version on Packagist Monthly Downloads on Packagist Total Downloads on Packagist PHP Version Required License CI Codecov Open Issues

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/Core and src/Router
  • Checked-in sample app: Example/

Documentation

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 with imports, providers, controllers, and bootstrap
  • 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() and Request::getRouteParams()
  • Query-string parsing through Request::getQueryParams()
  • PSR HTTP message implementations for Request, Response, Uri, and Stream
  • A minimal TCP listener through App::listen()

Runtime Caveats

  • App::listen() is a blocking single-process server loop.
  • The example front controller reads SERVER_ADDRESS and SERVER_PORT from the environment and defaults to 0.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. Only listen() 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 Response with an explicit JSON content type and json_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.yml on PHP 8.4 and 8.5.
  • composer coverage:check enforces a minimum 80% statement coverage from the generated Clover XML.
  • codecov.yml separately configures Codecov project and patch targets at 50%.
  • php tools/check-docs.php requires PHP DOM and proc_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.