Skip to content

API Routing

Dory Azar edited this page Nov 15, 2026 · 2 revisions

Routing and requests

Caligrafy Quill 2.26.0. Generated from the source by php qa/build-api-reference.php; the guides explain how to use these classes, this reference says exactly what each public method takes and returns. Back to the API reference.

Declaring requests and reading them: routes, the request, forms, downloads, webhooks, web-only actions, errors and flash messages.

Class What it is
Caligrafy\Route Class that builds the routing logic of the framework
Caligrafy\AuthRoutes Route::auth() (after Auth::authentication('User', 'users') in web.php) registers, with the views of the authentication module (application/views/Auth/login.pug and register.pug)
Caligrafy\Request (no doc comment)
Caligrafy\Web A capability declared #[Capability(..., webOnly: true)] and served by Route::action() may return one of these instead of plain data, to choose what the browser gets
Caligrafy\Action Route::action('/connect', 'cloud_connect') answers a request with a capability declared webOnly: true, for what a page or a form cannot say: a sign-in between two sites, a session that decides where to go, a gate, a ...
Caligrafy\Form Route::form('/books', 'create_book', ['success' => '/books/{id}', 'flash' => 'Book added']) answers the form's POST (or its PUT / DELETE through _method) with a capability, without a controller: 1. the CSRF token of the ...
Caligrafy\Download Route::download('/books.csv', 'list_books', ['as' => 'csv', 'filename' => 'books.csv']) answers a GET with the result of a read-only capability as an attachment, without a controller: - inputs: the query string and the ...
Caligrafy\Webhook Route::webhook('/stripe/events', 'record_payment', ['verify' => 'stripe']) answers the POSTs of another service with a capability, without a controller: - no CSRF token (the sender has no session): the signature of the ...
Caligrafy\Uploads A capability declares a file input with the rule file, an optional allow-list and an optional maximum size
Caligrafy\Throttle The counts are kept in the Cache (CACHE_STORE: the caligrafy_cache table or a private folder, shared by every session, so that a new cookie does not start again), or in the session when the cache is off.
Caligrafy\Errors The Errors Class is the dictionary of all the error messages in the system
Caligrafy\Flash Route::form() puts the field errors, the old input and a message here before it redirects; the page of the next request reads them with the view helpers errors($field), old($field) and flash($key), then they are gone.
Caligrafy\Context Built by the channel adapters (web pages, REST, MCP, CLI) and read by the services: a service never looks at the request, the session or the headers itself.

class Caligrafy\Route

Class that builds the routing logic of the framework

Its 30 public methods have a page of their own: Caligrafy\Route.

class Caligrafy\AuthRoutes

Route::auth() (after Auth::authentication('User', 'users') in web.php) registers, with the views of the authentication module (application/views/Auth/login.pug and register.pug)

Its 15 public methods have a page of their own: Caligrafy\AuthRoutes.

class Caligrafy\Request

namespace Caligrafy;

class Request extends \stdClass

Source: framework/core/Request.php:6

No doc comment on this class.

Public properties

  • public $uriParameters: array uriParameters defines the uri request parameters
  • public $method: array method fetches the http request method
  • public $uri: string uri defines the uri of the request without query parameters
  • public $fullUri: string fulluri defines the full uri of the request includng the query parameters
  • public $uriComponents: array uriComponents defines a list of all the elements of the uri
  • public $host: string host reads the host information as taken from the headers of the request
  • public $authorization: string host reads the host information as taken from the headers of the request
  • public $cache: array cache has all the headers information of the request
  • public $userAgent: array userAgent has all the headers information of the request
  • public $accept: array accept has all the headers information of the request
  • public $acceptEncoding: array acceptEncoding has all the headers information of the request
  • public $acceptLanguage: array acceptLanguage has all the headers information of the request
  • public $cookie: array cookie has all the headers information of the request
  • public $parsedURI: array parsedURI has all the headers information of the request
  • public $currentRoute: array currentRoute has all the headers information of the request
  • public $currentBind: array currentBind has all the headers information of the request
  • public $fetch: array fetch has all the headers information of the request
  • public $parameters: array fetches all the request parameters
  • public $token: array|null the personal access token of the request, once authorizeApiRequest() or Context::fromRequest() checked it

Methods

->__construct()

public function __construct()

No doc comment.

Source: framework/core/Request.php:129

->authorizeApiRequest()

