Learn

Aprenda Sobre 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 aplicativo web. É 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 artigo curto 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.

Além disso, um excelente tutorial foi criado por @lubiana. Embora não entre em grande detalhe sobre o Flight especificamente, este guia ajudará você a entender alguns dos principais conceitos em torno de 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.

Roteamento

Aprenda como gerenciar rotas para o seu aplicativo web. Isso também inclui agrupamento de 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.

Modelos HTML

Aprenda como usar o mecanismo de visualização integrado para renderizar seus modelos 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

Em termos de retrocompatibilidade, em sua maioria, foi mantida, mas existem algumas alterações das quais você deve estar ciente ao migrar da v2 para a v3.

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 há algumas alterações das quais deve estar ciente ao migrar da v2 para a v3.

## Comportamento do Buffer de Saída (3.5.0)

O [buffer de saída](https://stackoverflow.com/questions/2832010/what-is-output-buffering-in-php) é o processo pelo qual 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 que seja enviada ao cliente.

Em uma aplicação MVC, o Controlador é o "gerenciador" e ele gerencia o que a visualização faz. Ter saída gerada fora do controlador (ou no caso do Flight às vezes uma função anônima) quebra o padrão MVC. Esta mudança visa estar mais alinhada 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 tratado de forma que não fechava consistentemente seu próprio buffer de saída, o que tornava os [testes unitários](https://github.com/flightphp/core/pull/545/files#diff-eb93da0a3473574fba94c3c4160ce68e20028e30b267875ab0792ade0b0539a0R42) e [streaming](https://github.com/flightphp/core/issues/413) mais difíceis. Para a maioria dos usuários, esta mudança pode na verdade não afetá-lo. No entanto, se você estiver dando echo de conteúdo fora de chamáveis e controladores (por exemplo, em um hook), provavelmente encontrará problemas. Dar echo de conteúdo nos hooks e antes do framework realmente executar pode ter funcionado no passado, mas não funcionará daqui para frente.

### Onde você pode ter problemas
```php
// 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á correto
    echo '<p>Esta frase Olá Mundo foi trazida para você pela letra "O"</p>';
});

Flight::before('start', function(){
    // coisas como essa irão causar 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>';
});

Ativando o Comportamento de Renderização da v2

Ainda pode manter seu código antigo como está sem precisar reescrever para fazê-lo 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 como true. Isso permitirá que continue a usar o comportamento de renderização antigo, mas é recomendável corrigi-lo ao longo do tempo. 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 

Mudanças no Despachante (3.7.0)

Se você tem chamado diretamente métodos estáticos para Dispatcher como Dispatcher::invokeMethod(), Dispatcher::execute(), etc., precisará atualizar seu código para não chamar diretamente esses métodos. Dispatcher foi convertido para ser mais orientado a objetos para que os Contêineres de Injeção de Dependência possam ser usados de forma mais fácil. Se precisar invocar um método de forma semelhante ao que Dispatcher fazia, 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 configurando 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 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);

Manipulação de Erros

Erros e Exceções

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

Você pode substituir esse comportamento conforme suas necessidades:

Flight::map('error', function (Throwable $error) {
  // Tratar 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 Não Encontrado HTTP 404 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

A segurança é muito importante quando se trata de aplicações web. Você quer garantir que sua aplicação seja segura e que os dados de seus usuários estejam protegidos. O Flight fornece várias funcionalidades para ajudar a proteger suas aplicações web.

Cabeçalhos

Os cabeçalhos HTTP são uma das maneiras mais fáceis de proteger suas aplicações web. Você pode usar cabeçalhos para evitar ataques de clickjacking, XSS e outros. Existem diversas 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: este cabeçalho pode ficar muito complexo, portanto é recomendável
//  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 a quantidade de informações do referrer enviadas
Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');

// Defina o cabeçalho Strict-Transport-Security para forçar o 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 utilizados
Flight::response()->header('Permissions-Policy', 'geolocation=()');

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

Adicionar como um Filtro

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

// 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 Intermediário

Você também pode adicioná-los como uma classe intermediária. 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
// FYI, este grupo de string vazia atua como um intermediário global para
// todas as rotas. Claro, você também pode fazer o mesmo e adicionar
// isso apenas a rotas específicas.
Flight::group('', function(Router $router) {
    $router->get('/users', [ 'UserController', 'getUsers' ]);
    // mais rotas
}, [ new SecurityHeadersMiddleware() ]);

Cross Site Request Forgery (CSRF)

Cross Site Request Forgery (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 executar ações no seu site sem o conhecimento do usuário. O Flight não fornece um mecanismo de proteção CSRF integrado, mas você pode facilmente implementar o seu próprio usando um intermediário.

Configuração

Primeiro, você precisa gerar um token CSRF e armazená-lo na sessão do usuário. Você pode então usar este 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
// (presumindo que você criou um objeto de sessão e o anexou ao Flight)
// Você só precisa gerar um único token por sessão (para que funcione
// em várias abas 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.

// Definir uma função personalizada para exibir o token CSRF
// Observação: a visualização foi configurada com Latte como mecanismo 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 intermediário 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') {

        // captura 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 intermediária:

// 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('/users', [ 'UserController', 'getUsers' ]);
    // 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 no 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 dos seus usuários! Sempre assuma que todos são os melhores hackers do mundo. Eles podem injetar JavaScript ou HTML malicioso na sua página. Esse código pode ser usado para roubar informações dos seus usuários ou executar ações no seu site. Usando a classe de visualização do Flight, você pode escapar facilmente a saída para prevenir ataques XSS.

// Vamos supor que o usuário seja inteligente e tente usar isso como seu nome
$nome = '<script>alert("XSS")</script>';

// Isso irá 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('template', ['nome' => $nome]);

Injeção de SQL

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

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

// Se você usar a classe PdoWrapper, isso pode ser feito facilmente 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 ?
$declaração = Flight::db()->fetchAll('SELECT * FROM usuarios WHERE nome = ?', [ $nome ]);

// Apenas prometa que nunca JAMAIS fará algo assim...
$usuarios = Flight::db()->fetchAll("SELECT * FROM usuarios WHERE nome = '{$nome}' LIMIT 5");
// porque e se $nome = "' OU 1=1; -- "; 
// Após a consulta ser montada, ela se parece com isso
// 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

Cross-Origin Resource Sharing (CORS) é um mecanismo que permite solicitar muitos recursos (por exemplo, fontes, JavaScript, etc.) de uma página web a partir de outro domínio fora do domínio do qual o recurso se originou. O Flight não possui funcionalidade integrada, mas isso pode ser tratado facilmente com um intermediário ou filtros de evento semelhante ao CSRF.

// app/middleware/CorsMiddleware.php

namespace app\middleware;

class CorsMiddleware
{
    public function before(array $params): void
    {
        $resposta = Flight::response();
        if (isset($_SERVER['HTTP_ORIGIN'])) {
            $this->permitirOrigens();
            $resposta->header('Access-Control-Allow-Credentials: true');
            $resposta->header('Access-Control-Max-Age: 86400');
        }

        if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
            if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) {
                $resposta->header(
                    'Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS'
                );
            }
            if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {
                $resposta->header(
                    "Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}"
                );
            }
            $resposta->send();
            exit(0);
        }
    }

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

        if (in_array($_SERVER['HTTP_ORIGIN'], $permitido)) {
            $resposta = Flight::response();
            $resposta->header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
        }
    }
}

// index.php ou onde quer que tenha suas rotas
Flight::route('/usuarios', function() {
    $usuarios = Flight::db()->fetchAll('SELECT * FROM usuarios');
    Flight::json($usuarios);
})->addMiddleware(new CorsMiddleware());

Conclusão

A segurança é muito importante e é fundamental garantir que suas aplicações web sejam seguras. O Flight fornece várias funcionalidades para ajudar a proteger suas aplicações web, mas é importante estar sempre vigilante e garantir que você está fazendo tudo o que pode para manter os dados de seus usuários seguros. Sempre assuma o pior e nunca confie na entrada dos seus usuários. Sempre escape a saída e use declarações preparadas para prevenir injeção de SQL. Sempre use intermediários 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 aprofundada.

O encaminhamento básico no Flight é feito correspondendo 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 correspondidas na ordem em que são definidas. A primeira rota a corresponder a uma solicitação será invocada.

Callbacks/Funções

O retorno pode ser qualquer objeto que seja chamável. Portanto, 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 Greeting {
    public static function hello() {
        echo 'olá mundo!';
    }
}

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

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


// Greeting.php
class Greeting
{
    public function __construct() {
        $this->name = 'Fulano';
    }

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

// index.php
$greeting = new Greeting();

Flight::route('/', [ $greeting, 'hello' ]);
// Você também pode fazer isso sem criar o objeto primeiro
// Nota: Nenhum argumento será injetado no construtor
Flight::route('/', [ 'Greeting', 'hello' ]);

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

Se você deseja usar injeção de dependência por meio de um contêiner (PSR-11, PHP-DI, Dice, etc), o único tipo de rotas onde isso está disponível é criando diretamente o objeto sozinho e usando o contêiner 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 Injeção de Dependência para mais informações.

Aqui está um exemplo rápido:


use flight\database\PdoWrapper;

// Greeting.php
class Greeting
{
    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 contêiner com os parâmetros necessários
// Consulte 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 contêiner
Flight::registerContainerHandler(function($class, $params) use ($dice) {
    return $dice->create($class, $params);
});

// Rotas como o normal
Flight::route('/hello/@id', [ 'Greeting', 'hello' ]);
// ou
Flight::route('/hello/@id', 'Greeting->hello');
// ou
Flight::route('/hello/@id', 'Greeting::hello');

Flight::start();

Encaminhamento por Método

Por padrão, os padrões de rota são correspondidos a todos os métodos de solicitação. Você pode responder a métodos específicos colocando um identificador antes da URL.

Flight::route('GET /', function () {
  echo 'Recebi uma solicitação GET.';
});

Flight::route('POST /', function () {
  echo 'Recebi uma solicitação POST.';
});

// Você não pode usar Flight::get() para rotas, pois esse é um método
//    para obter variáveis, não criar uma rota.
// Flight::post('/', function() { /* código */ });
// Flight::patch('/', function() { /* código */ });
// Flight::put('/', function() { /* código */ });
// Flight::delete('/', function() { /* código */ });

Você também pode mapear vários métodos para um único retorno usando um delimitador |:

Flight::route('GET|POST /', function () {
  echo 'Recebi uma solicitação GET ou POST.';
});

Além disso, você pode obter o objeto Router que possui alguns métodos auxiliares para você usar:


$router = Flight::router();

// mapeia todos os métodos
$router->map('/', function() {
    echo 'olá mundo!';
});

// solicitação GET
$router->get('/users', function() {
    echo 'usuários';
});
// $router->post();
// $router->put();
// $router->delete();
// $router->patch();

Expressões Regulares

Você pode usar expressões regulares em suas rotas:

Flight::route('/user/[0-9]+', function () {
  // Isso corresponderá a /user/1234
});

Embora este método esteja disponível, é recomendado usar parâmetros nomeados, ou parâmetros nomeados com expressões regulares, pois são mais legíveis e mais fáceis de manter.

Parâmetros Nomeados

Você pode especificar parâmetros nomeados em suas rotas que serão passados para sua função de retorno.

Flight::route('/@name/@id', function (string $name, string $id) {
  echo "olá, $name ($id)!";
});

Você também pode incluir expressões regulares com seus parâmetros nomeados usando o delimitador ::

Flight::route('/@name/@id:[0-9]{3}', function (string $name, string $id) {
  // Isso corresponderá a /bob/123
  // Mas não corresponderá a /bob/12345
});

Nota: Não é suportacio corresponder grupos regex () com parâmetros nomeados. :'(

Parâmetros Opcionais

Você pode especificar parâmetros nomeados que são opcionais para corresponder ao envolver segmentos em parênteses.

Flight::route(
  '/blog(/@year(/@month(/@day)))',
  function(?string $year, ?string $month, ?string $day) {
    // Isso corresponderá aos seguintes URLs:
    // /blog/2012/12/10
    // /blog/2012/12
    // /blog/2012
    // /blog
  }
);

Quaisquer parâmetros opcionais que não sejam correspondidos serão passados como NULL.

Coringas

A correspondência é feita apenas em segmentos individuais de URL. Se deseja corresponder a vários segmentos, você pode usar o coringa *.

Flight::route('/blog/*', function () {
  // Isso corresponderá a /blog/2000/02/01
});

Para rotear todas as solicitações a um único retorno, você pode fazer:

Flight::route('*', function () {
  // Faça algo
});

Passando Adiante

Você pode passar a execução para a próxima rota correspondente retornando true de sua função de retorno.

Flight::route('/user/@name', function (string $name) {
  // Verificar alguma condição
  if ($name !== "João") {
    // Continuar para a próxima rota
    return true;
  }
});

Flight::route('/user/*', function () {
  // Isso será chamado
});

Alias de Rota

Você pode atribuir um alias a uma rota, para que a URL possa ser gerada dinamicamente mais tarde em seu código (como em um modelo, por exemplo).

Flight::route('/users/@id', function($id) { echo 'usuário:'.$id; }, false, 'visualizacao_usuario');

// mais tarde no código em algum lugar
Flight::getUrl('visualizacao_usuario', [ 'id' => 5 ]); // retornará '/users/5'

Isso é especialmente útil se sua URL mudar. No exemplo acima, digamos que os usuários foram movidos para /admin/users/@id. Com o uso de alias, você não precisa alterar em nenhum lugar que você faz referência ao alias, pois o alias agora retornará /admin/users/5 como no exemplo acima.

O alias de rota ainda funciona em grupos também:

Flight::group('/users', function() {
    Flight::route('/@id', function($id) { echo 'usuário:'.$id; }, false, 'visualizacao_usuario');
});

// mais tarde no código em algum lugar
Flight::getUrl('visualizacao_usuario', [ 'id' => 5 ]); // retornará '/users/5'

Informações de Rota

Se você deseja inspecionar as informações da rota correspondente, pode solicitar que o objeto de rota seja passado para sua função de retorno passando true como terceiro parâmetro no método de rota. O objeto de rota sempre será o último parâmetro passado para sua função de retorno.

Flight::route('/', function(\flight\net\Route $route) {
  // Array de métodos HTTP correspondentes
  $route->methods;

  // Array de parâmetros nomeados
  $route->params;

  // Expressão regular correspondente
  $route->regex;

  // Contém o conteúdo de qualquer '*' usado no padrão de URL
  $route->splat;

  // Mostra o caminho de URL....se você realmente precisar
  $route->pattern;

  // Mostra qual middleware foi atribuído a isso
  $route->middleware;

  // Mostra o alias atribuído a esta rota
  $route->alias;
}, true);

Agrupamento de Rotas

Pode haver momentos em que você deseja agrupar rotas relacionadas juntas (como /api/v1). Você pode fazer isso usando o método group:

Flight::group('/api/v1', function () {
  Flight::route('/users', function () {
    // Corresponde a /api/v1/users
  });

  Flight::route('/posts', function () {
    // Corresponde a /api/v1/posts
  });
});

Você também pode aninhar grupos de grupos:

Flight::group('/api', function () {
  Flight::group('/v1', function () {
    // Flight::get() obtém variáveis, não define uma rota! Veja o contexto do objeto abaixo
    Flight::route('GET /users', function () {
      // Corresponde a GET /api/v1/users
    });

    Flight::post('/posts', function () {
      // Corresponde a POST /api/v1/posts
    });

    Flight::put('/posts/1', function () {
      // Corresponde a PUT /api/v1/posts
    });
  });
  Flight::group('/v2', function () {

    // Flight::get() obtém variáveis, não define uma rota! Veja o contexto do objeto abaixo
    Flight::route('GET /users', function () {
      // Corresponde a GET /api/v2/users
    });
  });
});

Agrupamento com Contexto de Objeto

Você ainda pode usar o agrupamento de rotas com o objeto Engine da seguinte maneira:

$app = new \flight\Engine();
$app->group('/api/v1', function (Router $router) {

  // use a variável $router
  $router->get('/users', function () {
    // Corresponde a GET /api/v1/users
  });

  $router->post('/posts', function () {
    // Corresponde a POST /api/v1/posts
  });
});

Streaming

Agora você pode enviar respostas em tempo real para o cliente usando o método streamWithHeaders(). Isso é útil para enviar arquivos grandes, processos longos em execução ou gerar respostas grandes. O streaming de uma rota é tratado um pouco diferente do que uma rota regular.

Nota: As respostas de streaming estão disponíveis somente se você tiver flight.v2.output_buffering definido como false.

Flight::route('/stream-users', function() {

    // Se você tiver cabeçalhos adicionais para definir aqui após a execução da rota
    // você deve defini-los antes de qualquer coisa ser ecoada.
    // Todos eles devem ser uma chamada bruta à função header() ou
    // uma chamada para Flight::response()->setRealHeader()
    header('Content-Disposition: attachment; filename="users.json"');
    // ou
    Flight::response()->setRealHeader('Content-Disposition', 'attachment; filename="users.json"');

    // como você puxa seus dados, apenas como exemplo...
    $users_stmt = Flight::db()->query("SELECT id, first_name, last_name FROM users");

    echo '{';
    $user_count = count($users);
    while($user = $users_stmt->fetch(PDO::FETCH_ASSOC)) {
        echo json_encode($user);
        if(--$user_count > 0) {
            echo ',';
        }

        // Isso é necessário para enviar os dados para o cliente
        ob_flush();
    }
    echo '}';

// É assim que você definirá os cabeçalhos antes de iniciar o streaming.
})->streamWithHeaders([
    'Tipo de Conteúdo' => 'application/json',
    // código de status opcional, padrão é 200
    'status' => 200
]);

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 Contêiner de Injeção de Dependência (DIC) é uma ferramenta poderosa que permite que você gerencie as dependências da sua aplicação. É um conceito-chave nos frameworks modernos de PHP e é usado para gerenciar a instanciação e configuração de objetos. Alguns exemplos de bibliotecas DIC são: Dice, Pimple, PHP-DI e league/container.

Um DIC é uma forma sofisticada de dizer que permite que você crie e gerencie 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 entender melhor isso.

Exemplo Básico

A forma antiga de fazer as coisas pode se parecer com isso:


require 'vendor/autoload.php';

// classe para gerenciar usuários do 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();

Dá para ver no código acima que estamos criando um novo objeto PDO e passando para nossa classe UserController. Isso é bom para uma aplicação pequena, mas à medida que sua aplicação cresce, você verá que está criando o mesmo objeto PDO em vários lugares. Aqui é onde um DIC se torna útil.

Aqui está o mesmo exemplo usando um DIC (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 contêiner
$container = new \Dice\Dice;
// não se esqueça de reatribuir a ele mesmo como abaixo!
$container = $container->addRule('PDO', [
    // shared significa que o mesmo objeto será retornado sempre
    'shared' => true,
    'constructParams' => ['mysql:host=localhost;dbname=test', 'user', 'pass' ]
]);

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

// agora podemos usar o contêiner 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 foi adicionado muito código extra ao exemplo. A magia acontece quando você tem outro controlador que necessita 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 DIC é que os testes de unidade se tornam muito mais fáceis. Você pode criar um objeto simulado 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 utilizar qualquer contêiner compatível com PSR-11. Isso significa que você pode usar qualquer contêiner que implemente a interface PSR-11. Aqui está um exemplo usando o contêiner PSR-11 da League:


require 'vendor/autoload.php';

// mesma classe de UserController como 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();

Apesar de ser um pouco mais verboso que o exemplo anterior com Dice, ainda assim faz o trabalho com os mesmos benefícios!

Manipulador de DIC Personalizado

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

Adicionalmente, existem algumas configurações padrão que facilitarão sua vida ao usar o Flight.

Instância do Motor

Se você estiver utilizando a instância do Engine em seus controladores/middlewares, 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/middlewares

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

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

Adicionando Outras Classes

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


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

class MinhaClassePersonalizada {
    public function parseThing() {
        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) do retorno da rota. Esta é uma ótima maneira de adicionar verificações de autenticação de 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 do retorno da rota.
// não há funções de middleware "after" exceto para classes (veja abaixo)
Flight::route('/caminho', function() { echo ' Aqui estou!'; })->addMiddleware(function() {
    echo 'Middleware primeiro!';
});

Flight::start();

// Isso irá produzir "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 você precisar da funcionalidade "after", você deve usar uma classe.

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

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

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

Flight::start();

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

Agrupando Middleware

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


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

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

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


// adicionado no final do método de grupo
Flight::group('', function() {
    Flight::route('/usuarios', function() { echo 'usuarios'; }, false, 'usuarios');
    Flight::route('/usuarios/@id', function($id) { echo 'usuário:'.$id; }, false, 'visualização_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 há alguns pontos válidos a serem considerados sobre as desvantagens de usar 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. Não fornece tantas funcionalidades quanto frameworks maiores como Laravel ou Symfony. No entanto, ele fornece grande parte da 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, o Flight é um ótimo framework para iniciantes para começar. Isso o ajudará a aprender sobre as vantagens de usar frameworks sem sobrecarregar você 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? O 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 com que seu site faça 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 por meio do roteamento.

Pode funcionar assim:

E Por que é Importante?

Ter um roteador centralizado adequado pode realmente facilitar dramaticamente sua vida! Pode ser difícil ver a princípio. Aqui estão algumas razões pelas quais:

Tenho certeza de que você está familiarizado com a maneria script por script de criar um site. Você pode ter um arquivo chamado index.php que possui uma série 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 sair rapidamente do controle. 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
    }
}

Espero que você possa 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. Isso é o núcleo do que um framework web faz. Ele recebe uma solicitação do navegador do usuário, a processa e depois envia uma resposta de volta. É 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 publique uma nova postagem em um blog.

Solicitações

Uma solicitação é o que o navegador do usuário envia para o seu servidor quando ele visita 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, quais dados o usuário deseja enviar para 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 lê-la.

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 ele visita 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 ao 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 ao navegador do usuário. Você pode usar este objeto para enviar uma resposta ao 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 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 resposta para você, mas você tem a maior parte do controle sobre o que enviar 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 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 ao usuário, e o Flight capturará e enviará de volta ao 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
// Content-Type: 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 assim
    $body = 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 nenhum argumento:

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

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 enviar respostas JSON e JSONP. Para enviar uma resposta JSON, você passa alguns dados a serem codificados em JSON:

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

JSONP

Para solicitações JSONP, você pode passar opcionalmente o nome do parâmetro da 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.

Redirecionar para outra URL

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

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

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

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

Parando

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 e mensagem HTTP opcional:

Flight::halt(200, 'Já volto...');

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 oferece suporte integrado para cache em nível HTTP. Se a condição de cache for atendida, o Flight retornará uma resposta HTTP 304 Não Modificado. Na próxima vez que o cliente solicitar o mesmo recurso, ele será solicitado a usar sua versão em cache local.

Cache em Nível de Rota

Se você quiser armazenar em cache toda a resposta, você pode usar o método cache() e passar o tempo de armazenamento em 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 minutes');
  echo 'Este conteúdo será armazenado em cache.';
});

Última Modificação

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é 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 cache ETag é semelhante ao Última-Modificação, exceto que você pode especificar qualquer identificação desejada para o recurso:

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

Lembre-se de que chamar lastModified ou etag irá definir e verificar o valor em cache. Se o valor em 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

Ampliação

Flight é projetado para ser um framework extensível. O framework vem com um conjunto de métodos e componentes padrão, mas permite mapear seus próprios métodos, registrar suas próprias classes ou até mesmo substituir classes e métodos existentes.

Se você está procurando por um DIC (Dependency Injection Container), vá para a página de Dependency Injection Container.

Mapeamento de Métodos

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

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

// Chamar seu método personalizado
Flight::hello('Bob');

Isso é usado mais quando você precisa passar variáveis para o seu método para obter um valor esperado. Usar o método register() como abaixo é mais para passar configurações 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:

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

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

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

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

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

// e se você precisar mais tarde em 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 seu novo objeto. A função de retorno de chamada recebe um parâmetro, uma instância do novo objeto.

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

Por padrão, toda vez que você carregar 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 usando o mesmo nome, apenas o método mapeado será invocado.

Substituindo Métodos do Framework

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

Por exemplo, quando o Flight não consegue corresponder uma URL a uma rota, ele chama o método notFound, o qual 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 de erro 404 personalizada
  include 'errors/404.html';
});

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

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

// Quando o Flight carrega a instância do Router, 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ê quiser carregar automaticamente suas próprias classes, pode usar o método Flight::path para especificar um diretório para carregar as classes.

Exemplo Básico

Vamos supor que temos uma estrutura 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 (isso está em letras maiúsculas de propósito para um exemplo posterior)
│   └── views
└── public
    └── css
    └── js
    └── index.php

Você pode especificar cada diretório a ser carregado da seguinte maneira:


/**
 * public/index.php
 */

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

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

// nenhum espaço de nome necessário

// Todas as classes carregadas automaticamente são recomendadas para serem Pascal Case (cada palavra capitalizada, sem espaços)
// É um requisito que você não pode ter um sublinhado no nome da sua classe
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 de 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 para o carregador automático
Flight::path(__DIR__.'/../');

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

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

// espaços de nomes são obrigatórios
// os espaços de nomes são os mesmos que a estrutura de diretórios
// os espaços de nomes devem seguir o mesmo caso que a estrutura de diretórios
// espaços de nomes e diretórios não podem ter sublinhados
namespace app\controllers;

// Todas as classes carregadas automaticamente são recomendadas para serem Pascal Case (cada palavra capitalizada, sem espaços)
// É um requisito que você não pode ter um sublinhado no nome da sua classe
class MyController {

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

E se você deseja carregar automaticamente uma classe em seu diretório de utilitários, você faria basicamente a mesma coisa:


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

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

class ArrayHelperUtil {

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

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

Classe Auxiliar PDO PdoWrapper

Flight vem com uma classe auxiliar para PDO. Permite que você consulte facilmente seu banco de dados com toda a maluquice de prepared/execute/fetchAll(). Simplifica muito como você pode consultar seu banco de dados. Cada resultado de linha é retornado como uma classe Flight Collection que permite acessar seus dados via sintaxe de array ou sintaxe de objeto.

Registrando a Classe Auxiliar PDO

// Registrar a classe auxiliar de PDO
Flight::register('db', \flight\database\PdoWrapper::class, ['mysql:host=localhost;dbname=cool_db_name', 'user', 'pass', [
        PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'utf8mb4\'',
        PDO::ATTR_EMULATE_PREPARES => false,
        PDO::ATTR_STRINGIFY_FETCHES => false,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    ]
]);

Uso

Este objeto estende o PDO, então todos os métodos normais do PDO estão disponíveis. Os seguintes métodos são adicionados para facilitar a consulta ao banco de dados:

runQuery(string $sql, array $params = []): PDOStatement

Use isso para INSERTS, UPDATES ou se planeja usar um SELECT em um loop while

$db = Flight::db();
$statement = $db->runQuery("SELECT * FROM table WHERE something = ?", [ $something ]);
while($row = $statement->fetch()) {
    // ...
}

// Ou escrevendo no banco de dados
$db->runQuery("INSERT INTO table (name) VALUES (?)", [ $name ]);
$db->runQuery("UPDATE table SET name = ? WHERE id = ?", [ $name, $id ]);

fetchField(string $sql, array $params = []): mixed

Recupera o primeiro campo da consulta

$db = Flight::db();
$count = $db->fetchField("SELECT COUNT(*) FROM table WHERE something = ?", [ $something ]);

fetchRow(string $sql, array $params = []): array

Recupera uma linha da consulta

$db = Flight::db();
$row = $db->fetchRow("SELECT id, name FROM table WHERE id = ?", [ $id ]);
echo $row['name'];
// ou
echo $row->name;

fetchAll(string $sql, array $params = []): array

Recupera todas as linhas da consulta

$db = Flight::db();
$rows = $db->fetchAll("SELECT id, name FROM table WHERE something = ?", [ $something ]);
foreach($rows as $row) {
    echo $row['name'];
    // ou
    echo $row->name;
}

Nota com a sintaxe IN()

Também tem uma função auxiliar útil para declarações IN(). Você pode simplesmente passar um ponto de interrogação como um espaço reservado para IN() e depois um array de valores. Aqui está um exemplo de como isso pode ser:

$db = Flight::db();
$name = 'Bob';
$company_ids = [1,2,3,4,5];
$rows = $db->fetchAll("SELECT id, name FROM table WHERE name = ? AND company_id IN (?)", [ $name, $company_ids ]);

Exemplo Completo

// Rota de exemplo e como você usaria este wrapper
Flight::route('/usuarios', function () {
    // Obter todos os usuários
    $users = Flight::db()->fetchAll('SELECT * FROM users');

    // Transmitir todos os usuários
    $statement = Flight::db()->runQuery('SELECT * FROM users');
    while ($user = $statement->fetch()) {
        echo $user['name'];
        // ou echo $user->name;
    }

    // Obter um único usuário
    $user = Flight::db()->fetchRow('SELECT * FROM users WHERE id = ?', [123]);

    // Obter um único valor
    $count = Flight::db()->fetchField('SELECT COUNT(*) FROM users');

    // Sintaxe especial IN() para ajudar (certifique-se de que IN esteja em maiúsculas)
    $users = Flight::db()->fetchAll('SELECT * FROM users WHERE id IN (?)', [[1,2,3,4,5]]);
    // você também poderia fazer isso
    $users = Flight::db()->fetchAll('SELECT * FROM users WHERE id IN (?)', [ '1,2,3,4,5']);

    // Inserir um novo usuário
    Flight::db()->runQuery("INSERT INTO users (name, email) VALUES (?, ?)", ['Bob', 'bob@example.com']);
    $insert_id = Flight::db()->lastInsertId();

    // Atualizar um usuário
    Flight::db()->runQuery("UPDATE users SET name = ? WHERE id = ?", ['Bob', 123]);

    // Excluir um usuário
    Flight::db()->runQuery("DELETE FROM users WHERE id = ?", [123]);

    // Obter o número de linhas afetadas
    $statement = Flight::db()->runQuery("UPDATE users SET name = ? WHERE name = ?", ['Bob', 'Sally']);
    $affected_rows = $statement->rowCount();

});

Awesome-plugins/session

# Ghostff/Session

Gerenciador de Sessão em PHP (não-bloqueante, flash, segmento, encriptação de sessão). Utiliza open_ssl do PHP para opcionalmente encriptar/descriptografar os dados da sessão. Suporta Arquivo, MySQL, Redis e Memcached.

## Instalação

Instale com o composer.

```bash
composer require ghostff/session

Configuração Básica

Não é necessário passar nada para usar as configurações padrão com sua sessão. Você pode ler sobre mais configurações no Readme do Github.


use Ghostff\Session\Session;

require 'vendor/autoload.php';

$app = Flight ::app();

$app->register('sessão', Session ::class);

// uma coisa a lembrar é que você deve fazer commit de 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ê poderia 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('está_logado', true);
    $session->set('usuário', $usuário);

    // todas as vezes que você escrever na sessão, deve commitar deliberadamente.
    $session->commit();
});

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

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

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

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

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

