# Framework X puts a reactive PHP app in one file and a vendor directory

> Framework X is a PHP micro framework from the clue project for building reactive web applications that run either behind a traditional web server or as a stand-alone process. The whole quickstart is a single `public/index.php` and one Composer command, and the trade-off is that you get routing and a request handler with nothing else attached.

**clue/framework-x** — Framework X – the simple and fast micro framework for building reactive web applications that run anywhere.

- Repository: https://github.com/clue/framework-x
- Website: https://framework-x.org
- Stars: 943 · Forks: 57
- Language: PHP
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/clue-framework-x

## A whole example application is one PHP file

The quickest way to judge Framework X is to look at the size of its quickstart. The README asks you to create an empty project directory, add a `public/` directory, and put an `index.php` file inside it. That single file then does everything: it loads the autoloader, creates the app object, registers two routes, and starts the server.

```php
<?php
require __DIR__ . '/../vendor/autoload.php';
$app = new FrameworkX\App();
$app->get('/', function () {
    return React\Http\Message\Response::plaintext(
        "Hello wörld!\n"
    );
});
$app->run();
```

There is no bootstrap class hierarchy to learn and no configuration file to write before the first request. The routing calls read like a framework in miniature: `get` takes a path pattern and a handler, and the pattern can carry a placeholder, as the second route in the quickstart shows.

```php
$app->get('/users/{name}', function (Psr\Http\Message\ServerRequestInterface $request) {
    return React\Http\Message\Response::plaintext(
        "Hello " . $request->getAttribute('name') . "!\n"
    );
});
```

The handler receives the PSR-7 server request and returns a response built with `React\Http\Message\Response::plaintext`. The route parameter arrives as a request attribute, so `getAttribute('name')` is how the placeholder is read. That is a small but meaningful detail: the project hands you PSR interfaces rather than a proprietary request object, which is what makes the rest of the design legible.

## Reactive means the request handler returns a promise, not a string

The framework describes itself as a micro framework for building reactive web applications, and the code gives the mechanism away. The response object is created through the `React\Http\Message` namespace, and the route handlers are plain closures. Nothing in the quickstart blocks on a database call or a slow API, because a handler can return a promise that resolves to a response later, and the framework settles it.

That design has a practical consequence. A handler that needs to call an external service does not have to hold the request open with a blocking call, so one process can overlap many in-flight requests. This is the difference between the framework and a plain PHP script that echoes and exits.

What the framework does not do is fill in the rest of a web application. The top level of the repository holds `src/`, `tests/`, `docs/`, `composer.json`, `phpstan.neon.dist`, `phpunit.xml.dist` and `phpunit.xml.legacy`. There is no shipped template layer, no object mapper and no session helper. Those are choices you make, and for a JSON API or an internal service that is exactly what you want. For a server-rendered marketing site, it means you assemble that stack yourself.

## Composer install, then serve it in one of two ways

The install step is a single Composer requirement in your project directory.

```bash
$ composer require clue/framework-x:^0.17
```

The README points at `CHANGELOG.md` for details about version upgrades, which is worth reading before you pin that constraint. After the install, the example runs with the built-in PHP web server:

```bash
$ php public/index.php
```

The README is explicit that the same application also works behind traditional web server setups, so the stand-alone process is one deployment option rather than the only one. The distinction matters operationally. Behind an existing web server, the `public/` directory is the document root and the server hands requests to your front controller. Stand-alone, `php public/index.php` is the server itself, which is convenient for local work and for a container that supervises the process, but it is not the same as running behind nginx or Apache with all the control that gives you.

The quickstart checks the result with curl, and the expected body is fixed:

```bash
$ curl http://localhost:8080/
Hello wörld!
```

That fixed output is a useful sanity check after a Composer change, because the plain text response comes straight out of the handler with no template engine in the path to break.

## The test suite is the real specification of the framework

For a project this small, the tests are unusually informative, and the README tells you exactly how to run them. Clone the repository, install the dependencies with Composer, and run the suite from the project root.

```bash
$ composer install
$ vendor/bin/phpunit
```

The suite is configured to always ensure 100% code coverage across all supported environments, and there is a coverage report command when the Xdebug extension is installed.

```bash
$ XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-text
```

Static analysis is run with PHPStan on max level, which is worth noting for anyone evaluating the codebase as a dependency rather than a starting point.

```bash
$ vendor/bin/phpstan
```

There is also an integration layer that runs the framework examples behind a real web server, using the URL of your installation.

```bash
$ php tests/integration/public/index.php
$ tests/integration.bash http://localhost:8080/
```

The practical takeaway is that a 100% coverage policy and a max-level PHPStan run mean the internal behaviour is exercised closely. The caveat is that the coverage figure describes this repository's own code, not the behaviour of your application, so treat it as evidence of discipline rather than as a promise about your build.

## Version 0.17 is a pre-1.0 line with a quiet release history

The release record is short and the version number is below 1.0, which tells you what to expect from the API surface. The listed releases are v0.17.0 on 2024-12-23, and v0.16.0 and v0.15.0 both dated 2024-03-05.

