diff --git a/docs/index.html b/docs/index.html index 6d3cc25..f8091d0 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,10 +1,351 @@ - + - - Docs + + + ShiftPHP Developer Documentation +
+

ShiftPHP Developer Documentation

+

ShiftPHP is an API-only PHP 8.3 framework for modular monolith applications.

+
+ + +
+
+

Requirements

+ +
+ +
+

Architecture

+

The public framework namespace is Shift\. Composer currently maps it to the Engine/ directory.

+

Application code lives under application/. Modules live under application/modules/{ModuleName} and are autoloaded with the Modules\ namespace.

+ +

Request Flow

+
Request
+  -> Shift\App
+  -> Middleware pipeline
+  -> Router
+  -> Controller action
+  -> Response
+  -> ResponseEmitter
+ +

Core Namespaces

+
+
Shift
+
Application kernel, request object, and base controller.
+
Shift\Response
+
Response objects and response emitter.
+
Shift\Routing
+
Attribute route loader and routing attributes.
+
Shift\Routing\Router
+
Router, route, and route match objects.
+
Shift\Middleware
+
Middleware contract and middleware pipeline.
+
Shift\Modules
+
Module contracts and module loader.
+
Shift\Service
+
Small service container and service interface.
+
Shift\Console
+
CLI command dispatcher and built-in commands.
+
Shift\Error
+
HTTP and framework error handling.
+
+
+ +
+

Bootstrap

+

The HTTP entry point is index.php. It creates a request, creates the app, loads modules, registers module services and routes, then starts the app.

+ +
use Shift\App;
+use Shift\Modules\ModuleLoader;
+use Shift\Request;
+
+require_once 'bootstrap.php';
+
+$request = new Request();
+$app = new App($request);
+
+$modules = (new ModuleLoader())->load();
+$modules->registerServices($app->getContainer());
+$modules->registerRoutes($app->getRouter());
+
+$app->start();
+ +

Run the application locally with PHP's built-in server:

+
php -S 127.0.0.1:8000 index.php
+
+ +
+

Modules

+

A module owns its controllers, routes, services, and CLI commands.