Exemplo Mais Complexo

Aqui está um exemplo mais complexo de como você poderia 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 dê a ele uma sequência aleatória para o id da sessão
$app->register('sessão', Session ::class, ['caminho/para/session_config.php', bin2hex(random_bytes(32))], function(Sessão $sessão) {
        // ou você pode substituir manualmente as opções de configuração
        $session->updateConfiguration([
            // se você quiser armazenar seus dados de sessão em um banco de dados (bom se você quiser algo como "sair de todos os dispositivos" funcional)
            Session::CONFIG_DRIVER        => Ghostff\Session\Drivers\MySql::class,
            Session::CONFIG_ENCRYPT_DATA  => true,
            Session::CONFIG_SALT_KEY      => hash('sha256', 'meu-super-secreto-sal'), // por favor, mude isso para ser algo diferente
            Session::CONFIG_AUTO_COMMIT   => true, // só faça isso se for necessário e/ou for difícil commitar() 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'   => 'meu_banco_de_dados_do_aplicativo',   # Nome do banco de dados
                'db_table'  => 'sessões',          # 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'=> falso,          # Evitar 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 CONTRA oLADO VOCÊ MESMO
            ]
        ]);
    }
);

Documentação

Visite o Readme do Github 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 analisar este pacote você mesmo.

