{"id":19764369,"url":"https://github.com/brick/app","last_synced_at":"2025-05-12T14:32:32.755Z","repository":{"id":20733918,"uuid":"24018163","full_name":"brick/app","owner":"brick","description":"Web application framework for PHP","archived":false,"fork":false,"pushed_at":"2024-12-30T23:42:40.000Z","size":250,"stargazers_count":25,"open_issues_count":0,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-31T23:19:20.093Z","etag":null,"topics":["php","web-application-framework"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"jabbany/CommentCoreLibrary","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brick.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-09-14T09:22:15.000Z","updated_at":"2024-12-30T23:41:24.000Z","dependencies_parsed_at":"2024-12-31T00:24:43.638Z","dependency_job_id":"c55cbdec-2030-46b2-b33a-5208ab4840be","html_url":"https://github.com/brick/app","commit_stats":{"total_commits":238,"total_committers":1,"mean_commits":238.0,"dds":0.0,"last_synced_commit":"db802d41c85a56a012142bc65db0442449f514f7"},"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brick%2Fapp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brick%2Fapp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brick%2Fapp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brick%2Fapp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brick","download_url":"https://codeload.github.com/brick/app/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253754966,"owners_count":21958934,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["php","web-application-framework"],"created_at":"2024-11-12T04:13:38.137Z","updated_at":"2025-05-12T14:32:32.689Z","avatar_url":"https://github.com/brick.png","language":"PHP","readme":"Brick\\App\n=========\n\n\u003cimg src=\"https://raw.githubusercontent.com/brick/brick/master/logo.png\" alt=\"\" align=\"left\" height=\"64\"\u003e\n\nA web application framework.\n\n[![Build Status](https://github.com/brick/app/workflows/CI/badge.svg)](https://github.com/brick/app/actions)\n[![Coverage Status](https://coveralls.io/repos/github/brick/app/badge.svg?branch=master)](https://coveralls.io/github/brick/app?branch=master)\n[![Latest Stable Version](https://poser.pugx.org/brick/app/v/stable)](https://packagist.org/packages/brick/app)\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](http://opensource.org/licenses/MIT)\n\nInstallation\n------------\n\nThis library is installable via [Composer](https://getcomposer.org/):\n\n```bash\ncomposer require brick/app\n```\n\nRequirements\n------------\n\nThis library requires PHP 8.0 or later.\n\n\nProject status \u0026 release process\n--------------------------------\n\nThis library is still under development.\n\nThe current releases are numbered `0.x.y`. When a non-breaking change is introduced (adding new methods, optimizing existing code, etc.), `y` is incremented.\n\n**When a breaking change is introduced, a new `0.x` version cycle is always started.**\n\nIt is therefore safe to lock your project to a given release cycle, such as `0.6.*`.\n\nIf you need to upgrade to a newer release cycle, check the [release history](https://github.com/brick/app/releases)\nfor a list of changes introduced by each further `0.x.0` version.\n\nOverview\n--------\n\n### Setup\n\nWe assume that you have already installed the library with Composer.\n\nLet's create an `index.php` file that contains the simplest possible application:\n\n```php\nuse Brick\\App\\Application;\n\nrequire 'vendor/autoload.php';\n\n$application = Application::create();\n$application-\u003erun();\n```\n\nIf 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.\n\nBefore 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:\n\n```\nRewriteEngine on\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteCond %{REQUEST_FILENAME} !-d\nRewriteRule ^.*$ /index.php [L]\n```\n\nNow 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.\n\n### Creating controllers\n\nA 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.\n\nA controller can be any `callable`, but is generally a class with a number of methods that correspond to different pages or actions.\n\nLet's create a simple controller class:\n\n```php\nnamespace MyApp\\Controller;\n\nuse Brick\\Http\\Response;\n\nclass IndexController\n{\n    public function indexAction()\n    {\n        return new Response('This is the index page.');\n    }\n\n    public function helloAction()\n    {\n        return new Response('Hello, world');\n    }\n}\n```\n\nThe 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.\n\n### Adding routes\n\nThe 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`.\n\nThe 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.\n\nLet's add an off-the-box route that maps a path directory to a controller:\n\n```php\nuse Brick\\App\\Route\\SimpleRoute;\n\n$route = new SimpleRoute([\n    '/' =\u003e MyApp\\Controller\\IndexController::class,\n]);\n\n$application-\u003eaddRoute($route);\n```\n\nOpen your browser at `/`, you should get the index page message.\nOpen your browser at `/hello`, you should get the \"Hello, world\" message.\n\n### Getting request data\n\nReturning 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:\n\n```php\npublic function helloAction(Request $request)\n{\n    return new Response('Hello, ' . $request-\u003egetQuery('name'));\n}\n```\n\nNow if you open your browser at `/hello?name=John`, you should get a \"Hello, John\" message.\n\n### Adding plugins\n\nThe application can already do interesting things, but is still pretty dumb. Fortunately, there is a great way to extend it with extra functionality: *plugins*.\n\nThe 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.\n\nLet's add this plugin to our application:\n\n```php\nuse Brick\\App\\Plugin\\RequestParamPlugin;\n\n$plugin = new RequestParamPlugin();\n$application-\u003eaddPlugin($plugin);\n```\n\nThat's it. Let's update our `helloAction()` once again to use this new functionality:\n\n```php\nnamespace MyApp\\Controller;\n\nuse Brick\\App\\Controller\\Attribute\\QueryParam;\nuse Brick\\Http\\Response;\n\nclass Index\n{\n    #[QueryParam('name')]\n    public function helloAction(string $name)\n    {\n        return new Response('Hello, ' . $name);\n    }\n}\n```\n\nIf 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.\n\n### Writing your own plugins\n\nYou 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.\n\nFirst let's have a look at the `Plugin` interface:\n\n```php\ninterface Plugin\n{\n    public function register(EventDispatcher $dispatcher) : void;\n}\n```\n\nJust 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.\n\n#### IncomingRequestEvent\n\nThis event is dispatched as soon as the application receives a Request.\n\nThis event contains the `Request` object.\n\n#### RouteMatchedEvent\n\nThis 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`.\n\nThis event contains the `Request` and the `RouteMatch` objects.\n\n#### ControllerReadyEvent\n\nThis 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.\n\nThis event contains the `Request` and the `RouteMatch` objects, and the controller instance *if* the controller is a class method.\n\n#### NonResponseResultEvent\n\nThis 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.\n\nThis 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.\n\n#### ControllerInvocatedEvent\n\nThis event is dispatched after controller invocation, regardless of whether an exception was thrown or not.\n\nThis event contains the `Request` and the `RouteMatch` objects, and the controller instance *if* the controller is a class method.\n\n#### ResponseReceivedEvent\n\nThis 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.\n\nThis event contains the `Request`, `Response` and `RouteMatch` objects, and the controller instance *if* the controller is a class method.\n\n#### ExceptionCaughtEvent\n\nThis 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.\n\nThis event provides an opportunity to modify the default response to present a customized error message to the client.\n\nThis event contains the `HttpException`, `Request` and `Response` objects.\n\n---\n\nWe can see that the two events that are called immediately before and after the controller is invoked are:\n\n- `ControllerReadyEvent`\n- `ControllerInvocatedEvent`\n\nWhat we just need to do is to map each of these events to a function that does the job. Let's do it:\n\n```php\nuse Brick\\App\\Event\\ControllerReadyEvent;\nuse Brick\\App\\Event\\ControllerInvocatedEvent;\nuse Brick\\App\\Plugin;\nuse Brick\\Event\\EventDispatcher;\n\nclass TransactionPlugin implements Plugin\n{\n    private $pdo;\n\n    public function __construct(\\PDO $pdo)\n    {\n        $this-\u003epdo = $pdo;\n    }\n\n    public function register(EventDispatcher $dispatcher) : void\n    {\n        $dispatcher-\u003eaddListener(ControllerReadyEvent::class, function() {\n            $this-\u003epdo-\u003ebeginTransaction();\n        });\n\n        $dispatcher-\u003eaddListener(ControllerInvocatedEvent::class, function() {\n            $this-\u003epdo-\u003ecommit();\n        });\n    }\n}\n```\n\nEasy as pie! Let's add our plugin to our application:\n\n```php\n$pdo = new PDO(/* insert parameters to connect to your database */);\n$plugin = new TransactionPlugin($pdo);\n$application-\u003eaddPlugin($plugin);\n```\n\nWe 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.\n\n### Sessions\n\nThe 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.\n\n#### What's wrong with native PHP sessions?\n\nNative sessions are stored in a single block of data, and **session files are locked for the entire duration of the PHP script**.\nAs 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.\nThis 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.\n\nBrick\\App's session manager works differently:\n\n- each key-value pair in the session is stored independently\n- each key-value pair is only loaded when explicitly requested\n- each key-value pair can be read or written without locking using `has()`, `get()`, `set()` and `remove()`\n- when locking is required, a key-value pair can be read and written using the `synchronize()` method:\n\n```php\n    $session-\u003esynchronize('session-key', function($currentValue) {\n        // ...\n        return $newValue;\n    });\n```\n\n**Only the given key is locked**, and **the lock is released as soon as the function returns.**\n\n#### Installing the session plugin\n\nTo store your sessions in the filesystem, alongside traditional PHP sessions, just use:\n\n```php\nuse Brick\\App\\Session\\CookieSession;\nuse Brick\\App\\Plugin\\SessionPlugin;\n\n$session = new CookieSession();\n$app-\u003eaddPlugin(new SessionPlugin($session));\n```\n\nYou 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`.\n\n#### Using the sessions\n\nIf you're using dependency injection in your app, you can have the `Session` object passed to your controller easily.\nJust register the container in your application, and instruct it to resolve sessions:\n\n```php\nuse Brick\\DI\\Container;\nuse Brick\\App\\Application;\nuse Brick\\App\\Session\\CookieSession;\nuse Brick\\App\\Session\\Session;\nuse Brick\\App\\Plugin\\SessionPlugin;\n\n// Create a DI container, and use it with our app\n$container = Container::create();\n$app = Application::create($container);\n\n// Create a session, add the session plugin to our app\n$session = new CookieSession();\n$app-\u003eaddPlugin(new SessionPlugin($session));\n\n// Instruct the DI container to resolve the Session object \n$container-\u003eset(Session::class, $session);\n```\n\nNow the app can resolve your session automatically in your controller functions:\n\n```php\npublic function indexAction(Request $request, Session $session)\n{\n    $userId = $session-\u003eget('user-id');\n    // ...\n}\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrick%2Fapp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrick%2Fapp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrick%2Fapp/lists"}