Learn

Aprenda sobre o Flight

O Flight é um framework rápido, simples e extensível para PHP. Ele é bastante versátil e pode ser usado para construir qualquer tipo de aplicação web. Ele é construído com simplicidade em mente e é escrito de uma forma que é fácil de entender e usar.

Conceitos Importantes do Framework

Por que um Framework?

Aqui está um pequeno artigo sobre por que você deve usar um framework. É uma boa ideia entender os benefícios de usar um framework antes de começar a usar um.

Adicionalmente, um excelente tutorial foi criado por @lubiana. Embora não entre em grandes detalhes sobre o Flight especificamente, este guia ajudará você a entender alguns dos principais conceitos envolvendo um framework e por que eles são benéficos de usar. Você pode encontrar o tutorial aqui.

Tópicos Principais

Carregamento Automático

Aprenda como carregar automaticamente suas próprias classes em sua aplicação.

Rotas

Aprenda como gerenciar rotas para sua aplicação web. Isso também inclui agrupar rotas, parâmetros de rota e middleware.

Middleware

Aprenda como usar middleware para filtrar solicitações e respostas em sua aplicação.

Solicitações

Aprenda como lidar com solicitações e respostas em sua aplicação.

Respostas

Aprenda como enviar respostas para seus usuários.

Templates HTML

Aprenda como usar o mecanismo de visualização integrado para renderizar seus templates HTML.

Segurança

Aprenda como proteger sua aplicação contra ameaças de segurança comuns.

Configuração

Aprenda como configurar o framework para sua aplicação.

Extendendo o Flight

Aprenda como estender o framework adicionando seus próprios métodos e classes.

Eventos e Filtragem

Aprenda como usar o sistema de eventos para adicionar ganchos aos seus métodos e métodos internos do framework.

Contêiner de Injeção de Dependência

Aprenda como usar contêineres de injeção de dependência (DIC) para gerenciar as dependências de sua aplicação.

API do Framework

Saiba mais sobre os métodos principais do framework.

Migrando para v3

A compatibilidade com versões anteriores foi mantida em grande parte, mas há algumas alterações das quais você deve estar ciente ao migrar da v2 para a v3.

Resolução de Problemas

Há alguns problemas comuns que você pode encontrar ao usar o Flight. Esta página ajudará você a solucionar esses problemas.

Learn/stopping

Parando

Você pode parar o framework a qualquer momento chamando o método halt:

Flight::halt();

Você também pode especificar um código de status HTTP opcional e uma mensagem:

Flight::halt(200, 'Volto em breve...');

Chamar halt irá descartar qualquer conteúdo de resposta até esse ponto. Se você quiser parar o framework e exibir a resposta atual, use o método stop:

Flight::stop();

Learn/errorhandling

Manipulação de Erros

Erros e Exceções

Todos os erros e exceções são interceptados pelo Flight e passados para o método error. O comportamento padrão é enviar uma resposta genérica de HTTP 500 Erro Interno do Servidor com algumas informações de erro.

Você pode substituir esse comportamento para suas próprias necessidades:

Flight::map('error', function (Throwable $error) {
  // Lidar com o erro
  echo $error->getTraceAsString();
});

Por padrão, os erros não são registrados no servidor web. Você pode habilitar isso alterando a configuração:

Flight::set('flight.log_errors', true);

Não Encontrado

Quando um URL não pode ser encontrado, o Flight chama o método notFound. O comportamento padrão é enviar uma resposta de HTTP 404 Não Encontrado com uma mensagem simples.

Você pode substituir esse comportamento para suas próprias necessidades:

Flight::map('notFound', function () {
  // Lidar com não encontrado
});

Learn/migrating_to_v3

Migração para v3

A compatibilidade com versões anteriores foi em grande parte mantida, mas existem algumas alterações das quais você deve estar ciente ao migrar da v2 para a v3.

Comportamento de Buffer de Saída (3.5.0)

Buffer de saída é o processo em que a saída gerada por um script PHP é armazenada em um buffer (interno ao PHP) antes de ser enviada para o cliente. Isso permite que você modifique a saída antes de ser enviada para o cliente.

Em uma aplicação MVC, o Controlador é o "gerente" e ele gerencia o que a visualização faz. Ter saída gerada fora do controlador (ou, no caso do Flight, às vezes em uma função anônima) quebra o padrão MVC. Essa alteração visa estar mais em conformidade com o padrão MVC e tornar o framework mais previsível e fácil de usar.

Na v2, o buffer de saída era manipulado de forma que não fechava consistentemente seu próprio buffer de saída, o que tornava os testes unitários e o streaming mais difíceis. Para a maioria dos usuários, essa alteração pode não afetar realmente você. No entanto, se você estiver ecoando conteúdo fora de chamáveis e controladores (por exemplo, em um gancho), provavelmente terá problemas. Ecoar conteúdo em ganchos e antes do framework realmente ser executado pode ter funcionado no passado, mas não funcionará mais no futuro.

Onde você pode ter problemas

// index.php
require 'vendor/autoload.php';

// apenas um exemplo
define('START_TIME', microtime(true));

function hello() {
    echo 'Olá Mundo';
}

Flight::map('hello', 'hello');
Flight::after('hello', function(){
    // isso na verdade estará tudo bem
    echo '<p>Esta frase Olá Mundo foi trazida a você pela letra "H"</p>';
});

Flight::before('start', function(){
    // coisas assim causarão um erro
    echo '<html><head><title>Minha Página</title></head><body>';
});

Flight::route('/', function(){
    // isso na verdade está tudo bem
    echo 'Olá Mundo';

    // Isso também deverá estar tudo bem
    Flight::hello();
});

Flight::after('start', function(){
    // isso causará um erro
    echo '<div>Sua página carregou em '.(microtime(true) - START_TIME).' segundos</div></body></html>';
});

Habilitando o Comportamento de Renderização da v2

Você ainda pode manter o seu código antigo da maneira como está sem precisar reescrevê-lo para funcionar com a v3? Sim, pode! Você pode ativar o comportamento de renderização da v2 configurando a opção de configuração flight.v2.output_buffering para true. Isso permitirá que você continue a usar o comportamento de renderização antigo, mas é recomendado corrigi-lo seguindo em frente. Na v4 do framework, isso será removido.

// index.php
require 'vendor/autoload.php';

Flight::set('flight.v2.output_buffering', true);

Flight::before('start', function(){
    // Agora isso estará tudo bem
    echo '<html><head><title>Minha Página</title></head><body>';
});

// mais código

Alterações no Despachante (3.7.0)

Se você tem chamado diretamente os métodos estáticos para Dispatcher como Dispatcher::invokeMethod(), Dispatcher::execute(), etc., você precisará atualizar seu código para não chamar diretamente esses métodos. Dispatcher foi convertido para ser mais orientado a objetos para que Contêineres de Injeção de Dependência possam ser usados de maneira mais fácil. Se você precisa invocar um método de forma semelhante ao que o Dispatcher fazia, você pode usar manualmente algo como $result = $class->$method(...$params); ou call_user_func_array() em vez disso.

Learn/configuration

Configuração

Você pode personalizar determinados comportamentos do Flight definindo valores de configuração através do método set.

Flight::set('flight.log_errors', true);

Configurações Disponíveis

A seguir está uma lista de todas as configurações disponíveis:

Variáveis

O Flight permite que você salve variáveis para que possam ser usadas em qualquer lugar da sua aplicação.

// Salve sua variável
Flight::set('id', 123);

// Em outro lugar na sua aplicação
$id = Flight::get('id');

Para verificar se uma variável foi definida, você pode fazer:

if (Flight::has('id')) {
  // Faça algo
}

Você pode limpar uma variável fazendo:

// Limpa a variável id
Flight::clear('id');

// Limpa todas as variáveis
Flight::clear();

O Flight também usa variáveis para fins de configuração.

Flight::set('flight.log_errors', true);

Manipulação de Erros

Erros e Exceções

Todos os erros e exceções são capturados pelo Flight e passados ao método error. O comportamento padrão é enviar uma resposta genérica de HTTP 500 Internal Server Error com algumas informações de erro.

Você pode substituir esse comportamento conforme suas necessidades:

Flight::map('error', function (Throwable $error) {
  // Lidar com o erro
  echo $error->getTraceAsString();
});

Por padrão, os erros não são registrados no servidor web. Você pode habilitar isso alterando a configuração:

Flight::set('flight.log_errors', true);

Não Encontrado

Quando uma URL não pode ser encontrada, o Flight chama o método notFound. O comportamento padrão é enviar uma resposta de HTTP 404 Not Found com uma mensagem simples.

Você pode substituir esse comportamento conforme suas necessidades:

Flight::map('notFound', function () {
  // Lidar com não encontrado
});

Learn/security

Segurança

Segurança é extremamente importante quando se trata de aplicações web. Você deseja garantir que sua aplicação seja segura e que os dados de seus usuários estejam protegidos. O Flight fornece uma série de recursos para ajudá-lo a proteger suas aplicações web.

Cabeçalhos

Os cabeçalhos HTTP são uma das formas mais fáceis de proteger suas aplicações web. Você pode usar cabeçalhos para evitar clickjacking, XSS e outros ataques. Existem várias maneiras de adicionar esses cabeçalhos à sua aplicação.

Dois ótimos sites para verificar a segurança de seus cabeçalhos são securityheaders.com e observatory.mozilla.org.

Adicionar Manualmente

Você pode adicionar manualmente esses cabeçalhos usando o método header no objeto Flight\Response.

// Defina o cabeçalho X-Frame-Options para evitar clickjacking
Flight::response()->header('X-Frame-Options', 'SAMEORIGIN');

// Defina o cabeçalho Content-Security-Policy para evitar XSS
// Observação: esse cabeçalho pode ficar muito complexo, então você vai querer
// consultar exemplos na internet para a sua aplicação
Flight::response()->header("Content-Security-Policy", "default-src 'self'");

// Defina o cabeçalho X-XSS-Protection para evitar XSS
Flight::response()->header('X-XSS-Protection', '1; mode=block');

// Defina o cabeçalho X-Content-Type-Options para evitar sniffing MIME
Flight::response()->header('X-Content-Type-Options', 'nosniff');

// Defina o cabeçalho Referrer-Policy para controlar quanto informações de referência são enviadas
Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');

// Defina o cabeçalho Strict-Transport-Security para forçar HTTPS
Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');

// Defina o cabeçalho Permissions-Policy para controlar quais recursos e APIs podem ser usados
Flight::response()->header('Permissions-Policy', 'geolocation=()');

Esses podem ser adicionados no início de seus arquivos bootstrap.php ou index.php.

Adicionar como um Filtro

Você também pode adicioná-los em um filtro/interceptador da seguinte maneira:

// Adicione os cabeçalhos em um filtro
Flight::before('start', function() {
    Flight::response()->header('X-Frame-Options', 'SAMEORIGIN');
    Flight::response()->header("Content-Security-Policy", "default-src 'self'");
    Flight::response()->header('X-XSS-Protection', '1; mode=block');
    Flight::response()->header('X-Content-Type-Options', 'nosniff');
    Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');
    Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
    Flight::response()->header('Permissions-Policy', 'geolocation=()');
});

Adicionar como um Middleware

Você também pode adicioná-los como uma classe de middleware. Esta é uma boa maneira de manter seu código limpo e organizado.

// app/middleware/SecurityHeadersMiddleware.php

namespace app\middleware;

class SecurityHeadersMiddleware
{
    public function before(array $params): void
    {
        Flight::response()->header('X-Frame-Options', 'SAMEORIGIN');
        Flight::response()->header("Content-Security-Policy", "default-src 'self'");
        Flight::response()->header('X-XSS-Protection', '1; mode=block');
        Flight::response()->header('X-Content-Type-Options', 'nosniff');
        Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');
        Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
        Flight::response()->header('Permissions-Policy', 'geolocation=()');
    }
}

// index.php ou onde quer que tenha suas rotas
// A propósito, este grupo de string vazia atua como um middleware global para
// todas as rotas. Claro, você poderia fazer a mesma coisa e adicionar
// isso apenas a rotas específicas.
Flight::group('', function(Router $router) {
    $router->get('/usuarios', [ 'UserController', 'obterUsuarios' ]);
    // mais rotas
}, [ new SecurityHeadersMiddleware() ]);

Falsificação de Solicitação entre Sites (CSRF)

Falsificação de Solicitação entre Sites (CSRF) é um tipo de ataque em que um site malicioso pode fazer o navegador de um usuário enviar uma solicitação para o seu site. Isso pode ser usado para realizar ações no seu site sem o conhecimento do usuário. O Flight não fornece um mecanismo integrado de proteção contra CSRF, mas você pode facilmente implementar o seu próprio usando um middleware.

Configuração

Primeiro, você precisa gerar um token CSRF e armazená-lo na sessão do usuário. Você pode então usar esse token em seus formulários e verificá-lo quando o formulário for enviado.

// Gerar um token CSRF e armazená-lo na sessão do usuário
// (supondo que você tenha criado um objeto de sessão e o tenha anexado ao Flight)
// Você só precisa gerar um único token por sessão (para funcionar
// em várias guias e solicitações para o mesmo usuário)
if(Flight::session()->get('csrf_token') === null) {
    Flight::session()->set('csrf_token', bin2hex(random_bytes(32)) );
}
<!-- Use o token CSRF em seu formulário -->
<form method="post">
    <input type="hidden" name="csrf_token" value="<?= Flight::session()->get('csrf_token') ?>">
    <!-- outros campos do formulário -->
</form>

Usando Latte

Você também pode definir uma função personalizada para exibir o token CSRF em seus modelos Latte.

// Defina uma função personalizada para exibir o token CSRF
// Observação: View foi configurado com o Latte como engine de visualização
Flight::view()->addFunction('csrf', function() {
    $csrfToken = Flight::session()->get('csrf_token');
    return new \Latte\Runtime\Html('<input type="hidden" name="csrf_token" value="' . $csrfToken . '">');
});

Agora, em seus modelos Latte, você pode usar a função csrf() para exibir o token CSRF.

<form method="post">
    {csrf()}
    <!-- outros campos do formulário -->
</form>

Curto e simples, não é?

Verificar o Token CSRF

Você pode verificar o token CSRF usando filtros de evento:

// Este middleware verifica se a solicitação é uma solicitação POST e, se for, verifica se o token CSRF é válido
Flight::before('start', function() {
    if(Flight::request()->method == 'POST') {

        // capturar o token csrf dos valores do formulário
        $token = Flight::request()->data->csrf_token;
        if($token !== Flight::session()->get('csrf_token')) {
            Flight::halt(403, 'Token CSRF inválido');
        }
    }
});

Ou você pode usar uma classe de middleware:

// app/middleware/CsrfMiddleware.php

namespace app\middleware;

class CsrfMiddleware
{
    public function before(array $params): void
    {
        if(Flight::request()->method == 'POST') {
            $token = Flight::request()->data->csrf_token;
            if($token !== Flight::session()->get('csrf_token')) {
                Flight::halt(403, 'Token CSRF inválido');
            }
        }
    }
}

// index.php ou onde quer que tenha suas rotas
Flight::group('', function(Router $router) {
    $router->get('/usuarios', [ 'UserController', 'obterUsuarios' ]);
    // mais rotas
}, [ new CsrfMiddleware() ]);

Cross Site Scripting (XSS)

Cross Site Scripting (XSS) é um tipo de ataque em que um site malicioso pode injetar código em seu site. A maioria dessas oportunidades vem de valores de formulários que seus usuários preencherão. Você nunca deve confiar na saída de seus usuários! Sempre assuma que todos são os melhores hackers do mundo. Eles podem injetar JavaScript ou HTML maliciosos em sua página. Esse código pode ser usado para roubar informações de seus usuários ou realizar ações em seu site. Usando a classe de visualização do Flight, você pode escapar facilmente a saída para evitar ataques XSS.

// Vamos assumir que o usuário é esperto e tenta usar isso como seu nome
$nome = '<script>alert("XSS")</script>';

// Isso escapará a saída
Flight::view()->set('nome', $nome);
// Isso irá gerar: &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;

// Se você usar algo como Latte registrado como sua classe de visualização, ele também escapará automaticamente isso.
Flight::view()->render('modelo', ['nome' => $nome]);

