rodriguezric / corto
A small framework based on functions.
Installs: 5
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 2
Forks: 0
Open Issues: 0
pkg:composer/rodriguezric/corto
This package is auto-updated.
Last update: 2025-12-07 23:47:18 UTC
README
Synopsis
Corto is a microframework for building REST APIs. The philosophy for this framework is built around using functions, singletons and static calls to objects to replace dependencies on global variables. This framework does not attempt to make a large, object-oriented solution, rather, an opinionated approach to chaining functions from requests.
Classes
There are two classes in this framework: Request and Pipe. Everything
else is a function.
Request
The Request object is a singleton that extracts the URI and HTTP
method from the #$_SERVER global variable, and converts php://input
into an array. It expects the input to be JsonSerializable.
Pipe
The Pipe is an invokable object is has a mixed property called $value.
Invoking the object returns a callable that mutates $value and then
returns $this. This allows to create a succinct syntax for piping
functions on a value:
(new Pipe(10)) (fn($x) => $x * 2); // returns 20
Functions
Corto is built around a workflow of functions:
requestfor retrieving request data.routefor listening to HTTP requests, parses paths and returning responses.responsefor setting the HTTP code and returning JSON.
request
Calling request() returns the Request singleton. When creating a
function request()->input is used for accessing the data sent.
route
The route function is the most complex in the framework. It’s a curried
function that starts by describing the HTTP method:
route('GET');
This returns a function that accepts two parameters: the path and the callback.
Paths are strings that are compared to the URI of the request. There are two
types of paths: literal paths and variable paths.
Literal Paths
Literal paths have no variables included and are compared directly with
the URI. An example of a literal path is /path/to/resource.
route('GET')('/path/to/resource', ...);
Variable Paths
Variable paths include curly braces to represent values to path to the
callback function. Variable paths are converted to regular expressions
before being compared to the URI. An example of a variable path is
/path/to/resource/{variable}
route('GET')('/path/to/resource/{variable}', ...);
Note the word between the curly braces is meaningless, any word between the braces can be used to represent that value. If there are more than one variable included in a variable path they are passed to the callback in order.
response
The response function is a curried function that starts with the HTTP response code.
The callable returned by the function expect either array or JsonSerializable data.
It sets the HTTP response code and then converts the data into JSON, echoing the
JSON format. After the echo is complete, it exits.
This function is tightly coupled to JSON output and also terminates the program.
I am comfortable with these decisions, keeping the framework strictly defined
for one purpose: delivering JSON responses based on requests.
Example
Here is an example of how to implement the features described in the Functions section.
This would go in your public-facing script, like index.php
<?php declare(strict_types=1); include __DIR__ . "/../vendor/autoload.php"; use function Rodriguezric\Corto\{route, route_chain, response, request}; set_exception_handler( function($exception) { file_put_contents("php://stderr", $exception->getMessage()); return response(400)(['error' => 'Exception thrown.']); } ); $hello = fn() => response(200)(['message' => 'Hello']); route('GET')('/', $hello); $create_person = function() { if (request()->is_missing('name')) { return response(400)(['error' => 'Missing arguments']); } $name = request()->input['name']; return response(200)(['message' => "Created new person with name {$name}"]); }; route('POST')('/person', $create_person); $update_person = fn($id) => response(200)(['message' => "Updated person with id: {$id}"]); route('PUT')('/person/{id}', $update_person); response(400)(['message' => 'Resource not found.']);
I’ll explain some pieces of the example to elaborate on their purpose.
Exception Handling
set_exception_handler( function($exception) { file_put_contents("php://stderr", $exception->getMessage()); return response(400)(['error' => 'Exception thrown.']); } );
This piece will send a 400 response to the requestor with a JSON response:
{
"error": "Exception thrown."
}
Meanwhile, on the server it will send the actual exception message to standard error. I separate the two so I can view the error in my container log, give a response to the requestor that let’s them know there was an error but still hide the details from them.
Request Examples
GET
$hello = fn() => response(200)(['message' => 'Hello']); route('GET')('/', $hello);
This section sets up a simple GET request and passes a function as
the callable. The callable returns the result of a response call.
The function response echos its results and terminates the script.
POST
$create_person = function() { if (request()->is_missing('name')) { return response(400)(['error' => 'Missing arguments']); } $name = request()->input['name']; return response(200)(['message' => "Created new person with name {$name}"]); }; route('POST')('/person', $create_person);
This section sets up a POST request. The callable tests the request to
make sure the field name is in the request. If it is missing, it sends
a 400 HTTP response code along with a JSON message representing the error:
{
"error": "Missing arguments."
}
If the name property is in the request, it uses that value to create its
response. This example is contrived in that it only displays JSON with the
name supplied, normally we would perform our work here and send a response
with useful information.
PUT
$update_person = fn($id) => response(200)(['message' => "Updated person with id: {$id}"]); route('PUT')('/person/{id}', $update_person);
In this example we are using a PUT request. This example is the first
where we are using a Variable Path. The callable curried to the route
function must have a parameter for the variable described in the path
in order to use it.