Awesome-plugins/tracy_extensions

Tracy Flight Painel 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 Solicitação do Flight

Instalação

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

Configuração

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

<?php

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

// código de inicialização
require __DIR__ . '/vendor/autoload.php';

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

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

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

// mais código

Flight::start();

Configuração Adicional

Dados de 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 o Tracy e ele os exibirá automaticamente para você. Passe-o 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 senão o Tracy não consegue 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 Latte ao Tracy
    $latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);
});

if(Debugger::$showBar === true) {
    // Isso precisa ser falso senão o Tracy não consegue 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 active record mapeia uma entidade de banco de dados para um objeto PHP. Falando simplesmente, se você tem uma tabela de usuários em seu banco de dados, você pode "traduzir" uma linha nessa tabela para uma classe User e um objeto $user em seu código. Veja o 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 recomendável 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($database_connection)
    {
        // você pode configurá-lo desta forma
        parent::__construct($database_connection, 'users');
        // ou desta forma
        parent::__construct($database_connection, null, [ 'table' => 'users']);
    }
}

Agora veja a mágica acontecer!

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

// para mysql
$database_connection = new PDO('mysql:host=localhost;dbname=test_db&charset=utf8bm4', 'username', 'password');

// ou mysqli
$database_connection = new mysqli('localhost', 'username', 'password', 'test_db');
// ou mysqli com criação não baseada em objeto
$database_connection = mysqli_connect('localhost', 'username', 'password', 'test_db');

