Skip to content

Requests and responses

NAF exposes a PSR-7 server request and expects a PSR-7 response from every handler. Import helpers in each PHP file that uses them.

Read the request

use function Naf\request;

$method = request()->getMethod();
$path = request()->getUri()->getPath();
$query = request()->getQueryParams();
$authorization = request()->getHeaderLine('Authorization');
$form = request()->getParsedBody() ?? [];
$rawBody = (string) request()->getBody();

getParsedBody() exposes parsed form data. It does not automatically decode JSON. For a JSON-only endpoint, decode the body explicitly and reject invalid input:

use function Naf\{json, request};

try {
    $data = json_decode((string) request()->getBody(), true, 512, JSON_THROW_ON_ERROR);
} catch (\JsonException) {
    return json(['error' => 'Invalid JSON'], 400);
}
if (!is_array($data)) {
    return json(['error' => 'Expected an object or array'], 400);
}

Combined request parameters

use function Naf\param;

$email = param()->get('email');
$city = param()->get('address.city', 'unknown');
$data = param()->all();

param() merges query parameters and parsed form data. If the parsed body is empty and Content-Type contains application/json, it decodes JSON and merges those values instead. Body values override query values at the top level. Invalid JSON contributes no values; use explicit decoding when malformed JSON must be distinguished from missing fields. A literal key containing a dot takes precedence over a nested lookup.

Use the body directly when a value from the query string should not be accepted. Handling a POST request applies this to forms.

Uploaded files

use function Naf\{json, request};

$file = request()->getUploadedFiles()['document'] ?? null;
if ($file === null || $file->getError() !== UPLOAD_ERR_OK) {
    return json(['error' => 'Upload failed'], 400);
}

// The application creates this private directory and decides which file types to accept.
$destination = BASE_PATH . '/storage/uploads/' . bin2hex(random_bytes(16));
$file->moveTo($destination);

Uploads are PSR-7 UploadedFileInterface objects. Do not use the client-supplied filename as an unchecked filesystem path. Validate type and size according to your application's needs.

Responses

use function Naf\{json, response};

return response('Hello', 200, ['Content-Type' => 'text/plain; charset=UTF-8']);

response($content = '', $status = 200, $headers = []) accepts a PSR-7-compatible body. The helper sets no default content type; state it explicitly when it matters.

use function Naf\json;

return json(['message' => 'Created'], 201);

json() serializes the value and sets Content-Type: application/json; charset=UTF-8. For a response with no body use response('', 204), not json(null, 204).

HTML views

use function Naf\View\render;

return render('home', ['name' => 'World']);

This needs naf/view. render() returns a response; view() returns a string. Templates must escape untrusted values explicitly with Naf\View\s(). See Views and templates.

Redirect and refresh

use function Naf\{redirect, refresh};

return redirect('/login');       // 302
return redirect('/new-url', 301);
return refresh();               // 302 to the current URL path, without its query string

Custom responses

use Nyholm\Psr7\Response;

return new Response(202, ['Content-Type' => 'text/plain'], 'Accepted');

PSR-7 methods such as withStatus() and withHeader() return a new object. Return or assign that new response. See Events for response-header listeners.

Logging

use function Naf\log;

log()->info('Import finished', ['count' => 12]);

The default PSR-3 logger writes to logs/app.log under the application root. The directory must be writable. log() is the NAF helper; there is no logger() helper.

Redirects on the PHP development server

The released naf/framework 0.2.1 sets the protocol of redirect() and refresh() to HTTP/2. PHP's built-in HTTP/1.x server cannot emit that status line correctly. The bootstrap in Your first application normalizes outgoing responses to the incoming request's protocol. If you use a different bootstrap, normalize the individual response:

use function Naf\redirect;

return redirect('/login')->withProtocolVersion('1.1');