public function authorizeApiRequest($scope = '*')

Checks if API requests are authorized

  • a personal access token (Bearer cq_...) that grants $scope; the controller routes of web.php ask for '*', so they accept the tokens that grant everything, as the legacy key did
  • the legacy API key (any key generated for this APP_KEY), unless LEGACY_API_KEY=false

Parameters

  • $scope string, default '*': the scope the route needs

Source: framework/core/Request.php:162

->authorizeSessionRequest()

public function authorizeSessionRequest()

Whether a JSON call comes from a page of this application: it carries the session's CSRF token in an X-CSRF-Token header (or _token), which only pages of this origin can read. The controller then sees the signed-in user, or a visitor, exactly as for a form post

Source: framework/core/Request.php:184

->apiRefusal()

public function apiRefusal()

Why a JSON call to a controller route was refused, for the [ERROR 1004] message

Source: framework/core/Request.php:195

->bearer()

public function bearer()

The bearer value of the Authorization header (null when there is none)

Source: framework/core/Request.php:216

->isLegacyApiKey()

public function isLegacyApiKey($key)

Whether a value is a legacy API key of this project (encrypts APP_KEY), and legacy keys are allowed

Parameters

  • $key

Source: framework/core/Request.php:224

Request::legacyKeysDisabled()

public static function legacyKeysDisabled()

LEGACY_API_KEY=false in .env turns the legacy API key off (tokens only)

Source: framework/core/Request.php:236

->wantsJson()

public function wantsJson()

Checks if request needs a Json as a way to recognize API calls

Source: framework/core/Request.php:245

->fetch()

public function fetch($argument)

Fetches all the arguments and parameters of a request call

Parameters

  • $argument: defines the argument looked up

Source: framework/core/Request.php:264

->forClient()

public function forClient()

What a client app page may see of the request (env.request of the Client/app view, 2.19.1): the address, the method, the route and its bound values, and the query string. Never the Cookie (the session id) nor the Authorization header, nor any other header that can carry a credential. A POST body is left out too: only the query string of the address is given

Returns array

Source: framework/core/Request.php:283

->fullUri()

public function fullUri()

Fetches the full uri a request

Source: framework/core/Request.php:306

->parameters()

public function parameters()

Fetches the parameters of a request

Source: framework/core/Request.php:314

->files()

public function files($fileAttribute)

Fetches the files uploaded with a request

Parameters

  • $fileAttribute

Source: framework/core/Request.php:324

class Caligrafy\Web

namespace Caligrafy;

class Web

Source: framework/core/Web.php:22

Class Web (2.21.0) A capability declared #[Capability(..., webOnly: true)] and served by Route::action() may return one of these instead of plain data, to choose what the browser gets:

return Web::view('Books/show', ['book' => $book]);
return Web::view('Books/missing', ['message' => 'No such book'], 404);
return Web::redirect('/books');
return Web::redirect('https://checkout.stripe.com/...', 303);
return Web::json(['enrolled' => true], 201);
return Web::text('ok')->header('Cache-Control', 'no-store');

Methods

Web::view()

public static function view($view, array $data = array(), $status = 200)

A view, given who is signed in as Route::page gives it ('authorized', 'user'); $data wins

Parameters

  • $view
  • $data array, default array()
  • $status, default 200

Source: framework/core/Web.php:39

Web::redirect()

public static function redirect($to, $status = 302)

A path of this site or a full http(s) address

Parameters

  • $to
  • $status, default 302

Source: framework/core/Web.php:45

Web::json()

public static function json($data, $status = 200)

No doc comment.

Parameters

  • $data
  • $status, default 200

Source: framework/core/Web.php:50

Web::text()

public static function text($body = '', $status = 200)

No doc comment.

Parameters

  • $body, default ''
  • $status, default 200

Source: framework/core/Web.php:55

->header()

public function header($name, $value)

One more response header

Parameters

  • $name
  • $value

Source: framework/core/Web.php:61

->describe()

public function describe()

What it answers, for tests: ['type', 'target', 'data', 'status', 'headers']

Source: framework/core/Web.php:68

->send()

public function send()

Answers the request

Source: framework/core/Web.php:74

class Caligrafy\Action

namespace Caligrafy;

class Action

Source: framework/core/Action.php:27