Injeção de SQL

Injeção de SQL é um tipo de ataque em que um usuário malicioso pode injetar código SQL em seu banco de dados. Isso pode ser usado para roubar informações de seu banco de dados ou realizar ações em seu banco de dados. Novamente, você nunca deve confiar na entrada de seus usuários! Sempre assuma que eles estão atrás de você. Você pode usar instruções preparadas em seus objetos PDO para prevenir a injeção de SQL.

// Supondo que você tenha Flight::db() registrado como seu objeto PDO
$statement = Flight::db()->prepare('SELECT * FROM usuarios WHERE nome = :nome');
$statement->execute([':nome' => $nome]);
$usuarios = $statement->fetchAll();

// Se você usar a classe PdoWrapper, isso pode ser facilmente feito em uma linha
$usuarios = Flight::db()->fetchAll('SELECT * FROM usuarios WHERE nome = :nome', [ 'nome' => $nome ]);

// Você pode fazer a mesma coisa com um objeto PDO com espaços reservados ?
$statement = Flight::db()->fetchAll('SELECT * FROM usuarios WHERE nome = ?', [ $nome ]);

// Apenas prometa que nunca fará algo assim...
$usuarios = Flight::db()->fetchAll("SELECT * FROM usuarios WHERE nome = '{$nome}' LIMIT 5");
// porque e se $nome = "' OR 1=1; -- "; 
// Depois que a consulta for construída, ela ficará assim
// SELECT * FROM usuarios WHERE nome = '' OR 1=1; -- LIMIT 5
// Parece estranho, mas é uma consulta válida que funcionará. Na verdade,
// é um ataque de injeção de SQL muito comum que retornará todos os usuários.

CORS

Compartilhamento de Recursos de Origem Cruzada (CORS) é um mecanismo que permite que muitos recursos (por exemplo, fontes, JavaScript, etc.) em uma página da web sejam solicitados de outro domínio fora do domínio de onde o recurso se originou. O Flight não possui funcionalidades incorporadas, mas isso pode ser facilmente tratado com um gancho para ser executado antes que o método Flight::start() seja chamado.

// app/utils/CorsUtil.php

namespace app\utils;

class CorsUtil
{
    public function set(array $params): void
    {
        $request = Flight::request();
        $response = Flight::response();
        if ($request->getVar('HTTP_ORIGIN') !== '') {
            $this->allowOrigins();
            $response->header('Access-Control-Allow-Credentials', 'true');
            $response->header('Access-Control-Max-Age', '86400');
        }

        if ($request->method === 'OPTIONS') {
            if ($request->getVar('HTTP_ACCESS_CONTROL_REQUEST_METHOD') !== '') {
                $response->header(
                    'Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD'
                );
            }
            if ($request->getVar('HTTP_ACCESS_CONTROL_REQUEST_HEADERS') !== '') {
                $response->header(
                    "Access-Control-Allow-Headers",
                    $request->getVar('HTTP_ACCESS_CONTROL_REQUEST_HEADERS')
                );
            }

            $response->status(200);
            $response->send();
            exit;
        }
    }

    private function allowOrigins(): void
    {
        // personalize seus hosts permitidos aqui.
        $permitidos = [
            'capacitor://localhost',
            'ionic://localhost',
            'http://localhost',
            'http://localhost:4200',
            'http://localhost:8080',
            'http://localhost:8100',
        ];

        $request = Flight::request();

        if (in_array($request->getVar('HTTP_ORIGIN'), $permitidos, true) === true) {
            $resposta = Flight::response();
            $resposta->header("Access-Control-Allow-Origin", $request->getVar('HTTP_ORIGIN'));
        }
    }
}

// index.php ou onde quer que tenha suas rotas
$CorsUtil = new CorsUtil();
Flight::before('start', [ $CorsUtil, 'setupCors' ]);

Conclusão

Segurança é muito importante e é fundamental garantir que suas aplicações web sejam seguras. O Flight fornece uma série de recursos para ajudá-lo a proteger suas aplicações web, mas é importante sempre estar vigilante e garantir que você esteja fazendo tudo o que pode para manter os dados de seus usuários seguros. Sempre assuma o pior e nunca confie na entrada de seus usuários. Sempre escape a saída e use instruções preparadas para evitar a injeção de SQL. Sempre use middleware para proteger suas rotas contra ataques de CSRF e CORS. Se você fizer todas essas coisas, estará bem encaminhado para construir aplicações web seguras.

Learn/overriding

Substituição

O Flight permite que você substitua sua funcionalidade padrão para atender às suas próprias necessidades, sem ter que modificar nenhum código.

Por exemplo, quando o Flight não consegue corresponder a uma URL a uma rota, ele invoca o método notFound, que envia uma resposta genérica de HTTP 404. Você pode substituir esse comportamento usando o método map:

Flight::map('notFound', function() {
  // Exibir página 404 personalizada
  include 'errors/404.html';
});

O Flight também permite que você substitua componentes principais do framework. Por exemplo, você pode substituir a classe do Roteador padrão pela sua própria classe personalizada:

// Registre sua classe personalizada
Flight::register('router', MyRouter::class);

// Quando o Flight carrega a instância do Roteador, ele carregará sua classe
$myrouter = Flight::router();

Métodos do framework como map e register, no entanto, não podem ser substituídos. Você receberá um erro se tentar fazê-lo.

Learn/routing

Encaminhamento

Nota: Quer entender mais sobre encaminhamento? Confira a página "por que um framework?" para uma explicação mais detalhada.

O encaminhamento básico no Flight é feito combinando um padrão de URL com uma função de retorno ou um array de uma classe e método.

Flight::route('/', function(){
    echo 'olá mundo!';
});

As rotas são combinadas na ordem em que são definidas. A primeira rota a corresponder a uma requisição será invocada.

Callbacks/Funções

O callback pode ser qualquer objeto que seja invocável. Então você pode usar uma função regular:

function hello(){
    echo 'olá mundo!';
}

Flight::route('/', 'hello');

Classes

Você também pode usar um método estático de uma classe:

class Saudacao {
    public static function hello() {
        echo 'olá mundo!';
    }
}

Flight::route('/', [ 'Saudacao','hello' ]);

Ou criando um objeto primeiro e depois chamando o método:


// Saudacao.php
class Saudacao
{
    public function __construct() {
        $this->name = 'João da Silva';
    }

    public function hello() {
        echo "Olá, {$this->name}!";
    }
}

// index.php
$saudacao = new Saudacao();

Flight::route('/', [ $saudacao, 'hello' ]);
// Também é possível fazer isso sem criar o objeto primeiro
// Nota: Nenhum argumento será injetado no construtor
Flight::route('/', [ 'Saudacao', 'hello' ]);

Injeção de Dependência via DIC (Container de Injeção de Dependência)

Se você deseja utilizar injeção de dependência via um container (PSR-11, PHP-DI, Dice, etc), o único tipo de rotas em que isso está disponível é ou criando diretamente o objeto você mesmo e usando o container para criar seu objeto ou você pode usar strings para definir a classe e método a serem chamados. Você pode ir para a página de Injeção de Dependência para mais informações.

Aqui está um exemplo rápido:


use flight\database\PdoWrapper;

// Saudacao.php
class Saudacao
{
    protected PdoWrapper $pdoWrapper;
    public function __construct(PdoWrapper $pdoWrapper) {
        $this->pdoWrapper = $pdoWrapper;
    }

    public function hello(int $id) {
        // faça algo com $this->pdoWrapper
        $name = $this->pdoWrapper->fetchField("SELECT name FROM users WHERE id = ?", [ $id ]);
        echo "Olá, mundo! Meu nome é {$name}!";
    }
}

// index.php

// Configure o container com os parâmetros necessários
// Veja a página de Injeção de Dependência para mais informações sobre PSR-11
$dice = new \Dice\Dice();

// Não se esqueça de reatribuir a variável com '$dice = '!!!!!
$dice = $dice->addRule('flight\database\PdoWrapper', [
    'shared' => true,
    'constructParams' => [ 
        'mysql:host=localhost;dbname=test', 
        'root',
        'password'
    ]
]);

// Registre o manipulador do container
Flight::registerContainerHandler(function($class, $params) use ($dice) {
    return $dice->create($class, $params);
});

// Rotas como de costume
Flight::route('/hello/@id', [ 'Saudacao', 'hello' ]);
// ou
Flight::route('/hello/@id', 'Saudacao->hello');
// ou
Flight::route('/hello/@id', 'Saudacao::hello');

Flight::start();

Learn/variables

Variáveis

O Flight permite que você salve variáveis para que possam ser usadas em qualquer lugar de sua aplicação.

// Salve sua variável
Flight::set('id', 123);

// Em outro lugar de sua aplicação
$id = Flight::get('id');

Para verificar se uma variável foi definida, você pode fazer:

if (Flight::has('id')) {
  // Faça algo
}

Você pode limpar uma variável fazendo:

// Limpa a variável id
Flight::clear('id');

// Limpa todas as variáveis
Flight::clear();

O Flight também usa variáveis para fins de configuração.

Flight::set('flight.log_errors', true);

Learn/dependency_injection_container

Contentor de Injeção de Dependência

Introdução

O Contentor de Injeção de Dependência (CID) é uma ferramenta poderosa que permite gerenciar as dependências de sua aplicação. É um conceito-chave nos frameworks PHP modernos e é usado para gerenciar a instanciação e configuração de objetos. Alguns exemplos de bibliotecas CID são: Dice, Pimple, PHP-DI e league/container.

Um CID é uma forma sofisticada de dizer que permite criar e gerenciar suas classes em um local centralizado. Isso é útil quando você precisa passar o mesmo objeto para múltiplas classes (como seus controladores). Um exemplo simples pode ajudar a tornar isso mais claro.

Exemplo Básico

A maneira antiga de fazer as coisas poderia parecer assim:


require 'vendor/autoload.php';

// classe para gerenciar usuários no banco de dados
class UserController {

    protected PDO $pdo;

    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
    }

    public function view(int $id) {
        $stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = :id');
        $stmt->execute(['id' => $id]);

        print_r($stmt->fetch());
    }
}

$User = new UserController(new PDO('mysql:host=localhost;dbname=test', 'user', 'pass'));
Flight::route('/user/@id', [ $UserController, 'view' ]);

Flight::start();

Você pode ver a partir do código acima que estamos criando um novo objeto PDO e passando-o para nossa classe UserController. Isso é bom para uma aplicação pequena, mas à medida que a aplicação cresce, você descobrirá que está criando o mesmo objeto PDO em múltiplos lugares. É aqui que um CID se torna útil.

Aqui está o mesmo exemplo usando um CID (usando Dice):


require 'vendor/autoload.php';

// mesma classe que acima. Nada mudou
class UserController {

    protected PDO $pdo;

    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
    }

    public function view(int $id) {
        $stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = :id');
        $stmt->execute(['id' => $id]);

        print_r($stmt->fetch());
    }
}

// criar um novo contentor
$container = new \Dice\Dice;
// não se esqueça de reatribuí-lo a si mesmo como abaixo!
$container = $container->addRule('PDO', [
    // shared significa que o mesmo objeto será retornado toda vez
    'shared' => true,
    'constructParams' => ['mysql:host=localhost;dbname=test', 'user', 'pass' ]
]);

// Isso registra o manipulador do contentor para que o Flight saiba usá-lo.
Flight::registerContainerHandler(function($class, $params) use ($container) {
    return $container->create($class, $params);
});

// agora podemos usar o contentor para criar nosso UserController
Flight::route('/user/@id', [ 'UserController', 'view' ]);
// ou alternativamente você pode definir a rota assim
Flight::route('/user/@id', 'UserController->view');
// ou
Flight::route('/user/@id', 'UserController::view');

Flight::start();

Aposto que você pode estar pensando que houve muito código extra adicionado ao exemplo. A magia acontece quando você tem outro controlador que precisa do objeto PDO.


// Se todos os seus controladores têm um construtor que precisa de um objeto PDO
// cada uma das rotas abaixo terá automaticamente ele injetado!!!
Flight::route('/empresa/@id', 'CompanyController->view');
Flight::route('/organizacao/@id', 'OrganizationController->view');
Flight::route('/categoria/@id', 'CategoryController->view');
Flight::route('/configuracoes', 'SettingsController->view');

O benefício adicional de utilizar um CID é que os testes unitários se tornam muito mais fáceis. Você pode criar um objeto falso e passá-lo para sua classe. Isso é um grande benefício ao escrever testes para sua aplicação!

PSR-11

O Flight também pode usar qualquer contentor compatível com o PSR-11. Isso significa que você pode usar qualquer contentor que implemente a interface PSR-11. Aqui está um exemplo usando o contentor PSR-11 da League:


require 'vendor/autoload.php';

// mesma classe UserController que acima

$container = new \League\Container\Container();
$container->add(UserController::class)->addArgument(PdoWrapper::class);
$container->add(PdoWrapper::class)
    ->addArgument('mysql:host=localhost;dbname=test')
    ->addArgument('user')
    ->addArgument('pass');
Flight::registerContainerHandler($container);

Flight::route('/user', [ 'UserController', 'view' ]);

Flight::start();

Embora isso possa ser um pouco mais verboso do que o exemplo anterior com Dice, ainda faz o trabalho com os mesmos benefícios!

Manipulador CID Personalizado

Você também pode criar seu próprio manipulador CID. Isso é útil se você tiver um contentor personalizado que deseja usar que não seja PSR-11 (Dice). Veja o exemplo básico de como fazer isso.

Além disso, existem alguns padrões úteis que facilitarão sua vida ao usar o Flight.

Instância do Motor

Se você estiver usando a instância Engine em seus controladores/funções intermediárias, aqui está como você configuraria:


// Em algum lugar do seu arquivo de inicialização
$engine = Flight::app();

$container = new \Dice\Dice;
$container = $container->addRule('*', [
    'substitutions' => [
        // Aqui é onde você passa a instância
        Engine::class => $engine
    ]
]);

$engine->registerContainerHandler(function($class, $params) use ($container) {
    return $container->create($class, $params);
});

// Agora você pode usar a instância do Engine em seus controladores/funções intermediárias

class MeuControlador {
    public function __construct(Engine $app) {
        $this->app = $app;
    }

    public function index() {
        $this->app->render('index');
    }
}

Adicionando Outras Classes

Se você tem outras classes que deseja adicionar ao contentor, com Dice é fácil, pois elas serão automaticamente resolvidas pelo contentor. Aqui está um exemplo:


$container = new \Dice\Dice;
// Se você não precisa injetar nada em sua classe
// você não precisa definir nada!
Flight::registerContainerHandler(function($class, $params) use ($container) {
    return $container->create($class, $params);
});

class MinhaClassePersonalizada {
    public function parseCoisa() {
        return 'coisa';
    }
}

class UserController {

    protected MyCustomClass $MyCustomClass;

    public function __construct(MyCustomClass $MyCustomClass) {
        $this->MyCustomClass = $MyCustomClass;
    }

    public function index() {
        echo $this->MyCustomClass->parseThing();
    }
}

Flight::route('/user', 'UserController->index');

Learn/middleware

Middleware de Rota

O Flight suporta middleware de rota e de grupo de rotas. O middleware é uma função que é executada antes (ou depois) da chamada de rota. Esta é uma ótima maneira de adicionar verificações de autenticação da API em seu código, ou para validar se o usuário tem permissão para acessar a rota.

Middleware Básico

Aqui está um exemplo básico:

// Se você fornecer apenas uma função anônima, ela será executada antes da chamada de rota. 
// não existem funções de middleware "depois" exceto para classes (veja abaixo)
Flight::route('/caminho', function() { echo ' Aqui estou!'; })->addMiddleware(function() {
    echo 'Middleware primeiro!';
});

Flight::start();

// Isso irá exibir "Middleware primeiro! Aqui estou!"

Existem algumas notas muito importantes sobre middleware que você deve estar ciente antes de usá-los:

Classes de Middleware

O middleware também pode ser registrado como uma classe. Se precisar da funcionalidade "depois", você deve usar uma classe.

class MeuMiddleware {
    public function before($params) {
        echo 'Middleware primeiro!';
    }

    public function after($params) {
        echo 'Middleware último!';
    }
}