Two facts sit side by side here. The last push to the repository was on 2026-09-23, so the code is being worked on. The most recent tagged release is v0.17.0 from 2024-12-23, so the `^0.17` constraint in the quickstart pins you to a version line whose latest tag predates that recent work. Code on the default branch may well be ahead of what `composer require` hands you.

That gap is the main upgrade question for anyone adopting this, and the README points straight at `CHANGELOG.md` for version upgrade details. Read it before moving off `^0.17`, and be aware that a pre-1.0 version line gives no stability promise about minor version bumps. The `CHANGELOG.md` file at the top level of the repository and the `docs/` folder are the two places to look for what changed.

## MIT licence, PHPStan and PHPUnit in the toolchain

The licence field on the repository says MIT, and the README states the project is released under the permissive MIT licence. For a framework you embed in someone else's product, that means you can read it, change it and ship it inside a commercial or closed-source application, with the notice requirement being the part to respect. That is the plain reading of the identifier, not legal advice.

The toolchain around the code is worth listing because it tells you what a contributor or a serious dependent will deal with. The repository carries `phpstan.neon.dist` and `phpunit.xml.dist` alongside a `phpunit.xml.legacy` file, plus `composer.json` as the dependency manifest. The `src/` folder holds the implementation, `tests/` holds unit tests and acceptance tests, and `docs/` holds the source files the website documentation is built from.

The documentation arrangement is unusual enough to mention. The site at framework-x.org is generated from the `docs/` folder in this repository, and a separate website repository, `clue/framework-x-website`, handles fixes to the published site. Anyone reporting a typo on the site is pointed at the docs folder first and the website repository second. The site is not a third-party manual, so corrections to the framework documentation go through the same pull request path as code changes.

## When a micro framework is the wrong size of tool

There are clear cases where this framework is the wrong answer, and they are the mirror image of its strengths.

If you need request routing, a JSON or PSR-7 response, and nothing else, the single file quickstart is hard to argue with. The cost appears as soon as you want the other half of a web application. There is no template engine, no form builder, no ORM and no authentication scaffolding in the shipped code, and the top level of the repository confirms that by listing no such directories. A team that expects those to exist will spend its first week writing them, and the framework's simplicity will read as absence rather than as a clean core.

The alternative approach is to pick a full stack framework and accept more structure, more configuration and a larger dependency surface in exchange for those pieces existing. The difference in approach is real: Framework X hands you a request handler and expects you to choose everything around it, while a full stack framework makes most of those choices for you and offers conventions to follow. Neither is better in the abstract. The deciding question is whether you want to own that layer.

The other wrong-tool case is deployment. The `php public/index.php` process is the frame of reference the quickstart uses, and while the README says the application also works behind traditional web server setups, it does not document production configuration for any particular server in the README itself. The deployment documentation referenced by the integration test section is where that lives. Read it before you point a real domain at this.

## Conclusion

Framework X suits PHP developers who want a reactive, event-driven HTTP service with a small dependency surface and who are comfortable wiring PSR-7 responses by hand. It does not suit teams that want a template engine, an ORM or a form helper waiting for them, and the stand-alone `php public/index.php` process is not a production server on its own. Before adopting it, read `CHANGELOG.md` for the upgrade path between the `^0.17` releases, then serve the `public/index.php` example behind your own web server and run `$ tests/integration.bash http://localhost:8080/` to confirm the framework behaves the way your setup expects.

## FAQ

### How do I install Framework X with Composer?

Run `composer require clue/framework-x:^0.17` in your project directory, then create a `public/` directory with an `index.php` file that requires `vendor/autoload.php` and calls `$app->run()`. The README notes the CHANGELOG for details about version upgrades.

### How do I run a Framework X application locally?

The README shows the example served by the built-in web server with `$ php public/index.php`, then checked with `$ curl http://localhost:8080/`, which should print the plain text response. The same application also works behind traditional web server setups.

### How do I read a route parameter in Framework X?

Route patterns use placeholders such as `/users/{name}`, and the handler receives a `Psr\Http\Message\ServerRequestInterface` from which you read the value with `$request->getAttribute('name')`.

### How do I run the Framework X test suite?

Clone the repository, run `composer install`, then run `$ vendor/bin/phpunit` from the project root. A coverage report needs Xdebug and uses `$ XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-text`, and PHPStan runs at max level via `$ vendor/bin/phpstan`.

### What licence is Framework X released under?

The README states the project is released under the permissive MIT licence, and the licence field on the repository says MIT. That covers modification and redistribution in commercial or closed source applications, subject to the notice requirement.

## Sources

- [clue/framework-x on GitHub](https://github.com/clue/framework-x)
- [License: MIT](https://github.com/clue/framework-x/blob/main/LICENSE)
- [Project website](https://framework-x.org)
- [README](https://github.com/clue/framework-x/blob/main/README.md)
- [Releases](https://github.com/clue/framework-x/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/clue-framework-x
