PHP code example of psf / framework

1. Go to this page and download the library: Download psf/framework library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

psf / framework example snippets




use Psf\Enumerators\DBDriver;

return [
    'environment' => 'dev', // 'prd' mantém o cache de rotas estático (limpe no deploy)

    'db' => [
        'default' => [
            'driver'   => DBDriver::MySQL, // MySQL, PostgreSQL, SQLServer ou SQLite
            'hostname' => 'localhost',     // 'host', 'user' e 'pass' também são aceitos
            'username' => 'root',
            'password' => '',
            'database' => 'meu_banco',     // SQLite: caminho do arquivo ou ':memory:'
            'port'     => 3306,
        ],
    ],

    'jwt' => [
        'secret' => 'uma-chave-longa-e-aleatoria', // mínimo de 16 caracteres
    ],

    'settings' => [
        'controllers' => [__DIR__ . '/app/Controllers'], // diretórios varridos em busca de #[Router]
        'debug'       => false, // true expõe mensagem/arquivo/linha dos erros de banco na resposta
        'verifyauth'  => [App\Auth::class, 'verify'],    // usado pelo middleware 'authentication'
        'docsapi'     => 'api/docs',                     // opcional: página de documentação da API
        'cors'        => [
            'origins'     => ['https://app.exemplo.com'], // ou '*'
            'credentials' => false,
        ],
    ],
];



sf\Http\{Http, Router};

PSF::init(['config' => __DIR__ . '/../config.php']);

try {
    (new Router())->handle();
} catch (\Throwable $e) {
    // O Router sinaliza 404 (rota não encontrada), 4xx do controller e 500 lançando
    // exceções com o status HTTP no código. Sem este catch, elas chegam à página do Ignition.
    $code   = $e->getCode();
    $status = is_int($code) && $code >= 400 && $code < 600 ? $code : 500;
    Http::response($status < 500 ? $e->getMessage() : 'Erro interno', [], $status);
}



namespace App\Models;

use Psf\Model\{Model, ModelTrait};
use Psf\Model\Attributes\{Table, Column, PrimaryKey, Type, Nullable, Standard, ColumnCreatedDate, ColumnDeletedDate};

#[Table('usuarios')]
class Usuario extends Model
{
    use ModelTrait; // find(), findById(), col(), createMany()

    #[PrimaryKey, Column('id'), Type('INT UNSIGNED AUTO_INCREMENT')]
    public ?int $id = null;

    #[Column('nome'), Nullable(false), Type('VARCHAR(100) NOT NULL')]
    public ?string $nome = null;

    #[Column('status'), Standard('ativo'), Type('VARCHAR(20)')]
    public ?string $status = null;

    #[Column('created_at'), ColumnCreatedDate, Type('DATETIME')]
    public ?string $createdAt = null;

    #[Column('deleted_at'), ColumnDeletedDate, Type('DATETIME')] // delete() vira soft delete
    public ?string $deletedAt = null;
}



namespace App\Controllers;

use App\Models\Usuario;
use Psf\Http\{Http, Router};
use Psf\Model\ControllerBase;

class UsuarioController extends ControllerBase
{
    #[Router(method: 'GET', path: '/usuarios/{id:int}', version: 1)]
    public function show(int $id)
    {
        $usuario = Usuario::findById($id);
        if (!$usuario) {
            Http::response('Usuário não encontrado', [], 404);
        }

        Http::response('OK', $usuario->toArray());
    }

    #[Router(method: 'POST', path: '/usuarios', version: 1, middlewares: ['authentication'], docs: [
        'title'  => 'Criar usuário',
        'fields' => [['name' => 'nome', 'type' => 'string', '



namespace App;

use Psf\Http\RequestParser;
use Psf\Utils\JWT;

class Auth
{
    public function verify(): object|bool
    {
        $token   = RequestParser::extractBearerToken();
        $payload = $token ? JWT::verify($token) : false; // valida assinatura, exp e nbf

        return $payload ? (object) $payload : false;
    }
}

$pagina = Usuario::find()
    ->andWhere([Usuario::col('status') => 'ativo'])
    ->andWhere([Usuario::col('createdAt'), '>=', '2026-01-01'])
    ->orderBy(Usuario::col('nome'))
    ->paginator(page: 1, itensPerPage: 20);

'settings' => [
    'monitor' => [
        'enabled'   => true,               // padrão: false (monitor totalmente desligado)
        'database'  => 'default',          // chave de config['db'] onde ficam as tabelas
        'sample'    => ['normal' => 0.1],  // fração dos requests "normais" gravados
        'slowMs'   => ['request' => 1000, 'query' => 200, 'external' => 1000],
        'retention' => ['rawDays' => 7, 'aggregateDays' => 90],
        'release'   => '1.4.2',            // opcional, texto livre
    ],
],

meu-projeto/
  config.php            ← configuração do PSF (o vendor/bin/psf procura este arquivo na raiz)
  app/
    Auth.php
    Controllers/
    Models/
  db/
    migrations/
  public/
    index.php
    .htaccess
  composer.json         ← "autoload": {"psr-4": {"App\\": "app/"}}

public/index.php ─ PSF::init(config) ─ Router
  Router::__construct   Monitor::boot (se ligado) e tabela de rotas (RouteCacheManager, APCu)
  Router::handle        CORS (preflight → 204) · página de docs · casamento da rota (vN + path)
                        middleware 'authentication' (verifyauth) · validação de docs.fields
                        controller (ControllerBase: $this->data, $this->token, $this->method)
  Http::response()      JSON + exit  →  shutdown: flush do monitor, depois da resposta
cron
17 * * * * cd /caminho/do/projeto && php vendor/bin/psf monitor:maintain