Class Action (2.21.0) Route::action('/connect', 'cloud_connect') answers a request with a capability declared webOnly: true, for what a page or a form cannot say: a sign-in between two sites, a session that decides where to go, a gate, a payment page.

  1. access: the 'signedIn', 'level', 'comeBack' and 'visitorsOnly' options of Route::page
  2. the capability is called with the query string or the form, and the route variables (which win), as the person who sent it, with its rules, permissions and audit log like any capability
  3. its answer goes to the browser: a Caligrafy\Web (a view, a redirect, JSON, text), or JSON of the plain data A JSON call needs the page's X-CSRF-Token or an API token, signed in or not ([ERROR 1004], 401)
  4. a refusal it does not answer itself is treated as in Route::form: invalid input or a conflict goes back with errors(), old() and flash('error'); a visitor goes to sign in; forbidden is 403, anything else 404. JSON requests get the REST error. Unlike every other capability, it may read the session and the request and may sign people in, and a GET may change data: so a webOnly capability is reachable through Route::action only, never through REST, MCP, the JS client, Route::page, Route::form or a controller.

Methods

Action::handle()

public static function handle($capability, array $options = array(), $request = null)

No doc comment.

Parameters

  • $capability
  • $options array, default array()
  • $request, default null

Source: framework/core/Action.php:29

Action::answer()

public static function answer($answer)

Sends what the capability answered

Parameters

  • $answer

Source: framework/core/Action.php:64

class Caligrafy\Form

namespace Caligrafy;

class Form

Source: framework/core/Form.php:29

Class Form (2.19.0) Route::form('/books', 'create_book', ['success' => '/books/{id}', 'flash' => 'Book added']) answers the form's POST (or its PUT / DELETE through _method) with a capability, without a controller:

  1. the CSRF token of the form is checked by the request, as for every form (error 3000, 403)
  2. the capability is called with the form fields, the files of its file inputs (Uploads) and the route variables (which win), as the person who sent the form (Context::fromRequest(..., 'web'))
  3. success: a redirect to 'success' (a path whose {placeholders} are filled from the result, then from the route variables, or a callable($result) returning the path; without it, back to the form), with the 'flash' message (also with placeholders) for flash('success')
  4. invalid input (or a conflict): back to the form ('back', else the Referer of this site, else /) with the field errors, the message and the old input for the next request: errors($field), old($field), flash('error')
  5. a visitor where the capability needs someone: to the sign-in page ('signIn', default /login) when Auth is active, else 403; forbidden: 403 (error 1006); not found: 404 (error 1005) A JSON request (Accept: application/json) gets the REST answer instead of redirects: {"data": ...} with 200 (201 for a POST that changes data), or {"error": ...} with 401, 403, 404, 409 or 422. Sent with the session cookie, it needs the X-CSRF-Token header, as REST.

Constants

  • SECRET = '/pass|secret|token|card|cvc|cvv|iban/i': fields never kept as old input

Methods

Form::handle()

public static function handle($capability, array $options = array(), $request = null)

