https://github.com/radiergummi/shore
Shore is a lean PHP framework for APIs.
https://github.com/radiergummi/shore
api api-server framework-php middleware php php-router router
Last synced: 7 months ago
JSON representation
Shore is a lean PHP framework for APIs.
- Host: GitHub
- URL: https://github.com/radiergummi/shore
- Owner: Radiergummi
- Created: 2018-10-03T10:58:06.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2018-10-08T22:51:21.000Z (over 7 years ago)
- Last Synced: 2025-06-07T07:40:58.248Z (8 months ago)
- Topics: api, api-server, framework-php, middleware, php, php-router, router
- Language: PHP
- Size: 150 KB
- Stars: 8
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Shore
A lean framework for PHP-driven APIs
--------
Shore is an opinionated framework for building APIs. It get's out of your way mostly, as it only takes care of handling
requests and responses.
As any other web framework, Shore lets you define _routes_ that can be hooked to closures or controllers. Route handlers
will always be called in context of the application container, which holds the services you registered.
Of course, there's support for middleware, too.
**Beware, though: Shore does _not_ implement PSR-7 HTTP messages. If that's a killer for you, I'm sorry.** The
reasoning being, I find the responses way too bloated. I don't want to use streams as response bodies, but that prevents
me from implementing PSR-7 all together, as well as the PSR-15 middleware standard - since that relies on PSR-7.
So if you're looking for a small alternative to Laravel, Symfony at al to build your system upon however, you've come to
the right place.
## Features
- Shore is _fast_: Currently, the whole framework stack takes around 0.01s to execute, including setup, route matching
and sending the responses.
- A framework you can understand: Take a look at the code, hack it, whatever. Everything's simple, thoroughly
documented and open for exploration.
- No fixed layout whatsoever: There are three constants in the [index.php](./public/index.php) that define where
the code for your application is to be found. Just add some routes and build up the layout you desire.
- Real-world way to do things: No, Shore won't let you inject five different dispatchers to send XML/JSON/RPC/HTML.
Sorry. It sends JSON or strings, just like that.
## Getting started
Clone this repository at the root of your new project, add routes in the main route file and you're ready. To follow the
flow of requests, start at [`public/index.php`](./public/index.php). Comments will guide you.
## Defining routes
All routing has to happen in the main routes file which is `require_once`'d in [`app/bootstrap`](./app/bootstrap.php).
Let's look at an example:
```php
get('title')
Router::get('/books/{title}', '\Shore\App\Controllers\BooksController@byTitle');
// Separated by a colon, you can pass arbitrary regular expression constraints for your placeholders
Router::get('/books/{id:\d+}', '\Shore\App\Controllers\BooksController@byId');
// Add a new route group. The group URI will be prefixed on all routes within the callback. Infinitely nestable!
Router::group('/authors', function() {
// This route will be available at /authors/search, as it's grouped
Router::get('/search', '\Shore\App\Controllers\AuthorsController@search');
});
```
## Creating route handlers
Route handlers are callables or controller methods that can handle requests. They can either return the response
directly (anything _not_ a string will trigger the response to be sent as JSON automatically) or modify the response
object and return that.
```php
withBody('Hello world');
};
```
Requests and responses both implement most of the PSR-7 style messages, except the cloning and stream parts.
## Creating and using middleware
Middlewares are layers any request or response has to pass through. Each middleware can decide whether to send a
response or pass on to the next one. This allows for neat use cases such as authentication, CSRF or trailing slash
redirection. Shore has first-class middleware support, in fact, the whole route handling happens inside the
[Kernel middleware](./lib/Http/Kernel.php).
Middleware can be supplied as classes implementing the `MiddlewareInterface` or simply callables. To return a response,
they'll need to return the response object. To pass on, they need to call the `$handler->next($request);` method. Easy.
```php
get('token')) {
return $handler->next($request);
}
return Response::error('Token missing from request', Response::STATUS_UNAUTHORIZED);
};
```
To load middleware into your stack, add the `middleware` key to your application config array and insert middleware
instances into that:
```php
[
new MyFirstMiddleware(),
new MySecondMiddleware($withConfiguration)
]
];
```
They will be loaded in order of occurrence.
## Dependency injection
At the core of Shore sits the `Application` instance which implements the PSR-11 container interface. It holds all your
services and is set as the context of any route handler. What is a service, you ask? Well, just anything, identified by
a string. You can use that string to retrieve services throughout the lifecycle of your app.
To stick with the DI paradigm, it's often a good idea to use the class path of an interface as the service ID:
`DatabaseInterface::class`, for example. Should you switch out the database later on, you'll only need to do so in one
place, without risking missing it somewhere.
To add your own services, add the `services` key to your application config array and insert your services into that:
```php
[
'database' => new DatabaseConnection($dbConfig),
MessageQueueInterface::class => new RedisQueue($redisConfig)
]
];
```
They will be registered with the name you passed as the key, so inside any route handler, you can use the following to
access your service:
```php
// ...
$db = $this->get('database'); // Wham! There is your configured instance.
```
## Facades
Maybe you've spotted the `Facade` classes already. These are special classes that allow accessing instance methods on
your services via static calls, which makes certain aspects easier to handle - without giving up on dependency
injection. Custom facades only need to implement a single call that returns the name of the service they wish to
provide, everything else is handled automatically.
You don't have to use the facades feature, but it will make your life easier. Let's create a fully functional facade for
the database service from the previous section:
```php