$user = new User($database_connection);
$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!!!');
$user->insert();
// não é possível usar $user->save() aqui ou 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 o retorna.
echo $user->name; // 'Bobby Tables'

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

$users = $user->findAll();

E com uma condição específica?

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

Veja como isso é divertido? Vamos instalar 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 Flight PHP Framework. Completamente com você.

Independente

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

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

$User = new User($pdo_connection);

Flight PHP Framework

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

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

// 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 o atribui ao objeto atual. Se você passar um $id de algum tipo, ele realizará uma consulta na chave primária com esse valor. Se nada for passado, ele simplesmente encontrará o primeiro registro na tabela.

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

// encontrar um registro com algumas condições antecipadas
$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 recuperado (buscado no banco de dados).

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

insert(): boolean|ActiveRecord

Insere o registro atual no banco de dados.

$user = new User($pdo_connection);
$user->name = 'demo';
$user->password = md5('demo');
$user->insert();

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($pdo_connection);
$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 forem definidas, instanciadas e tiverem dados 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 "dirty" se referem aos dados que foram alterados em um registro.

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

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

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

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

$user->dirty([ 'name' => 'algo', 'password' => password_hash('uma senha diferente') ]);
$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('uma senha diferente') ]);
$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(); // verdadeiro

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, ele 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($pdo_connection);

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