Answers the current request (the route's closure)

Parameters

  • $capability
  • $options array, default array()
  • $request, default null

Source: framework/core/Form.php:37

Form::environment()

public static function environment($request)

What outcome() and refused() need to know about the request

Parameters

  • $request

Source: framework/core/Form.php:47

Form::outcome()

public static function outcome(
    $capability,
    array $options,
    array $input,
    Context $context,
    $json,
    array $env = array()
)

What a form post leads to, without answering it (testable without a server). Flash data is written to the session.

Parameters

  • $capability
  • $options array
  • $input array
  • $context Context
  • $json
  • $env array, default array(): referer, host, route (route variables), bearer (bool), csrf (bool: X-CSRF-Token is the session's), method, uploads (Uploads|null)

Returns array: ['redirect' => url] | ['status' => int, 'json' => array] | ['error' => 1005|1006]

Source: framework/core/Form.php:67

Form::problems()

public static function problems(array $options, array $input)

What the 'same' and 'accepted' options refuse (2.20.0): [field => message]

'same' => ['password_again' => ['password', 'The two passwords are not the same']]
'accepted' => ['terms' => 'Accept the terms of use to create an account']

Parameters

  • $options array
  • $input array

Source: framework/core/Form.php:121

Form::withServerInputs()

public static function withServerInputs(array $options, array $input, array $route = array())

The inputs the server sets itself (2.20.0, 'inputs': an array, or a callable($input) returning one): they replace what the form sent. The route variables still win. They are not kept for old().

Parameters

  • $options array
  • $input array
  • $route array, default array()

Source: framework/core/Form.php:144

Form::perform()

public static function perform(array $outcome)

Answers an outcome

Parameters

  • $outcome array

Source: framework/core/Form.php:174

Form::fill()

public static function fill($template, array $values, $encode = true)

Fills {name} placeholders: Form::fill('/books/{id}', ['id' => 3]) is /books/3

Parameters

  • $template

  • $values array

  • $encode bool, default true: url-encode the values (paths)

  • @throws \LogicException when a placeholder has no value

Source: framework/core/Form.php:199

Form::back()

public static function back(array $options, array $env)

Where "back" is: the 'back' option (with the route variables), else the Referer when it is a page of this site, else the home page

Parameters

  • $options array
  • $env array

Source: framework/core/Form.php:214

Form::sameSite()

public static function sameSite($url, $host)

Whether a URL is a page of this host (host and port): never redirect elsewhere from a Referer

Parameters

  • $url
  • $host

Source: framework/core/Form.php:229

Form::oldInput()

public static function oldInput(array $input)

The input kept for old(): no secrets, no files, no framework fields

Parameters

  • $input array

Source: framework/core/Form.php:242

Form::refused()

public static function refused(ServiceError $e, array $options, array $input, $json, array $env)

No doc comment.

Parameters

  • $e ServiceError
  • $options array
  • $input array
  • $json
  • $env array

Source: framework/core/Form.php:256

class Caligrafy\Download

namespace Caligrafy;

class Download

Source: framework/core/Download.php:32

Class Download (2.19.0) Route::download('/books.csv', 'list_books', ['as' => 'csv', 'filename' => 'books.csv']) answers a GET with the result of a read-only capability as an attachment, without a controller:

  • inputs: the query string and the route variables (which win), as the person who opens the link (Context::fromRequest(..., 'web')); the capability must be readOnly (a link must not change data)
  • 'as' => 'csv' (default): RFC 4180 (CRLF, fields with a comma, a quote or a line break in double quotes, quotes doubled), UTF-8, the header row from the first row's keys; 'bom' => true starts the file with a UTF-8 BOM (Excel); text cells that start with = + - @ or a tab are prefixed with ' so that spreadsheets do not run them as formulas ('formulas' => true keeps them as they are); arrays in a cell are written as JSON, booleans as true / false. 'as' => 'json': the result, pretty-printed
  • 'as' => 'zip' (2.22.0): the files the capability lists, in one ZIP archive streamed from a temporary file. Each item is a path (the file keeps its name) or ['file' => path, 'name' => 'folder/name in the archive']; a page of paginate() gives its rows. Paths are relative to 'root', a folder of the project (default 'public/uploads'): a file outside it, or missing, is left out, so a link never reaches other files. Duplicate names get " (2)". Images, video, audio and archives are stored as they are, other files compressed. No file at all: 404 (error 1005). Needs the PHP zip extension (in Caligrafy's Docker images and on Caligrafy Cloud)
  • 'filename': a name, or a callable($result, $input) returning one (default: the capability's name)
  • refusals as Route::page: a visitor is sent to sign in when Auth is active, else 403 (error 1006); forbidden 403; anything else 404 (error 1005)

Constants

  • FORMATS = array('csv', 'json', 'zip')
  • STORED = ...

Methods

Download::handle()

public static function handle($capability, array $options = array(), $request = null)

Answers the current request (the route's closure)

Parameters

  • $capability
  • $options array, default array()
  • $request, default null

Source: framework/core/Download.php:42

Download::columns()

public static function columns(array $options)

The 'columns' option (2.19.5): a list of column names, or null for the keys of the first row

Parameters

  • $options array

Source: framework/core/Download.php:99

Download::format()

public static function format(array $options)

No doc comment.

Parameters

  • $options array

Source: framework/core/Download.php:111

Download::root()

public static function root(array $options)

The 'root' option of a ZIP (2.22.0): a folder of the project, 'public/uploads' by default, as an absolute path (null when it does not exist)

Parameters

  • $options array

Source: framework/core/Download.php:127

Download::files()

public static function files($result, $root)

The files of a result for a ZIP: [absolute path => name in the archive], in order. Paths outside the root or that are not files are left out; names are cleaned (no .., no leading /) and made unique

Parameters

  • $result
  • $root

Source: framework/core/Download.php:142

Download::entryName()

public static function entryName($name)

A name inside a ZIP: folders kept, without empty parts, . or .., control characters or a leading /

Parameters

  • $name

Source: framework/core/Download.php:173

Download::archive()

public static function archive($result, array $options = array())

The ZIP of a result (2.22.0): a temporary file the caller removes, or null when no file can go in it. Usable on its own, in a test: ['path' => ..., 'count' => ..., 'names' => [...]]

Parameters

  • $result
  • $options array, default array()

Source: framework/core/Download.php:189

Download::rows()

public static function rows($result)

The rows of a result: a list of rows as it is, one row (an associative array) as a list of one, a list of values as rows of one 'value' column

Parameters

  • $result

Source: framework/core/Download.php:247

Download::csvLines()

public static function csvLines(array $rows, $bom = false, $escapeFormulas = true, ?array $columns = null)

The lines of a CSV file (each ends with CRLF): the header row from the first row's keys, then one line per row in that order (a missing column is empty, an extra one left out)

Parameters

  • $rows array
  • $bom, default false
  • $escapeFormulas, default true
  • $columns ?array, default null

Source: framework/core/Download.php:270

Download::csv()

public static function csv(array $rows, $bom = false, $escapeFormulas = true, ?array $columns = null)

A whole CSV file

Parameters

  • $rows array
  • $bom, default false
  • $escapeFormulas, default true
  • $columns ?array, default null

Source: framework/core/Download.php:293

Download::cell()

public static function cell($value, $escapeFormulas = true)

One field of a CSV line, quoted when it must be (RFC 4180)

Parameters

  • $value
  • $escapeFormulas, default true

Source: framework/core/Download.php:301

Download::filename()

public static function filename($option, $result, array $input, $default)

The file name: the option, or what its callable returns, without a path, quotes or control characters

Parameters

  • $option
  • $result
  • $input array
  • $default

Source: framework/core/Download.php:319

Download::disposition()

public static function disposition($filename)

Content-Disposition: attachment, with an ASCII name and the UTF-8 one (RFC 6266)

Parameters

  • $filename

Source: framework/core/Download.php:330

class Caligrafy\Webhook

namespace Caligrafy;

class Webhook

Source: framework/core/Webhook.php:24

Class Webhook (2.19.0) Route::webhook('/stripe/events', 'record_payment', ['verify' => 'stripe']) answers the POSTs of another service with a capability, without a controller:

  • no CSRF token (the sender has no session): the signature of the body proves who sent it. 'verify' is required: 'stripe' checks the Stripe-Signature header with \Stripe\Webhook::constructEvent and the secret in the variable 'secret_env' names (default STRIPE_WEBHOOK_SECRET), with 'tolerance' seconds (default 300); a callable function (string $rawBody, array $headers): bool checks anything else (headers with lower-case names)
  • the raw body (php://input) must be JSON; the capability gets ['event' => the decoded body], as the system (Context::system('webhook', $path)): declare public $rules = ['event' => 'required']
  • answers: 200 {"received": true}; 400 for a bad signature or a body that is not JSON; the status of a ServiceError; 500 for anything else (written to the PHP error log)

Public properties

  • public static $logger = null: callable|null replaces error_log() (tests)

Methods

Webhook::handle()

public static function handle($path, $capability, array $options)

Answers the current request (the route's closure)

Parameters

  • $path
  • $capability
  • $options array

Source: framework/core/Webhook.php:32

Webhook::process()

public static function process($path, $capability, array $options, $raw, array $headers)

What a webhook post leads to, without answering it (testable without a server)

Parameters

  • $path
  • $capability
  • $options array
  • $raw
  • $headers array

Returns array: [status, body]

Source: framework/core/Webhook.php:47

Webhook::verify()

public static function verify($raw, array $headers, array $options)

Whether the body comes from the sender

Parameters

  • $raw
  • $headers array: lower-case names
  • $options array

Returns true|string: true, or why not

  • @throws \LogicException when the webhook is not set up (no secret, unknown 'verify')

Source: framework/core/Webhook.php:84

class Caligrafy\Uploads

namespace Caligrafy;

class Uploads

Source: framework/core/Uploads.php:28

Class Uploads (2.19.0) A capability declares a file input with the rule file, an optional allow-list and an optional maximum size:

public $rules = ['cover' => 'required|file|extension,png;jpg|max_size,2mb'];
  • web pages (Route::form) and REST (Route::capability, Route::capabilities) take the field from $_FILES once the caller is allowed to call the capability, check it (one file, the allow-list - never an executable type, see isAllowedUpload() - and the size), save it in public/uploads under a new random name with uploadFile() (the name the client sent is never used) and give handle() the saved file's public URL, a string. A value sent in that field without a file is dropped. The saved file is deleted when the call fails afterwards.
  • MCP and the command line cannot send files: the field is a URL or a path string; its extension is checked against the allow-list.

The file rules (file, extension, max_size, required_file) are never given to GUMP: Service::validate() strips them. A field with required_file (or required) and no file is invalid: 'The <Field> field needs a file' (2.19.1). Without extension the allow-list is DEFAULT_UPLOAD_EXTENSIONS.

Constants

  • RULES = array('file', 'extension', 'max_size', 'required_file')

Methods

->__construct()

public function __construct(?array $files = null, ?callable $store = null, ?callable $delete = null)

No doc comment.

Parameters

  • $files ?array, default null
  • $store ?callable, default null
  • $delete ?callable, default null

Source: framework/core/Uploads.php:44

Uploads::fields()

public static function fields(array $rules)

The file inputs of a set of rules

Parameters

  • $rules array

Returns array: field => ['extensions' => [..] or null, 'max' => bytes or null]

Source: framework/core/Uploads.php:55

Uploads::parse()

public static function parse($rule)

The file part of one rule: null when the rule has no file

Parameters

  • $rule

Source: framework/core/Uploads.php:70

Uploads::required()

public static function required($rule)

Whether a file input must be sent: its rule has required_file, or required (2.19.1)

Parameters

  • $rule

Source: framework/core/Uploads.php:95

Uploads::strip()

public static function strip(array $rules)

Rules without their file parts, for GUMP

Parameters

  • $rules array

Source: framework/core/Uploads.php:111

Uploads::bytes()

public static function bytes($size)

'2mb', '500kb', '1.5MB', '1g' or a number of bytes

Parameters

  • $size

Source: framework/core/Uploads.php:132

Uploads::allowed()

public static function allowed(array $spec)

The allow-list of a file input (never an executable type: isAllowedUpload() refuses those anyway)

Parameters

  • $spec array

Source: framework/core/Uploads.php:144

Uploads::check()

public static function check($file, array $spec)

What is wrong with an uploaded file (an entry of $_FILES), or null

Parameters

  • $file
  • $spec array

Source: framework/core/Uploads.php:152

Uploads::checkString()

public static function checkString($value, array $spec)

What is wrong with a file input sent as a string (MCP, the command line), or null

Parameters

  • $value
  • $spec array

Source: framework/core/Uploads.php:177

->apply()

public function apply(array $input, array $rules)

Replaces the file inputs of $input with the saved files of the request (web and REST)

Parameters

  • $input array: the inputs
  • $rules array: the capability's rules

Returns array: the inputs, with the public URL of each saved file

  • @throws ServiceError invalid, with a message per file field

Source: framework/core/Uploads.php:196

->discard()

public function discard()

Deletes the files apply() saved (the call failed afterwards)

Source: framework/core/Uploads.php:231

->saved()

public function saved()

The URLs apply() saved

Source: framework/core/Uploads.php:242

class Caligrafy\Throttle

namespace Caligrafy;

class Throttle

Source: framework/core/Throttle.php:19

Class Throttle (2.19.0)

$throttle = new Throttle(5, 900);          // 5 failures within 15 minutes lock the key for the rest of them
if ($throttle->locked($key)) ...; $throttle->fail($key); $throttle->clear($key);

The counts are kept in the Cache (CACHE_STORE: the caligrafy_cache table or a private folder, shared by every session, so that a new cookie does not start again), or in the session when the cache is off. Each failure keeps the count for $seconds more.

Public properties

  • public $max: int
  • public $seconds: int

Methods

->__construct()

public function __construct($max = 5, $seconds = 900, ?array $store = null)

No doc comment.

Parameters

  • $max, default 5
  • $seconds, default 900
  • $store ?array, default null

Source: framework/core/Throttle.php:30

Throttle::key()

public static function key($scope, $ip, $username)

A key for a person at an address: the IP and the username (lower case), hashed

Parameters

  • $scope
  • $ip
  • $username

Source: framework/core/Throttle.php:40

->limitedTo()

public function limitedTo($max)

The same counts and window with another limit (2.19.6: Route::auth's attemptsPerUser)

Parameters

  • $max

Source: framework/core/Throttle.php:48

->locked()

public function locked($key)

Whether the key failed $max times within the window

Parameters

  • $key

Source: framework/core/Throttle.php:58

->failures()

public function failures($key)

The failures of the key within the window

Parameters

  • $key

Source: framework/core/Throttle.php:66

->fail()

public function fail($key)

Counts one failure; returns the failures of the key

Parameters

  • $key

Source: framework/core/Throttle.php:75

->clear()

public function clear($key)

Forgets the failures of the key (a success)

Parameters

  • $key

Source: framework/core/Throttle.php:85

class Caligrafy\Errors

namespace Caligrafy;

class Errors extends \stdClass

Source: framework/core/Errors.php:18

The Errors Class is the dictionary of all the error messages in the system

Constants

  • GENERIC_MESSAGE = '[ERROR 500] Internal Server Error'

Methods

Errors::status()

public static function status($errorCode)

Get the HTTP status code of an error code

Parameters

  • $errorCode int: identifies the error code

Returns int: HTTP status code (500 when the error code is unknown)

Source: framework/core/Errors.php:78

Errors::raise()

public static function raise($errorCode, $detail = null)

Creates the exception of a framework error and sets its HTTP status code right away, so that the status is correct even when the exception is caught by an application that only echoes the message

Parameters

  • $errorCode int: identifies the error code
  • $detail string|null, default null: why, added to the message: '[ERROR 1004] Access Denied: <detail>'

Returns \Exception: to be thrown

Source: framework/core/Errors.php:89

Errors::handle()

public static function handle(\Throwable $e, $hideUnknown = false)

Sends an error response: HTTP status code and the error text (HTML requests) or {"error": {"code": ..., "message": ...}} (requests that want JSON) Framework errors keep their message. Other exceptions caught by the application keep theirs too unless $hideUnknown is true (uncaught exceptions), in which case the message is only shown when APP_DEBUG is true

Parameters

  • $e \Throwable: the error
  • $hideUnknown bool, default false: hides the message of errors that are not framework errors unless APP_DEBUG is true

Source: framework/core/Errors.php:105

Errors::shutdown()

public static function shutdown()

Shutdown function: turns PHP fatal errors into a 500 response (message only when APP_DEBUG is true)

Source: framework/core/Errors.php:121

Errors::get()

public static function get($errorCode)

Get error message based on error code

Parameters

  • $errorCode string: identifies the error code

Returns string: identifying the error message

Source: framework/core/Errors.php:169

Errors::set()

public static function set($errorMessage, $errorCode)

No doc comment.

Parameters

  • $errorMessage
  • $errorCode

Source: framework/core/Errors.php:173

class Caligrafy\Flash

namespace Caligrafy;

class Flash

Source: framework/core/Flash.php:20

Class Flash (2.19.0) Route::form() puts the field errors, the old input and a message here before it redirects; the page of the next request reads them with the view helpers errors($field), old($field) and flash($key), then they are gone. Route::run() ages them when there are some: what the previous request put becomes this request's, and leaves the session (without Route::run, the first read does).

Kept in $_SESSION['__flash'] as ['errors' => [field => message], 'old' => [field => value], 'messages' => [key => text]].

Constants

  • KEY = '__flash'

Methods

Flash::age()

public static function age()

Moves what the previous request put into this request (Route::run calls it once)

Source: framework/core/Flash.php:30

Flash::put()

public static function put($key, $value)

Keeps a value for the next request

Parameters

  • $key string: errors, old or messages
  • $value

Source: framework/core/Flash.php:42

Flash::message()

public static function message($key, $text)

Keeps a message for the next request: Flash::message('success', 'Book added')

Parameters

  • $key
  • $text

Source: framework/core/Flash.php:56

Flash::get()

public static function get($key, $default = null)

What the previous request put (errors, old or messages)

Parameters

  • $key
  • $default, default null

Source: framework/core/Flash.php:66

Flash::reset()

public static function reset()

Forgets what this request read (tests)

Source: framework/core/Flash.php:77

class Caligrafy\Context

namespace Caligrafy;

class Context

Source: framework/core/Context.php:23

Class Context Built by the channel adapters (web pages, REST, MCP, CLI) and read by the services: a service never looks at the request, the session or the headers itself.

The actor is one of:

  • user: a signed-in user (session), or the owner of a token
  • token: a personal access token (Tokens), with its scopes
  • key: the legacy API key (every generated key is the same key: full access, as before 2.1)
  • cli: a script or the command line (full access); a scheduled job is a system context on this channel
  • guest: nobody: only public capabilities

Constants

  • CHANNELS = array('web', 'rest', 'mcp', 'cli')

Public properties

  • public static $levelAttribute = 'permissions': Name of the users column that holds the permission level compared with a capability's level (Auth::guard)
  • public $channel: string web, rest, mcp or cli
  • public $viaAction = false: bool the call comes from Route::action() (2.21.0): the only way to a webOnly capability
  • public $user: object|null the user model (signed in, or the owner of the token)
  • public $token: array|null the token row (id, name, scopes, user_id...)

Methods

->__construct()

public function __construct($channel = 'cli', $user = null, $token = null, $full = false)

No doc comment.

Parameters

  • $channel, default 'cli'
  • $user, default null
  • $token, default null
  • $full, default false

Source: framework/core/Context.php:53

Context::cli()

public static function cli()

The context of the command line and of scripts: full access

Source: framework/core/Context.php:67

Context::local()

public static function local($channel)

Full access through another channel for a local process (php caligrafer.php mcp without a token)

Parameters

  • $channel

Source: framework/core/Context.php:75

Context::system()

public static function system($type, $name)

Full access for work the system does on its own, such as a scheduled job: the audit log records the actor type (schedule) and its name (the job's)

Parameters

  • $type string: the actor type, e.g. schedule
  • $name string: the actor id, e.g. the job's name

Source: framework/core/Context.php:88

Context::guest()

public static function guest($channel = 'rest')

The context of nobody: only public capabilities

Parameters

  • $channel, default 'rest'

Source: framework/core/Context.php:99

Context::forToken()

public static function forToken(array $token, $channel = 'rest')

The context of a personal access token: its scopes, and its owner when it has one and authentication is on

Parameters

  • $token array
  • $channel, default 'rest'

Source: framework/core/Context.php:107

Context::fromRequest()

public static function fromRequest(Request $request, $channel = 'rest')

The context of the current HTTP request: a token (Authorization: Bearer cq_...), else the legacy API key, else the signed-in user, else a guest

Parameters

  • $request Request: the request
  • $channel string, default 'rest': web, rest or mcp

Source: framework/core/Context.php:122

->authenticated()

public function authenticated()

Whether someone is acting (anything but a guest)

Source: framework/core/Context.php:150

->allows()

public function allows($scope, $level = 0)

Whether this context may use a capability that needs a scope and a minimum user level

  • the CLI and the legacy key: always
  • a token: it must grant the scope, and its owner (if any) must have the level
  • a signed-in user: the level (users have every scope their level allows)

Parameters

  • $scope string: e.g. books:write ('' = no scope needed)
  • $level int, default 0: minimum value of the users permission column (0 = any signed-in user)

Source: framework/core/Context.php:163

->level()

public function level()

The permission level of the user (0 when there is none)

Source: framework/core/Context.php:186

->userId()

public function userId()

The user id, when a user acts (signed in or owner of the token)

Source: framework/core/Context.php:195

->actorType()

public function actorType()

user, token, key, cli or guest (as written to the audit log)

Source: framework/core/Context.php:203

->actorId()

public function actorId()

The token id or the user id, as a string (null for the CLI, the legacy key and guests)

Source: framework/core/Context.php:220

Caligrafy: Describe it. Approve it. Ship it. Your idea turned into a complete app: every feature built once, for web pages, Vue apps, a REST API and AI agents, and online.

Start here

Why Caligrafy: the benefits, the measurements, and what it is not yet

  1. Quickstart with Claude: describe it, approve it, ship it

    or the Quickstart: the same three steps by hand, in 5 minutes

  2. How Caligrafy works: the ideas in 5 minutes
  3. Your first capability: build a feature, page included

Capabilities

Experiences

Data and people

  1. Models
  2. Relationships
  3. Queries: filters, paging, related rows
  4. Sign-in, users and tokens

Built-in features

Put it online

Build with AI

Reference

Clone this wiki locally