PHPackages                             brick/app - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Templating &amp; Views](/categories/templating)
4. /
5. brick/app

AbandonedArchivedLibrary[Templating &amp; Views](/categories/templating)

brick/app
=========

Web application framework

0.8.2(1y ago)251.7k5MITPHPPHP ^8.0CI passing

Since Nov 3Pushed 1y ago1 watchersCompare

[ Source](https://github.com/brick/app)[ Packagist](https://packagist.org/packages/brick/app)[ RSS](/packages/brick-app/feed)WikiDiscussions master Synced 3w ago

READMEChangelog (10)Dependencies (7)Versions (23)Used By (0)

Brick\\App
==========

[](#brickapp)

[![](https://raw.githubusercontent.com/brick/brick/master/logo.png)](https://raw.githubusercontent.com/brick/brick/master/logo.png)

A web application framework.

[![Build Status](https://github.com/brick/app/workflows/CI/badge.svg)](https://github.com/brick/app/actions)[![Coverage Status](https://camo.githubusercontent.com/c55b02b4bc295452f1e70d6fb25b1919f1a59d9124c9a465c9b0a44ee9590a1e/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f6769746875622f627269636b2f6170702f62616467652e7376673f6272616e63683d6d6173746572)](https://coveralls.io/github/brick/app?branch=master)[![Latest Stable Version](https://camo.githubusercontent.com/31716354e17e181dacb538db2d500f17eec0c3257ee527c26fbe17d087ee968c/68747470733a2f2f706f7365722e707567782e6f72672f627269636b2f6170702f762f737461626c65)](https://packagist.org/packages/brick/app)[![License](https://camo.githubusercontent.com/7013272bd27ece47364536a221edb554cd69683b68a46fc0ee96881174c4214c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e737667)](http://opensource.org/licenses/MIT)

Installation
------------

[](#installation)

This library is installable via [Composer](https://getcomposer.org/):

```
composer require brick/app
```

Requirements
------------

[](#requirements)

This library requires PHP 8.0 or later.

Project status
--------------

[](#project-status)

This library is abandoned and will not receive further development.

Overview
--------

[](#overview)

### Setup

[](#setup)

We assume that you have already installed the library with Composer.

Let's create an `index.php` file that contains the simplest possible application:

```
use Brick\App\Application;

require 'vendor/autoload.php';

$application = Application::create();
$application->run();
```

If you run this file in your browser, you should get a `404` page that details an `HttpNotFoundException`. That's perfectly normal, our application is empty.

Before adding more stuff to our application, let's create a `.htaccess` file to tell Apache to redirect all requests that do not target an existing file, to our `index.php` file:

```
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.*$ /index.php [L]

```

Now if you open any path in your browser, you should get a similar exception page. What we created here is called a [front controller](http://en.wikipedia.org/wiki/Front_Controller_pattern), and is a really handy pattern to ensure that all requests enter your application by the same door.

### Creating controllers

[](#creating-controllers)

A controller is a piece of code that returns a `Response` for an incoming `Request`. This is where you put all the glue logic to work with models, interact with the database, and generate HTML content.

A controller can be any `callable`, but is generally a class with a number of methods that correspond to different pages or actions.

Let's create a simple controller class:

```
namespace MyApp\Controller;

use Brick\Http\Response;

class IndexController
{
    public function indexAction()
    {
        return new Response('This is the index page.');
    }

    public function helloAction()
    {
        return new Response('Hello, world');
    }
}
```

The controller class does not need to extend any particular class, and the only requirement on the controller method is that it must return a `Response` object. This requirement can even be alleviated by using a plugin that creates a Response from the controller return value.

### Adding routes

[](#adding-routes)

The next step is to instruct our application what controller to invoke for a given request. An object that maps a request to a controller is called a `Route`.

The application ships with a few routes that cover the most common use cases. If you have more complex requirements, you can easily write your own routes.

Let's add an off-the-box route that maps a path directory to a controller:

```
use Brick\App\Route\SimpleRoute;

$route = new SimpleRoute([
    '/' => MyApp\Controller\IndexController::class,
]);

$application->addRoute($route);
```

Open your browser at `/`, you should get the index page message. Open your browser at `/hello`, you should get the "Hello, world" message.

### Getting request data

[](#getting-request-data)

Returning information is great, but most of the time you will need to get data from the current request first. It's very easy to get access to the `Request` object, just add it as a parameter to your method:

```
public function helloAction(Request $request)
{
    return new Response('Hello, ' . $request->getQuery('name'));
}
```

Now if you open your browser at `/hello?name=John`, you should get a "Hello, John" message.

### Adding plugins

[](#adding-plugins)

The application can already do interesting things, but is still pretty dumb. Fortunately, there is a great way to extend it with extra functionality: *plugins*.

The application ships with a few useful plugins. Let's have a look at one of them: the `RequestParamPlugin`. This plugin allows you to automatically map request parameters to your controller parameters, with attributes.

Let's add this plugin to our application:

```
use Brick\App\Plugin\RequestParamPlugin;

$plugin = new RequestParamPlugin();
$application->addPlugin($plugin);
```

That's it. Let's update our `helloAction()` once again to use this new functionality:

```
namespace MyApp\Controller;

use Brick\App\Controller\Attribute\QueryParam;
use Brick\Http\Response;

class Index
{
    #[QueryParam('name')]
    public function helloAction(string $name)
    {
        return new Response('Hello, ' . $name);
    }
}
```

If you open your browser at `/hello?name=Bob`, you should get "Hello, Bob". We did not need to interact directly with the Request object anymore. Request variables are now automatically injected in our controller parameters. Magic.

### Writing your own plugins

[](#writing-your-own-plugins)

You can extend the application indefinitely with the use of plugins. It's easy to write your own, as we will see through this example. Let's imagine that we want to create a plugin that begins a PDO transaction before the controller is invoked, and commits it automatically after the controller returns.

First let's have a look at the `Plugin` interface:

```
interface Plugin
{
    public function register(EventDispatcher $dispatcher) : void;
}
```

Just one method to implement. This method allows you to register your plugin inside the application's event dispatcher, that is, tell the application which events it wants to receive. Here is an overview of the events dispatched by the application.

#### IncomingRequestEvent

[](#incomingrequestevent)

This event is dispatched as soon as the application receives a Request.

This event contains the `Request` object.

#### RouteMatchedEvent

[](#routematchedevent)

This event is dispatched after the router has returned a match. If no match is found, the request handling is interrupted, and `ExceptionCaughtEvent` is dispatched with an `HttpNotFoundException`.

This event contains the `Request` and the `RouteMatch` objects.

#### ControllerReadyEvent

[](#controllerreadyevent)

This event is dispatched when the controller is ready to be invoked. If the controller is a class method, the class will have been instantiated and this controller instance is made available to the event.

This event contains the `Request` and the `RouteMatch` objects, and the controller instance *if* the controller is a class method.

#### NonResponseResultEvent

[](#nonresponseresultevent)

This event is dispatched if the controller does not return a `Response` object. This event provides an opportunity for plugins to transform an arbitrary controller result into a `Response` object. For example, it could be used to JSON-encode the controller return value and wrap it into a `Response` object with the proper Content-Type header.

This event contains the `Request` and the `RouteMatch` objects, the controller instance *if* the controller is a class method, and the return value of the controller.

#### ControllerInvocatedEvent

[](#controllerinvocatedevent)

This event is dispatched after controller invocation, regardless of whether an exception was thrown or not.

This event contains the `Request` and the `RouteMatch` objects, and the controller instance *if* the controller is a class method.

#### ResponseReceivedEvent

[](#responsereceivedevent)

This event is dispatched after the controller response has been received. If an `HttpException` is caught during the controller method invocation, the exception it is converted to a `Response`, and this event is dispatched as well. Other exceptions break the application flow and don't trigger this event.

This event contains the `Request`, `Response` and `RouteMatch` objects, and the controller instance *if* the controller is a class method.

#### ExceptionCaughtEvent

[](#exceptioncaughtevent)

This event is dispatched if an exception is caught. If the exception is not an `HttpException`, it is wrapped in an `HttpInternalServerErrorException` first, so that this event always receives an `HttpException`. A default response is created to display the details of the exception.

This event provides an opportunity to modify the default response to present a customized error message to the client.

This event contains the `HttpException`, `Request` and `Response` objects.

---

We can see that the two events that are called immediately before and after the controller is invoked are:

- `ControllerReadyEvent`
- `ControllerInvocatedEvent`

What we just need to do is to map each of these events to a function that does the job. Let's do it:

```
use Brick\App\Event\ControllerReadyEvent;
use Brick\App\Event\ControllerInvocatedEvent;
use Brick\App\Plugin;
use Brick\Event\EventDispatcher;

class TransactionPlugin implements Plugin
{
    private $pdo;

    public function __construct(\PDO $pdo)
    {
        $this->pdo = $pdo;
    }

    public function register(EventDispatcher $dispatcher) : void
    {
        $dispatcher->addListener(ControllerReadyEvent::class, function() {
            $this->pdo->beginTransaction();
        });

        $dispatcher->addListener(ControllerInvocatedEvent::class, function() {
            $this->pdo->commit();
        });
    }
}
```

Easy as pie! Let's add our plugin to our application:

```
$pdo = new PDO(/* insert parameters to connect to your database */);
$plugin = new TransactionPlugin($pdo);
$application->addPlugin($plugin);
```

We just implemented a plugin, available to all controllers in our application, in no time. This implementation is of course still naive, but does what it says on the tin, and is a good starting point for more advanced functionality.

### Sessions

[](#sessions)

The framework features a powerful alternative to native PHP sessions, allowing synchronized and non-blocking read/write to individual session entries, and supports a pluggable storage mechanism.

#### What's wrong with native PHP sessions?

[](#whats-wrong-with-native-php-sessions)

Native sessions are stored in a single block of data, and **session files are locked for the entire duration of the PHP script**. As a consequence, all requests for a single session are serialized: if several requests targeting the same session are received concurrently, they are queued and processed one after the other. This is good enough in a traditional page-to-page browsing situation, but may cause bottlenecks when a web page issues potentially concurrent HTTP calls using AJAX.

Brick\\App's session manager works differently:

- each key-value pair in the session is stored independently
- each key-value pair is only loaded when explicitly requested
- each key-value pair can be read or written without locking using `has()`, `get()`, `set()` and `remove()`
- when locking is required, a key-value pair can be read and written using the `synchronize()` method:

```
    $session->synchronize('session-key', function($currentValue) {
        // ...
        return $newValue;
    });
```

**Only the given key is locked**, and **the lock is released as soon as the function returns.**

#### Installing the session plugin

[](#installing-the-session-plugin)

To store your sessions in the filesystem, alongside traditional PHP sessions, just use:

```
use Brick\App\Session\CookieSession;
use Brick\App\Plugin\SessionPlugin;

$session = new CookieSession();
$app->addPlugin(new SessionPlugin($session));
```

You can alternatively provide a custom storage adapter and use `new CookieSession($storage)` instead. A filesystem adapter and a database (PDO) adapter are provided; you can also write your own adapter by implementing `SessionStorage`.

#### Using the sessions

[](#using-the-sessions)

If you're using dependency injection in your app, you can have the `Session` object passed to your controller easily. Just register the container in your application, and instruct it to resolve sessions:

```
use Brick\DI\Container;
use Brick\App\Application;
use Brick\App\Session\CookieSession;
use Brick\App\Session\Session;
use Brick\App\Plugin\SessionPlugin;

// Create a DI container, and use it with our app
$container = Container::create();
$app = Application::create($container);

// Create a session, add the session plugin to our app
$session = new CookieSession();
$app->addPlugin(new SessionPlugin($session));

// Instruct the DI container to resolve the Session object
$container->set(Session::class, $session);
```

Now the app can resolve your session automatically in your controller functions:

```
public function indexAction(Request $request, Session $session)
{
    $userId = $session->get('user-id');
    // ...
}
```

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance43

Moderate activity, may be stable

Popularity26

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity68

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~130 days

Recently: every ~377 days

Total

21

Last Release

550d ago

PHP version history (4 changes)0.1.0PHP &gt;=7.1

0.5.0PHP &gt;=7.2

0.7.0PHP &gt;=7.4

0.8.0PHP ^8.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/57189121968030f0770811b461cc92f9c19c08f5c4767292f2ede48b7277cfad?d=identicon)[BenMorel](/maintainers/BenMorel)

---

Top Contributors

[![BenMorel](https://avatars.githubusercontent.com/u/1952838?v=4)](https://github.com/BenMorel "BenMorel (263 commits)")

---

Tags

phpweb-application-frameworkbrickviewappapplicationcontroller

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/brick-app/health.svg)

```
[![Health](https://phpackages.com/badges/brick-app/health.svg)](https://phpackages.com/packages/brick-app)
```

###  Alternatives

[aplus/mvc

Aplus Framework MVC Library

2621.6M3](/packages/aplus-mvc)[laminas/laminas-view

Fast and type safe HTML templating library with a flexible plugin system supporting multistep template composition

7528.4M268](/packages/laminas-laminas-view)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