+ +
application/modules/Health/
+|-- Module.php
+|-- Controllers/
+|-- Services/
+`-- Commands/
+ +

Every module boundary implements Shift\Modules\ModuleInterface. Most modules can extend Shift\Modules\AbstractModule and override only the methods they need.

+ +
namespace Modules\Health;
+
+use Shift\Modules\AbstractModule;
+use Shift\Routing\AttributeRouteLoader;
+use Shift\Routing\Router\Router;
+use Shift\Service\ServiceContainer;
+use Modules\Health\Controllers\HealthController;
+use Modules\Health\Services\HealthService;
+
+class Module extends AbstractModule
+{
+    public function getName(): string
+    {
+        return 'health';
+    }
+
+    public function registerServices(ServiceContainer $container): void
+    {
+        $container->singleton(HealthService::class, HealthService::class);
+    }
+
+    public function registerRoutes(Router $router): void
+    {
+        (new AttributeRouteLoader())->load($router, [
+            HealthController::class,
+        ]);
+    }
+
+    public function getCommandMappings(): array
+    {
+        return [
+            [
+                'dir' => __DIR__ . '/Commands/',
+                'namespace' => 'Modules\\Health\\Commands\\',
+            ],
+        ];
+    }
+}
+ +

Shift\Modules\ModuleLoader discovers modules by convention from application/modules/*/Module.php.

+
+ +
+

Routing

+

Routes are registered on Shift\Routing\Router\Router. Modules usually register routes through attributes and Shift\Routing\AttributeRouteLoader.

+ +

Supported HTTP Method Attributes

+ + +

Route Metadata Attributes

+ + +

Parameter Binding Attributes

+ + +
#[RoutePrefix('/users')]
+final class UserController extends Controller
+{
+    #[Get('/{id}')]
+    public function show(
+        #[PathParam] int $id,
+        #[QueryParam('include')] ?string $include = null
+    ): JsonResponse {
+        return $this->json([
+            'id' => $id,
+            'include' => $include,
+        ]);
+    }
+}
+ +

The router supports path placeholders such as /users/{id}. Unsupported HTTP methods return 405 Method Not Allowed with an Allow header.

+
+ +
+

Controllers

+

Controllers extend Shift\Controller. The current Shift\Request and Shift\Service\ServiceContainer are injected through the constructor.

+ +

Controller actions can return:

+ + +

Controller Helpers

+ +
+ +
+

Requests

+

Shift\Request wraps server data, query values, post data, route parameters, headers, and JSON request bodies.

+ +
$request->getMethod();
+$request->getPath();
+$request->getQueryParams();
+$request->getPostData();
+$request->query('page', 1);
+$request->post('name');
+$request->input('name');
+$request->getRawBody();
+$request->getJson();
+$request->getHeader('Authorization');
+$request->getUserAgent();
+$request->getIpAddress();
+$request->getRouteParams();
+$request->routeParam('id');
+ +

getJson() returns an empty array for an empty body. Malformed JSON throws an HTTP error and is returned as 400 Bad Request.

+
+ +
+

Responses

+

Shift\Response\Response stores response content, status code, and headers. Shift\Response\ResponseEmitter emits those values to PHP's HTTP response.

+ +
use Shift\Response\Response;
+use Shift\Response\JsonResponse;
+
+return new Response('Accepted', 202, ['X-State' => 'queued']);
+return JsonResponse::ok(['status' => 'ok']);
+return JsonResponse::created(['id' => 10]);
+return JsonResponse::error('Invalid payload', 422);
+ +

JsonResponse automatically sets Content-Type: application/json.

+
+ +
+

Middleware

+

Middleware runs before controller dispatch. It can continue the request by calling $next($request), modify the returned response, or return a response immediately.

+ +
namespace Modules\Users\Middleware;
+
+use Shift\Middleware\MiddlewareInterface;
+use Shift\Request;
+use Shift\Response\JsonResponse;
+use Shift\Response\Response;
+
+final class AuthMiddleware implements MiddlewareInterface
+{
+    public function handle(Request $request, callable $next): Response
+    {
+        if ($request->getHeader('Authorization') === null) {
+            return JsonResponse::error('Unauthorized', 401);
+        }
+
+        return $next($request);
+    }
+}
+ +

Register class middleware on the app:

+
$app->middleware(AuthMiddleware::class);
+ +

Callable middleware is also supported:

+
$app->middleware(function (Request $request, callable $next): Response {
+    $response = $next($request);
+
+    return new Response(
+        $response->getContent(),
+        $response->getStatusCode(),
+        $response->getHeaders() + ['X-Api' => 'Shift']
+    );
+});
+ +

Middleware may be a class string, an object implementing MiddlewareInterface, or a callable. Class strings are resolved from the service container when registered there.

+
+ +
+

Service Container

+

Shift\Service\ServiceContainer stores regular services and singletons. It can resolve closures, class names, and already-created objects.

+ +
$container->register(UserRepository::class, UserRepository::class);
+$container->singleton(HealthService::class, HealthService::class);
+$container->singleton('request', $request);
+
+$service = $container->resolve(HealthService::class);
+$exists = $container->has(HealthService::class);
+ +

The app registers the current request and router as default singleton services under request and router.

+
+ +
+

CLI

+

The CLI entry point is shift.php. Built-in commands live under Shift\Console\Commands, and module commands are loaded from module command mappings.

+ +
php shift.php route:list
+php shift.php health
+ +

Commands implement Shift\Console\CommandInterface.

+
+ +
+

Errors

+

Framework HTTP errors are represented by Shift\Error\HttpError. The app normalizes HTTP errors to JSON responses.

+ +
{
+  "error": {
+    "message": "Endpoint not found",
+    "status": 404
+  }
+}
+ +

Malformed JSON returns 400. Missing routes return 404. Wrong methods return 405 with an Allow header. Unexpected runtime errors return a generic 500 Internal Server Error.

+
+ +
+

Testing

+

The current lightweight test suite is in tests/ApiCoreTest.php.

+ +
composer test
+ +

The API workflow also validates Composer configuration, dumps autoload files, lints PHP files, runs the API tests, and verifies the route list command.

+
+
- \ No newline at end of file +