getBuiltSql(): string (v0.4.1)

Depois de rodar 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 $campo1 [, string $campo2 ... ])

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

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

from(string $tabela)

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

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

join(string $nome_tabela, string $condição_join)

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

$user->join('contacts', 'contacts.user_id = users.id')->find();

where(string $condições_where)

Você pode definir algumas condições where personalizadas (você não pode definir parâmetros nesta declaraçã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 "SQL injection attacks php" e você encontrará muitos artigos sobre esse assunto. A maneira correta de lidar com isso com esta biblioteca é, em vez deste 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 possui $pdo->quote($var) para escapar isso para você. Somente após usar quote() você pode usá-lo em uma instrução where().

group(string $grupo_por_declaração)/groupBy(string $grupo_por_declaração)

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 int for dado, ele será offset, limit igual ao 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->muitos e um->um entre tabelas. Isso requer uma configuração um pouco mais detalhada na classe antecipadamente.

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


protected array $relations = [
    // você pode nomear a chave como quiser. O nome do ActiveRecord provavelmente é bom. Ex: user, contact, client
    'user' => [
        // obrigatório
        // self::HAS_MANY, self::HAS_ONE, self::BELONGS_TO
        self::HAS_ONE, // este é o tipo de relacionamento

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

        // necessário
        // dependendo do tipo de relacionamento
        // self::HAS_ONE = a chave estrangeira que faz referência à junção
        // self::HAS_MANY = a chave estrangeira que faz referência à junção
        // self::BELONGS_TO = a chave local que faz referência à associação
        'local_or_foreign_key',
        // só para avisar, isso também se junta à chave primária do modelo "outro"

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

        // opcional
        'back_reference_name' // isso é se você quiser referenciar esse relacionamento de volta a si mesmo Ex: $user->contact->user;
    ];
]
class User extends ActiveRecord{
    protected array $relations = [
        'contacts' => [ self::HAS_MANY, Contact::class, 'user_id' ],
        'contact' => [ self::HAS_ONE, Contact::class, 'user_id' ],
    ];

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }
}