$MeuMiddleware = new MeuMiddleware();
Flight::route('/caminho', function() { echo ' Aqui estou! '; })->addMiddleware($MeuMiddleware); // também ->addMiddleware([ $MeuMiddleware, $MeuMiddleware2 ]);

Flight::start();

// Isso exibirá "Middleware primeiro! Aqui estou! Middleware último!"

Lidando com Erros de Middleware

Digamos que você tenha um middleware de autenticação e deseje redirecionar o usuário para uma página de login se ele não estiver autenticado. Você tem algumas opções à sua disposição:

  1. Você pode retornar false a partir da função de middleware e o Flight retornará automaticamente um erro 403 Proibido, mas sem personalização.
  2. Você pode redirecionar o usuário para uma página de login usando Flight::redirect().
  3. Você pode criar um erro personalizado dentro do middleware e interromper a execução da rota.

Exemplo Básico

Aqui está um exemplo simples de retorno de false:

class MeuMiddleware {
    public function before($params) {
        if (isset($_SESSION['user']) === false) {
            return false;
        }

        // como é true, tudo continua
    }
}

Exemplo de Redirecionamento

Aqui está um exemplo de redirecionamento do usuário para uma página de login:

class MeuMiddleware {
    public function before($params) {
        if (isset($_SESSION['user']) === false) {
            Flight::redirect('/login');
            exit;
        }
    }
}

Exemplo de Erro Personalizado

Digamos que você precise retornar um erro JSON porque está construindo uma API. Você pode fazer isso assim:

class MeuMiddleware {
    public function before($params) {
        $autorizacao = Flight::request()->headers['Authorization'];
        if(empty($autorizacao)) {
            Flight::json(['erro' => 'Você precisa estar logado para acessar esta página.'], 403);
            exit;
            // ou
            Flight::halt(403, json_encode(['error' => 'Você precisa estar logado para acessar esta página.']);
        }
    }
}

Agrupamento de Middleware

Você pode adicionar um grupo de rota e, em seguida, cada rota nesse grupo terá o mesmo middleware também. Isso é útil se você precisar agrupar um monte de rotas, por exemplo, por um middleware de autenticação para verificar a chave da API no cabeçalho.


// adicionado ao final do método do grupo
Flight::group('/api', function() {

    // Esta rota com aparência "vazia" corresponderá na verdade a /api
    Flight::route('', function() { echo 'api'; }, false, 'api');
    Flight::route('/usuarios', function() { echo 'usuários'; }, false, 'usuários');
    Flight::route('/usuarios/@id', function($id) { echo 'usuário:'.$id; }, false, 'visualizar_usuario');
}, [ new ApiAuthMiddleware() ]);

Se você deseja aplicar um middleware global a todas as suas rotas, você pode adicionar um grupo "vazio":


// adicionado ao final do método do grupo
Flight::group('', function() {
    Flight::route('/usuarios', function() { echo 'usuários'; }, false, 'usuários');
    Flight::route('/usuarios/@id', function($id) { echo 'usuário:'.$id; }, false, 'visualizar_usuario');
}, [ new ApiAuthMiddleware() ]);

Learn/filtering

Filtragem

O Flight permite que você filtre métodos antes e depois de serem chamados. Não há ganchos predefinidos que você precise memorizar. Você pode filtrar qualquer um dos métodos padrão do framework, bem como quaisquer métodos personalizados que você tenha mapeado.

Uma função de filtro parece com isso:

function (array &$params, string &$output): bool {
  // Código do filtro
}

Usando as variáveis passadas, você pode manipular os parâmetros de entrada e/ou a saída.

Você pode ter um filtro sendo executado antes de um método fazendo:

Flight::before('start', function (array &$params, string &$output): bool {
  // Faça algo
});

Você pode ter um filtro sendo executado após um método fazendo:

Flight::after('start', function (array &$params, string &$output): bool {
  // Faça algo
});

Você pode adicionar quantos filtros quiser a qualquer método. Eles serão chamados na ordem em que são declarados.

Aqui está um exemplo do processo de filtragem:

// Mapeie um método personalizado
Flight::map('hello', function (string $name) {
  return "Olá, $name!";
});

// Adicione um filtro antes
Flight::before('hello', function (array &$params, string &$output): bool {
  // Manipule o parâmetro
  $params[0] = 'Fred';
  return true;
});

// Adicione um filtro após
Flight::after('hello', function (array &$params, string &$output): bool {
  // Manipule a saída
  $output .= " Tenha um bom dia!";
  return true;
});

// Invoque o método personalizado
echo Flight::hello('Bob');

Isso deve exibir:

Olá Fred! Tenha um bom dia!

Se você definiu vários filtros, você pode interromper a cadeia retornando false em qualquer uma de suas funções de filtro:

Flight::before('start', function (array &$params, string &$output): bool {
  echo 'um';
  return true;
});

Flight::before('start', function (array &$params, string &$output): bool {
  echo 'dois';

  // Isso irá encerrar a cadeia
  return false;
});

// Isso não será chamado
Flight::before('start', function (array &$params, string &$output): bool {
  echo 'três';
  return true;
});

Observação, métodos principais como map e register não podem ser filtrados porque são chamados diretamente e não invocados dinamicamente.

Learn/requests

Pedidos

O Flight encapsula o pedido HTTP em um único objeto, que pode ser acessado através de:

$request = Flight::request();

O objeto de pedido fornece as seguintes propriedades:

Você pode acessar as propriedades query, data, cookies e files como arrays ou objetos.

Portanto, para obter um parâmetro da string de consulta, você pode fazer:

$id = Flight::request()->query['id'];

Ou você pode fazer:

$id = Flight::request()->query->id;

Corpo do Pedido Bruto

Para obter o corpo bruto do pedido HTTP, por exemplo, ao lidar com pedidos PUT, você pode fazer:

$body = Flight::request()->getBody();

Entrada JSON

Se você enviar um pedido com o tipo application/json e os dados {"id": 123}, eles estarão disponíveis na propriedade data:

$id = Flight::request()->data->id;

Acessando $_SERVER

Há um atalho disponível para acessar a matriz $_SERVER através do método getVar():


$host = Flight::request()->getVar['HTTP_HOST'];

Acessando Cabeçalhos do Pedido

Você pode acessar os cabeçalhos do pedido usando o método getHeader() ou getHeaders():


// Talvez você precise do cabeçalho de Autorização
$host = Flight::request()->getHeader('Authorization');

// Se precisar pegar todos os cabeçalhos
$headers = Flight::request()->getHeaders();

Learn/frameworkmethods

# Métodos do Framework

Flight é projetado para ser fácil de usar e entender. O seguinte é o conjunto completo
de métodos para o framework. Consiste em métodos principais, que são métodos estáticos regulares,
e métodos extensíveis, que são métodos mapeados que podem ser filtrados
ou substituídos.

## Métodos Principais

```php
Flight::map(string $name, callable $callback, bool $pass_route = false) // Cria um método de framework personalizado.
Flight::register(string $name, string $class, array $params = [], ?callable $callback = null) // Registra uma classe a um método de framework.
Flight::before(string $name, callable $callback) // Adiciona um filtro antes de um método de framework.
Flight::after(string $name, callable $callback) // Adiciona um filtro após um método de framework.
Flight::path(string $path) // Adiciona um caminho para carregar classes automaticamente.
Flight::get(string $key) // Obtém uma variável.
Flight::set(string $key, mixed $value) // Define uma variável.
Flight::has(string $key) // Verifica se uma variável está definida.
Flight::clear(array|string $key = []) // Limpa uma variável.
Flight::init() // Inicializa o framework com suas configurações padrão.
Flight::app() // Obtém a instância do objeto de aplicativo

Métodos Extensíveis

Flight::start() // Inicia o framework.
Flight::stop() // Para o framework e envia uma resposta.
Flight::halt(int $code = 200, string $message = '') // Para o framework com um código de status e mensagem opcionais.
Flight::route(string $pattern, callable $callback, bool $pass_route = false) // Mapeia um padrão de URL para um retorno de chamada.
Flight::group(string $pattern, callable $callback) // Cria agrupamento para urls, o padrão deve ser uma string.
Flight::redirect(string $url, int $code) // Redireciona para outra URL.
Flight::render(string $file, array $data, ?string $key = null) // Renderiza um arquivo de modelo.
Flight::error(Throwable $error) // Envia uma resposta HTTP 500.
Flight::notFound() // Envia uma resposta HTTP 404.
Flight::etag(string $id, string $type = 'string') // Realiza o cache do HTTP ETag.
Flight::lastModified(int $time) // Realiza o cache do HTTP de última modificação.
Flight::json(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Envia uma resposta JSON.
Flight::jsonp(mixed $data, string $param = 'jsonp', int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Envia uma resposta JSONP.

Quaisquer métodos personalizados adicionados com map e register também podem ser filtrados.

Learn/api

# Métodos da API do Framework

O Flight é projetado para ser fácil de usar e entender. O seguinte é o conjunto completo
de métodos para o framework. Ele consiste em métodos principais, que são
métodos estáticos regulares, e métodos extensíveis, que são métodos mapeados que podem ser filtrados
ou substituídos.

## Métodos Principais

Esses métodos são essenciais para o framework e não podem ser substituídos.

```php
Flight::map(string $name, callable $callback, bool $pass_route = false) // Cria um método personalizado no framework.
Flight::register(string $name, string $class, array $params = [], ?callable $callback = null) // Registra uma classe em um método do framework.
Flight::unregister(string $name) // Cancela o registro de uma classe em um método do framework.
Flight::before(string $name, callable $callback) // Adiciona um filtro antes de um método do framework.
Flight::after(string $name, callable $callback) // Adiciona um filtro depois de um método do framework.
Flight::path(string $path) // Adiciona um caminho para carregar automaticamente classes.
Flight::get(string $key) // Obtém uma variável.
Flight::set(string $key, mixed $value) // Define uma variável.
Flight::has(string $key) // Verifica se uma variável está definida.
Flight::clear(array|string $key = []) // Limpa uma variável.
Flight::init() // Inicializa o framework com suas configurações padrão.
Flight::app() // Obtém a instância do objeto de aplicação
Flight::request() // Obtém a instância do objeto de solicitação
Flight::response() // Obtém a instância do objeto de resposta
Flight::router() // Obtém a instância do objeto de roteador
Flight::view() // Obtém a instância do objeto de visualização

Métodos Extensíveis

Flight::start() // Inicia o framework.
Flight::stop() // Interrompe o framework e envia uma resposta.
Flight::halt(int $code = 200, string $message = '') // Interrompe o framework com um código de status e mensagem opcional.
Flight::route(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Mapeia um padrão de URL para uma chamada de retorno.
Flight::post(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Mapeia um padrão de URL de solicitação POST para uma chamada de retorno.
Flight::put(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Mapeia um padrão de URL de solicitação PUT para uma chamada de retorno.
Flight::patch(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Mapeia um padrão de URL de solicitação PATCH para uma chamada de retorno.
Flight::delete(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Mapeia um padrão de URL de solicitação DELETE para uma chamada de retorno.
Flight::group(string $pattern, callable $callback) // Cria agrupamento para URLs, o padrão deve ser uma string.
Flight::getUrl(string $name, array $params = []) // Gera uma URL com base em um alias de rota.
Flight::redirect(string $url, int $code) // Redireciona para outra URL.
Flight::render(string $file, array $data, ?string $key = null) // Renderiza um arquivo de modelo.
Flight::error(Throwable $error) // Envia uma resposta HTTP 500.
Flight::notFound() // Envia uma resposta HTTP 404.
Flight::etag(string $id, string $type = 'string') // Executa o cacheamento HTTP ETag.
Flight::lastModified(int $time) // Executa o cacheamento HTTP modificado por último.
Flight::json(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Envia uma resposta JSON.
Flight::jsonp(mixed $data, string $param = 'jsonp', int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Envia uma resposta JSONP.

Quaisquer métodos personalizados adicionados com map e register também podem ser filtrados.

Learn/why_frameworks

Por Que um Framework?

Alguns programadores são veementemente contra o uso de frameworks. Eles argumentam que os frameworks são inchados, lentos e difíceis de aprender. Eles dizem que os frameworks são desnecessários e que você pode escrever um código melhor sem eles. Certamente, existem alguns pontos válidos a serem considerados sobre as desvantagens do uso de frameworks. No entanto, também existem muitas vantagens em usar frameworks.

Razões Para Usar um Framework

Aqui estão algumas razões pelas quais você pode querer considerar o uso de um framework:

Flight é um micro-framework. Isso significa que é pequeno e leve. Ele não fornece tanta funcionalidade quanto os frameworks maiores como Laravel ou Symfony. No entanto, ele fornece muita funcionalidade necessária para construir aplicações web. Também é fácil de aprender e usar. Isso o torna uma boa escolha para construir aplicações web de forma rápida e fácil. Se você é novo em frameworks, Flight é um ótimo framework para iniciantes para começar. Isso ajudará você a aprender sobre as vantagens de usar frameworks sem sobrecarregá-lo com muita complexidade. Depois de ter alguma experiência com o Flight, será mais fácil passar para frameworks mais complexos como Laravel ou Symfony, no entanto, o Flight ainda pode criar uma aplicação robusta e bem-sucedida.

O Que é Roteamento?

O roteamento é o núcleo do framework Flight, mas o que é exatamente? Roteamento é o processo de pegar uma URL e combiná-la com uma função específica em seu código. É assim que você pode fazer seu site fazer coisas diferentes com base na URL solicitada. Por exemplo, você pode querer mostrar o perfil de um usuário quando eles visitam /user/1234, mas mostrar uma lista de todos os usuários quando eles visitam /users. Tudo isso é feito através do roteamento.

Pode funcionar assim:

E Por Que é Importante?

Ter um roteador centralizado apropriado pode realmente tornar sua vida dramaticamente mais fácil! Pode ser difícil de ver no início. Aqui estão algumas razões pelas quais:

Tenho certeza de que você está familiarizado com o método de criar um site script por script. Você pode ter um arquivo chamado index.php que possui um monte de declarações if para verificar a URL e executar uma função específica com base na URL. Isso é uma forma de roteamento, mas não é muito organizado e pode ficar fora de controle rapidamente. O sistema de roteamento do Flight é uma forma muito mais organizada e poderosa de lidar com o roteamento.

Isso?


// /user/view_profile.php?id=1234
if ($_GET['id']) {
    $id = $_GET['id'];
    viewUserProfile($id);
}

// /user/edit_profile.php?id=1234
if ($_GET['id']) {
    $id = $_GET['id'];
    editUserProfile($id);
}

// etc...

Ou isso?


// index.php
Flight::route('/user/@id', [ 'UserController', 'viewUserProfile' ]);
Flight::route('/user/@id/edit', [ 'UserController', 'editUserProfile' ]);

// Em talvez seu app/controllers/UserController.php
class UserController {
    public function viewUserProfile($id) {
        // faça algo
    }

    public function editUserProfile($id) {
        // faça algo
    }
}

Com sorte, você pode começar a ver os benefícios de usar um sistema de roteamento centralizado. É muito mais fácil de gerenciar e entender a longo prazo!

Solicitações e Respostas

O Flight fornece uma maneira simples e fácil de lidar com solicitações e respostas. Este é o núcleo do que um framework web faz. Ele recebe uma solicitação do navegador de um usuário, a processa e envia de volta uma resposta. É assim que você pode construir aplicações web que fazem coisas como mostrar o perfil de um usuário, permitir que um usuário faça login ou permitir que um usuário poste um novo post em um blog.

Solicitações

Uma solicitação é o que o navegador do usuário envia para o seu servidor quando eles visitam seu site. Esta solicitação contém informações sobre o que o usuário deseja fazer. Por exemplo, pode conter informações sobre qual URL o usuário deseja visitar, que dados o usuário deseja enviar para o seu servidor ou que tipo de dados o usuário deseja receber do seu servidor. É importante saber que uma solicitação é somente leitura. Você não pode alterar a solicitação, mas pode ler dela.

O Flight fornece uma maneira simples de acessar informações sobre a solicitação. Você pode acessar informações sobre a solicitação usando o método Flight::request(). Este método retorna um objeto Request que contém informações sobre a solicitação. Você pode usar este objeto para acessar informações sobre a solicitação, como a URL, o método ou os dados que o usuário enviou para o seu servidor.

Respostas

Uma resposta é o que seu servidor envia de volta para o navegador do usuário quando eles visitam seu site. Esta resposta contém informações sobre o que seu servidor deseja fazer. Por exemplo, pode conter informações sobre que tipo de dados seu servidor deseja enviar para o usuário, que tipo de dados seu servidor deseja receber do usuário, ou que tipo de dados seu servidor deseja armazenar no computador do usuário.

O Flight fornece uma maneira simples de enviar uma resposta para o navegador do usuário. Você pode enviar uma resposta usando o método Flight::response(). Este método recebe um objeto Response como argumento e envia a resposta para o navegador do usuário. Você pode usar este objeto para enviar uma resposta para o navegador do usuário, como HTML, JSON ou um arquivo. O Flight ajuda a gerar automaticamente algumas partes da resposta para facilitar as coisas, mas, em última instância, você tem controle sobre o que envia de volta para o usuário.

Learn/httpcaching

Caching HTTP

Voo fornece suporte embutido para o caching de nível HTTP. Se a condição de caching for atendida, Voo retornará uma resposta 304 Not Modified HTTP. Da próxima vez que o cliente solicitar o mesmo recurso, eles serão convidados a usar sua versão em cache localmente.

Última Modificação

Você pode usar o método lastModified e passar um carimbo de data/hora UNIX para definir a data e hora em que a página foi modificada pela última vez. O cliente continuará a usar seu cache até que o valor da última modificação seja alterado.

Flight::route('/noticias', function () {
  Flight::lastModified(1234567890);
  echo 'Este conteúdo será armazenado em cache.';
});

ETag

O caching ETag é semelhante ao Última Modificação, exceto que você pode especificar qualquer ID que desejar para o recurso:

Flight::route('/noticias', function () {
  Flight::etag('meu-id-único');
  echo 'Este conteúdo será armazenado em cache.';
});

Tenha em mente que chamar lastModified ou etag definirá e verificará o valor do cache. Se o valor do cache for o mesmo entre as solicitações, Voo enviará imediatamente uma resposta HTTP 304 e interromperá o processamento.

Learn/responses

Respostas

O Flight ajuda a gerar parte dos cabeçalhos de respostas para você, mas você controla a maior parte do que envia de volta para o usuário. Às vezes você pode acessar o objeto Response diretamente, mas na maioria das vezes você usará a instância do Flight para enviar uma resposta.

Enviando uma Resposta Básica

O Flight usa ob_start() para armazenar em buffer a saída. Isso significa que você pode usar echo ou print para enviar uma resposta para o usuário e o Flight irá capturá-la e enviá-la de volta para o usuário com os cabeçalhos apropriados.


// Isso enviará "Olá, Mundo!" para o navegador do usuário
Flight::route('/', function() {
    echo "Olá, Mundo!";
});

// HTTP/1.1 200 OK
// Tipo de Conteúdo: text/html
//
// Olá, Mundo!

Como alternativa, você pode chamar o método write() para adicionar ao corpo também.


// Isso enviará "Olá, Mundo!" para o navegador do usuário
Flight::route('/', function() {
    // detalhado, mas funciona às vezes quando você precisa
    Flight::response()->write("Olá, Mundo!");

    // se você quiser recuperar o corpo que definiu até este ponto
    // você pode fazer isso assim
    $corpo = Flight::response()->getBody();
});

Códigos de Status

Você pode definir o código de status da resposta usando o método status:

Flight::route('/@id', function($id) {
    if($id == 123) {
        Flight::response()->status(200);
        echo "Olá, Mundo!";
    } else {
        Flight::response()->status(403);
        echo "Proibido";
    }
});

Se quiser obter o código de status atual, você pode usar o método status sem argumentos:

Flight::response()->status(); // 200

Executando um Callback no Corpo da Resposta

Você pode executar um callback no corpo da resposta usando o método addResponseBodyCallback:

Flight::route('/usuarios', function() {
    $bd = Flight::db();
    $usuarios = $bd->fetchAll("SELECT * FROM usuarios");
    Flight::render('tabela_usuarios', ['usuarios' => $usuarios]);
});

// Isso irá compactar todas as respostas para qualquer rota
Flight::response()->addResponseBodyCallback(function($corpo) {
    return gzencode($corpo, 9);
});

Você pode adicionar vários callbacks e eles serão executados na ordem em que foram adicionados. Como isso pode aceitar qualquer callable, pode aceitar um array de classe [ $classe, 'método' ], um closure $strReplace = function($corpo) { str_replace('olá', 'aí', $corpo); };, ou um nome de função 'minificar' se você tivesse uma função para minificar seu código html, por exemplo.

Nota: Callbacks de rota não funcionarão se você estiver usando a opção de configuração flight.v2.output_buffering.

Callback de Rota Específica

Se você quiser que isso se aplique apenas a uma rota específica, você pode adicionar o callback na rota em si:

Flight::route('/usuarios', function() {
    $bd = Flight::db();
    $usuarios = $bd->fetchAll("SELECT * FROM usuarios");
    Flight::render('tabela_usuarios', ['usuarios' => $usuarios]);

    // Isso irá compactar apenas a resposta para esta rota
    Flight::response()->addResponseBodyCallback(function($corpo) {
        return gzencode($corpo, 9);
    });
});

Opção de Middleware

Você também pode usar middleware para aplicar o callback a todas as rotas via middleware:

// MinifyMiddleware.php
class MinifyMiddleware {
    public function before() {
        Flight::response()->addResponseBodyCallback(function($corpo) {
            // Isto é um
            return $this->minify($corpo);
        });
    }

    protected function minify(string $corpo): string {
        // minificar o corpo
        return $corpo;
    }
}

// index.php
Flight::group('/usuarios', function() {
    Flight::route('', function() { /* ... */ });
    Flight::route('/@id', function($id) { /* ... */ });
}, [ new MinifyMiddleware() ]);

Definindo um Cabeçalho de Resposta

Você pode definir um cabeçalho como o tipo de conteúdo da resposta usando o método header:


// Isso enviará "Olá, Mundo!" para o navegador do usuário em texto simples
Flight::route('/', function() {
    Flight::response()->header('Content-Type', 'text/plain');
    echo "Olá, Mundo!";
});

JSON

O Flight fornece suporte para envio de respostas JSON e JSONP. Para enviar uma resposta JSON você passa alguns dados para serem codificados em JSON:

Flight::json(['id' => 123]);

JSON com Código de Status

Você também pode passar um código de status como segundo argumento:

Flight::json(['id' => 123], 201);

JSON com Impressão Simples

Você também pode passar um argumento para a última posição para habilitar a impressão simples:

Flight::json(['id' => 123], 200, true, 'utf-8', JSON_PRETTY_PRINT);

Se você estiver alterando as opções passadas para Flight::json() e quiser uma sintaxe mais simples, você pode apenas remapear o método JSON:

Flight::map('json', function($dados, $codigo = 200, $opções = 0) {
    Flight::_json($dados, $codigo, true, 'utf-8', $opções);
}

// E agora pode ser usado assim
Flight::json(['id' => 123], 200, JSON_PRETTY_PRINT);

JSON e Parar a Execução

Se quiser enviar uma resposta JSON e interromper a execução, você pode usar o método jsonHalt. Isso é útil para casos em que você está verificando talvez algum tipo de autorização e se o usuário não estiver autorizado, você pode enviar imediatamente uma resposta JSON, limpar o conteúdo do corpo existente e interromper a execução.

Flight::route('/usuarios', function() {
    $autorizado = algumaVerificaçãoDeAutorização();
    // Verifique se o usuário está autorizado
    if($autorizado === false) {
        Flight::jsonHalt(['erro' => 'Não autorizado'], 401);
    }

    // Continue com o restante da rota
});

JSONP

Para solicitações JSONP, você pode opcionalmente passar o nome do parâmetro de consulta que você está usando para definir sua função de retorno de chamada:

Flight::jsonp(['id' => 123], 'q');

Assim, ao fazer uma solicitação GET usando ?q=MinhaFunc, você deverá receber a saída:

MinhaFunc({"id":123});

Se você não passar um nome de parâmetro de consulta, ele será padrão para jsonp.

Redirecionar para outra URL

Você pode redirecionar a solicitação atual usando o método redirect() e passando em uma nova URL:

Flight::redirect('/nova/localizacao');

Por padrão, o Flight envia um código de status HTTP 303 ("Veja Outro"). Você pode opcionalmente definir um código personalizado:

Flight::redirect('/nova/localizacao', 401);

Parar

Você pode parar o framework em qualquer ponto chamando o método halt:

Flight::halt();

Você também pode especificar um código de status HTTP opcional e uma mensagem:

Flight::halt(200, 'Voltamos em breve...');

Chamar halt descartará qualquer conteúdo de resposta até aquele ponto. Se você quiser parar o framework e exibir a resposta atual, use o método stop:

Flight::stop();

Cache HTTP

O Flight fornece suporte embutido para o cache de nível HTTP. Se a condição de cache for atendida, o Flight retornará uma resposta HTTP 304 Não Modificado. A próxima vez que o cliente solicitar o mesmo recurso, ele será solicitado a usar sua versão em cache localmente.

Cache de Nível de Rota

Se você deseja armazenar em cache toda a sua resposta, você pode usar o método cache() e passar o tempo de cache.


// Isso armazenará em cache a resposta por 5 minutos
Flight::route('/noticias', function () {
  Flight::response()->cache(time() + 300);
  echo 'Este conteúdo será armazenado em cache.';
});

// Alternativamente, você pode usar uma string que passaria
// para o método strtotime()
Flight::route('/noticias', function () {
  Flight::response()->cache('+5 minutos');
  echo 'Este conteúdo será armazenado em cache.';
});

Modificado pela última vez

Você pode usar o método lastModified e passar um carimbo de data UNIX para definir a data e hora em que uma página foi modificada pela última vez. O cliente continuará a usar seu cache até o valor da última modificação ser alterado.

Flight::route('/noticias', function () {
  Flight::lastModified(1234567890);
  echo 'Este conteúdo será armazenado em cache.';
});

ETag

O cache ETag é semelhante ao Modificado pela última vez, exceto que você pode especificar qualquer ID desejado para o recurso:

Flight::route('/noticias', function () {
  Flight::etag('meu-id-único');
  echo 'Este conteúdo será armazenado em cache.';
});

Lembre-se que chamar lastModified ou etag definirá e verificará o valor de cache. Se o valor da cache for o mesmo entre as solicitações, o Flight enviará imediatamente uma resposta HTTP 304 e interromperá o processamento.

Learn/frameworkinstance

Instância do Framework

Em vez de executar o Flight como uma classe estática global, você pode executá-lo opcionalmente como uma instância de objeto.

require 'flight/autoload.php';

$app = Flight::app();

$app->route('/', function () {
  echo 'olá mundo!';
});

$app->start();

Portanto, em vez de chamar o método estático, você chamaria o método de instância com o mesmo nome no objeto Engine.

Learn/redirects

Redirecionamentos

Você pode redirecionar a solicitação atual usando o método redirect e passando uma nova URL:

Flight::redirect('/novo/local');

Por padrão, o Flight envia um código de status HTTP 303. Você pode opcionalmente definir um código personalizado:

Flight::redirect('/novo/local', 401);

Learn/views

Vistas

Flight fornece alguma funcionalidade básica de modelagem por padrão. Para exibir uma vista chame o método render com o nome do arquivo de modelo e dados de modelo opcionais:

Flight::render('hello.php', ['name' => 'Bob']);

Os dados do modelo que você passa são automaticamente injetados no modelo e podem ser referenciados como uma variável local. Os arquivos de modelo são simplesmente arquivos PHP. Se o conteúdo do arquivo de modelo hello.php for:

Olá, <?= $name ?>!

A saída seria:

Olá, Bob!

Você também pode definir manualmente variáveis de visualização usando o método set:

Flight::view()->set('name', 'Bob');

A variável name agora está disponível em todas as suas visualizações. Então você pode simplesmente fazer:

Flight::render('hello');

Observe que ao especificar o nome do modelo no método render, você pode omitir a extensão .php.

Por padrão, o Flight procurará um diretório views para arquivos de modelo. Você pode definir um caminho alternativo para seus modelos configurando o seguinte:

Flight::set('flight.views.path', '/caminho/para/views');

Layouts

É comum que sites tenham um único arquivo de modelo de layout com conteúdo alternante. Para renderizar conteúdo a ser usado em um layout, você pode passar um parâmetro opcional para o método render.

Flight::render('header', ['heading' => 'Olá'], 'headerContent');
Flight::render('body', ['body' => 'Mundo'], 'bodyContent');

Sua visualização então terá variáveis salvas chamadas headerContent e bodyContent. Você pode então renderizar seu layout fazendo:

Flight::render('layout', ['title' => 'Página Inicial']);

Se os arquivos de modelo forem assim:

header.php:

<h1><?= $heading ?></h1>

body.php:

<div><?= $body ?></div>

layout.php:

<html>
  <head>
    <title><?= $title ?></title>
  </head>
  <body>
    <?= $headerContent ?>
    <?= $bodyContent ?>
  </body>
</html>

A saída seria:

<html>
  <head>
    <title>Página Inicial</title>
  </head>
  <body>
    <h1>Olá</h1>
    <div>Mundo</div>
  </body>
</html>

Vistas Personalizadas

O Flight permite que você substitua a engine de visualização padrão simplesmente registrando sua própria classe de visualização. Veja como você usaria o Smarty template engine para suas visualizações:

// Carregar biblioteca do Smarty
require './Smarty/libs/Smarty.class.php';

// Registrar Smarty como a classe de visualização
// Também passar uma função de retorno de chamada para configurar o Smarty ao carregar
Flight::register('view', Smarty::class, [], function (Smarty $smarty) {
  $smarty->setTemplateDir('./templates/');
  $smarty->setCompileDir('./templates_c/');
  $smarty->setConfigDir('./config/');
  $smarty->setCacheDir('./cache/');
});

// Atribuir dados do modelo
Flight::view()->assign('name', 'Bob');

// Exibir o modelo
Flight::view()->display('hello.tpl');

Para completude, você também deve substituir o método de renderização padrão do Flight:

Flight::map('render', function(string $template, array $data): void {
  Flight::view()->assign($data);
  Flight::view()->display($template);
});

Learn/templates

Visualizações

O Flight fornece alguma funcionalidade básica de modelagem por padrão.

Se você precisar de necessidades de modelagem mais complexas, consulte exemplos do Smarty e Latte na seção Visualizações Personalizadas.

Para exibir um modelo de visualização, chame o método render com o nome do arquivo de modelo e dados de modelo opcionais:

Flight::render('hello.php', ['name' => 'Bob']);

Os dados do modelo que você passa são automaticamente injetados no modelo e podem ser referenciados como uma variável local. Os arquivos de modelo são simplesmente arquivos PHP. Se o conteúdo do arquivo de modelo hello.php for:

Olá, <?= $name ?>!

A saída seria:

Olá, Bob!

Você também pode definir manualmente variáveis de visualização usando o método set:

Flight::view()->set('name', 'Bob');

A variável name agora está disponível em todas as suas visualizações. Portanto, você pode simplesmente fazer:

Flight::render('hello');

Observe que ao especificar o nome do modelo no método de renderização, você pode omitir a extensão .php.

Por padrão, o Flight procurará um diretório views para arquivos de modelo. Você pode definir um caminho alternativo para seus modelos configurando o seguinte:

Flight::set('flight.views.path', '/caminho/para/views');

Layouts

É comum que os sites tenham um único arquivo de modelo de layout com conteúdo alternante. Para renderizar conteúdo a ser usado em um layout, você pode passar um parâmetro opcional para o método render.

Flight::render('header', ['heading' => 'Olá'], 'headerContent');
Flight::render('body', ['body' => 'Mundo'], 'bodyContent');

Sua visualização terá então variáveis salvas chamadas headerContent e bodyContent. Você pode então renderizar seu layout fazendo:

Flight::render('layout', ['title' => 'Página Inicial']);

Se os arquivos de modelo forem assim:

header.php:

<h1><?= $heading ?></h1>

body.php:

<div><?= $body ?></div>

layout.php:

<html>
  <head>
    <title><?= $title ?></title>
  </head>
  <body>
    <?= $headerContent ?>
    <?= $bodyContent ?>
  </body>
</html>

A saída seria:

<html>
  <head>
    <title>Página Inicial</title>
  </head>
  <body>
    <h1>Olá</h1>
    <div>Mundo</div>
  </body>
</html>

Visualizações Personalizadas

O Flight permite que você substitua o mecanismo de visualização padrão simplesmente registrando sua própria classe de visualização.

Smarty

Veja como você usaria o Smarty mecanismo de modelo para suas visualizações:

// Carregar biblioteca Smarty
requerer './Smarty/libs/Smarty.class.php';

// Registrar o Smarty como a classe de visualização
// Passe também uma função de retorno de chamada para configurar o Smarty ao carregar
Flight::register('view', Smarty::class, [], function (Smarty $smarty) {
  $smarty->setTemplateDir('./templates/');
  $smarty->setCompileDir('./templates_c/');
  $smarty->setConfigDir('./config/');
});

// Atribuir dados de modelo
Flight::view()->assign('name', 'Bob');

// Exibir o modelo
Flight::view()->display('hello.tpl');

Para completude, você também deve substituir o método de renderização padrão do Flight:

Flight::map('render', função (string $template, array $data): vazio {
  Flight::view()->assign($data);
  Flight::view()->display($template);
});

Latte

Veja como você usaria o Latte mecanismo de modelo para suas visualizações:


// Registrar o Latte como a classe de visualização
// Passe também uma função de retorno de chamada para configurar o Latte ao carregar
Flight::register('view', Latte\Engine::class, [], function (Latte\Engine $latte) {
  // Aqui é onde o Latte irá armazenar em cache seus modelos para acelerar as coisas
  // Uma coisa legal sobre o Latte é que ele atualiza automaticamente seu
  // cache quando você faz alterações em seus modelos!
  $latte->setTempDirectory(__DIR__ . '/../cache/');

  // Diga ao Latte onde o diretório raiz de suas visualizações estará.
  $latte->setLoader(new \Latte\Loaders\FileLoader(__DIR__ . '/../views/'));
});

// E envolva-o para que você possa usar Flight::render() corretamente
Flight::map('render', função (string $template, array $data): vazio {
  // Isso é como $latte_engine->render($template, $data);
  echo Flight::view()->render($template, $data);
});

Learn/extending

Extensão

O Flight foi projetado para ser um framework extensível. O framework vem com um conjunto de métodos e componentes padrão, mas permite que você mapeie seus próprios métodos, registre suas próprias classes ou até mesmo substitua classes e métodos existentes.

Se você está procurando por um DIC (Dependency Injection Container), consulte a página do Dependency Injection Container.

Mapeando Métodos

Para mapear seu próprio método customizado simples, você usa a função map:

// Mapeie seu método
Flight::map('hello', function (string $name) {
  echo "olá $name!";
});

// Chame seu método customizado
Flight::hello('Bob');

Isso é usado mais quando você precisa passar variáveis para o seu método para obter um valor esperado. O uso do método register() como abaixo é mais para passar configuração e então chamar sua classe pré-configurada.

Registrando Classes

Para registrar sua própria classe e configurá-la, você usa a função register:

// Registre sua classe
Flight::register('user', User::class);

// Obtenha uma instância da sua classe
$user = Flight::user();

O método de registro também permite que você passe parâmetros para o construtor da sua classe. Portanto, ao carregar sua classe customizada, ela será pré-inicializada. Você pode definir os parâmetros do construtor passando um array adicional. Aqui está um exemplo de carregamento de uma conexão de banco de dados:

// Registrar classe com parâmetros do construtor
Flight::register('db', PDO::class, ['mysql:host=localhost;dbname=test', 'usuario', 'senha']);

// Obter uma instância da sua classe
// Isso criará um objeto com os parâmetros definidos
//
// new PDO('mysql:host=localhost;dbname=test', 'usuario', 'senha');
//
$db = Flight::db();

// e se você precisar dele mais tarde no seu código, basta chamar o mesmo método novamente
class SomeController {
  public function __construct() {
    $this->db = Flight::db();
  }
}

Se você passar um parâmetro de retorno de chamada adicional, ele será executado imediatamente após a construção da classe. Isso permite que você execute quaisquer procedimentos de configuração para o seu novo objeto. A função de retorno de chamada recebe um parâmetro, uma instância do novo objeto.

// O callback receberá o objeto que foi construído
Flight::register(
  'db',
  PDO::class,
  ['mysql:host=localhost;dbname=test', 'usuario', 'senha'],
  function (PDO $db) {
    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  }
);

Por padrão, toda vez que você carrega sua classe, você obterá uma instância compartilhada. Para obter uma nova instância de uma classe, basta passar false como parâmetro:

// Instância compartilhada da classe
$compartilhado = Flight::db();

// Nova instância da classe
$novo = Flight::db(false);

Lembre-se de que métodos mapeados têm precedência sobre classes registradas. Se você declarar ambos com o mesmo nome, apenas o método mapeado será invocado.

Substituindo Métodos do Framework

O Flight permite que você substitua sua funcionalidade padrão para atender às suas próprias necessidades, sem precisar modificar nenhum código.

Por exemplo, quando o Flight não consegue corresponder a uma URL a uma rota, ele invoca o método notFound que envia uma resposta genérica HTTP 404. Você pode substituir esse comportamento usando o método map:

Flight::map('notFound', function() {
  // Exibir página de erro personalizada 404
  include 'errors/404.html';
});

O Flight também permite que você substitua componentes essenciais do framework. Por exemplo, você pode substituir a classe de Roteador padrão por sua própria classe personalizada:

// Registre sua classe personalizada
Flight::register('router', MyRouter::class);

// Quando o Flight carregar a instância do Roteador, ele carregará sua classe
$meuRoteador = Flight::router();

No entanto, métodos do framework como map e register não podem ser substituídos. Você receberá um erro se tentar fazer isso.

Learn/json

# JSON

Flight fornece suporte para o envio de respostas JSON e JSONP. Para enviar uma resposta JSON, você
passa alguns dados a serem codificados em JSON:

```php
Flight::json(['id' => 123]);

Para solicitações JSONP, você pode passar opcionalmente o nome do parâmetro de consulta que está usando para definir sua função de retorno de chamada:

Flight::jsonp(['id' => 123], 'q');

Portanto, ao fazer uma solicitação GET usando ?q=my_func, você deve receber a saída:

my_func({"id":123});

Se você não passar um nome de parâmetro de consulta, ele será padrão para jsonp.

Learn/autoloading

Carregamento Automático

O carregamento automático é um conceito em PHP onde você especifica um diretório ou diretórios para carregar classes. Isso é muito mais benéfico do que usar require ou include para carregar classes. Também é um requisito para usar pacotes do Composer.

Por padrão, qualquer classe Flight é carregada automaticamente para você graças ao composer. No entanto, se você deseja carregar suas próprias classes, pode usar o método Flight::path para especificar um diretório para carregar classes.

Exemplo Básico

Vamos supor que temos uma árvore de diretórios como a seguinte:

# Caminho de Exemplo
/home/user/project/my-flight-project/
├── app
│   ├── cache
│   ├── config
│   ├── controllers - contém os controladores para este projeto
│   ├── translations
│   ├── UTILS - contém classes apenas para esta aplicação (em maiúsculas de propósito para um exemplo posterior)
│   └── views
└── public
    └── css
    └── js
    └── index.php

Você pode especificar cada diretório para carregar da seguinte forma:


/**
 * public/index.php
 */

// Adicione um caminho ao carregador automático
Flight::path(__DIR__.'/../app/controllers/');
Flight::path(__DIR__.'/../app/utils/');

/**
 * app/controllers/MyController.php
 */

// nenhum espaço de nomes necessário

// Todas as classes carregadas automaticamente são recomendadas para serem Pascal Case (cada palavra em maiúscula, sem espaços)
// A partir da versão 3.7.2, você pode usar Pascal_Snake_Case para os nomes de suas classes executando Loader::setV2ClassLoading(false);
class MyController {

    public function index() {
        // faça algo
    }
}

Espaços de Nomes

Se você tiver espaços de nomes, na verdade se torna muito fácil implementar isso. Você deve usar o método Flight::path() para especificar o diretório raiz (não o diretório do documento ou a pasta public/) de sua aplicação.


/**
 * public/index.php
 */

// Adicione um caminho ao carregador automático
Flight::path(__DIR__.'/../');

Agora é assim que o seu controlador pode ser parecido. Observe o exemplo abaixo, mas preste atenção nos comentários para informações importantes.

/**
 * app/controllers/MyController.php
 */

// espaços de nomes são necessários
// os espaços de nomes são iguais à estrutura de diretórios
// os espaços de nomes devem seguir o mesmo caso que a estrutura de diretórios
// os espaços de nomes e diretórios não podem ter nenhum sublinhado (a menos que Loader::setV2ClassLoading(false) seja definido)
namespace app\controllers;

// Todas as classes carregadas automaticamente são recomendadas para ser Pascal Case (cada palavra em maiúscula, sem espaços)
// A partir da versão 3.7.2, você pode usar Pascal_Snake_Case para os nomes de suas classes executando Loader::setV2ClassLoading(false);
class MyController {

    public function index() {
        // faça algo
    }
}

E se você quisesse carregar automaticamente uma classe em seu diretório de utils, você faria basicamente a mesma coisa:


/**
 * app/UTILS/ArrayHelperUtil.php
 */

// o espaço de nomes deve corresponder à estrutura e ao caso do diretório (observe que o diretório UTILS está em maiúsculas
//     como na árvore de arquivos acima)
namespace app\UTILS;

class ArrayHelperUtil {

    public function changeArrayCase(array $array) {
        // faça algo
    }
}

Sublinhados em Nomes de Classes

A partir da versão 3.7.2, você pode usar Pascal_Snake_Case para os nomes de suas classes executando Loader::setV2ClassLoading(false);. Isso permitirá que você use sublinhados em seus nomes de classes. Isso não é recomendado, mas está disponível para aqueles que precisam.


/**
 * public/index.php
 */

// Adicione um caminho ao carregador automático
Flight::path(__DIR__.'/../app/controllers/');
Flight::path(__DIR__.'/../app/utils/');
Loader::setV2ClassLoading(false);

/**
 * app/controllers/My_Controller.php
 */

// nenhum espaço de nomes necessário

class My_Controller {

    public function index() {
        // faça algo
    }
}

Learn/troubleshooting

Resolução de Problemas

Esta página irá ajudar você a resolver problemas comuns que podem surgir ao usar o Flight.

Problemas Comuns

404 Não Encontrado ou Comportamento de Rota Inesperado

Se você estiver vendo um erro 404 Não Encontrado (mas jura pela sua vida que realmente está lá e não é um erro de digitação), isso na verdade pode ser um problema com você retornando um valor no final da sua rota em vez de apenas ecoá-lo. O motivo para isso é intencional, mas pode pegar alguns desenvolvedores de surpresa.


Flight::route('/hello', function(){
    // Isso pode causar um erro 404 Não Encontrado
    return 'Olá Mundo';
});

// O que você provavelmente deseja
Flight::route('/hello', function(){
    echo 'Olá Mundo';
});

O motivo para isso é por causa de um mecanismo especial incorporado no roteador que trata a saída de retorno como um sinal de "ir para a próxima rota". Você pode ver o comportamento documentado na seção de Roteamento.

Install

Instalação

Baixe os arquivos.

Se estiver a usar Composer, pode executar o seguinte comando:

composer require flightphp/core

OU então pode baixar os arquivos diretamente e extrair para o seu diretório web.

Configure o seu servidor web.

Apache

Para o Apache, edite o seu ficheiro .htaccess com o seguinte:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]

Nota: Se necessitar de utilizar o Flight numa subpasta, adicione a linha RewriteBase /subdir/ logo após RewriteEngine On.

Nota: Se quiser proteger todos os ficheiros do servidor, como um ficheiro de bd ou env. Coloque isto no seu ficheiro .htaccess:

RewriteEngine On
RewriteRule ^(.*)$ index.php

Nginx

Para o Nginx, adicione o seguinte à declaração do seu servidor:

server {
  location / {
    try_files $uri $uri/ /index.php;
  }
}

Crie o seu ficheiro index.php.

<?php

// Se estiver a usar o Composer, requer o autoloader.
require 'vendor/autoload.php';
// se não estiver a usar o Composer, carregue o framework diretamente
// require 'flight/Flight.php';

// Depois defina uma rota e atribua uma função para lidar com o pedido.
Flight::route('/', function () {
  echo 'hello world!';
});

// Por fim, inicie o framework.
Flight::start();

License

A Licença MIT (MIT)

Direitos autorais © 2023 @mikecao, @n0nag0n

É concedida permissão, gratuitamente, a qualquer pessoa que obtenha uma cópia deste software e documentação associada (os "Software"), para lidar com o Software sem restrições, incluindo, sem limitação, os direitos de usar, copiar, modificar, mesclar, publicar, distribuir, sublicenciar e/ou vender cópias do Software, e permitir às pessoas a quem o Software é fornecido, que o façam, sujeito às seguintes condições:

O aviso de direitos autorais acima e este aviso de permissão devem ser incluídos em todas as cópias ou porções substanciais do Software.

O SOFTWARE É FORNECIDO "COMO ESTÁ", SEM GARANTIA DE QUALQUER TIPO, EXPRESSA OU IMPLÍCITA, INCLUINDO, MAS NÃO SE LIMITANDO ÀS GARANTIAS DE COMERCIABILIDADE, ADEQUAÇÃO A UMA FINALIDADE ESPECÍFICA E NÃO VIOLAÇÃO. EM NENHUM CASO OS AUTORES OU TITULARES DE DIREITOS AUTORAIS SERÃO RESPONSÁVEIS POR QUALQUER REIVINDICAÇÃO, DANOS OU OUTRA RESPONSABILIDADE, SEJA EM UMA AÇÃO DE CONTRATO, DELITO OU OUTRO, DECORRENTE DE, FORA DE OU EM CONEXÃO COM O SOFTWARE OU O USO OU OUTRAS TRANSAÇÕES NO SOFTWARE.

About

O que é o Flight?

O Flight é um framework rápido, simples e extensível para PHP. É bastante versátil e pode ser usado para construir qualquer tipo de aplicação web. É construído com simplicidade em mente e é escrito de forma fácil de entender e usar.

O Flight é um ótimo framework para iniciantes que são novos no PHP e desejam aprender como construir aplicações web. Também é um ótimo framework para desenvolvedores experientes que desejam ter mais controle sobre suas aplicações web. É projetado para facilitar a construção de uma API RESTful, uma aplicação web simples ou uma aplicação web complexa.

Início Rápido

<?php

// se instalado com o composer
require 'vendor/autoload.php';
// ou se instalado manualmente por arquivo zip
// require 'flight/Flight.php';

Flight::route('/', function() {
  echo 'olá mundo!';
});

Flight::route('/json', function() {
  Flight::json(['olá' => 'mundo']);
});

Flight::start();

Simples o suficiente, certo? Saiba mais sobre o Flight na documentação!

Esqueleto/Modelo de Aplicativo

Há um aplicativo de exemplo que pode ajudá-lo a começar com o Framework Flight. Visite flightphp/skeleton para instruções sobre como começar! Você também pode visitar a página de exemplos para se inspirar em algumas coisas que você pode fazer com o Flight.

Comunidade

Estamos no Matrix! Converse conosco em #flight-php-framework:matrix.org.

Contribuindo

Existem duas maneiras de contribuir com o Flight:

  1. Você pode contribuir para o framework principal visitando o repositório principal.
  2. Você pode contribuir para a documentação. Este site de documentação é hospedado no Github. Se notar um erro ou quiser aprimorar algo, sinta-se à vontade para corrigir e enviar um pull request! Tentamos manter as coisas atualizadas, mas atualizações e traduções de idiomas são bem-vindas.

Requisitos

O Flight requer PHP 7.4 ou superior.

Nota: O PHP 7.4 é suportado porque no momento da escrita (2024) o PHP 7.4 é a versão padrão para algumas distribuições LTS do Linux. Forçar a mudança para o PHP >8 causaria muitos problemas para esses usuários. O framework também suporta o PHP >8.

Licença

O Flight é lançado sob a licença MIT.

Awesome-plugins/php_cookie

Cookies

overclokk/cookie é uma biblioteca simples para gerenciar cookies em seu aplicativo.

Instalação

A instalação é simples com o composer.

composer require overclokk/cookie

Uso

O uso é tão simples quanto registrar um novo método na classe Flight.


use Overclokk\Cookie\Cookie;

/*
 * Defina em seu arquivo de inicialização ou public/index.php
 */

Flight::register('cookie', Cookie::class);

/**
 * ExampleController.php
 */

class ExampleController {
    public function login() {
        // Defina um cookie

        // você vai querer que isso seja falso para obter uma nova instância
        // use o comentário abaixo se quiser autocompletar
        /** @var \Overclokk\Cookie\Cookie $cookie */
        $cookie = Flight::cookie(false);
        $cookie->set(
            'stay_logged_in', // nome do cookie
            '1', // o valor que você deseja definir
            86400, // número de segundos que o cookie deve durar
            '/', // caminho em que o cookie estará disponível
            'example.com', // domínio em que o cookie estará disponível
            true, // o cookie só será transmitido por uma conexão HTTPS segura
            true // o cookie só estará disponível por meio do protocolo HTTP
        );

        // opcionalmente, se você quiser manter os valores padrão
        // e ter uma maneira rápida de definir um cookie por um longo tempo
        $cookie->forever('stay_logged_in', '1');
    }

    public function home() {
        // Verifique se você tem o cookie
        if (Flight::cookie()->has('stay_logged_in')) {
            // colocá-los na área do painel, por exemplo.
            Flight::redirect('/dashboard');
        }
    }
}

Awesome-plugins/php_encryption

Criptografia PHP

defuse/php-encryption é uma biblioteca que pode ser usada para criptografar e descriptografar dados. Começar a usar é bastante simples para começar a criptografar e descriptografar dados. Eles têm um ótimo tutorial que ajuda a explicar o básico de como usar a biblioteca, bem como importantes implicações de segurança relacionadas à criptografia.

Instalação

A instalação é simples com o composer.

composer require defuse/php-encryption

Configuração

Em seguida, você precisará gerar uma chave de criptografia.

vendor/bin/generate-defuse-key

Isso vai gerar uma chave que você precisará manter em segurança. Você poderia guardar a chave em seu arquivo app/config/config.php no array no final do arquivo. Embora não seja o local perfeito, é pelo menos algo.

Uso

Agora que você tem a biblioteca e uma chave de criptografia, você pode começar a criptografar e descriptografar dados.


use Defuse\Crypto\Crypto;
use Defuse\Crypto\Key;

/*
 * Defina em seu arquivo de inicialização ou public/index.php
 */

// Método de criptografia
Flight::map('encrypt', function($dados_brutos) {
    $chave_criptografia = /* $config['encryption_key'] ou um file_get_contents de onde você colocou a chave */;
    return Crypto::encrypt($dados_brutos, Key::loadFromAsciiSafeString($chave_criptografia));
});

// Método de descriptografia
Flight::map('decrypt', function($dados_criptografados) {
    $chave_criptografia = /* $config['encryption_key'] ou um file_get_contents de onde você colocou a chave */;
    try {
        $dados_brutos = Crypto::decrypt($dados_criptografados, Key::loadFromAsciiSafeString($chave_criptografia));
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
        // Um ataque! Ou a chave errada foi carregada, ou o texto cifrado foi
        // alterado desde que foi criado - corrompido no banco de dados ou
        // intencionalmente modificado por Eve tentando realizar um ataque.

        // ... trate este caso de uma maneira adequada à sua aplicação ...
    }
    return $dados_brutos;
});

Flight::route('/encrypt', function() {
    $dados_criptografados = Flight::encrypt('Isto é um segredo');
    echo $dados_criptografados;
});

Flight::route('/decrypt', function() {
    $dados_criptografados = '...'; // Obtenha os dados criptografados de algum lugar
    $dados_descriptografados = Flight::decrypt($dados_criptografados);
    echo $dados_descriptografados;
});

Awesome-plugins/php_file_cache

Wruczek/PHP-File-Cache

Classe de cache em arquivo PHP leve, simples e independente

Vantagens

Instalação

Instale via composer:

composer require wruczek/php-file-cache

Uso

O uso é bastante direto.

use Wruczek\PhpFileCache\PhpFileCache;

$app = Flight::app();

// Você passa o diretório em que o cache será armazenado para o construtor
$app->register('cache', PhpFileCache::class, [ __DIR__ . '/../cache/' ], function(PhpFileCache $cache) {

    // Isso garante que o cache seja usado somente em modo de produção
    // AMBIENTE é uma constante definida em seu arquivo de inicialização ou em outro lugar em seu aplicativo
    $cache->setDevMode(ENVIRONMENT === 'development');
});

Então você pode usá-lo em seu código assim:


// Obter instância de cache
$cache = Flight::cache();
$data = $cache->refreshIfExpired('simple-cache-test', function () {
    return date("H:i:s"); // retornar dados a serem armazenados em cache
}, 10); // 10 segundos

// ou
$data = $cache->retrieve('simple-cache-test');
if(empty($data)) {
    $data = date("H:i:s");
    $cache->store('simple-cache-test', $data, 10); // 10 segundos
}

Documentação

Visite https://github.com/Wruczek/PHP-File-Cache para ver a documentação completa e certifique-se de verificar a pasta de exemplos.

Awesome-plugins/index

Plugins Incríveis

O Flight é incrivelmente extensível. Existem vários plugins que podem ser usados para adicionar funcionalidades à sua aplicação Flight. Alguns são oficialmente suportados pela Equipe do Flight e outros são bibliotecas micro/lite para ajudá-lo a começar.

Caching

O armazenamento em cache é uma ótima maneira de acelerar a sua aplicação. Existem diversas bibliotecas de cache que podem ser usadas com o Flight.

Debugging

Depurar é crucial quando você está desenvolvendo no seu ambiente local. Existem alguns plugins que podem aprimorar a sua experiência de depuração.

Bancos de Dados

Os bancos de dados são a base da maioria das aplicações. É assim que você armazena e recupera dados. Algumas bibliotecas de banco de dados são simplesmente wrappers para escrever consultas e outras são ORMs completos.

Sessão

As sessões não são realmente úteis para APIs, mas para desenvolver uma aplicação web, as sessões podem ser cruciais para manter o estado e as informações de login.

Templating

A criação de modelos é essencial para qualquer aplicação web com uma interface de usuário. Existem várias engines de templates que podem ser usadas com o Flight.

Contribuindo

Tem um plugin que gostaria de compartilhar? Envie uma solicitação pull para adicioná-lo à lista!

Awesome-plugins/pdo_wrapper

Awesome-plugins/session

Ghostff/Session

Gestor de Sessão PHP (não bloqueante, flash, segmento, criptografia de sessão). Usa PHP open_ssl para criptografia/opcional descriptografia de dados de sessão. Suporta Arquivo, MySQL, Redis e Memcached.

Instalação

Instale com o composer.

composer require ghostff/session

Configuração Básica

Você não precisa passar nada para usar as configurações padrão com sua sessão. Você pode ler sobre mais configurações no Github Readme.


use Ghostff\Session\Session;

require 'vendor/autoload.php';

$app = Flight::app();

$app->register('session', Session::class);

// uma coisa para lembrar é que você deve confirmar sua sessão em cada carregamento de página
// ou você precisará executar auto_commit em sua configuração.

Exemplo Simples

Aqui está um exemplo simples de como você pode usar isso.

Flight::route('POST /login', function() {
    $session = Flight::session();

    // faça sua lógica de login aqui
    // valide a senha, etc.

    // se o login for bem-sucedido
    $session->set('is_logged_in', true);
    $session->set('user', $user);

    // toda vez que escrever na sessão, você deve confirmar deliberate.
    $session->commit();
});

// Esta verificação poderia estar na lógica da página restrita, ou envolta com middleware.
Flight::route('/alguma-página-restrita', function() {
    $session = Flight::session();

    if(!$session->get('is_logged_in')) {
        Flight::redirect('/login');
    }

    // faça sua lógica da página restrita aqui
});

// a versão de middleware
Flight::route('/alguma-página-restrita', function() {
    // lógica regular da página
})->addMiddleware(function() {
    $session = Flight::session();

    if(!$session->get('is_logged_in')) {
        Flight::redirect('/login');
    }
});

Exemplo Mais Complexo

Aqui está um exemplo mais complexo de como você pode usar isso.


use Ghostff\Session\Session;

require 'vendor/autoload.php';

$app = Flight::app();

// defina um caminho personalizado para o arquivo de configuração da sua sessão e forneça uma sequência aleatória para o id da sessão
$app->register('session', Session::class, [ 'caminho/para/session_config.php', bin2hex(random_bytes(32)) ], function(Session $session) {
        // ou você pode substituir manualmente as opções de configuração
        $session->updateConfiguration([
            // se deseja armazenar seus dados de sessão em um banco de dados (bom se desejar algo como funcionalidade "sair de todos os dispositivos")
            Session::CONFIG_DRIVER        => Ghostff\Session\Drivers\MySql::class,
            Session::CONFIG_ENCRYPT_DATA  => true,
            Session::CONFIG_SALT_KEY      => hash('sha256', 'minha-super-senha-s3cr3ta'), // por favor, altere isso para ser algo diferente
            Session::CONFIG_AUTO_COMMIT   => true, // faça isso somente se for necessário e/ou for difícil confirmar() sua sessão.
                                                   // além disso, você poderia fazer Flight::after('start', function() { Flight::session()->commit(); });
            Session::CONFIG_MYSQL_DS         => [
                'driver'    => 'mysql',             # driver de banco de dados para dns do PDO eg(mysql:host=...;dbname=...)
                'host'      => '127.0.0.1',         # host do banco de dados
                'db_name'   => 'nome_do_meu_banco_de_dados',   # nome do banco de dados
                'db_table'  => 'sessoes',          # tabela do banco de dados
                'db_user'   => 'root',              # nome de usuário do banco de dados
                'db_pass'   => '',                  # senha do banco de dados
                'persistent_conn'=> false,          # Evite o overhead de estabelecer uma nova conexão toda vez que um script precisa falar com um banco de dados, resultando em um aplicativo web mais rápido. ENCONTRE O LADO RUIM SOZINHO
            ]
        ]);
    }
);

Socorro! Meus Dados de Sessão Não Estão Persistindo!

Você está definindo seus dados de sessão e eles não estão persistindo entre as solicitações? Você pode ter esquecido de confirmar os dados de sua sessão. Você pode fazer isso chamando $session->commit() depois de definir seus dados de sessão.

Flight::route('POST /login', function() {
    $session = Flight::session();

    // faça sua lógica de login aqui
    // valide a senha, etc.

    // se o login for bem-sucedido
    $session->set('is_logged_in', true);
    $session->set('user', $user);

    // toda vez que escrever na sessão, você deve confirmar deliberate.
    $session->commit();
});

Outra maneira de contornar isso é quando você configura o serviço de sessão, você deve definir auto_commit como true em sua configuração. Isso irá confirmar automaticamente seus dados de sessão após cada solicitação.


$app->register('session', Session::class, [ 'caminho/para/session_config.php', bin2hex(random_bytes(32)) ], function(Session $session) {
        $session->updateConfiguration([
            Session::CONFIG_AUTO_COMMIT   => true,
        ]);
    }
);

Além disso, você poderia fazer Flight::after('start', function() { Flight::session()->commit(); }); para confirmar seus dados de sessão após cada solicitação.

Documentação

Visite o Github Readme para documentação completa. As opções de configuração estão bem documentadas no arquivo default_config.php em si. O código é simples de entender se você quiser examinar este pacote você mesmo.

Awesome-plugins/runway

Pista

A pista é uma aplicação CLI que ajuda a gerenciar suas aplicações Flight. Pode gerar controladores, exibir todas as rotas e muito mais. É baseado na excelente biblioteca adhocore/php-cli.

Instalação

Instale com o composer.

composer require flightphp/runway

Configuração Básica

Da primeira vez que você executar a pista, ele irá guiá-lo através de um processo de configuração e criar um arquivo de configuração .runway.json na raiz do seu projeto. Este arquivo conterá algumas configurações necessárias para a pista funcionar corretamente.

Uso

A pista possui vários comandos que você pode usar para gerenciar sua aplicação Flight. Existem duas maneiras fáceis de usar a Pista.

  1. Se estiver usando o projeto esqueleto, você pode executar php runway [comando] a partir da raiz do seu projeto.
  2. Se estiver usando a Pista como um pacote instalado via composer, você pode executar vendor/bin/runway [comando] da raiz do seu projeto.

Para qualquer comando, você pode passar a flag --help para obter mais informações sobre como usar o comando.

php runway routes --help

Aqui estão alguns exemplos:

Gerar um Controlador

Com base na configuração em seu arquivo .runway.json, a localização padrão gerará um controlador para você no diretório app/controllers/.

php runway make:controller MyController

Gerar um Modelo de Active Record

Com base na configuração em seu arquivo .runway.json, a localização padrão gerará um controlador para você no diretório app/records/.

php runway make:record users

Se por acaso você tiver a tabela users com o seguinte esquema: id, name, email, created_at, updated_at, um arquivo semelhante ao seguinte será criado no arquivo app/records/UserRecord.php:

<?php

declare(strict_types=1);

namespace app\records;

/**
 * Classe ActiveRecord para a tabela de usuários.
 * @link https://docs.flightphp.com/awesome-plugins/active-record
 * 
 * @property int $id
 * @property string $name
 * @property string $email
 * @property string $created_at
 * @property string $updated_at
 */
class UserRecord extends \flight\ActiveRecord
{
    /**
     * @var array $relations Defina os relacionamentos para o modelo
     *   https://docs.flightphp.com/awesome-plugins/active-record#relationships
     */
    protected array $relations = [];

    /**
     * Construtor
     * @param mixed $databaseConnection A conexão com o banco de dados
     */
    public function __construct($databaseConnection)
    {
        parent::__construct($databaseConnection, 'users');
    }
}

Exibir Todas as Rotas

Isso exibirá todas as rotas atualmente registradas no Flight.

php runway routes

Se você deseja visualizar apenas rotas específicas, pode passar uma flag para filtrar as rotas.

# Mostrar apenas rotas GET
php runway routes --get

# Mostrar apenas rotas POST
php runway routes --post

# etc.

Personalizando a Pista

Se estiver criando um pacote para o Flight, ou deseja adicionar seus próprios comandos personalizados em seu projeto, você pode fazer isso criando um diretório src/commands/, flight/commands/, app/commands/ ou commands/ para seu projeto/pacote.

Para criar um comando, você simplesmente estende a classe AbstractBaseCommand e implementa no mínimo um método __construct e um método execute.

<?php

declare(strict_types=1);

namespace flight\commands;

class ExampleCommand extends AbstractBaseCommand
{
    /**
     * Construtor
     *
     * @param array<string,mixed> $config Configuração JSON de .runway-config.json
     */
    public function __construct(array $config)
    {
        parent::__construct('make:example', 'Criar um exemplo para a documentação', $config);
        $this->argument('<funny-gif>', 'O nome do gif engraçado');
    }

    /**
     * Executa a função
     *
     * @return void
     */
    public function execute(string $controller)
    {
        $io = $this->app()->io();

        $io->info('Criando exemplo...');

        // Faça algo aqui

        $io->ok('Exemplo criado!');
    }
}

Consulte a Documentação do adhocore/php-cli para obter mais informações sobre como criar seus próprios comandos personalizados em sua aplicação Flight!

Awesome-plugins/tracy_extensions

Tracy Flight Painel de Extensões

=====

Este é um conjunto de extensões para tornar o trabalho com o Flight um pouco mais rico.

Este é o Painel

Barra do Flight

E cada painel exibe informações muito úteis sobre sua aplicação!

Dados do Flight Banco de Dados do Flight Requisição do Flight

Instalação

Execute composer require flightphp/tracy-extensions --dev e você está pronto!

Configuração

Há muito pouca configuração que você precisa fazer para começar. Você precisará iniciar o depurador Tracy antes de usar isso https://tracy.nette.org/en/guide:

<?php

use Tracy\Debugger;
use flight\debug\tracy\TracyExtensionLoader;

// código bootstrap
require __DIR__ . '/vendor/autoload.php';

Debugger::enable();
// Você pode precisar especificar seu ambiente com Debugger::enable(Debugger::DEVELOPMENT)

// se você usa conexões de banco de dados em seu aplicativo, há um
// wrapper PDO necessário para usar APENAS NO DESENVOLVIMENTO (por favor, não em produção!)
// Tem os mesmos parâmetros de uma conexão PDO regular
$pdo = new PdoQueryCapture('sqlite:test.db', 'user', 'pass');
// ou se você conectar isso ao framework Flight
Flight::register('db', PdoQueryCapture::class, ['sqlite:test.db', 'user', 'pass']);
// agora sempre que você fizer uma consulta, irá capturar o tempo, consulta e parâmetros

// Isso conecta os pontos
if(Debugger::$showBar === true) {
    // Isso precisa ser falso ou Tracy não pode realmente renderizar :(
    Flight::set('flight.content_length', false);
    new TracyExtensionLoader(Flight::app());
}

// mais código

Flight::start();

Configuração Adicional

Dados da Sessão

Se você tiver um manipulador de sessão personalizado (como ghostff/session), você pode passar qualquer array de dados de sessão para Tracy e ele os exibirá automaticamente para você. Você passa com a chave session_data no segundo parâmetro do construtor TracyExtensionLoader.


use Ghostff\Session\Session;

require 'vendor/autoload.php';

$app = Flight::app();

$app->register('session', Session::class);

if(Debugger::$showBar === true) {
    // Isso precisa ser falso ou Tracy não pode realmente renderizar :(
    Flight::set('flight.content_length', false);
    new TracyExtensionLoader(Flight::app(), [ 'session_data' => Flight::session()->getAll() ]);
}

// rotas e outras coisas...

Flight::start();

Latte

Se você tiver o Latte instalado em seu projeto, você pode usar o painel Latte para analisar seus modelos. Você pode passar a instância do Latte para o construtor TracyExtensionLoader com a chave latte no segundo parâmetro.



use Latte\Engine;

require 'vendor/autoload.php';

$app = Flight::app();

$app->register('latte', Engine::class, [], function($latte) {
    $latte->setTempDirectory(__DIR__ . '/temp');

    // aqui é onde você adiciona o Painel do Latte ao Tracy
    $latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);
});

if(Debugger::$showBar === true) {
    // Isso precisa ser falso ou Tracy não pode realmente renderizar :(
    Flight::set('flight.content_length', false);
    new TracyExtensionLoader(Flight::app());
}

Awesome-plugins/tracy

Tracy

Tracy é um incrível manipulador de erros que pode ser usado com Flight. Possui vários painéis que podem ajudá-lo a depurar sua aplicação. É também muito fácil de estender e adicionar seus próprios painéis. A equipe do Flight criou alguns painéis especificamente para projetos do Flight com o plugin flightphp/tracy-extensions.

Instalação

Instale com o compositor. E na verdade, você vai querer instalar isso sem a versão de desenvolvimento, já que o Tracy vem com um componente de tratamento de erros de produção.

composer require tracy/tracy

Configuração Básica

Existem algumas opções de configuração básicas para começar. Você pode ler mais sobre elas na Documentação do Tracy.


require 'vendor/autoload.php';

use Tracy\Debugger;

// Habilitar Tracy
Debugger::enable();
// Debugger::enable(Debugger::DEVELOPMENT) // às vezes você precisa ser explícito (também Debugger::PRODUCTION)
// Debugger::enable('23.75.345.200'); // você também pode fornecer um array de endereços IP

// Aqui é onde os erros e exceções serão registrados. Certifique-se de que este diretório exista e seja gravável.
Debugger::$logDirectory = __DIR__ . '/../log/';
Debugger::$strictMode = true; // exibir todos os erros
// Debugger::$strictMode = E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED; // todos os erros exceto avisos obsoletos
if (Debugger::$showBar) {
    $app->set('flight.content_length', false); // se a barra do Debugger estiver visível, então o comprimento do conteúdo não pode ser definido pelo Flight

    // Isso é específico para a Extensão do Tracy para o Flight se você incluiu isso
    // caso contrário, comente isso.
    new TracyExtensionLoader($app);
}

Dicas Úteis

Ao depurar seu código, existem algumas funções muito úteis para exibir dados para você.

Awesome-plugins/active_record

ActiveRecord do Flight

Um registro ativo está mapeando uma entidade do banco de dados para um objeto PHP. Falando claramente, se você tem uma tabela de usuários no seu banco de dados, você pode "traduzir" uma linha dessa tabela para uma classe User e um objeto $user no seu código-fonte. Veja exemplo básico.

Exemplo Básico

Vamos assumir que você tenha a seguinte tabela:

CREATE TABLE users (
    id INTEGER PRIMARY KEY, 
    name TEXT, 
    password TEXT 
);

Agora você pode configurar uma nova classe para representar essa tabela:

/**
 * Uma classe ActiveRecord geralmente é singular
 * 
 * É altamente recomendado adicionar as propriedades da tabela como comentários aqui
 * 
 * @property int    $id
 * @property string $name
 * @property string $password
 */ 
class User extends flight\ActiveRecord {
    public function __construct($conexao_banco_de_dados)
    {
        // você pode configurar assim
        parent::__construct($conexao_banco_de_dados, 'users');
        // ou assim
        parent::__construct($conexao_banco_de_dados, null, [ 'table' => 'users']);
    }
}

Agora veja a mágica acontecer!

// para sqlite
$conexao_banco_de_dados = new PDO('sqlite:test.db'); // isso é apenas um exemplo, normalmente você usaria uma conexão de banco de dados real

// para mysql
$conexao_banco_de_dados = new PDO('mysql:host=localhost;dbname=test_db&charset=utf8bm4', 'nome_usuario', 'senha');

// ou mysqli
$conexao_banco_de_dados = new mysqli('localhost', 'nome_usuario', 'senha', 'test_db');
// ou mysqli com criação não baseada em objeto
$conexao_banco_de_dados = mysqli_connect('localhost', 'nome_usuario', 'senha', 'test_db');

$user = new User($conexao_banco_de_dados);
$user->name = 'Bobby Tables';
$user->password = password_hash('alguma senha legal');
$user->insert();
// ou $user->save();

echo $user->id; // 1

$user->name = 'Joseph Mamma';
$user->password = password_hash('outra senha legal novamente!!!');
$user->insert();
// não é possível usar $user->save() aqui, senão ele pensará que é uma atualização!

echo $user->id; // 2

E foi tão fácil adicionar um novo usuário! Agora que há uma linha de usuário no banco de dados, como você a extrai?

$user->find(1); // encontra id = 1 no banco de dados e retorna
echo $user->name; // 'Bobby Tables'

E se você quiser encontrar todos os usuários?

$users = $user->findAll();

E com uma determinada condição?

$users = $user->like('name', '%mamma%')->findAll();

Veja como isso é divertido? Vamos instalá-lo e começar!

Instalação

Simplesmente instale com o Composer

composer require flightphp/active-record 

Uso

Isso pode ser usado como uma biblioteca independente ou com o Framework PHP Flight. Completamente com você.

Independente

Apenas certifique-se de passar uma conexão PDO para o construtor.

$conexao_pdo = new PDO('sqlite:test.db'); // isso é apenas um exemplo, normalmente você usaria uma conexão de banco de dados real

$User = new User($pdo_connection);

Framework PHP Flight

Se você estiver usando o Framework PHP Flight, você pode registrar a classe ActiveRecord como um serviço (mas honestamente você não precisa).

Flight::register('user', 'User', [ $conexao_pdo ]);

// então você pode usá-lo assim em um controlador, uma função, etc.

Flight::user()->find(1);

Funções CRUD

find($id = null) : boolean|ActiveRecord

Encontra um registro e atribui ao objeto atual. Se você passar um $id de algum tipo, ele fará uma consulta na chave primária com esse valor. Se nada for passado, ele encontrará apenas o primeiro registro da tabela.

Além disso, você pode passar outros métodos auxiliares para consultar sua tabela.

// encontrar um registro com algumas condições antecipadamente
$user->notNull('password')->orderBy('id DESC')->find();

// encontrar um registro por um id específico
$id = 123;
$user->find($id);

findAll(): array<int,ActiveRecord>

Encontra todos os registros na tabela que você especificar.

$user->findAll();

isHydrated(): boolean (v0.4.0)

Retorna true se o registro atual foi preenchido (buscado do banco de dados).

$user->find(1);
// se um registro for encontrado com dados...
$user->isHydrated(); // true

insert(): boolean|ActiveRecord

Insere o registro atual no banco de dados.

$user = new User($conexao_banco_de_dados);
$user->name = 'demo';
$user->password = md5('demo');
$user->insert();
Chaves Primárias Baseadas em Texto

Se você tiver uma chave primária baseada em texto (como um UUID), você pode definir o valor da chave primária antes de inserir de duas maneiras.

$user = new User($conexao_banco_de_dados, [ 'primaryKey' => 'uuid' ]);
$user->uuid = 'algum-uuid';
$user->name = 'demo';
$user->password = md5('demo');
$user->insert(); // ou $user->save();

ou você pode ter a chave primária gerada automaticamente para você por meio de eventos.

class User extends flight\ActiveRecord {
    public function __construct($conexao_banco_de_dados)
    {
        parent::__construct($conexao_banco_de_dados, 'users', [ 'primaryKey' => 'uuid' ]);
        // você também pode definir a chave primária assim em vez do array acima.
        $this->primaryKey = 'uuid';
    }

    protected function beforeInsert(self $self) {
        $self->uuid = uniqid(); // ou como você precisa gerar seus IDs exclusivos
    }
}

Se você não definir a chave primária antes de inserir, ela será definida como rowid e o banco de dados a gerará para você, mas não persistirá porque esse campo pode não existir em sua tabela. Por isso é recomendado usar o evento para lidar automaticamente com isso para você.

update(): boolean|ActiveRecord

Atualiza o registro atual no banco de dados.

$user->greaterThan('id', 0)->orderBy('id desc')->find();
$user->email = 'teste@example.com';
$user->update();

save(): boolean|ActiveRecord

Insere ou atualiza o registro atual no banco de dados. Se o registro tiver um id, ele será atualizado, caso contrário será inserido.

$user = new User($conexao_banco_de_dados);
$user->name = 'demo';
$user->password = md5('demo');
$user->save();

Nota: Se você tiver relacionamentos definidos na classe, ele salvará recursivamente essas relações também se tiverem sido definidas, instanciadas e tiverem dados sujos para atualizar. (v0.4.0 e acima)

delete(): boolean

Exclui o registro atual do banco de dados.

$user->gt('id', 0)->orderBy('id desc')->find();
$user->delete();

Você também pode excluir vários registros executando uma pesquisa antes.

$user->like('name', 'Bob%')->delete();

dirty(array $dirty = []): ActiveRecord

Dados sujos referem-se aos dados que foram alterados em um registro.

$user->greaterThan('id', 0)->orderBy('id desc')->find();

// nada está "sujo" até este ponto.

$user->email = 'teste@example.com'; // agora o email é considerado "sujo" porque foi alterado.
$user->update();
// agora não há dados sujos porque foram atualizados e persistidos no banco de dados

$user->password = password_hash()'nova_senha'); // agora isso está sujo
$user->dirty(); // passar nada limpará todas as entradas sujas.
$user->update(); // nada será atualizado porque nada foi capturado como sujo.

$user->dirty([ 'name' => 'algo', 'password' => password_hash('outra senha') ]);
$user->update(); // tanto o nome quanto a senha são atualizados.

copyFrom(array $data): ActiveRecord (v0.4.0)

Este é um alias para o método dirty(). É um pouco mais claro o que você está fazendo.

$user->copyFrom([ 'name' => 'algo', 'password' => password_hash('outra senha') ]);
$user->update(); // tanto o nome quanto a senha são atualizados.

isDirty(): boolean (v0.4.0)

Retorna true se o registro atual foi alterado.

$user->greaterThan('id', 0)->orderBy('id desc')->find();
$user->email = 'teste@email.com';
$user->isDirty(); // true

reset(bool $include_query_data = true): ActiveRecord

Redefine o registro atual para seu estado inicial. Isso é realmente bom de usar em comportamentos de loop. Se você passar true, também redefinirá os dados da consulta que foram usados para encontrar o objeto atual (comportamento padrão).

$users = $user->greaterThan('id', 0)->orderBy('id desc')->find();
$user_company = new UserCompany($conexao_banco_de_dados);

foreach($users as $user) {
    $user_company->reset(); // comece com uma tela limpa
    $user_company->user_id = $user->id;
    $user_company->company_id = $algum_id_empresa;
    $user_company->insert();
}

getBuiltSql(): string (v0.4.1)

Depois de executar um método find(), findAll(), insert(), update(), ou save() você pode obter o SQL que foi construído e usá-lo para fins de depuração.

Métodos de Consulta SQL

select(string $field1 [, string $field2 ... ])

Você pode selecionar apenas algumas das colunas em uma tabela se desejar (é mais eficiente em tabelas muito largas com muitas colunas)

$user->select('id', 'name')->find();

from(string $table)

Você pode escolher outra tabela também! Por que diabos não?!

$user->select('id', 'name')->from('user')->find();

join(string $table_name, string $join_condition)

Você também pode juntar-se a outra tabela no banco de dados.

$user->join('contatos', 'contatos.id_usuario = usuarios.id')->find();

where(string $where_conditions)

Você pode definir alguns argumentos where personalizados (você não pode definir parâmetros nesta instrução where)

$user->where('id=1 AND name="demo"')->find();

Nota de Segurança - Você pode ser tentado a fazer algo como $user->where("id = '{$id}' AND name = '{$name}'")->find();. POR FAVOR NÃO FAÇA ISSO!!! Isso é suscetível ao que é conhecido como ataques de injeção de SQL. Há muitos artigos online, por favor pesquise "ataques de injeção de sql php" e você encontrará muitos artigos sobre esse assunto. A maneira correta de lidar com isso com esta biblioteca é em vez do método where(), você faria algo mais como $user->eq('id', $id)->eq('name', $name)->find(); Se você absolutamente tiver que fazer isso, a biblioteca PDO tem $pdo->quote($var) para escapar isso para você. Somente após usar quote() você pode usá-lo em uma declaração where().

group(string $group_by_statement)/groupBy(string $group_by_statement)

Agrupe seus resultados por uma condição específica.

$user->select('COUNT(*) as count')->groupBy('name')->findAll();

order(string $order_by_statement)/orderBy(string $order_by_statement)

Ordene a consulta retornada de uma maneira específica.

$user->orderBy('name DESC')->find();

limit(string $limit)/limit(int $offset, int $limit)

Limite a quantidade de registros retornados. Se um segundo inteiro for fornecido, ele será um deslocamento, limitando apenas como no SQL.

$user->orderby('name DESC')->limit(0, 10)->findAll();

Condições WHERE

equal(string $field, mixed $value) / eq(string $field, mixed $value)

Onde field = $value

$user->eq('id', 1)->find();

notEqual(string $field, mixed $value) / ne(string $field, mixed $value)

Onde field <> $value

$user->ne('id', 1)->find();

isNull(string $field)

Onde field IS NULL

$user->isNull('id')->find();

isNotNull(string $field) / notNull(string $field)

Onde field IS NOT NULL

$user->isNotNull('id')->find();

greaterThan(string $field, mixed $value) / gt(string $field, mixed $value)

Onde field > $value

$user->gt('id', 1)->find();

lessThan(string $field, mixed $value) / lt(string $field, mixed $value)

Onde field < $value

$user->lt('id', 1)->find();

greaterThanOrEqual(string $field, mixed $value) / ge(string $field, mixed $value) / gte(string $field, mixed $value)

Onde field >= $value

$user->ge('id', 1)->find();

lessThanOrEqual(string $field, mixed $value) / le(string $field, mixed $value) / lte(string $field, mixed $value)

Onde field <= $value

$user->le('id', 1)->find();

like(string $field, mixed $value) / notLike(string $field, mixed $value)

Onde field LIKE $value ou field NOT LIKE $value

$user->like('name', 'de')->find();

in(string $field, array $values) / notIn(string $field, array $values)

Onde field IN($value) ou field NOT IN($value)

$user->in('id', [1, 2])->find();

between(string $field, array $values)

Onde field BETWEEN $value AND $value1

$user->between('id', [1, 2])->find();

Relacionamentos

Você pode definir vários tipos de relacionamentos usando esta biblioteca. Você pode definir relacionamentos um para muitos e um para um entre tabelas. Isso requer uma configuração um pouco extra na classe antecipadamente.

Configurar o $relations array não é difícil, mas adivinhar a sintaxe correta pode ser confuso.

protected array $relations = [
    // você pode nomear a chave como desejar. O nome do ActiveRecord é provavelmente bom. Ex: usuário, contato, cliente
    'utilisateur' => [
        // obrigatório
        // self::HAS_MANY, self::HAS_ONE, self::BELONGS_TO
        self::HAS_ONE, // este é o tipo de relacionamento

        // obrigatório
        'Some_Class', // esta é a classe ActiveRecord "outra" que será referenciada

        // obrigatório
        // dependendo do tipo de relacionamento
        // self::HAS_ONE = a chave estrangeira que referencia o join
        // self::HAS_MANY = a chave estrangeira que referencia o join
        // self::BELONGS_TO = a chave local que referencia o join
        'chave_local_ou_estrageira',
        // só para informação, isso também só junta à chave primária do modelo "outro"

        // opcional
        [ 'eq' => [ 'id_cliente', 5 ], 'select' => 'COUNT(*) as count', 'limit' 5 ], // condições adicionais que você deseja ao juntar a relação
        // $registro->eq('id_cliente', 5)->select('COUNT(*) as count')->limit(5))

        // opcional
        'nome_referencia_de_retorno' // isso é se você quiser fazer referência a esse relacionamento de volta para si mesmo Ex: $user->contato->usuário;
    ];
]
class User extends ActiveRecord{
    protected array $relations = [
        'contatos' => [ self::HAS_MANY, Contato::class, 'id_usuario' ],
        'contato' => [ self::HAS_ONE, Contato::class, 'id_usuario' ],
    ];

    public function __construct($conexao_banco_de_dados)
    {
        parent::__construct($conexao_banco_de_dados, 'usuarios');
    }
}

class Contato extends ActiveRecord{
    protected array $relations = [
        'usuario' => [ self::BELONGS_TO, User::class, 'id_usuario' ],
        'usuario_com_referencia_de_volta' => [ self::BELONGS_TO, User::class, 'id_usuario', [], 'contato' ],
    ];
    public function __construct($conexao_banco_de_dados)
    {
        parent::__construct($conexao_banco_de_dados, 'contatos');
    }
}

Agora que as referências estão configuradas, podemos usá-las muito facilmente!

$user = new User($conexao_pdo);

// encontrar o usuário mais recente.
$user->notNull('id')->orderBy('id desc')->find();

// obter contatos usando a relação:
foreach($user->contatos as $contato) {
    echo $contato->id;
}

// ou podemos ir pelo outro caminho.
$contato = new Contato();

// encontrar um contato
$contato->find();

// obter usuário usando a relação:
echo $contato->usuario->nome; // este é o nome do usuário

Muito legal, né?

Definindo Dados Personalizados

Às vezes, você pode precisar anexar algo único ao seu ActiveRecord, como um cálculo personalizado que pode ser mais fácil de anexar ao objeto para ser passado, por exemplo, para um modelo.

setCustomData(string $field, mixed $value)

Você anexa os dados personalizados com o método setCustomData().

$user->setCustomData('contagem_visualizacoes_pagina', $contagem_visualizacoes_pagina);

E então você simplesmente o referencia como uma propriedade normal do objeto.

echo $user->contagem_visualizacoes_pagina;

Eventos

Uma característica muito incrível sobre esta biblioteca é sobre os eventos. Os eventos são acionados em determinados momentos com base em certos métodos que você chama. Eles são muito, muito úteis na configuração de dados automaticamente para você.

onConstruct(ActiveRecord $ActiveRecord, array &config)

Isso é realmente útil se você precisar definir uma conexão padrão ou algo assim.

// index.php ou bootstrap.php
Flight::register('db', 'PDO', [ 'sqlite:test.db' ]);

//
//
//

// User.php
class User extends flight\ActiveRecord {

    protected function onConstruct(self $self, array &$config) { // não esqueça a referência de &
        // você poderia fazer isso para definir automaticamente a conexão
        $config['connection'] = Flight::db();
        // ou isso
        $self->transformAndPersistConnection(Flight::db());

        // Você também pode definir o nome da tabela desta forma.
        $config['table'] = 'usuarios';
    } 
}

beforeFind(ActiveRecord $ActiveRecord)

Isso provavelmente só é útil se você precisar de uma manipulação da consulta toda vez.

class User extends flight\ActiveRecord {

    public function __construct($conexao_banco_de_dados)
    {
        parent::__construct($conexao_banco_de_dados, 'usuarios');
    }

    protected function beforeFind(self $self) {
        // sempre executar id >= 0 se for do seu interesse
        $self->gte('id', 0); 
    } 
}

afterFind(ActiveRecord $ActiveRecord)

Este é provavelmente mais útil se você precisar executar alguma lógica sempre que este registro for buscado. Você precisa descriptografar algo? Precisa executar uma consulta de contagem personalizada toda vez (não performático, mas tanto faz)?

class User extends flight\ActiveRecord {

    public function __construct($conexao_banco_de_dados)
    {
        parent::__construct($conexao_banco_de_dados, 'usuarios');
    }

    protected function afterFind(self $self) {
        // descriptografando algo
        $self->secreto = suaFuncaoDescriptografar($self->secreto, $alguma_chave);

        // talvez armazenando algo personalizado como uma consulta???
        $self->setCustomData('contagem_visualizacoes', $self->selecionar('COUNT(*) contagem')->de('visualizacoes_usuarios')->eq('id_usuario', $self->id)['contagem']; 
    } 
}

beforeFindAll(ActiveRecord $ActiveRecord)

Isso provavelmente só é útil se você precisar de uma manipulação da consulta toda vez.

class User extends flight\ActiveRecord {

    public function __construct($conexao_banco_de_dados)
    {
        parent::__construct($conexao_banco_de_dados, 'usuarios');
    }

    protected function beforeFindAll(self $self) {
        // sempre executar id >= 0 se for do seu interesse
        $self->gte('id', 0); 
    } 
}

afterFindAll(array<int,ActiveRecord> $results)

Similar a afterFind() mas você faz isso para todos os registros!

class User extends flight\ActiveRecord {

    public function __construct($conexao_banco_de_dados)
    {
        parent::__construct($conexao_banco_de_dados, 'usuarios');
    }

    protected function afterFindAll(array $results) {

        foreach($results as $self) {
            // faça algo legal como em afterFind()
        }
    } 
}

beforeInsert(ActiveRecord $ActiveRecord)

Realmente útil se você precisa que alguns valores padrão sejam definidos sempre.

class User extends flight\ActiveRecord {

    public function __construct($conexao_banco_de_dados)
    {
        parent::__construct($conexao_banco_de_dados, 'usuarios');
    }

    protected function beforeInsert(self $self) {
        // definir alguns valores padrão
        if(!$self->data_criacao) {
            $self->data_criacao = gmdate('Y-m-d');
        }

        if(!$self->senha) {
            $self->senha = password_hash((string) microtime(true));
        }
    } 
}

afterInsert(ActiveRecord $ActiveRecord)

Talvez você tenha um caso de uso para alterar dados após a inserção?

class User extends flight\ActiveRecord {

    public function __construct($conexao_banco_de_dados)
    {
        parent::__construct($conexao_banco_de_dados, 'usuarios');
    }

    protected function afterInsert(self $self) {
        // faça o que quiser
        Flight::cache()->set('id_insercao_mais_recente', $self->id);
        // ou qualquer outra coisa....
    } 
}

beforeUpdate(ActiveRecord $ActiveRecord)

Realmente útil se você precisa que alguns valores padrão sejam definidos sempre em uma atualização.

class User extends flight\ActiveRecord {

    public function __construct($conexao_banco_de_dados)
    {
        parent::__construct($conexao_banco_de_dados, 'usuarios');
    }

    protected function beforeInsert(self $self) {
        // definir alguns valores padrão
        if(!$self->data_atualizacao) {
            $self->data_atualizacao = gmdate('Y-m-d');
        }
    } 
}

afterUpdate(ActiveRecord $ActiveRecord)

Talvez você tenha um caso de uso para alterar dados após uma atualização?

class User extends flight\ActiveRecord {

    public function __construct($conexao_banco_de_dados)
    {
        parent::__construct($conexao_banco_de_dados, 'usuarios');
    }

    protected function afterInsert(self $self) {
        // faça o que quiser
        Flight::cache()->set('id_usuario_atualizado_recentemente', $self->id);
        // ou qualquer outra coisa....
    } 
}

beforeSave(ActiveRecord $ActiveRecord)/afterSave(ActiveRecord $ActiveRecord)

Isso é útil se você quer que eventos aconteçam tanto quando inserções quanto atualizações acontecem. Vou poupar a longa explicação, mas tenho certeza de que você pode adivinhar do que se trata.

class User extends flight\ActiveRecord {

    public function __construct($conexao_banco_de_dados)
    {
        parent::__construct($conexao_banco_de_dados, 'usuarios');
    }

    protected function beforeSave(self $self) {
        $self->ultima_atualizacao = gmdate('Y-m-d H:i:s');
    } 
}

beforeDelete(ActiveRecord $ActiveRecord)/afterDelete(ActiveRecord $ActiveRecord)

Não tenho certeza do que você gostaria de fazer aqui, mas sem julgamentos aqui! Vá em frente!

class User extends flight\ActiveRecord {

    public function __construct($conexao_banco_de_dados)
    {
        parent::__construct($conexao_banco_de_dados, 'usuarios');
    }

    protected function beforeDelete(self $self) {
        echo 'Ele foi um bravo soldado... :cry-face:';
    } 
}

Gerenciamento de Conexão com o Banco de Dados

Quando você está usando esta biblioteca, você pode definir a conexão do banco de dados de algumas maneiras diferentes. Você pode definir a conexão no construtor, você pode configurá-la via uma variável de configuração $config['connection'] ou você pode configurá-la via setDatabaseConnection() (v0.4.1).

$conexao_pdo = new PDO('sqlite:test.db'); // por exemplo
$user = new User($conexao_pdo);
// ou
$user = new User(null, [ 'connection' => $conexao_pdo ]);
// ou
$user = new User();
$user->setDatabaseConnection($conexao_pdo);

Se você precisar atualizar a conexão do banco de dados, por exemplo, se estiver executando um script CLI de longa execução e precisar atualizar a conexão periodicamente, você pode redefinir a conexão com $seu_registro->setDatabaseConnection($conexao_pdo).

Contribuição

Por favor faça. :D

Configuração

Ao contribuir, certifique-se de executar composer test-coverage para manter 100% de cobertura dos testes (isso não é uma cobertura verdadeira de teste unitário, mais como testes de integração).

Também certifique-se de executar composer beautify e composer phpcs para corrigir quaisquer erros de lint.

Licença

MIT

Awesome-plugins/latte

Latte

Latte é um mecanismo de modelagem completo que é muito fácil de usar e se aproxima mais da sintaxe do PHP do que o Twig ou o Smarty. Também é muito fácil de ampliar e adicionar seus próprios filtros e funções.

Instalação

Instale com o composer.

composer require latte/latte

Configuração Básica

Existem algumas opções de configuração básicas para começar. Você pode ler mais sobre elas na Documentação do Latte.


use Latte\Engine as LatteEngine;

require 'vendor/autoload.php';

$app = Flight::app();

$app->register('latte', LatteEngine::class, [], function(LatteEngine $latte) use ($app) {

    // Aqui é onde o Latte irá armazenar em cache seus modelos para acelerar as coisas
    // Uma coisa legal sobre o Latte é que ele atualiza automaticamente seu
    // cache quando você faz alterações em seus modelos!
    $latte->setTempDirectory(__DIR__ . '/../cache/');

    // Diga ao Latte onde o diretório raiz para suas visualizações estará.
    $latte->setLoader(new \Latte\Loaders\FileLoader($app->get('flight.views.path')));
});

Exemplo de Layout Simples

Aqui está um exemplo simples de um arquivo de layout. Este é o arquivo que será usado para envolver todas as suas outras visualizações.

<!-- app/views/layout.latte -->
<!doctype html>
<html lang="pt">
    <head>
        <title>{$title ? $title . ' - '}My App</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <nav>
                <!-- seus elementos de navegação aqui -->
            </nav>
        </header>
        <div id="content">
            <!-- Aqui está a mágica -->
            {block content}{/block}
        </div>
        <div id="footer">
            &copy; Direitos Autorais
        </div>
    </body>
</html>

E agora temos seu arquivo que será renderizado dentro desse bloco de conteúdo:

<!-- app/views/home.latte -->
<!-- Isso diz ao Latte que este arquivo está "dentro" do arquivo layout.latte -->
{extends layout.latte}

<!-- Este é o conteúdo que será renderizado dentro do layout dentro do bloco de conteúdo -->
{block content}
    <h1>Página Inicial</h1>
    <p>Bem-vindo ao meu aplicativo!</p>
{/block}

Então, quando você for renderizar isso dentro de sua função ou controlador, você faria algo assim:

// rota simples
Flight::route('/', function () {
    Flight::latte()->render('home.latte', [
        'title' => 'Página Inicial'
    ]);
});

// ou se estiver usando um controlador
Flight::route('/', [HomeController::class, 'index']);

// HomeController.php
class HomeController
{
    public function index()
    {
        Flight::latte()->render('home.latte', [
            'title' => 'Página Inicial'
        ]);
    }
}

Veja a Documentação do Latte para mais informações sobre como usar o Latte ao máximo!

Awesome-plugins/awesome_plugins

Incríveis Plugins

Flight é incrivelmente extensível. Existem vários plugins que podem ser usados para adicionar funcionalidades à sua aplicação do Flight. Alguns são oficialmente suportados pela Equipe do Flight e outros são bibliotecas micro/lite para ajudá-lo a começar.

Cache

O cache é uma ótima maneira de acelerar sua aplicação. Existem várias bibliotecas de cache que podem ser usadas com o Flight.

CLI

Aplicações CLI são uma ótima maneira de interagir com sua aplicação. Você pode usá-las para gerar controladores, exibir todas as rotas e muito mais.

Cookies

Cookies são uma ótima maneira de armazenar pequenos pedaços de dados no lado do cliente. Eles podem ser usados para armazenar preferências do usuário, configurações da aplicação e muito mais.

Depuração

A depuração é crucial ao desenvolver em seu ambiente local. Existem alguns plugins que podem aprimorar sua experiência de depuração.

Bancos de Dados

Bancos de dados são essenciais para a maioria das aplicações. É assim que você armazena e recupera dados. Algumas bibliotecas de banco de dados são simplesmente wrappers para escrever consultas e outras são ORMs completos.

Criptografia

A criptografia é crucial para qualquer aplicação que armazena dados sensíveis. Criptografar e descriptografar os dados não é tão difícil, mas armazenar corretamente a chave de criptografia pode ser difícil. A coisa mais importante é nunca armazenar sua chave de criptografia em um diretório público ou incluí-la em seu repositório de código.

Sessão

As sessões não são realmente úteis para APIs, mas para construir uma aplicação web, as sessões podem ser cruciais para manter o estado e as informações de login.

Templating

A criação de modelos é fundamental para qualquer aplicação web com uma UI. Existem várias engines de templating que podem ser usadas com o Flight.

Contribuição

Tem um plugin que gostaria de compartilhar? Envie um pull request para adicioná-lo à lista!

Examples

Precisa de um começo rápido?

Você tem duas opções para começar com o Flight:

Precisa de Alguma Inspiração?

Embora estes não sejam oficialmente patrocinados pela equipe do Flight, eles podem lhe dar ideias sobre como estruturar seus próprios projetos construídos com o Flight!

Quer Compartilhar Seu Próprio Exemplo?

Se você tem um projeto que deseja compartilhar, por favor envie uma solicitação de pull request para adicioná-lo a esta lista!