class Contact extends ActiveRecord{
    protected array $relations = [
        'user' => [ self::BELONGS_TO, User::class, 'user_id' ],
        'user_with_backref' => [ self::BELONGS_TO, User::class, 'user_id', [], 'contact' ],
    ];
    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'contacts');
    }
}

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

$user = new User($pdo_connection);

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

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

// ou podemos fazer o contrário.
$contact = new Contact();

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

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

Bem 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 apenas anexar ao objeto e depois passar para um modelo, por exemplo.

setCustomData(string $field, mixed $value)

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

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

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

echo $user->page_view_count;

Eventos

Outra funcionalidade super incrível sobre esta biblioteca é sobre eventos. Os eventos são acionados em momentos específicos com base em certos métodos que você chama. Eles são muito, muito úteis para configurar dados automaticamente para você.

onConstruct(ActiveRecord $ActiveRecord, array &config)

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

// index.php or bootstrap.php```markdown
# ActiveRecord do Flight

Um active record está mapeando uma entidade de banco de dados para um objeto PHP. Falando claramente, se você tiver uma tabela de usuários em seu banco de dados, você pode "traduzir" uma linha nessa tabela para uma classe `User` e um objeto `$user` em sua base de código. Veja o [exemplo básico](#exemplo-básico).

## Exemplo Básico

Vamos assumir que você tenha a seguinte tabela:

```sql
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 recomendável 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($database_connection)
    {
        // você pode configurá-lo desta forma
        parent::__construct($database_connection, 'users');
        // ou desta forma
        parent::__construct($database_connection, null, [ 'table' => 'users']);
    }
}

Agora veja a mágica acontecer!

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

// para mysql
$database_connection = new PDO('mysql:host=localhost;dbname=test_db&charset=utf8bm4', 'username', 'password');

// ou mysqli
$database_connection = new mysqli('localhost', 'username', 'password', 'test_db');
// ou mysqli com criação não baseada em objeto
$database_connection = mysqli_connect('localhost', 'username', 'password', 'test_db');

$user = new User($database_connection);
$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!!!');
$user->insert();
// não é possível usar $user->save() aqui ou 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 o retorna.
echo $user->name; // 'Bobby Tables'

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

$users = $user->findAll();

E com uma condição específica?

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

Veja como isso é divertido? Vamos instalar 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 Flight PHP Framework. Completamente com você.

Independente

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

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

$User = new User($pdo_connection);

Flight PHP Framework

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

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

// 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 o atribui ao objeto atual. Se você passar um $id de algum tipo, ele realizará uma consulta na chave primária com esse valor. Se nada for passado, ele simplesmente encontrará o primeiro registro na tabela.

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

// encontrar um registro com algumas condições antecipadas
$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 recuperado (buscado no banco de dados).

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

insert(): boolean|ActiveRecord

Insere o registro atual no banco de dados.

$user = new User($pdo_connection);
$user->name = 'demo';
$user->password = md5('demo');
$user->insert();

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($pdo_connection);
$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 forem definidas, instanciadas e tiverem dados 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();

...

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

Plug-ins Incríveis

O Flight é incrivelmente extensível. Existem vários plug-ins 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 várias bibliotecas de cache que podem ser utilizadas com o Flight.

Cookies

Os 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.

Debugging

A depuração é crucial quando você está desenvolvendo em seu ambiente local. Existem alguns plug-ins que podem elevar 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 envoltórios para escrever consultas e algumas são ORMs completos.

Criptografia

A criptografia é crucial para qualquer aplicação que armazena dados sensíveis. Criptografar e descriptografar os dados não é muito 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 enviá-la para o seu repositório de código.

Sessão

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

Modelagem

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

Contribuindo

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

Examples

Precisa de um início rápido?

Visite o repositório flightphp/skeleton para começar! Este é um projeto que inclui um arquivo de página única contendo tudo o que você precisa para executar seu aplicativo. Também inclui um exemplo mais completo com controladores e visualizações.

Precisa de Inspiração?

Embora esses exemplos não sejam oficialmente patrocinados pela Equipe do Flight, 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 para compartilhar, por favor envie uma solicitação de pull request para adicioná-lo a esta lista!