Learn
Aprenda Sobre o Flight
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. Ele é criado com simplicidade em mente e é escrito de uma maneira fácil de entender e usar.
Conceitos Importantes do Framework
Por que um Framework?
Aqui está um curto artigo sobre por que você deve usar um framework. É uma boa ideia entender os benefícios de usar um framework antes de começar a usar um.
Além disso, um excelente tutorial foi criado por @lubiana. Embora não entre em grandes detalhes sobre o Flight especificamente, este guia ajudará você a entender alguns dos principais conceitos relacionados a um framework e por que eles são benéficos de usar. Você pode encontrar o tutorial aqui.
Flight Comparado a Outros Frameworks
Se você está migrando de outro framework como Laravel, Slim, Fat-Free ou Symfony para o Flight, esta página ajudará você a entender as diferenças entre os dois.
Tópicos Centrais
Carregamento Automático
Aprenda como carregar automaticamente suas próprias classes em sua aplicação.
Roteamento
Aprenda como gerenciar rotas para sua aplicação web. Isso também inclui agrupar rotas, parâmetros de rota e middleware.
Middleware
Aprenda como usar middleware para filtrar solicitações e respostas em sua aplicação.
Solicitações
Aprenda como lidar com solicitações e respostas em sua aplicação.
Respostas
Aprenda como enviar respostas para seus usuários.
Eventos
Aprenda como usar o sistema de eventos para adicionar eventos personalizados à sua aplicação.
Modelos HTML
Aprenda como usar o mecanismo de visualização embutido para renderizar seus modelos HTML.
Segurança
Aprenda como proteger sua aplicação de ameaças comuns de segurança.
Configuração
Aprenda como configurar o framework para sua aplicação.
Estendendo o Flight
Aprenda como estender o framework adicionando seus próprios métodos e classes.
Eventos e Filtros
Aprenda como usar o sistema de eventos para adicionar hooks aos seus métodos e métodos internos do framework.
Container de Injeção de Dependência
Aprenda como usar containers de injeção de dependência (DIC) para gerenciar as dependências da sua aplicação.
API do Framework
Saiba mais sobre os métodos principais do framework.
Migrando para v3
A compatibilidade reversa foi, em grande parte, mantida, mas há algumas mudanças das quais você deve estar ciente ao migrar do v2 para o v3.
Solução de Problemas
Existem alguns problemas comuns que você pode encontrar ao usar o Flight. Esta página ajudará você a solucionar esses problemas.
Learn/stopping
Parando
Você pode parar o framework a qualquer momento chamando o método halt
:
Flight::halt();
Você também pode especificar um código de status HTTP
opcional e uma mensagem:
Flight::halt(200, 'Volto em breve...');
Chamar halt
irá descartar qualquer conteúdo de resposta até esse ponto. Se você quiser parar
o framework e exibir a resposta atual, use o método stop
:
Flight::stop();
Learn/errorhandling
Manipulação de Erros
Erros e Exceções
Todos os erros e exceções são interceptados pelo Flight e passados para o método error
.
O comportamento padrão é enviar uma resposta genérica de HTTP 500 Erro Interno do Servidor
com algumas informações de erro.
Você pode substituir esse comportamento para suas próprias necessidades:
Flight::map('error', function (Throwable $error) {
// Lidar com o erro
echo $error->getTraceAsString();
});
Por padrão, os erros não são registrados no servidor web. Você pode habilitar isso alterando a configuração:
Flight::set('flight.log_errors', true);
Não Encontrado
Quando um URL não pode ser encontrado, o Flight chama o método notFound
. O comportamento
padrão é enviar uma resposta de HTTP 404 Não Encontrado
com uma mensagem simples.
Você pode substituir esse comportamento para suas próprias necessidades:
Flight::map('notFound', function () {
// Lidar com não encontrado
});
Learn/flight_vs_laravel
Comparação Entre Flight e Laravel
O que é Laravel?
Laravel é um framework completo que possui todas as funcionalidades e uma incrível ecossistema focado no desenvolvedor, mas com um custo em termos de desempenho e complexidade. O objetivo do Laravel é fornecer ao desenvolvedor o mais alto nível de produtividade e tornar as tarefas comuns fáceis. Laravel é uma ótima escolha para desenvolvedores que desejam construir aplicações web empresariais completas. Isso vem com alguns compromissos, especificamente em termos de desempenho e complexidade. Aprender o básico do Laravel pode ser fácil, mas ganhar proficiência no framework pode levar algum tempo.
Também existem tantos módulos do Laravel que os desenvolvedores frequentemente sentem que a única maneira de solucionar problemas é através desses módulos, quando na verdade você poderia usar apenas outra biblioteca ou escrever seu próprio código.
Prós Comparados ao Flight
- Laravel possui um amplo ecossistema de desenvolvedores e módulos que podem ser usados para solucionar problemas comuns.
- Laravel possui um ORM completo que pode ser utilizado para interagir com seu banco de dados.
- Laravel tem uma quantidade incrível de documentação e tutoriais que podem ser utilizados para aprender o framework.
- Laravel possui um sistema de autenticação integrado que pode ser usado para proteger sua aplicação.
- Laravel possui podcasts, conferências, reuniões, vídeos e outros recursos que podem ser utilizados para aprender o framework.
- Laravel é direcionado para um desenvolvedor experiente que deseja construir uma aplicação web empresarial completa.
Contras Comparados ao Flight
- Laravel tem muito mais acontecendo sob o capô do que o Flight. Isso tem um custo dramático em termos de desempenho. Consulte os benchmarks do TechEmpower para mais informações.
- Flight é direcionado para um desenvolvedor que deseja construir uma aplicação web leve, rápida e fácil de usar.
- Flight é voltado para a simplicidade e facilidade de uso.
- Uma das características principais do Flight é fazer o seu melhor para manter a compatibilidade com versões anteriores. Laravel causa muita frustração entre versões principais.
- Flight é destinado a desenvolvedores que estão entrando no mundo dos frameworks pela primeira vez.
- Flight não possui dependências, enquanto o Laravel possui uma quantidade atroz de dependências.
- Flight também pode lidar com aplicações de nível empresarial, mas não possui tanto código de boilerplate quanto o Laravel. Também exigirá mais disciplina por parte do desenvolvedor para manter as coisas organizadas e bem estruturadas.
- O Flight dá ao desenvolvedor mais controle sobre a aplicação, enquanto o Laravel possui muita magia nos bastidores que pode ser frustrante.
Learn/migrating_to_v3
Migração para v3
A compatibilidade com versões anteriores foi em grande parte mantida, mas há algumas mudanças das quais você deve estar ciente ao fazer a migração da v2 para a v3.
Comportamento do Buffer de Saída (3.5.0)
O buffering de saída é o processo pelo qual a saída gerada por um script PHP é armazenada em um buffer (interno ao PHP) antes de ser enviada ao cliente. Isso permite que você modifique a saída antes de enviá-la ao cliente.
Em uma aplicação MVC, o Controlador é o "gerente" e ele gerencia o que a visualização faz. Ter saída gerada fora do controlador (ou no caso do Flight, às vezes em uma função anônima) quebra o padrão MVC. Essa 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 buffering de saída era tratado de uma maneira em que não estava consistentemente fechando seu próprio buffer de saída, o que tornava os testes unitários e streaming mais difíceis. Para a maioria dos usuários, essa mudança pode não afetá-lo de fato. No entanto, se você estiver dando um echo no conteúdo fora de callables e controladores (por exemplo, em um hook), provavelmente terá problemas. Dar echo no conteúdo em hooks e antes do framework realmente executar pode ter funcionado no passado, mas não funcionará mais para frente.
Onde você pode ter problemas
// index.php
require 'vendor/autoload.php';
// apenas um exemplo
define('START_TIME', microtime(true));
function hello() {
echo 'Olá Mundo';
}
Flight::map('hello', 'hello');
Flight::after('hello', function(){
// isso na verdade está bem
echo '<p>Esta frase de Olá Mundo foi trazida para você pela letra "H"</p>';
});
Flight::before('start', function(){
// coisas como esta causarão um erro
echo '<html><head><title>Minha Página</title></head><body>';
});
Flight::route('/', function(){
// isso na verdade está ok
echo 'Olá Mundo';
// Isso também deve estar ok
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 v2
Ainda é possível manter o seu código antigo exatamente como está sem a necessidade de uma reescrita para fazê-lo funcionar com a v3? Sim, é possível! Você pode ativar o comportamento de renderização v2 definindo a opção de configuração flight.v2.output_buffering
como true
. Isso permitirá que você continue usando o antigo comportamento de renderização, mas é recomendado corrigi-lo para o futuro. 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á bem
echo '<html><head><title>Minha Página</title></head><body>';
});
// mais código
Mudanças no Dispatcher (3.7.0)
Se você estava chamando diretamente métodos estáticos para Dispatcher
como Dispatcher::invokeMethod()
, Dispatcher::execute()
, etc., você precisará atualizar seu código para não chamar diretamente esses métodos. Dispatcher
foi convertido para ser mais orientado a objeto, de modo que Contêineres de Injeção de Dependência possam ser usados de forma mais simples. Se você precisar invocar um método semelhante ao que o Dispatcher fazia, você pode usar manualmente algo como $result = $class->$method(...$params);
ou call_user_func_array()
em vez disso.
Mudanças em halt()
stop()
redirect()
e error()
(3.10.0)
O comportamento padrão antes da versão 3.10.0 era limpar tanto os cabeçalhos quanto o corpo da resposta. Isso foi alterado para limpar apenas o corpo da resposta. Se você precisar limpar também os cabeçalhos, você pode usar Flight::response()->clear()
.
Learn/configuration
Configuração
Você pode personalizar determinados comportamentos do Flight definindo valores de configuração através do método set
.
Flight::set('flight.log_errors', true);
Configurações Disponíveis
A seguir está uma lista de todas as configurações disponíveis:
- flight.base_url
?string
- Substituir a URL base da solicitação. (padrão: null) - flight.case_sensitive
bool
- Correspondência com distinção entre maiúsculas e minúsculas para URLs. (padrão: false) - flight.handle_errors
bool
- Permitir que o Flight gerencie todos os erros internamente. (padrão: true) - flight.log_errors
bool
- Registrar erros no arquivo de log de erros do servidor web. (padrão: false) - flight.views.path
string
- Diretório contendo arquivos de modelo de visualização. (padrão: ./views) - flight.views.extension
string
- Extensão do arquivo de modelo de visualização. (padrão: .php) - flight.content_length
bool
- Definir o cabeçalhoContent-Length
. (padrão: true) - flight.v2.output_buffering
bool
- Usar o antigo buffer de saída. Consulte migrando para o v3. (padrão: false)
Configuração do Carregador
Existe adicionalmente uma outra configuração para o carregador. Isso permitirá
que você carregue classes com _
no nome da classe.
// Ativar o carregamento de classe com underscores
// Por padrão é verdadeiro
Loader::$v2ClassLoading = false;
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 ver 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 utiliza 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 HTTP 500 Erro interno do servidor
com algumas informações de erro.
Você pode substituir esse comportamento conforme suas necessidades:
Flight::map('error', function (Throwable $error) {
// Manipular erro
echo $error->getTraceAsString();
});
Por padrão, os erros não são registrados no servidor web. Você pode ativá-los alterando a configuração:
Flight::set('flight.log_errors', true);
Não Encontrado
Quando uma URL não pode ser encontrada, o Flight chama o método notFound
.
O comportamento padrão é enviar uma resposta de HTTP 404 Não encontrado
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 é um assunto importante quando se trata de aplicativos web. Você quer ter certeza de que seu aplicativo é seguro e que os dados dos seus usuários estão salvos. Flight oferece uma série de recursos para ajudá-lo a proteger seus aplicativos web.
Cabeçalhos
Os cabeçalhos HTTP são uma das maneiras mais fáceis de proteger seus aplicativos web. Você pode usar cabeçalhos para prevenir clickjacking, XSS e outros ataques. Existem várias maneiras de adicionar esses cabeçalhos ao seu aplicativo.
Dois ótimos sites para verificar a segurança dos seus cabeçalhos são securityheaders.com e observatory.mozilla.org.
Adicionar Manualmente
Você pode adicionar esses cabeçalhos manualmente usando o método header
no objeto Flight\Response
.
// Defina o cabeçalho X-Frame-Options para prevenir clickjacking
Flight::response()->header('X-Frame-Options', 'SAMEORIGIN');
// Defina o cabeçalho Content-Security-Policy para prevenir XSS
// Nota: este cabeçalho pode ficar muito complexo, então você vai querer
// consultar exemplos na internet para o seu aplicativo
Flight::response()->header("Content-Security-Policy", "default-src 'self'");
// Defina o cabeçalho X-XSS-Protection para prevenir XSS
Flight::response()->header('X-XSS-Protection', '1; mode=block');
// Defina o cabeçalho X-Content-Type-Options para prevenir sniffing MIME
Flight::response()->header('X-Content-Type-Options', 'nosniff');
// Defina o cabeçalho Referrer-Policy para controlar quanta informação de referenciador é enviada
Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');
// Defina o cabeçalho Strict-Transport-Security para forçar HTTPS
Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
// Defina o cabeçalho Permissions-Policy para controlar quais recursos e APIs podem ser usados
Flight::response()->header('Permissions-Policy', 'geolocation=()');
Esses podem ser adicionados no topo dos seus arquivos bootstrap.php
ou index.php
.
Adicionar como um Filtro
Você também pode adicioná-los em um filtro/gatilho 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 Middleware
Você também pode adicioná-los como uma classe de middleware. Esta é uma boa maneira de manter seu código limpo e organizado.
// app/middleware/SecurityHeadersMiddleware.php
namespace app\middleware;
class SecurityHeadersMiddleware
{
public function before(array $params): void
{
Flight::response()->header('X-Frame-Options', 'SAMEORIGIN');
Flight::response()->header("Content-Security-Policy", "default-src 'self'");
Flight::response()->header('X-XSS-Protection', '1; mode=block');
Flight::response()->header('X-Content-Type-Options', 'nosniff');
Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');
Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
Flight::response()->header('Permissions-Policy', 'geolocation=()');
}
}
// index.php ou onde quer que você tenha suas rotas
// FYI, este grupo de string vazia atua como um middleware global para
// todas as rotas. Claro que você poderia fazer a mesma coisa 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)
O Cross Site Request Forgery (CSRF) é um tipo de ataque onde um site malicioso pode fazer o navegador de um usuário enviar uma solicitação para seu site. Isso pode ser usado para realizar ações em seu site sem o conhecimento do usuário. O Flight não fornece um mecanismo de proteção CSRF embutido, mas você pode facilmente implementar o seu usando middleware.
Configuração
Primeiro, você precisa gerar um token CSRF e armazená-lo na sessão do usuário. Você pode então usar esse token em seus formulários e verificá-lo quando o formulário for enviado.
// Gere um token CSRF e armazene-o na sessão do usuário
// (supondo que você tenha criado um objeto de sessão e o anexado ao Flight)
// consulte a documentação da sessão para mais informações
Flight::register('session', \Ghostff\Session\Session::class);
// 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 templates Latte.
// Defina uma função personalizada para exibir o token CSRF
// Nota: A View foi configurada com Latte como o 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 . '">');
});
E agora, em seus templates 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, certo?
Verificar o Token CSRF
Você pode verificar o token CSRF usando filtros de evento:
// Este middleware verifica se a solicitação é uma solicitação POST e, se for, verifica se o token CSRF é válido
Flight::before('start', function() {
if(Flight::request()->method == 'POST') {
// capture 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 para uma resposta JSON
Flight::jsonHalt(['error' => 'Token CSRF inválido'], 403);
}
}
});
Ou você pode usar uma classe de middleware:
// app/middleware/CsrfMiddleware.php
namespace app\middleware;
class CsrfMiddleware
{
public function before(array $params): void
{
if(Flight::request()->method == 'POST') {
$token = Flight::request()->data->csrf_token;
if($token !== Flight::session()->get('csrf_token')) {
Flight::halt(403, 'Token CSRF inválido');
}
}
}
}
// index.php ou onde quer que você tenha suas rotas
Flight::group('', function(Router $router) {
$router->get('/users', [ 'UserController', 'getUsers' ]);
// mais rotas
}, [ new CsrfMiddleware() ]);
Cross Site Scripting (XSS)
O Cross Site Scripting (XSS) é um tipo de ataque onde um site malicioso pode injetar código em seu site. A maioria dessas oportunidades vem de valores de formulário que seus usuários finais preencherão. Você nunca deve confiar na saída de seus usuários! Sempre assuma que todos eles são os melhores hackers do mundo. Eles podem injetar JavaScript ou HTML malicioso em sua página. Este código pode ser usado para roubar informações de seus usuários ou realizar ações em seu site. Usando a classe de visualização do Flight, você pode facilmente escapar da saída para prevenir ataques XSS.
// Vamos supor que o usuário é esperto e tenta usar isto como seu nome
$name = '<script>alert("XSS")</script>';
// Isso vai escapar a saída
Flight::view()->set('name', $name);
// Isso vai exibir: <script>alert("XSS")</script>
// Se você usar algo como Latte registrado como sua classe de visualização, isso também escapará automaticamente.
Flight::view()->render('template', ['name' => $name]);
Injeção de SQL
A injeção de SQL é um tipo de ataque onde um usuário malicioso pode injetar código SQL em seu banco de dados. Isso pode ser usado para roubar informações
de seu banco de dados ou realizar ações em seu banco de dados. Novamente, você deve nunca confiar na entrada de seus usuários! Sempre assuma que eles estão
em busca de sangue. Você pode usar instruções preparadas em seus objetos PDO
para prevenir a injeção de SQL.
// Supondo que você tenha Flight::db() registrado como seu objeto PDO
$statement = Flight::db()->prepare('SELECT * FROM users WHERE username = :username');
$statement->execute([':username' => $username]);
$users = $statement->fetchAll();
// Se você usar a classe PdoWrapper, isso pode ser facilmente feito em uma linha
$users = Flight::db()->fetchAll('SELECT * FROM users WHERE username = :username', [ 'username' => $username ]);
// Você pode fazer a mesma coisa com um objeto PDO com ? espaços reservados
$statement = Flight::db()->fetchAll('SELECT * FROM users WHERE username = ?', [ $username ]);
// Apenas prometa que você nunca NUNCA fará algo como isso...
$users = Flight::db()->fetchAll("SELECT * FROM users WHERE username = '{$username}' LIMIT 5");
// porque e se $username = "' OR 1=1; -- ";
// Depois que a consulta é construída, ela fica assim
// SELECT * FROM users WHERE username = '' OR 1=1; -- LIMIT 5
// Parece estranho, mas é uma consulta válida que funcionará. Na verdade,
// é um ataque muito comum de injeção de SQL que retornará todos os usuários.
CORS
O Cross-Origin Resource Sharing (CORS) é um mecanismo que permite que muitos recursos (por exemplo, fontes, JavaScript, etc.) em uma página web sejam
solicitados de outro domínio fora do domínio de onde o recurso se originou. O Flight não tem funcionalidade embutida,
mas isso pode ser facilmente tratado com um gancho para ser executado antes que o método Flight::start()
seja chamado.
// app/utils/CorsUtil.php
namespace app\utils;
class CorsUtil
{
public function set(array $params): void
{
$request = Flight::request();
$response = Flight::response();
if ($request->getVar('HTTP_ORIGIN') !== '') {
$this->allowOrigins();
$response->header('Access-Control-Allow-Credentials', 'true');
$response->header('Access-Control-Max-Age', '86400');
}
if ($request->method === 'OPTIONS') {
if ($request->getVar('HTTP_ACCESS_CONTROL_REQUEST_METHOD') !== '') {
$response->header(
'Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD'
);
}
if ($request->getVar('HTTP_ACCESS_CONTROL_REQUEST_HEADERS') !== '') {
$response->header(
"Access-Control-Allow-Headers",
$request->getVar('HTTP_ACCESS_CONTROL_REQUEST_HEADERS')
);
}
$response->status(200);
$response->send();
exit;
}
}
private function allowOrigins(): void
{
// personalize seus hosts permitidos aqui.
$allowed = [
'capacitor://localhost',
'ionic://localhost',
'http://localhost',
'http://localhost:4200',
'http://localhost:8080',
'http://localhost:8100',
];
$request = Flight::request();
if (in_array($request->getVar('HTTP_ORIGIN'), $allowed, true) === true) {
$response = Flight::response();
$response->header("Access-Control-Allow-Origin", $request->getVar('HTTP_ORIGIN'));
}
}
}
// index.php ou onde quer que você tenha suas rotas
$CorsUtil = new CorsUtil();
// Isso precisa ser executado antes que o start seja executado.
Flight::before('start', [ $CorsUtil, 'setupCors' ]);
Tratamento de Erros
Oculte detalhes de erro sensíveis na produção para evitar vazamento de informações para atacantes.
// No seu bootstrap.php ou index.php
// no flightphp/skeleton, isso está em app/config/config.php
$environment = ENVIRONMENT;
if ($environment === 'production') {
ini_set('display_errors', 0); // Desabilitar a exibição de erros
ini_set('log_errors', 1); // Registrar erros em vez disso
ini_set('error_log', '/path/to/error.log');
}
// Nas suas rotas ou controladores
// Use Flight::halt() para respostas de erro controladas
Flight::halt(403, 'Acesso negado');
Sanitização de Entradas
Nunca confie na entrada do usuário. Sanitizá-la antes de processar para evitar que dados maliciosos sejam inseridos.
// Vamos supor uma solicitação $_POST com $_POST['input'] e $_POST['email']
// Sanitizar uma entrada de string
$clean_input = filter_var(Flight::request()->data->input, FILTER_SANITIZE_STRING);
// Sanitizar um email
$clean_email = filter_var(Flight::request()->data->email, FILTER_SANITIZE_EMAIL);
Hashing de Senhas
Armazene senhas de forma segura e verifique-as com segurança usando as funções embutidas do PHP.
$password = Flight::request()->data->password;
// Hash uma senha ao armazená-la (por exemplo, durante o registro)
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
// Verifique uma senha (por exemplo, durante o login)
if (password_verify($password, $stored_hash)) {
// A senha corresponde
}
Limitação de Taxa
Proteja-se contra ataques de força bruta limitando as taxas de solicitação com um cache.
// Supondo que você tenha flightphp/cache instalado e registrado
// Usando flightphp/cache em um middleware
Flight::before('start', function() {
$cache = Flight::cache();
$ip = Flight::request()->ip;
$key = "rate_limit_{$ip}";
$attempts = (int) $cache->retrieve($key);
if ($attempts >= 10) {
Flight::halt(429, 'Muitas solicitações');
}
$cache->set($key, $attempts + 1, 60); // Redefinir após 60 segundos
});
Conclusão
A segurança é um assunto importante e é fundamental garantir que seus aplicativos web sejam seguros. O Flight oferece uma série de recursos para ajudá-lo a proteger seus aplicativos web, mas é importante estar sempre alerta e garantir que você está fazendo tudo que pode para manter os dados dos seus usuários seguros. Sempre assuma o pior e nunca confie na entrada dos seus usuários. Sempre escape a saída e use instruções preparadas para evitar a injeção de SQL. Sempre use middleware para proteger suas rotas contra ataques CSRF e CORS. Se você fizer tudo isso, estará no caminho certo para construir aplicativos web seguros.
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
Roteamento
Nota: Quer entender mais sobre roteamento? Confira a página "por que um framework?" para uma explicação mais detalhada.
O roteamento básico no Flight é feito combinando um padrão de URL com uma função de retorno de chamada ou um array de uma classe e método.
Flight::route('/', function(){
echo 'olá mundo!';
});
As rotas são combinadas na ordem em que são definidas. A primeira rota que corresponder a uma solicitação será invocada.
Retornos de chamada/Funções
O retorno de chamada pode ser qualquer objeto que seja chamável. Então você pode usar uma função regular:
function hello() {
echo 'olá mundo!';
}
Flight::route('/', 'hello');
Classes
Você também pode usar um método estático de uma classe:
class Greeting {
public static function hello() {
echo 'olá mundo!';
}
}
Flight::route('/', [ 'Greeting','hello' ]);
Ou criando um objeto primeiro e depois chamando o método:
// Greeting.php
class Greeting
{
public function __construct() {
$this->name = 'John Doe';
}
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' ]);
// Além disso, você pode usar esta sintaxe mais curta
Flight::route('/', 'Greeting->hello');
// ou
Flight::route('/', Greeting::class.'->hello');
Injeção de Dependência via DIC (Container de Injeção de Dependência)
Se você quiser usar injeção de dependência via um container (PSR-11, PHP-DI, Dice, etc), o único tipo de rotas onde isso está disponível é criar o objeto você mesmo e usar o container para criar seu objeto ou você pode usar strings para definir a classe e método a serem chamados. Você pode acessar 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 container com quaisquer parâmetros que você precisar
// 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',
'senha'
]
]);
// Registre o manipulador do container
Flight::registerContainerHandler(function($class, $params) use ($dice) {
return $dice->create($class, $params);
});
// Rotas como normal
Flight::route('/hello/@id', [ 'Greeting', 'hello' ]);
// ou
Flight::route('/hello/@id', 'Greeting->hello');
// ou
Flight::route('/hello/@id', 'Greeting::hello');
Flight::start();
Roteamento de Método
Por padrão, os padrões de rota são combinados com 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 múltiplos métodos para um único retorno de chamada usando um delimitador |
:
Flight::route('GET|POST /', function () {
echo 'Recebi uma solicitação de 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 esse 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 de chamada. Isso é mais para a legibilidade da rota do que qualquer outra coisa. Por favor, veja a seção abaixo sobre a importante caveat.
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: Grupos regex correspondentes
()
com parâmetros posicionais não são suportados. :'(
Importante Caveat
Embora no exemplo acima, pareça que @name
está diretamente ligado à variável $name
, não está. A ordem dos parâmetros na função de retorno de chamada é o que determina o que é passado para ela. Portanto, se você mudar a ordem dos parâmetros na função de retorno de chamada, as variáveis também serão trocadas. Aqui está um exemplo:
Flight::route('/@name/@id', function (string $id, string $name) {
echo "olá, $name ($id)!";
});
E se você fosse para a seguinte URL: /bob/123
, a saída seria olá, 123 (bob)!
.
Por favor, tenha cuidado ao configurar suas rotas e suas funções de retorno de chamada.
Parâmetros Opcionais
Você pode especificar parâmetros nomeados que são opcionais para correspondência envolvendo segmentos entre parênteses.
Flight::route(
'/blog(/@year(/@month(/@day)))',
function(?string $year, ?string $month, ?string $day) {
// Isso corresponderá às seguintes URLs:
// /blog/2012/12/10
// /blog/2012/12
// /blog/2012
// /blog
}
);
Quaisquer parâmetros opcionais que não corresponderem serão passados como NULL
.
Coringas
A correspondência é feita apenas em segmentos de URL individuais. Se você quiser combinar múltiplos
segmentos, pode usar o coringa *
.
Flight::route('/blog/*', function () {
// Isso corresponderá a /blog/2000/02/01
});
Para direcionar todas as solicitações para um único retorno de chamada, você pode fazer:
Flight::route('*', function () {
// Faça algo
});
Passando
Você pode passar a execução para a próxima rota correspondente retornando true
de
sua função de retorno de chamada.
Flight::route('/user/@name', function (string $name) {
// Verifique alguma condição
if ($name !== "Bob") {
// Continue para a próxima rota
return true;
}
});
Flight::route('/user/*', function () {
// Isso será chamado
});
Alias para Rota
Você pode atribuir um alias a uma rota, para que a URL possa ser gerada dinamicamente mais tarde em seu código (como um modelo, por exemplo).
Flight::route('/users/@id', function($id) { echo 'usuário:'.$id; }, false, 'user_view');
// depois em algum lugar no código
Flight::getUrl('user_view', [ 'id' => 5 ]); // retornará '/users/5'
Isso é especialmente útil se sua URL mudar. No exemplo acima, suponha que os usuários foram movidos para /admin/users/@id
em vez disso.
Com o alias em prática, você não precisa mudar em nenhum lugar onde você referenciar o alias porque o alias agora retornará /admin/users/5
, como no
exemplo acima.
O alias da rota ainda funciona em grupos também:
Flight::group('/users', function() {
Flight::route('/@id', function($id) { echo 'usuário:'.$id; }, false, 'user_view');
});
// depois em algum lugar no código
Flight::getUrl('user_view', [ 'id' => 5 ]); // retornará '/users/5'
Informações da Rota
Se você quiser inspecionar as informações da rota correspondente, pode solicitar que o objeto de rota
seja passado para seu retorno de chamada passando true
como o 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 de chamada.
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 da URL....se você realmente precisar
$route->pattern;
// Mostra qual middleware está 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ê queira agrupar rotas relacionadas (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ê pode até 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 forma:
$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
});
});
Roteamento de Recursos
Você pode criar um conjunto de rotas para um recurso usando o método resource
. Isso criará
um conjunto de rotas para um recurso que segue as convenções RESTful.
Para criar um recurso, faça o seguinte:
Flight::resource('/users', UsersController::class);
E o que acontecerá nos bastidores é que ele criará as seguintes rotas:
[
'index' => 'GET ',
'create' => 'GET /create',
'store' => 'POST ',
'show' => 'GET /@id',
'edit' => 'GET /@id/edit',
'update' => 'PUT /@id',
'destroy' => 'DELETE /@id'
]
E seu controlador ficará assim:
class UsersController
{
public function index(): void
{
}
public function show(string $id): void
{
}
public function create(): void
{
}
public function store(): void
{
}
public function edit(string $id): void
{
}
public function update(string $id): void
{
}
public function destroy(string $id): void
{
}
}
Nota: Você pode visualizar as novas rotas adicionadas com
runway
executandophp runway routes
.
Personalizando Rotas de Recursos
Existem algumas opções para configurar as rotas de recursos.
Alias Base
Você pode configurar o aliasBase
. Por padrão, o alias é a última parte da URL especificada.
Por exemplo, /users/
resultaria em um aliasBase
de users
. Quando essas rotas são criadas,
os aliases são users.index
, users.create
, etc. Se você quiser mudar o alias, defina o aliasBase
para o valor que deseja.
Flight::resource('/users', UsersController::class, [ 'aliasBase' => 'user' ]);
Somente e Exceto
Você também pode especificar quais rotas deseja criar usando as opções only
e except
.
Flight::resource('/users', UsersController::class, [ 'only' => [ 'index', 'show' ] ]);
Flight::resource('/users', UsersController::class, [ 'except' => [ 'create', 'store', 'edit', 'update', 'destroy' ] ]);
Essas são basicamente opções de listagem e blacklist para que você possa especificar quais rotas deseja criar.
Middleware
Você também pode especificar middleware para ser executado em cada uma das rotas criadas pelo método resource
.
Flight::resource('/users', UsersController::class, [ 'middleware' => [ MyAuthMiddleware::class ] ]);
Streaming
Agora você pode transmitir respostas para o cliente usando o método streamWithHeaders()
.
Isso é útil para enviar arquivos grandes, processos longos em execução ou gerar grandes respostas.
Transmitir uma rota é tratado de forma um pouco diferente de uma rota regular.
Nota: Transmissões de respostas só estão disponíveis se você tiver
flight.v2.output_buffering
definido como falso.
Transmitir com Cabeçalhos Manuais
Você pode transmitir uma resposta para o cliente usando o método stream()
em uma rota. Se você
fizer isso, você deve definir todos os métodos à mão antes de emitir qualquer coisa para o cliente.
Isso é feito com a função header()
do php ou o método Flight::response()->setRealHeader()
.
Flight::route('/@filename', function($filename) {
// obviamente você sanitizaria o caminho e outras coisas.
$fileNameSafe = basename($filename);
// Se você tiver cabeçalhos adicionais para definir aqui após a execução da rota
// você deve defini-los antes que qualquer coisa seja ecoada.
// Todos devem ser uma chamada bruta à função header() ou
// uma chamada para Flight::response()->setRealHeader()
header('Content-Disposition: attachment; filename="'.$fileNameSafe.'"');
// ou
Flight::response()->setRealHeader('Content-Disposition', 'attachment; filename="'.$fileNameSafe.'"');
$fileData = file_get_contents('/some/path/to/files/'.$fileNameSafe);
// Captura de erros e outras coisas
if(empty($fileData)) {
Flight::halt(404, 'Arquivo não encontrado');
}
// defina manualmente o comprimento do conteúdo, se desejar
header('Content-Length: '.filesize($filename));
// Transmita os dados para o cliente
echo $fileData;
// Esta é a linha mágica aqui
})->stream();
Transmitir com Cabeçalhos
Você também pode usar o método streamWithHeaders()
para definir os cabeçalhos antes de começar a transmitir.
Flight::route('/stream-users', function() {
// você pode adicionar quaisquer cabeçalhos adicionais que desejar aqui
// você deve usar header() ou Flight::response()->setRealHeader()
// como você obtém 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 é exigido para enviar os dados ao cliente
ob_flush();
}
echo '}';
// Esta é a forma como você definirá os cabeçalhos antes de começar a transmitir.
})->streamWithHeaders([
'Content-Type' => 'application/json',
'Content-Disposition' => 'attachment; filename="users.json"',
// status code opcional, padrão é 200
'status' => 200
]);
Learn/flight_vs_symfony
Flight vs Symfony
O que é Symfony?
Symfony é um conjunto de componentes PHP reutilizáveis e um framework PHP para projetos web.
A base padrão na qual as melhores aplicações PHP são construídas. Escolha qualquer um dos 50 componentes independentes disponíveis para suas próprias aplicações.
Acelere a criação e manutenção de suas aplicações web PHP. Encerre tarefas repetitivas de codificação e aproveite o poder de controlar seu código.
Prós comparados ao Flight
- Symfony possui um ecossistema enorme de desenvolvedores e módulos que podem ser usados para resolver problemas comuns.
- Symfony possui um ORM completo (Doctrine) que pode ser usado para interagir com seu banco de dados.
- Symfony possui uma grande quantidade de documentação e tutoriais que podem ser usados para aprender o framework.
- Symfony possui podcasts, conferências, reuniões, vídeos e outros recursos que podem ser usados para aprender o framework.
- Symfony é voltado para um desenvolvedor experiente que procura construir um aplicativo web empresarial completo.
Contras comparados ao Flight
- Symfony tem muito mais acontecendo sob o capô do que o Flight. Isso tem um custo drástico em termos de desempenho. Veja os benchmarks TechEmpower para mais informações.
- Flight é voltado para um desenvolvedor que procura construir um aplicativo web leve, rápido e fácil de usar.
- Flight é voltado para simplicidade e facilidade de uso.
- Uma das principais características do Flight é que ele faz o possível para manter a compatibilidade com versões anteriores.
- Flight não possui dependências, enquanto Symfony possui uma série de dependências
- Flight é destinado a desenvolvedores que estão se aventurando no mundo dos frameworks pela primeira vez.
- Flight também pode lidar com aplicações de nível empresarial, mas não possui tantos exemplos e tutoriais quanto Symfony. Também exigirá mais disciplina por parte do desenvolvedor para manter as coisas organizadas e bem estruturadas.
- Flight dá ao desenvolvedor mais controle sobre a aplicação, enquanto o Symfony pode incluir alguma magia nos bastidores.
Learn/flight_vs_another_framework
Comparando Flight com Outro Framework
Se você está migrando de outro framework como Laravel, Slim, Fat-Free, ou Symfony para o Flight, esta página irá ajudá-lo a entender as diferenças entre os dois.
Laravel
Laravel é um framework completo que possui todos os recursos e uma incrível ecossistema focado no desenvolvedor, mas com um custo em desempenho e complexidade.
Veja a comparação entre Laravel e Flight.
Slim
Slim é um micro-framework que é semelhante ao Flight. Ele é projetado para ser leve e fácil de usar, mas pode ser um pouco mais complexo do que o Flight.
Veja a comparação entre Slim e Flight.
Fat-Free
Fat-Free é um framework full-stack em um pacote muito menor. Embora tenha todas as ferramentas necessárias, ele possui uma arquitetura de dados que pode tornar alguns projetos mais complexos do que precisam ser.
Veja a comparação entre Fat-Free e Flight.
Symfony
Symfony é um framework modular de nível empresarial projetado para ser flexível e escalável. Para projetos menores ou desenvolvedores mais novos, Symfony pode ser um pouco avassalador.
Veja a comparação entre Symfony e Flight.
Learn/variables
Variáveis
O Flight permite que você salve variáveis para que possam ser usadas em qualquer lugar de sua aplicação.
// Salve sua variável
Flight::set('id', 123);
// Em outro lugar de sua aplicação
$id = Flight::get('id');
Para verificar se uma variável foi definida, você pode fazer:
if (Flight::has('id')) {
// Faça algo
}
Você pode limpar uma variável fazendo:
// Limpa a variável id
Flight::clear('id');
// Limpa todas as variáveis
Flight::clear();
O Flight também usa variáveis para fins de configuração.
Flight::set('flight.log_errors', true);
Learn/dependency_injection_container
Contentor de Injeção de Dependência
Introdução
O Contentor de Injeção de Dependência (CID) é uma ferramenta poderosa que permite gerenciar as dependências de sua aplicação. É um conceito-chave nos frameworks PHP modernos e é usado para gerenciar a instanciação e configuração de objetos. Alguns exemplos de bibliotecas CID são: Dice, Pimple, PHP-DI e league/container.
Um CID é uma forma sofisticada de dizer que permite criar e gerenciar suas classes em um local centralizado. Isso é útil quando você precisa passar o mesmo objeto para múltiplas classes (como seus controladores). Um exemplo simples pode ajudar a tornar isso mais claro.
Exemplo Básico
A maneira antiga de fazer as coisas poderia parecer assim:
require 'vendor/autoload.php';
// classe para gerenciar usuários no banco de dados
class UserController {
protected PDO $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function view(int $id) {
$stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute(['id' => $id]);
print_r($stmt->fetch());
}
}
$User = new UserController(new PDO('mysql:host=localhost;dbname=test', 'user', 'pass'));
Flight::route('/user/@id', [ $UserController, 'view' ]);
Flight::start();
Você pode ver a partir do código acima que estamos criando um novo objeto PDO
e passando-o
para nossa classe UserController
. Isso é bom para uma aplicação pequena, mas à medida que
a aplicação cresce, você descobrirá que está criando o mesmo objeto PDO
em múltiplos
lugares. É aqui que um CID se torna útil.
Aqui está o mesmo exemplo usando um CID (usando Dice):
require 'vendor/autoload.php';
// mesma classe que acima. Nada mudou
class UserController {
protected PDO $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function view(int $id) {
$stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute(['id' => $id]);
print_r($stmt->fetch());
}
}
// criar um novo contentor
$container = new \Dice\Dice;
// não se esqueça de reatribuí-lo a si mesmo como abaixo!
$container = $container->addRule('PDO', [
// shared significa que o mesmo objeto será retornado toda vez
'shared' => true,
'constructParams' => ['mysql:host=localhost;dbname=test', 'user', 'pass' ]
]);
// Isso registra o manipulador do contentor para que o Flight saiba usá-lo.
Flight::registerContainerHandler(function($class, $params) use ($container) {
return $container->create($class, $params);
});
// agora podemos usar o contentor para criar nosso UserController
Flight::route('/user/@id', [ 'UserController', 'view' ]);
// ou alternativamente você pode definir a rota assim
Flight::route('/user/@id', 'UserController->view');
// ou
Flight::route('/user/@id', 'UserController::view');
Flight::start();
Aposto que você pode estar pensando que houve muito código extra adicionado ao exemplo.
A magia acontece quando você tem outro controlador que precisa do objeto PDO
.
// Se todos os seus controladores têm um construtor que precisa de um objeto PDO
// cada uma das rotas abaixo terá automaticamente ele injetado!!!
Flight::route('/empresa/@id', 'CompanyController->view');
Flight::route('/organizacao/@id', 'OrganizationController->view');
Flight::route('/categoria/@id', 'CategoryController->view');
Flight::route('/configuracoes', 'SettingsController->view');
O benefício adicional de utilizar um CID é que os testes unitários se tornam muito mais fáceis. Você pode criar um objeto falso e passá-lo para sua classe. Isso é um grande benefício ao escrever testes para sua aplicação!
PSR-11
O Flight também pode usar qualquer contentor compatível com o PSR-11. Isso significa que você pode usar qualquer contentor que implemente a interface PSR-11. Aqui está um exemplo usando o contentor PSR-11 da League:
require 'vendor/autoload.php';
// mesma classe UserController que acima
$container = new \League\Container\Container();
$container->add(UserController::class)->addArgument(PdoWrapper::class);
$container->add(PdoWrapper::class)
->addArgument('mysql:host=localhost;dbname=test')
->addArgument('user')
->addArgument('pass');
Flight::registerContainerHandler($container);
Flight::route('/user', [ 'UserController', 'view' ]);
Flight::start();
Embora isso possa ser um pouco mais verboso do que o exemplo anterior com Dice, ainda faz o trabalho com os mesmos benefícios!
Manipulador CID Personalizado
Você também pode criar seu próprio manipulador CID. Isso é útil se você tiver um contentor personalizado que deseja usar que não seja PSR-11 (Dice). Veja o exemplo básico de como fazer isso.
Além disso, existem alguns padrões úteis que facilitarão sua vida ao usar o Flight.
Instância do Motor
Se você estiver usando a instância Engine
em seus controladores/funções intermediárias, aqui está
como você configuraria:
// Em algum lugar do seu arquivo de inicialização
$engine = Flight::app();
$container = new \Dice\Dice;
$container = $container->addRule('*', [
'substitutions' => [
// Aqui é onde você passa a instância
Engine::class => $engine
]
]);
$engine->registerContainerHandler(function($class, $params) use ($container) {
return $container->create($class, $params);
});
// Agora você pode usar a instância do Engine em seus controladores/funções intermediárias
class MeuControlador {
public function __construct(Engine $app) {
$this->app = $app;
}
public function index() {
$this->app->render('index');
}
}
Adicionando Outras Classes
Se você tem outras classes que deseja adicionar ao contentor, com Dice é fácil, pois elas serão automaticamente resolvidas pelo contentor. Aqui está um exemplo:
$container = new \Dice\Dice;
// Se você não precisa injetar nada em sua classe
// você não precisa definir nada!
Flight::registerContainerHandler(function($class, $params) use ($container) {
return $container->create($class, $params);
});
class MinhaClassePersonalizada {
public function parseCoisa() {
return 'coisa';
}
}
class UserController {
protected MyCustomClass $MyCustomClass;
public function __construct(MyCustomClass $MyCustomClass) {
$this->MyCustomClass = $MyCustomClass;
}
public function index() {
echo $this->MyCustomClass->parseThing();
}
}
Flight::route('/user', 'UserController->index');
Learn/middleware
Middleware de Rota
O Flight suporta middleware de rota e de grupo de rotas. O middleware é uma função que é executada antes (ou depois) do retorno da rota. Esta é uma ótima maneira de adicionar verificações de autenticação da API em seu código, ou para validar se o usuário tem permissão para acessar a rota.
Middleware Básico
Aqui está um exemplo básico:
// Se você fornecer apenas uma função anônima, ela será executada antes do retorno da rota.
// não existem 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 ter em mente antes de usá-los:
- As funções do middleware são executadas na ordem em que são adicionadas à rota. A execução é semelhante a como o Framework Slim lida com isso.
- Os "Befores" são executados na ordem adicionada, e os "Afters" são executados na ordem inversa.
- Se sua função de middleware retornar falso, toda a execução será interrompida e um erro 403 Forbidden será lançado. Provavelmente você vai querer lidar com isso de maneira mais graciosa com um
Flight::redirect()
ou algo similar. - Se você precisar de parâmetros de sua rota, eles serão passados em um único array para sua função de middleware. (
function($params) { ... }
oupublic function before($params) {}
). A razão para isso é que você pode estruturar seus parâmetros em grupos e em alguns desses grupos, seus parâmetros podem realmente aparecer em uma ordem diferente que quebraria a função de middleware ao se referir ao parâmetro errado. Dessa forma, você pode acessá-los pelo nome em vez da posição. - Se você passar apenas o nome do middleware, ele será executado automaticamente pelo contêiner de injeção de dependência e o middleware será executado com os parâmetros necessários. Se você não tiver um contêiner de injeção de dependência registrado, ele passará a instância
flight\Engine
para o__construct()
.
Classes de Middleware
O middleware pode ser registrado como uma classe também. Se você precisa da funcionalidade "after", você deve usar uma classe.
class MeuMiddleware {
public function before($params) {
echo 'Middleware primeiro!';
}
public function after($params) {
echo 'Middleware último!';
}
}
$MeuMiddleware = new MeuMiddleware();
Flight::route('/caminho', function() { echo ' Aqui estou! '; })->addMiddleware($MeuMiddleware); // também ->addMiddleware([ $MeuMiddleware, $MeuMiddleware2 ]);
Flight::start();
// Isso irá exibir "Middleware primeiro! Aqui estou! Middleware último!"
Lidando com Erros de Middleware
Digamos que você tenha um middleware de autenticação e deseje redirecionar o usuário para uma página de login se ele não estiver autenticado. Você tem algumas opções à sua disposição:
- Você pode retornar falso da função do middleware e o Flight retornará automaticamente um erro 403 Forbidden, mas sem personalização.
- Você pode redirecionar o usuário para uma página de login usando
Flight::redirect()
. - Você pode criar um erro personalizado dentro do middleware e interromper a execução da rota.
Exemplo Básico
Aqui está um exemplo simples de retorno falso:
class MeuMiddleware {
public function before($params) {
if (isset($_SESSION['user']) === false) {
return false;
}
// como é verdadeiro, tudo continua normalmente
}
}
Exemplo de Redirecionamento
Aqui está um exemplo de redirecionamento do usuário para uma página de login:
class MeuMiddleware {
public function before($params) {
if (isset($_SESSION['user']) === false) {
Flight::redirect('/login');
exit;
}
}
}
Exemplo de Erro Personalizado
Digamos que você precise lançar um erro JSON porque está construindo uma API. Você pode fazer isso assim:
class MeuMiddleware {
public function before($params) {
$autorizacao = Flight::request()->headers['Authorization'];
if(empty($autorizacao)) {
Flight::jsonHalt(['error' => 'Você deve estar logado para acessar esta página.'], 403);
// ou
Flight::json(['error' => 'Você deve estar logado para acessar esta página.'], 403);
exit;
// ou
Flight::halt(403, json_encode(['error' => 'Você deve estar logado para acessar esta página.']);
}
}
}
Agrupando Middleware
Você pode adicionar um grupo de rota e, em seguida, cada rota nesse grupo terá o mesmo middleware também. Isso é útil se você precisar agrupar um monte de rotas, digamos por um middleware de Autenticação para verificar a chave da API no cabeçalho.
// adicionado no final do método de grupo
Flight::group('/api', function() {
// Esta rota com aparência "vazia" na verdade corresponderá a /api
Flight::route('', function() { echo 'api'; }, false, 'api');
// Esta corresponderá a /api/usuarios
Flight::route('/usuarios', function() { echo 'usuários'; }, false, 'usuários');
// Esta corresponderá a /api/usuarios/1234
Flight::route('/usuarios/@id', function($id) { echo 'usuário:'.$id; }, false, 'visualização_de_usuario');
}, [ new MiddlewaredeAutenticacaoApi() ]);
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() {
// Isto ainda é /usuários
Flight::route('/usuários', function() { echo 'usuários'; }, false, 'usuários');
// E isto ainda é /usuários/1234
Flight::route('/usuários/@id', function($id) { echo 'usuário:'.$id; }, false, 'visualização_de_usuario');
}, [ new MiddlewaredeAutenticacaoApi() ]);
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
Requisições
Flight encapsula a requisição HTTP em um único objeto, que pode ser acessado fazendo:
$request = Flight::request();
Casos de Uso Típicos
Quando você está trabalhando com uma requisição em uma aplicação web, tipicamente você vai
querer extrair um cabeçalho, ou um parâmetro $_GET
ou $_POST
, ou talvez
até mesmo o corpo bruto da requisição. Flight fornece uma interface simples para fazer todas essas coisas.
Aqui está um exemplo obtendo um parâmetro de string de consulta:
Flight::route('/search', function(){
$keyword = Flight::request()->query['keyword'];
echo "Você está pesquisando por: $keyword";
// consultar um banco de dados ou algo assim com o $keyword
});
Aqui está um exemplo de talvez um formulário com um método POST:
Flight::route('POST /submit', function(){
$name = Flight::request()->data['name'];
$email = Flight::request()->data['email'];
echo "Você enviou: $name, $email";
// salvar em um banco de dados ou algo assim com o $name e $email
});
Propriedades do Objeto de Requisição
O objeto de requisição fornece as seguintes propriedades:
- body - O corpo bruto da requisição HTTP
- url - A URL sendo requisitada
- base - O subdiretório pai da URL
- method - O método da requisição (GET, POST, PUT, DELETE)
- referrer - A URL de referência
- ip - Endereço IP do cliente
- ajax - Se a requisição é uma requisição AJAX
- scheme - O protocolo do servidor (http, https)
- user_agent - Informações do navegador
- type - O tipo de conteúdo
- length - O comprimento do conteúdo
- query - Parâmetros de string de consulta
- data - Dados POST ou dados JSON
- cookies - Dados de cookie
- files - Arquivos enviados
- secure - Se a conexão é segura
- accept - Parâmetros de aceitação HTTP
- proxy_ip - Endereço IP do proxy do cliente. Escaneia o array
$_SERVER
paraHTTP_CLIENT_IP
,HTTP_X_FORWARDED_FOR
,HTTP_X_FORWARDED
,HTTP_X_CLUSTER_CLIENT_IP
,HTTP_FORWARDED_FOR
,HTTP_FORWARDED
nessa ordem. - host - O nome do host da requisição
Você pode acessar as propriedades query
, data
, cookies
e files
como arrays ou objetos.
Então, para obter um parâmetro de string de consulta, você pode fazer:
$id = Flight::request()->query['id'];
Ou você pode fazer:
$id = Flight::request()->query->id;
Corpo Bruto da Requisição
Para obter o corpo bruto da requisição HTTP, por exemplo, ao lidar com requisições PUT, você pode fazer:
$body = Flight::request()->getBody();
Entrada JSON
Se você enviar uma requisição com o tipo application/json
e os dados {"id": 123}
eles estarão disponíveis na propriedade data
:
$id = Flight::request()->data->id;
$_GET
Você pode acessar o array $_GET
através da propriedade query
:
$id = Flight::request()->query['id'];
$_POST
Você pode acessar o array $_POST
através da propriedade data
:
$id = Flight::request()->data['id'];
$_COOKIE
Você pode acessar o array $_COOKIE
através da propriedade cookies
:
$myCookieValue = Flight::request()->cookies['myCookieName'];
$_SERVER
Há um atalho disponível para acessar o array $_SERVER
através do método getVar()
:
$host = Flight::request()->getVar['HTTP_HOST'];
Acessando Arquivos Enviados via $_FILES
Você pode acessar arquivos enviados através da propriedade files
:
$uploadedFile = Flight::request()->files['myFile'];
Processando Envio de Arquivos
Você pode processar o envio de arquivos usando o framework com alguns métodos auxiliares. Basicamente resume-se a puxar os dados do arquivo da requisição e movê-los para um novo local.
Flight::route('POST /upload', function(){
// Se você tiver um campo de entrada como <input type="file" name="myFile">
$uploadedFileData = Flight::request()->getUploadedFiles();
$uploadedFile = $uploadedFileData['myFile'];
$uploadedFile->moveTo('/caminho/para/uploads/' . $uploadedFile->getClientFilename());
});
Se você tiver vários arquivos enviados, você pode percorrê-los:
Flight::route('POST /upload', function(){
// Se você tiver um campo de entrada como <input type="file" name="myFiles[]">
$uploadedFiles = Flight::request()->getUploadedFiles()['myFiles'];
foreach ($uploadedFiles as $uploadedFile) {
$uploadedFile->moveTo('/caminho/para/uploads/' . $uploadedFile->getClientFilename());
}
});
Nota de Segurança: Sempre valide e sanitize a entrada do usuário, especialmente ao lidar com envios de arquivos. Sempre valide o tipo de extensões que você permitirá que sejam enviadas, mas você também deve validar os "bytes mágicos" do arquivo para garantir que ele é realmente do tipo de arquivo que o usuário afirma que é. Existem artigos e bibliotecas disponíveis para ajudar com isso.
Cabeçalhos da Requisição
Você pode acessar os cabeçalhos da requisição usando o método getHeader()
ou getHeaders()
:
// Talvez você precise do cabeçalho Authorization
$host = Flight::request()->getHeader('Authorization');
// ou
$host = Flight::request()->header('Authorization');
// Se você precisar obter todos os cabeçalhos
$headers = Flight::request()->getHeaders();
// ou
$headers = Flight::request()->headers();
Corpo da Requisição
Você pode acessar o corpo bruto da requisição usando o método getBody()
:
$body = Flight::request()->getBody();
Método da Requisição
Você pode acessar o método da requisição usando a propriedade method
ou o método getMethod()
:
$method = Flight::request()->method; // na verdade chama getMethod()
$method = Flight::request()->getMethod();
Nota: O método getMethod()
primeiro puxa o método de $_SERVER['REQUEST_METHOD']
, depois pode ser substituído
por $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']
se existir ou $_REQUEST['_method']
se existir.
URLs da Requisição
Há alguns métodos auxiliares para juntar partes de uma URL para sua conveniência.
URL Completa
Você pode acessar a URL completa da requisição usando o método getFullUrl()
:
$url = Flight::request()->getFullUrl();
// https://example.com/some/path?foo=bar
URL Base
Você pode acessar a URL base usando o método getBaseUrl()
:
$url = Flight::request()->getBaseUrl();
// Aviso, sem barra final.
// https://example.com
Análise de Consulta
Você pode passar uma URL para o método parseQuery()
para analisar a string de consulta em um array associativo:
$query = Flight::request()->parseQuery('https://example.com/some/path?foo=bar');
// ['foo' => 'bar']
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
Flight foi 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.
Flight::map(string $name, callable $callback, bool $pass_route = false) // Cria um método personalizado do framework.
Flight::register(string $name, string $class, array $params = [], ?callable $callback = null) // Registra uma classe a um método do framework.
Flight::unregister(string $name) // Cancela o registro de uma classe a 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 após um método do framework.
Flight::path(string $path) // Adiciona um caminho para autoloading de classes.
Flight::get(string $key) // Obtém uma variável definida por Flight::set().
Flight::set(string $key, mixed $value) // Define uma variável dentro do mecanismo Flight.
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 as configurações padrão.
Flight::app() // Obtém a instância do objeto da aplicação.
Flight::request() // Obtém a instância do objeto de requisiçã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() // 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, string $alias = '') // Mapeia um padrão de URL a um callback.
Flight::post(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Mapeia um padrão de URL de requisição POST a um callback.
Flight::put(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Mapeia um padrão de URL de requisição PUT a um callback.
Flight::patch(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Mapeia um padrão de URL de requisição PATCH a um callback.
Flight::delete(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Mapeia um padrão de URL de requisição DELETE a um callback.
Flight::group(string $pattern, callable $callback) // Cria agrupamentos 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::download(string $filePath) // Faz o download de um arquivo.
Flight::render(string $file, array $data, ?string $key = null) // Renderiza um arquivo de template.
Flight::error(Throwable $error) // Envia uma resposta HTTP 500.
Flight::notFound() // Envia uma resposta HTTP 404.
Flight::etag(string $id, string $type = 'string') // Realiza caching HTTP de ETag.
Flight::lastModified(int $time) // Realiza caching HTTP do último modificado.
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.
Flight::jsonHalt(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Envia uma resposta JSON e para o framework.
Flight::onEvent(string $event, callable $callback) // Registra um ouvinte de evento.
Flight::triggerEvent(string $event, ...$args) // Dispara um evento.
Qualquer método personalizado adicionado com map
e register
também pode ser filtrado. Para exemplos de como mapear esses métodos, consulte o guia Extending Flight.
Learn/why_frameworks
Por que um Framework?
Alguns programadores são veementemente contra o uso de frameworks. Eles argumentam que os frameworks são inflados, lentos e difíceis de aprender. 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 do uso de frameworks. No entanto, também existem muitas vantagens em usar frameworks.
Motivos para Usar um Framework
Aqui estão algumas razões pelas quais você pode querer considerar o uso de um framework:
- Desenvolvimento Rápido: Os frameworks fornecem muita funcionalidade pronta para uso. Isso significa que você pode construir aplicações web mais rapidamente. Você não precisa escrever tanto código, pois o framework fornece grande parte da funcionalidade necessária.
- Consistência: Os frameworks fornecem uma forma consistente de fazer as coisas. Isso facilita a compreensão de como o código funciona e torna mais fácil para outros desenvolvedores entenderem o seu código. Se você o tem script por script, pode perder a consistência entre scripts, especialmente se estiver trabalhando com uma equipe de desenvolvedores.
- Segurança: Os frameworks oferecem recursos de segurança que ajudam a proteger suas aplicações web contra ameaças de segurança comuns. Isso significa que você não precisa se preocupar tanto com a segurança, pois o framework cuida de grande parte disso para você.
- Comunidade: Os frameworks têm grandes comunidades de desenvolvedores que contribuem para o framework. Isso significa que você pode obter ajuda de outros desenvolvedores quando tiver perguntas ou problemas. Também significa que há muitos recursos disponíveis para ajudá-lo a aprender como usar o framework.
- Melhores Práticas: Os frameworks são construídos seguindo as melhores práticas. Isso significa que você pode aprender com o framework e usar as mesmas melhores práticas em seu próprio código. Isso pode ajudá-lo a se tornar um melhor programador. Às vezes, você não sabe o que não sabe e isso pode prejudicá-lo no final.
- Extensibilidade: Os frameworks são projetados para serem estendidos. Isso significa que você pode adicionar sua própria funcionalidade ao framework. Isso permite que você construa aplicações web adaptadas às suas necessidades específicas.
Flight é um micro-framework. Isso significa que ele é pequeno e leve. Ele não fornece tanta funcionalidade quanto frameworks maiores como Laravel ou Symfony. No entanto, ele fornece muita 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 rapidamente e facilmente. Se você é novo em frameworks, o Flight é um ótimo framework para iniciantes a começar. Vai ajudá-lo a entender as vantagens de usar frameworks sem sobrecarregá-lo com muita complexidade. Depois de adquirir 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 um aplicativo robusto e bem-sucedido.
O Que é Roteamento?
O roteamento é o núcleo do framework Flight, mas o que é exatamente? Roteamento é o processo de pegar uma URL e correspondê-la a uma função específica em seu código. É assim que você pode fazer seu site fazer coisas diferentes com base na URL solicitada. Por exemplo, você pode querer mostrar o perfil de um usuário quando ele visita /user/1234
, mas mostrar uma lista de todos os usuários quando eles visitam /users
. Tudo isso é feito por meio do roteamento.
Pode funcionar algo assim:
- Um usuário vai para o seu navegador e digita
http://exemplo.com/user/1234
. - O servidor recebe a solicitação, olha para a URL e a passa para o seu código de aplicativo Flight.
- Digamos que em seu código Flight você tenha algo como
Flight::route('/user/@id', [ 'ControladorDeUsuario', 'verPerfilDoUsuario' ]);
. Seu código de aplicativo Flight olha a URL e percebe que corresponde a uma rota que você definiu, e então executa o código que você definiu para essa rota. - O roteador do Flight vai então chamar o método
verPerfilDoUsuario($id)
na classeControladorDeUsuario
, passando o1234
como argumento$id
no método. - O código em seu método
verPerfilDoUsuario()
então vai rodar e fazer o que você mandou fazer. Você pode acabar ecoando um pouco de HTML para a página do perfil do usuário, ou se isso for uma API RESTful, você pode ecoar uma resposta JSON com a informação do usuário. - O Flight embrulha isso em um laço bonito, gera os cabeçalhos de resposta e envia de volta para o navegador do usuário.
- O usuário fica cheio de alegria e dá a si mesmo um caloroso abraço!
E Por que é Importante?
Ter um roteador centralizado apropriado pode realmente tornar sua vida dramaticamente mais fácil! Pode ser difícil ver isso à primeira vista. Aqui estão algumas razões:
- Roteamento Centralizado: Você pode manter todas as suas rotas em um só lugar. Isso torna mais fácil ver quais rotas você tem e o que elas fazem. Também facilita alterá-las, se necessário.
- Parâmetros de Rota: Você pode usar parâmetros de rota para passar dados para seus métodos de rota. Esta é uma ótima maneira de manter seu código limpo e organizado.
- Grupos de Rotas: Você pode agrupar rotas juntas. Isso é ótimo para manter seu código organizado e para aplicar middleware a um grupo de rotas.
- 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 um modelo, por exemplo). Ex: em vez de codificar
/user/1234
no seu código, você poderia referenciar o aliasuser_view
e passar oid
como parâmetro. Isso é maravilhoso no caso de decidir alterá-lo para/admin/user/1234
posteriormente. Você não precisará mudar todas as suas URLs codificadas, apenas a URL associada à rota. - Middleware de Rota: Você pode adicionar middleware às suas rotas. O middleware é incrivelmente poderoso para adicionar comportamentos específicos à sua aplicação, como autenticar que um determinado usuário pode acessar uma rota ou grupo de rotas.
Tenho certeza de que você está familiarizado com a maneira script por script de criar um site. Você pode ter um arquivo chamado index.php
que possui um monte de declarações if
para verificar a URL e em seguida executar uma função específica com base na URL. Isso é uma forma de roteamento, mas não é muito organizado e pode sair do controle rapidamente. O sistema de roteamento do Flight é uma maneira muito mais organizada e poderosa de lidar com o roteamento.
Isto?
// /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', [ 'ControladorDeUsuario', 'verPerfilDoUsuario' ]);
Flight::route('/user/@id/edit', [ 'ControladorDeUsuario', 'editarPerfilDoUsuario' ]);
// Em talvez o seu app/controllers/ControladorDeUsuario.php
class ControladorDeUsuario {
public function verPerfilDoUsuario($id) {
// faça algo
}
public function editarPerfilDoUsuario($id) {
// faça algo
}
}
Espero que você comece 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 oferece uma maneira simples e fácil de lidar com solicitações e respostas. Este é o núcleo do que um framework web faz. Ele recebe uma solicitação de um navegador de um usuário, a processa e envia de volta uma resposta. Com isso, você pode construir aplicações web que realizem tarefas como mostrar o perfil de um usuário, permitir que um usuário faça login, ou permitir que um usuário poste uma nova postagem em um blog.
Solicitações
Uma solicitação é o que o navegador de um usuário envia para o seu servidor quando eles visitam o seu site. Esta solicitação contém informações sobre o que o usuário quer fazer. Por exemplo, ela pode conter informações sobre qual URL o usuário deseja visitar, quais dados o usuário deseja enviar para o seu servidor, ou que tipo de dados o usuário deseja receber do seu servidor. É importante saber que uma solicitação é somente leitura. Você não pode alterar a solicitação, mas pode 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 esse 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 o seu servidor envia de volta para o navegador de um usuário quando eles visitam o seu site. Esta resposta contém informações sobre o que o seu servidor quer fazer. Por exemplo, ela pode conter informações sobre que tipo de dados o seu servidor quer enviar para o usuário, que tipo de dados seu servidor quer receber do usuário, ou que tipo de dados seu servidor quer armazenar no computador do usuário.
O Flight fornece uma maneira simples de enviar uma resposta para o navegador do usuário. Você pode enviar uma resposta usando o método Flight::response()
. Este método recebe um objeto Response
como argumento e envia a resposta para o navegador do usuário. Você pode usar este objeto para enviar uma resposta para o navegador do usuário, como HTML, JSON, ou um arquivo. O Flight ajuda a gerar automaticamente algumas partes da resposta para facilitar as coisas, mas, em última instância, você tem controle sobre o que enviar 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
Flight ajuda a gerar parte dos cabeçalhos de resposta para você, mas você possui a maior parte do controle sobre o que envia de volta ao 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
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 irá capturá-la e enviá-la 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 muitas vezes faz o trabalho quando você precisa
Flight::response()->write("Olá, Mundo!");
// se você quiser recuperar o corpo que definiu até este ponto
// você pode fazer isso 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 você quiser obter o código de status atual, pode usar o método status
sem argumentos:
Flight::response()->status(); // 200
Definindo um Corpo de Resposta
Você pode definir o corpo da resposta usando o método write
, no entanto, se você echo ou print qualquer coisa,
isso será capturado e enviado como o corpo da resposta através do buffering de saída.
Flight::route('/', function() {
Flight::response()->write("Olá, Mundo!");
});
// mesmo que
Flight::route('/', function() {
echo "Olá, Mundo!";
});
Limpando um Corpo de Resposta
Se você quiser limpar o corpo da resposta, pode usar o método clearBody
:
Flight::route('/', function() {
if($someCondition) {
Flight::response()->write("Olá, Mundo!");
} else {
Flight::response()->clearBody();
}
});
Executando um Callback no Corpo da Resposta
Você pode executar um callback no corpo da resposta usando o método addResponseBodyCallback
:
Flight::route('/users', function() {
$db = Flight::db();
$users = $db->fetchAll("SELECT * FROM users");
Flight::render('users_table', ['users' => $users]);
});
// Isso irá gzip todas as respostas para qualquer rota
Flight::response()->addResponseBodyCallback(function($body) {
return gzencode($body, 9);
});
Você pode adicionar múltiplos callbacks e eles serão executados na ordem em que foram adicionados. Como isso pode aceitar qualquer callable, pode aceitar um array de classe [ $class, 'method' ]
, uma closure $strReplace = function($body) { str_replace('hi', 'there', $body); };
, ou um nome de função 'minify'
se você tiver uma função para minificar seu código html, por exemplo.
Nota: Callbacks de rota não funcionarão se você estiver usando a opção de configuração flight.v2.output_buffering
.
Callback de Rota Específica
Se você quiser que isso se aplique apenas a uma rota específica, pode adicionar o callback na própria rota:
Flight::route('/users', function() {
$db = Flight::db();
$users = $db->fetchAll("SELECT * FROM users");
Flight::render('users_table', ['users' => $users]);
// Isso irá gzip somente a resposta para esta rota
Flight::response()->addResponseBodyCallback(function($body) {
return gzencode($body, 9);
});
});
Opção de Middleware
Você também pode usar middleware para aplicar o callback a todas as rotas via middleware:
// MinifyMiddleware.php
class MinifyMiddleware {
public function before() {
// Aplique o callback aqui no objeto response().
Flight::response()->addResponseBodyCallback(function($body) {
return $this->minify($body);
});
}
protected function minify(string $body): string {
// minifique o corpo de alguma forma
return $body;
}
}
// index.php
Flight::group('/users', function() {
Flight::route('', function() { /* ... */ });
Flight::route('/@id', function($id) { /* ... */ });
}, [ new MinifyMiddleware() ]);
Definindo um Cabeçalho de Resposta
Você pode definir um cabeçalho como 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');
// ou
Flight::response()->setHeader('Content-Type', 'text/plain');
echo "Olá, Mundo!";
});
JSON
Flight fornece suporte para o envio de respostas JSON e JSONP. Para enviar uma resposta JSON você passa alguns dados para serem codificados em JSON:
Flight::json(['id' => 123]);
Nota: Por padrão, o Flight enviará um cabeçalho
Content-Type: application/json
com a resposta. Ele também usará as constantesJSON_THROW_ON_ERROR
eJSON_UNESCAPED_SLASHES
ao codificar o JSON.
JSON com Código de Status
Você também pode passar um código de status como segundo argumento:
Flight::json(['id' => 123], 201);
JSON com Impressão Bonita
Você também pode passar um argumento para a última posição para habilitar a impressão bonita:
Flight::json(['id' => 123], 200, true, 'utf-8', JSON_PRETTY_PRINT);
Se você estiver mudando as opções passadas para Flight::json()
e quiser uma sintaxe mais simples, você pode
apenas reconfigurar o método JSON:
Flight::map('json', function($data, $code = 200, $options = 0) {
Flight::_json($data, $code, true, 'utf-8', $options);
}
// E agora pode ser usado assim
Flight::json(['id' => 123], 200, JSON_PRETTY_PRINT);
JSON e Parar Execução (v3.10.0)
Se você quiser enviar uma resposta JSON e parar a execução, pode usar o método jsonHalt
.
Isso é útil para casos em que você está verificando talvez algum tipo de autorização e se
o usuário não estiver autorizado, você pode enviar uma resposta JSON imediatamente, limpar o corpo
existente e parar a execução.
Flight::route('/users', function() {
$authorized = someAuthorizationCheck();
// Verifique se o usuário está autorizado
if($authorized === false) {
Flight::jsonHalt(['error' => 'Não Autorizado'], 401);
}
// Continue com o restante da rota
});
Antes da v3.10.0, você teria que fazer algo assim:
Flight::route('/users', function() {
$authorized = someAuthorizationCheck();
// Verifique se o usuário está autorizado
if($authorized === false) {
Flight::halt(401, json_encode(['error' => 'Não Autorizado']));
}
// Continue com o restante da rota
});
JSONP
Para solicitações JSONP você pode, opcionalmente, passar o nome do parâmetro de consulta que você está usando para definir sua função de callback:
Flight::jsonp(['id' => 123], 'q');
Assim, ao fazer uma solicitação GET usando ?q=my_func
, você deverá receber a saída:
my_func({"id":123});
Se você não passar um nome de parâmetro de consulta, ele será definido como jsonp
.
Redirecionar para outra URL
Você pode redirecionar a solicitação atual usando o método redirect()
e passar
uma nova URL:
Flight::redirect('/new/location');
Por padrão, o Flight envia um código de status HTTP 303 ("Veja Outro"). Você pode opcionalmente definir um código personalizado:
Flight::redirect('/new/location', 401);
Parando
Você pode parar o framework a qualquer momento chamando o método halt
:
Flight::halt();
Você também pode especificar um código e mensagem de status HTTP
opcionais:
Flight::halt(200, 'Be right back...');
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();
Limpando Dados da Resposta
Você pode limpar o corpo da resposta e os cabeçalhos usando o método clear()
. Isso limpará
quaisquer cabeçalhos atribuídos à resposta, limpará o corpo da resposta e definirá o código de status como 200
.
Flight::response()->clear();
Limpando Apenas o Corpo da Resposta
Se você apenas deseja limpar o corpo da resposta, pode usar o método clearBody()
:
// Isso ainda manterá quaisquer cabeçalhos definidos no objeto response().
Flight::response()->clearBody();
Cache HTTP
Flight fornece suporte embutido para caching a nível HTTP. Se a condição de cache
for atendida, o Flight retornará uma resposta HTTP 304 Not Modified
. Na próxima vez que o
cliente solicitar o mesmo recurso, ele será solicitado a usar sua versão armazenada em cache localmente.
Cache a Nível de Rota
Se você quiser armazenar em cache toda a sua resposta, pode usar o método cache()
e passar o tempo para cache.
// Isso armazenará em cache a resposta por 5 minutos
Flight::route('/news', function () {
Flight::response()->cache(time() + 300);
echo 'Este conteúdo será armazenado em cache.';
});
// Alternativamente, você pode usar uma string que você passaria
// para o método strtotime()
Flight::route('/news', function () {
Flight::response()->cache('+5 minutes');
echo 'Este conteúdo será armazenado em cache.';
});
Last-Modified
Você pode usar o método lastModified
e passar um timestamp UNIX para definir a data
e a 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('/news', function () {
Flight::lastModified(1234567890);
echo 'Este conteúdo será armazenado em cache.';
});
ETag
O cache ETag
é semelhante ao Last-Modified
, exceto que você pode especificar qualquer id que
quiser para o recurso:
Flight::route('/news', function () {
Flight::etag('my-unique-id');
echo 'Este conteúdo será armazenado em cache.';
});
Lembre-se de que chamar lastModified
ou etag
definirá e verificará ambos os valores de
cache. Se o valor de cache for o mesmo entre as solicitações, o Flight enviará imediatamente
uma resposta HTTP 304
e parará o processamento.
Fazer o Download de um Arquivo (v3.12.0)
Há um método auxiliar para fazer o download de um arquivo. Você pode usar o método download
e passar o caminho.
Flight::route('/download', function () {
Flight::download('/path/to/file.txt');
});
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/events
Sistema de Eventos no Flight PHP (v3.15.0+)
O Flight PHP introduz um sistema de eventos leve e intuitivo que permite registrar e acionar eventos personalizados em sua aplicação. Com a adição de Flight::onEvent()
e Flight::triggerEvent()
, agora você pode se conectar a momentos-chave do ciclo de vida do seu aplicativo ou definir seus próprios eventos para tornar seu código mais modular e extensível. Estes métodos fazem parte dos métodos mapeáveis do Flight, o que significa que você pode substituir seu comportamento para atender às suas necessidades.
Este guia cobre tudo que você precisa saber para começar com eventos, incluindo por que eles são valiosos, como usá-los e exemplos práticos para ajudar iniciantes a entender seu poder.
Por Que Usar Eventos?
Eventos permitem que você separe diferentes partes da sua aplicação para que não dependam muito umas das outras. Essa separação—frequentemente chamada de desacoplamento—torna seu código mais fácil de atualizar, estender ou depurar. Em vez de escrever tudo em um único bloco grande, você pode dividir sua lógica em partes menores e independentes que respondem a ações específicas (eventos).
Imagine que você está construindo um aplicativo de blog:
- Quando um usuário posta um comentário, você pode querer:
- Salvar o comentário no banco de dados.
- Enviar um e-mail para o proprietário do blog.
- Registrar a ação para segurança.
Sem eventos, você cramaria tudo em uma única função. Com eventos, você pode dividi-lo: uma parte salva o comentário, outra aciona um evento como 'comment.posted'
, e ouvintes separados lidam com o e-mail e o registro. Isso mantém seu código mais claro e lhe permite adicionar ou remover funcionalidades (como notificações) sem tocar na lógica central.
Usos Comuns
- Registro: Registrar ações como logins ou erros sem sobrecarregar seu código principal.
- Notificações: Enviar e-mails ou alertas quando algo acontece.
- Atualizações: Atualizar caches ou notificar outros sistemas sobre mudanças.
Registrando Ouvintes de Eventos
Para ouvir um evento, use Flight::onEvent()
. Este método permite que você defina o que deve acontecer quando um evento ocorrer.
Sintaxe
Flight::onEvent(string $event, callable $callback): void
$event
: Um nome para seu evento (por exemplo,'user.login'
).$callback
: A função a ser executada quando o evento for acionado.
Como Funciona
Você "se inscreve" em um evento dizendo ao Flight o que fazer quando ele acontece. O callback pode aceitar argumentos passados do acionador do evento.
O sistema de eventos do Flight é síncrono, o que significa que cada ouvinte de evento é executado em sequência, um após o outro. Quando você aciona um evento, todos os ouvintes registrados para esse evento serão executados até a conclusão antes que seu código continue. Isso é importante de entender, pois difere de sistemas de eventos assíncronos onde os ouvintes podem ser executados em paralelo ou em um momento posterior.
Exemplo Simples
Flight::onEvent('user.login', function ($username) {
echo "Bem-vindo de volta, $username!";
});
Aqui, quando o evento 'user.login'
é acionado, ele cumprimenta o usuário pelo nome.
Pontos Chave
- Você pode adicionar múltiplos ouvintes ao mesmo evento—eles serão executados na ordem em que foram registrados.
- O callback pode ser uma função, uma função anônima ou um método de uma classe.
Acionando Eventos
Para fazer um evento acontecer, use Flight::triggerEvent()
. Isso diz ao Flight para executar todos os ouvintes registrados para esse evento, passando qualquer dado que você fornecer.
Sintaxe
Flight::triggerEvent(string $event, ...$args): void
$event
: O nome do evento que você está acionando (deve corresponder a um evento registrado)....$args
: Argumentos opcionais a serem enviados aos ouvintes (podem ser qualquer número de argumentos).
Exemplo Simples
$username = 'alice';
Flight::triggerEvent('user.login', $username);
Isso aciona o evento 'user.login'
e envia 'alice'
para o ouvinte que definimos anteriormente, que irá gerar: Bem-vindo de volta, alice!
.
Pontos Chave
- Se nenhum ouvinte estiver registrado, nada acontecerá—seu aplicativo não quebrará.
- Use o operador de spread (
...
) para passar múltiplos argumentos de forma flexível.
Registrando Ouvintes de Eventos
...
Interrompendo Ouvintes Futuros:
Se um ouvinte retornar false
, nenhum ouvinte adicional para esse evento será executado. Isso permite que você interrompa a cadeia de eventos com base em condições específicas. Lembre-se, a ordem dos ouvintes importa, pois o primeiro a retornar false
impedirá o restante de serem executados.
Exemplo:
Flight::onEvent('user.login', function ($username) {
if (isBanned($username)) {
logoutUser($username);
return false; // Interrompe ouvintes subsequentes
}
});
Flight::onEvent('user.login', function ($username) {
sendWelcomeEmail($username); // isso nunca é enviado
});
Substituindo Métodos de Evento
Flight::onEvent()
e Flight::triggerEvent()
estão disponíveis para serem estendidos, o que significa que você pode redefinir como eles funcionam. Isso é ótimo para usuários avançados que desejam personalizar o sistema de eventos, como adicionar registro ou alterar como os eventos são enviados.
Exemplo: Personalizando onEvent
Flight::map('onEvent', function (string $event, callable $callback) {
// Registre cada registro de evento
error_log("Novo ouvinte de evento adicionado para: $event");
// Chame o comportamento padrão (supondo um sistema de eventos interno)
Flight::_onEvent($event, $callback);
});
Agora, toda vez que você registra um evento, ele é registrado antes de prosseguir.
Por Que Substituir?
- Adicionar depuração ou monitoramento.
- Restringir eventos em determinados ambientes (por exemplo, desativar em testes).
- Integrar-se com uma biblioteca de eventos diferente.
Onde Colocar Seus Eventos
Como iniciante, você pode se perguntar: onde registro todos esses eventos na minha aplicação? A simplicidade do Flight significa que não há uma regra rígida—você pode colocá-los onde fizer sentido para seu projeto. No entanto, mantê-los organizados ajuda você a manter seu código à medida que seu aplicativo cresce. Aqui estão algumas opções práticas e melhores práticas, adaptadas à natureza leve do Flight:
Opção 1: No Seu index.php
Principal
Para aplicativos pequenos ou protótipos rápidos, você pode registrar eventos diretamente em seu arquivo index.php
juntamente com suas rotas. Isso mantém tudo em um só lugar, o que é aceitável quando a simplicidade é sua prioridade.
require 'vendor/autoload.php';
// Registrar eventos
Flight::onEvent('user.login', function ($username) {
error_log("$username fez login em " . date('Y-m-d H:i:s'));
});
// Definir rotas
Flight::route('/login', function () {
$username = 'bob';
Flight::triggerEvent('user.login', $username);
echo "Conectado!";
});
Flight::start();
- Prós: Simples, sem arquivos extras, ótimo para pequenos projetos.
- Contras: Pode ficar bagunçado à medida que seu aplicativo cresce com mais eventos e rotas.
Opção 2: Um Arquivo events.php
Separado
Para um aplicativo um pouco maior, considere mover os registros de eventos para um arquivo dedicado como app/config/events.php
. Inclua este arquivo em seu index.php
antes de suas rotas. Isso imita como as rotas costumam ser organizadas em app/config/routes.php
nos projetos do Flight.
// app/config/events.php
Flight::onEvent('user.login', function ($username) {
error_log("$username fez login em " . date('Y-m-d H:i:s'));
});
Flight::onEvent('user.registered', function ($email, $name) {
echo "E-mail enviado para $email: Bem-vindo, $name!";
});
// index.php
require 'vendor/autoload.php';
require 'app/config/events.php';
Flight::route('/login', function () {
$username = 'bob';
Flight::triggerEvent('user.login', $username);
echo "Conectado!";
});
Flight::start();
- Prós: Mantém
index.php
focado em rotas, organiza eventos logicamente, fácil de encontrar e editar. - Contras: Adiciona um pequeno pouco de estrutura, o que pode parecer excessivo para aplicativos muito pequenos.
Opção 3: Perto de Onde Eles São Acionados
Outra abordagem é registrar eventos perto de onde eles são acionados, como dentro de um controlador ou definição de rota. Isso funciona bem se um evento é específico de uma parte do seu aplicativo.
Flight::route('/signup', function () {
// Registrar evento aqui
Flight::onEvent('user.registered', function ($email) {
echo "E-mail de boas-vindas enviado para $email!";
});
$email = 'jane@example.com';
Flight::triggerEvent('user.registered', $email);
echo "Registrado!";
});
- Prós: Mantém o código relacionado junto, bom para recursos isolados.
- Contras: Espalha registros de eventos, dificultando a visualização de todos os eventos de uma vez; riscos de registros duplicados se não tomar cuidado.
Melhor Prática para Flight
- Começar Simples: Para aplicativos pequenos, coloque eventos em
index.php
. É rápido e alinha-se com o minimalismo do Flight. - Crescer de Forma Inteligente: À medida que seu aplicativo se expande (por exemplo, mais de 5-10 eventos), use um arquivo
app/config/events.php
. É um passo natural, como organizar rotas, e mantém seu código limpo sem adicionar estruturas complexas. - Evitar Sobrecarga de Engenharia: Não crie uma classe ou diretório de "gerenciador de eventos" completo a menos que seu aplicativo cresça muito—o Flight prospera na simplicidade, então mantenha-o leve.
Dica: Agrupar por Propósito
No events.php
, agrupe eventos relacionados (por exemplo, todos os eventos relacionados ao usuário juntos) com comentários para clareza:
// app/config/events.php
// Eventos do Usuário
Flight::onEvent('user.login', function ($username) {
error_log("$username fez login");
});
Flight::onEvent('user.registered', function ($email) {
echo "Bem-vindo ao $email!";
});
// Eventos de Página
Flight::onEvent('page.updated', function ($pageId) {
unset($_SESSION['pages'][$pageId]);
});
Essa estrutura escala bem e continua amigável para iniciantes.
Exemplos para Iniciantes
Vamos percorrer alguns cenários do mundo real para mostrar como os eventos funcionam e por que eles são úteis.
Exemplo 1: Registrando um Login de Usuário
// Passo 1: Registrar um ouvinte
Flight::onEvent('user.login', function ($username) {
$time = date('Y-m-d H:i:s');
error_log("$username fez login em $time");
});
// Passo 2: Acionar no seu aplicativo
Flight::route('/login', function () {
$username = 'bob'; // Suponha que isso venha de um formulário
Flight::triggerEvent('user.login', $username);
echo "Oi, $username!";
});
Por que isso é útil: O código de login não precisa saber sobre logging—ele apenas aciona o evento. Você pode adicionar mais ouvintes depois (por exemplo, enviar um e-mail de boas-vindas) sem alterar a rota.
Exemplo 2: Notificando Sobre Novos Usuários
// Ouvinte para novos registros
Flight::onEvent('user.registered', function ($email, $name) {
// Simule o envio de um e-mail
echo "E-mail enviado para $email: Bem-vindo, $name!";
});
// Acionar quando alguém se inscreve
Flight::route('/signup', function () {
$email = 'jane@example.com';
$name = 'Jane';
Flight::triggerEvent('user.registered', $email, $name);
echo "Obrigado por se inscrever!";
});
Por que isso é útil: A lógica de inscrição foca na criação do usuário, enquanto o evento lida com as notificações. Você poderia adicionar mais ouvintes depois (por exemplo, registrar a inscrição).
Exemplo 3: Limpando um Cache
// Ouvinte para limpar um cache
Flight::onEvent('page.updated', function ($pageId) {
unset($_SESSION['pages'][$pageId]); // Limpe o cache da sessão, se aplicável
echo "Cache limpo para a página $pageId.";
});
// Acionar quando uma página é editada
Flight::route('/edit-page/(@id)', function ($pageId) {
// Suponha que atualizamos a página
Flight::triggerEvent('page.updated', $pageId);
echo "Página $pageId atualizada.";
});
Por que isso é útil: O código de edição não se preocupa com o cache—ele apenas sinaliza a atualização. Outras partes do aplicativo podem reagir conforme necessário.
Melhores Práticas
- Nomeie Eventos Claramente: Use nomes específicos como
'user.login'
ou'page.updated'
para que fique óbvio o que eles fazem. - Mantenha Ouvintes Simples: Não coloque tarefas lentas ou complexas em ouvintes—mantenha seu aplicativo rápido.
- Teste Seus Eventos: Acione-os manualmente para garantir que os ouvintes funcionam como esperado.
- Use Eventos Com Sabedoria: Eles são ótimos para desacoplamento, mas muitos podem tornar seu código difícil de seguir—use-os quando fizer sentido.
O sistema de eventos no Flight PHP, com Flight::onEvent()
e Flight::triggerEvent()
, oferece uma maneira simples e poderosa de construir aplicações flexíveis. Ao permitir que diferentes partes do seu aplicativo se comuniquem entre si por meio de eventos, você pode manter seu código organizado, reutilizável e fácil de expandir. Seja registrando ações, enviando notificações ou gerenciando atualizações, os eventos ajudam você a fazer isso sem emaranhar sua lógica. Além disso, com a capacidade de substituir esses métodos, você tem a liberdade de personalizar o sistema de acordo com suas necessidades. Comece pequeno com um único evento e veja como ele transforma a estrutura do seu aplicativo!
Eventos Integrados
O Flight PHP vem com alguns eventos integrados que você pode usar para se conectar ao ciclo de vida do framework. Esses eventos são acionados em pontos específicos do ciclo de solicitação/resposta, permitindo que você execute lógica personalizada quando certas ações ocorrem.
Lista de Eventos Integrados
flight.request.received
: Acionado quando uma solicitação é recebida, analisada e processada.flight.route.middleware.before
: Acionado após a execução do middleware anterior.flight.route.middleware.after
: Acionado após a execução do middleware posterior.flight.route.executed
: Acionado após uma rota ser executada e processada.flight.response.sent
: Acionado após uma resposta ser enviada ao cliente.
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
Visões e Modelos HTML
O Flight fornece algumas funcionalidades básicas de modelagem por padrão.
O Flight permite que você substitua o mecanismo de visualização padrão simplesmente registrando sua própria classe de visualização. Role para baixo para ver exemplos de como usar Smarty, Latte, Blade e mais!
Mecanismo de Visualização Integrado
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:
Hello, <?= $name ?>!
A saída seria:
Hello, Bob!
Você também pode definir variáveis de visualização manualmente 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 deixar de fora a extensão .php
.
Por padrão, o Flight irá 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', '/path/to/views');
Layouts
É comum que websites tenham um único arquivo de modelo de layout com conteúdo intercambiável. 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' => 'Hello'], 'headerContent');
Flight::render('body', ['body' => 'World'], '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' => 'Home Page']);
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>Home Page</title>
</head>
<body>
<h1>Hello</h1>
<div>World</div>
</body>
</html>
Smarty
Aqui está como você usaria o Smarty mecanismo de modelo para suas visualizações:
// Carregar biblioteca Smarty
require './Smarty/libs/Smarty.class.php';
// Registrar Smarty como a classe de visualização
// Também passe uma função de retorno 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 completar, você também deve sobrescrever o método render padrão do Flight:
Flight::map('render', function(string $template, array $data): void {
Flight::view()->assign($data);
Flight::view()->display($template);
});
Latte
Aqui está como você usaria o Latte mecanismo de modelo para suas visualizações:
// Registrar Latte como a classe de visualização
// Também passe uma função de retorno para configurar o Latte ao carregar
Flight::register('view', Latte\Engine::class, [], function (Latte\Engine $latte) {
// Aqui é onde o Latte irá armazenar seus modelos para acelerar as coisas
// Uma coisa interessante 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 estará o diretório raiz para suas visualizações.
$latte->setLoader(new \Latte\Loaders\FileLoader(__DIR__ . '/../views/'));
});
// E finalize para que você possa usar Flight::render() corretamente
Flight::map('render', function(string $template, array $data): void {
// Isso é como $latte_engine->render($template, $data);
echo Flight::view()->render($template, $data);
});
Blade
Aqui está como você usaria o Blade mecanismo de modelo para suas visualizações:
Primeiro, você precisa instalar a biblioteca BladeOne via Composer:
composer require eftec/bladeone
Em seguida, você pode configurar o BladeOne como a classe de visualização no Flight:
<?php
// Carregar biblioteca BladeOne
use eftec\bladeone\BladeOne;
// Registrar BladeOne como a classe de visualização
// Também passe uma função de retorno para configurar o BladeOne ao carregar
Flight::register('view', BladeOne::class, [], function (BladeOne $blade) {
$views = __DIR__ . '/../views';
$cache = __DIR__ . '/../cache';
$blade->setPath($views);
$blade->setCompiledPath($cache);
});
// Atribuir dados do modelo
Flight::view()->share('name', 'Bob');
// Exibir o modelo
echo Flight::view()->run('hello', []);
Para completar, você também deve sobrescrever o método render padrão do Flight:
<?php
Flight::map('render', function(string $template, array $data): void {
echo Flight::view()->run($template, $data);
});
Neste exemplo, o arquivo de modelo hello.blade.php pode ser assim:
<?php
Hello, {{ $name }}!
A saída seria:
Hello, Bob!
Seguindo esses passos, você pode integrar o mecanismo de modelo Blade com o Flight e usá-lo para renderizar suas visualizações.
Learn/flight_vs_fat_free
Voo vs Sem Gordura
O que é Fat-Free?
Sem Gordura (afetuosamente conhecido como SG) é um microframework PHP poderoso, mas fácil de usar, projetado para ajudá-lo a construir aplicativos web dinâmicos e robustos - rapidamente!
Voo se compara com Sem Gordura de muitas maneiras e provavelmente é o parente mais próximo em termos de recursos e simplicidade. Sem Gordura tem muitos recursos que Voo não tem, mas também tem muitos recursos que Voo tem. Sem Gordura está começando a mostrar sua idade e não é tão popular quanto já foi.
As atualizações estão se tornando menos frequentes e a comunidade não é mais tão ativa quanto já foi. O código é simples o suficiente, mas às vezes a falta de disciplina sintática pode tornar difícil de ler e entender. Ele funciona para o PHP 8.3, mas o código em si ainda parece que pertence ao PHP 5.3.
Prós em comparação com Voo
- Sem Gordura tem um pouco mais de estrelas no GitHub do que Voo.
- Sem Gordura tem uma documentação decente, mas falta clareza em algumas áreas.
- Sem Gordura tem alguns recursos escassos como tutoriais do YouTube e artigos online que podem ser usados para aprender o framework.
- Sem Gordura tem alguns plugins úteis integrados que às vezes são úteis.
- Sem Gordura tem um ORM integrado chamado Mapper que pode ser usado para interagir com seu banco de dados. Voo tem active-record.
- Sem Gordura tem Sessões, Cache e localização embutidos. Voo requer que você use bibliotecas de terceiros, mas está coberto na documentação.
- Sem Gordura tem um pequeno grupo de plugins criados pela comunidade que podem ser usados para estender o framework. Voo tem alguns cobertos na documentação e páginas de exemplos.
- Sem Gordura assim como Voo não possui dependências.
- Sem Gordura assim como Voo é voltado para dar ao desenvolvedor controle sobre seu aplicativo e uma experiência de desenvolvedor simples.
- Sem Gordura mantém compatibilidade com versões anteriores como Voo faz (parcialmente porque as atualizações estão se tornando menos frequentes menos frequentes).
- Sem Gordura assim como Voo é destinado a desenvolvedores que estão se aventurando no mundo dos frameworks pela primeira vez.
- Sem Gordura tem um mecanismo de modelo integrado que é mais robusto do que o mecanismo de modelo do Voo. Voo recomenda Latte para realizar isso.
- Sem Gordura possui um comando de tipo CLI único "route" onde você pode construir aplicativos CLI dentro do próprio Sem Gordura e tratá-lo como uma solicitação
GET
. Voo realiza isso com runway.
Contras em comparação com Voo
- Sem Gordura tem alguns testes de implementação e até possui sua própria classe de teste que é muito básica. No entanto, não é 100% testado unitariamente como Voo é.
- Você precisa usar um mecanismo de busca como o Google para realmente pesquisar o site de documentação.
- Voo tem modo escuro em seu site de documentação. (microfone derrubado)
- Sem Gordura tem alguns módulos que são lamentavelmente não mantidos.
- Voo tem um PdoWrapper simples que é um pouco mais simples do que a classe
DB\SQL
integrada do Sem Gordura. - Voo tem um plugin de permissões que pode ser usado para proteger seu aplicativo. Slim requer que você use uma biblioteca de terceiros.
- Voo tem um ORM chamado active-record que parece mais com um ORM do que o Mapper do Sem Gordura.
O benefício adicional do
active-record
é que você pode definir relacionamentos entre registros para junções automáticas onde o Mapper do Sem Gordura requer que você crie visualizações SQL. - Surpreendentemente, Sem Gordura não tem um espaço de nomes raiz. Voo tem espaço de nomes o tempo todo para não colidir com seu próprio código.
a classe
Cache
é o maior infrator aqui. - Sem Gordura não possui middleware. Em vez disso, existem ganchos
beforeroute
eafterroute
que podem ser usados para filtrar solicitações e respostas em controladores. - Sem Gordura não pode agrupar rotas.
- Sem Gordura possui um manipulador de contêiner de injeção de dependência, mas a documentação é incrivelmente escassa sobre como usá-lo.
- Depurar pode ficar um pouco complicado, uma vez que basicamente tudo é armazenado no que é chamado de
HIVE
Learn/extending
Estendendo
Flight foi projetado para ser um framework extensível. O framework vem com um conjunto de métodos e componentes padrão, mas permite que você mapeie seus próprios métodos, registre suas próprias classes ou até mesmo substitua classes e métodos existentes.
Se você está procurando um DIC (Container de Injeção de Dependência), vá para a página do Container de Injeção de Dependência.
Mapeando Métodos
Para mapear seu próprio método simples personalizado, você usa a função map
:
// Mapeie seu método
Flight::map('hello', function (string $name) {
echo "olá $name!";
});
// Chame seu método personalizado
Flight::hello('Bob');
Embora seja possível criar métodos personalizados simples, é recomendável apenas criar funções padrão em PHP. Isso possui autocompletar nos IDEs e é mais fácil de ler. O equivalente do código acima seria:
function hello(string $name) {
echo "olá $name!";
}
hello('Bob');
Isso é mais utilizado quando você precisa passar variáveis para seu método para obter um valor esperado. Usar o método register()
como abaixo é mais para passar configurações e, em seguida, chamar sua classe pré-configurada.
Registrando Classes
Para registrar sua própria classe e configurá-la, você usa a função register
:
// Registre sua classe
Flight::register('user', User::class);
// Obtenha uma instância de sua classe
$user = Flight::user();
O método de registro também permite que você passe parâmetros para o construtor da sua classe. Assim, quando você carregar sua classe personalizada, ela virá pré-inicializada. Você pode definir os parâmetros do construtor passando um array adicional. Aqui está um exemplo de como carregar uma conexão de banco de dados:
// Registre a classe com parâmetros do construtor
Flight::register('db', PDO::class, ['mysql:host=localhost;dbname=test', 'user', 'pass']);
// Obtenha uma instância de 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 callback adicional, ele será executado imediatamente após a construção da classe. Isso permite que você execute quaisquer procedimentos de preparação para seu novo objeto. A função de callback recebe um parâmetro, uma instância do novo objeto.
// O callback receberá o objeto que foi construído
Flight::register(
'db',
PDO::class,
['mysql:host=localhost;dbname=test', 'user', 'pass'],
function (PDO $db) {
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
);
Por padrão, cada vez que você carrega sua classe, receberá uma instância compartilhada. Para obter uma nova instância de uma classe, basta passar false
como parâmetro:
// Instância compartilhada da classe
$shared = Flight::db();
// Nova instância da classe
$new = 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.
Registro de Logs
Flight não possui um sistema de logs embutido, no entanto, é muito fácil utilizar uma biblioteca de logging com o Flight. Aqui está um exemplo usando a biblioteca Monolog:
// index.php ou bootstrap.php
// Registre o logger com o Flight
Flight::register('log', Monolog\Logger::class, [ 'name' ], function(Monolog\Logger $log) {
$log->pushHandler(new Monolog\Handler\StreamHandler('path/to/your.log', Monolog\Logger::WARNING));
});
Agora que está registrado, você pode usá-lo em sua aplicação:
// Em seu controlador ou rota
Flight::log()->warning('Esta é uma mensagem de aviso');
Isso registrará uma mensagem no arquivo de log que você especificou. E se você quiser registrar algo quando um erro ocorrer? Você pode usar o método error
:
// Em seu controlador ou rota
Flight::map('error', function(Throwable $ex) {
Flight::log()->error($ex->getMessage());
// Exiba sua página de erro personalizada
include 'errors/500.html';
});
Você também poderia criar um sistema básico de APM (Monitoramento de Desempenho da Aplicação) usando os métodos before
e after
:
// Em seu arquivo bootstrap
Flight::before('start', function() {
Flight::set('start_time', microtime(true));
});
Flight::after('start', function() {
$end = microtime(true);
$start = Flight::get('start_time');
Flight::log()->info('Requisição '.Flight::request()->url.' levou ' . round($end - $start, 4) . ' segundos');
// Você também poderia adicionar seus cabeçalhos de requisição ou resposta
// para registrá-los também (tenha cuidado, pois isso seria uma
// grande quantidade de dados se você tiver muitas requisições)
Flight::log()->info('Cabeçalhos da Requisição: ' . json_encode(Flight::request()->headers));
Flight::log()->info('Cabeçalhos da Resposta: ' . json_encode(Flight::response()->headers));
});
Substituindo Métodos do Framework
Flight permite que você substitua sua funcionalidade padrão para atender às suas próprias necessidades, sem precisar modificar nenhum código. Você pode visualizar todos os métodos que pode substituir aqui.
Por exemplo, quando o Flight não consegue corresponder uma URL a uma rota, ele invoca o método notFound
que envia uma resposta genérica HTTP 404
. Você pode substituir esse comportamento
usando o método map
:
Flight::map('notFound', function() {
// Exiba a página personalizada de 404
include 'errors/404.html';
});
Flight também permite que você substitua componentes centrais do framework. Por exemplo, você pode substituir a classe Router 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 Router, ele carregará sua classe
$myrouter = 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/flight_vs_slim
Comparação entre Flight e Slim
O que é o Slim?
Slim é um microframework PHP que ajuda você a escrever rapidamente aplicações web e APIs simples, porém poderosas.
Muita da inspiração para algumas das funcionalidades da versão 3 do Flight na verdade veio do Slim. Agrupar rotas e executar middleware em uma ordem específica são duas funcionalidades que foram inspiradas pelo Slim. O Slim v3 foi lançado voltado para a simplicidade, mas houve críticas mistas em relação ao v4.
Prós em comparação com o Flight
- O Slim tem uma comunidade maior de desenvolvedores, que por sua vez criam módulos úteis para ajudar você a não reinventar a roda.
- O Slim segue muitas interfaces e padrões comuns na comunidade PHP, o que aumenta a interoperabilidade.
- O Slim possui documentação decente e tutoriais que podem ser usados para aprender o framework (nada comparado ao Laravel ou Symfony, no entanto).
- O Slim possui diversos recursos como tutoriais no YouTube e artigos online que podem ser usados para aprender o framework.
- O Slim permite que você use os componentes que desejar para lidar com as funcionalidades de roteamento principais, pois é compatível com PSR-7.
Contras em comparação com o Flight
- Surpreendentemente, o Slim não é tão rápido quanto você imagina que seria para um microframework. Consulte os testes do TechEmpower para obter mais informações.
- O Flight é voltado para um desenvolvedor que deseja construir uma aplicação web leve, rápida e fácil de usar.
- O Flight não possui dependências, enquanto que o Slim possui algumas dependências que você precisa instalar.
- O Flight é direcionado para a simplicidade e facilidade de uso.
- Uma das funcionalidades principais do Flight é fazer o melhor possível para manter a compatibilidade com versões anteriores. A transição do Slim da v3 para a v4 foi uma mudança drástica.
- O Flight é destinado a desenvolvedores que estão se aventurando no mundo dos frameworks pela primeira vez.
- O Flight também pode lidar com aplicações de nível empresarial, mas não possui tantos exemplos e tutoriais quanto o Slim. Também exigirá mais disciplina por parte do desenvolvedor para manter as coisas organizadas e bem estruturadas.
- O Flight dá mais controle ao desenvolvedor sobre a aplicação, enquanto que o Slim pode introduzir alguma mágica nos bastidores.
- O Flight possui um simples PdoWrapper que pode ser usado para interagir com seu banco de dados. O Slim exige que você utilize uma biblioteca de terceiros.
- O Flight possui um plugin de permissões que pode ser usado para proteger sua aplicação. O Slim exige que você utilize uma biblioteca de terceiros.
- O Flight possui um ORM chamado active-record que pode ser usado para interagir com seu banco de dados. O Slim exige que você utilize uma biblioteca de terceiros.
- O Flight possui uma aplicação CLI chamada runway que pode ser usada para executar sua aplicação a partir da linha de comando. O Slim não possui.
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 graças ao Composer. No entanto, se você deseja carregar suas próprias classes, pode usar o método Flight::path()
para especificar um diretório para carregar classes.
Exemplo Básico
Vamos assumir 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 (tudo em 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
*/
// Adicionar um caminho ao carregador automático
Flight::path(__DIR__.'/../app/controllers/');
Flight::path(__DIR__.'/../app/utils/');
/**
* app/controllers/MyController.php
*/
// sem necessidade de namespace
// Todas as classes carregadas automaticamente são recomendadas a serem em Pascal Case (cada palavra capitalizada, sem espaços)
// A partir da versão 3.7.2, você pode usar Pascal_Snake_Case para os nomes de suas classes executando Loader::setV2ClassLoading(false);
class MyController {
public function index() {
// fazer algo
}
}
Namespaces
Se você tiver namespaces, na verdade se torna muito fácil implementar isso. Você deve usar o método Flight::path()
para especificar o diretório raiz (não o diretório do documento ou pasta public/
) de sua aplicação.
/**
* public/index.php
*/
// Adicionar um caminho ao carregador automático
Flight::path(__DIR__.'/../');
Agora, veja como seu controlador poderia ser. Observe o exemplo abaixo, mas preste atenção aos comentários para informações importantes.
/**
* app/controllers/MyController.php
*/
// namespaces são necessários
// os namespaces são iguais à estrutura de diretórios
// os namespaces devem seguir o mesmo padrão de caso que a estrutura de diretórios
// os namespaces e diretórios não podem ter nenhum sublinhado (a menos que Loader::setV2ClassLoading(false) seja definido)
namespace app\controllers;
// Todas as classes carregadas automaticamente são recomendadas a serem em Pascal Case (cada palavra capitalizada, sem espaços)
// A partir da versão 3.7.2, você pode usar Pascal_Snake_Case para os nomes de suas classes executando Loader::setV2ClassLoading(false);
class MyController {
public function index() {
// fazer algo
}
}
E se você quiser carregar automaticamente uma classe em seu diretório de utils, você faria basicamente a mesma coisa:
/**
* app/UTILS/ArrayHelperUtil.php
*/
// o namespace deve corresponder à estrutura de diretórios e caso (observe que o diretório UTILS está todo em maiúsculas
// como na árvore de arquivos acima)
namespace app\UTILS;
class ArrayHelperUtil {
public function changeArrayCase(array $array) {
// fazer algo
}
}
Sublinhados em Nomes de Classe
A partir da versão 3.7.2, você pode usar Pascal_Snake_Case para os nomes de suas classes executando Loader::setV2ClassLoading(false);
.
Isso permitirá que você use sublinhados em seus nomes de classe.
Isso não é recomendado, mas está disponível para aqueles que precisam.
/**
* public/index.php
*/
// Adicionar um caminho ao carregador automático
Flight::path(__DIR__.'/../app/controllers/');
Flight::path(__DIR__.'/../app/utils/');
Loader::setV2ClassLoading(false);
/**
* app/controllers/My_Controller.php
*/
// sem necessidade de namespace
class My_Controller {
public function index() {
// fazer algo
}
}
Learn/troubleshooting
# Solução de problemas
Esta página irá ajudá-lo a resolver problemas comuns que você pode encontrar ao usar o Flight.
## Problemas Comuns
### 404 Não Encontrado ou Comportamento de Rota Inesperado
Se você estiver vendo um erro 404 Não Encontrado (mas jura pela sua vida que está realmente lá e não é um erro de digitação) isso poderia
realmente ser um problema com você retornando um valor no ponto final da sua rota em vez de apenas ecoá-lo. A razão para isso é intencional,
mas pode pegar alguns desenvolvedores de surpresa.
```php
Flight::route('/hello', function(){
// Isso pode causar um erro 404 Não Encontrado
return 'Olá Mundo';
});
// O que você provavelmente quer
Flight::route('/hello', function(){
echo 'Olá Mundo';
});
A razão para isso é por causa de um mecanismo especial incorporado no roteador que trata a saída de retorno como um sinal para "ir para a próxima rota". Você pode ver o comportamento documentado na seção de Roteamento.
Classe Não Encontrada (autoload não funcionando)
Podem haver algumas razões para isso não estar funcionando. Abaixo estão alguns exemplos, mas certifique-se também de verificar a seção de autoload.
Nome do Arquivo Incorreto
O mais comum é que o nome da classe não corresponda ao nome do arquivo.
Se você tem uma classe chamada MyClass
, então o arquivo deve ser chamado MyClass.php
. Se você tem uma classe chamada MyClass
e o arquivo é chamado myclass.php
então o autoload não será capaz de encontrá-lo.
Namespace Incorreto
Se estiver usando namespaces, então o namespace deve corresponder à estrutura de diretórios.
// código
// se o seu MyController estiver no diretório app/controllers e estiver em um namespace
// isso não funcionará
Flight::route('/hello', 'MyController->hello');
// você precisará escolher uma dessas opções
Flight::route('/hello', 'app\controllers\MyController->hello');
// ou se você tiver um comando use no topo
use app\controllers\MyController;
Flight::route('/hello', [ MyController::class, 'hello' ]);
// também pode ser escrito
Flight::route('/hello', MyController::class.'->hello');
// também...
Flight::route('/hello', [ 'app\controllers\MyController', 'hello' ]);
path()
não definido
No aplicativo skeleton, isso é definido dentro do arquivo config.php
, mas para que suas classes sejam encontradas, você precisa garantir que o método path()
esteja definido (provavelmente na raiz do seu diretório) antes de tentar usá-lo.
// Adicione um caminho ao autoload
Flight::path(__DIR__.'/../');
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ósRewriteEngine 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();
Guides/blog
Construindo um Blog Simples com Flight PHP
Este guia o orienta na criação de um blog básico usando o framework Flight PHP. Você configurará um projeto, definirá rotas, gerenciará postagens com JSON e as renderizará com o motor de templates Latte—todos mostrando a simplicidade e flexibilidade do Flight. Ao final, você terá um blog funcional com uma página inicial, páginas de postagens individuais e um formulário de criação.
Pré-requisitos
- PHP 7.4+: Instalado em seu sistema.
- Composer: Para gerenciamento de dependências.
- Editor de Texto: Qualquer editor como VS Code ou PHPStorm.
- Conhecimento básico de PHP e desenvolvimento web.
Passo 1: Configure Seu Projeto
Comece criando um novo diretório de projeto e instalando o Flight via Composer.
-
Criar um Diretório:
mkdir flight-blog cd flight-blog
-
Instalar o Flight:
composer require flightphp/core
-
Criar um Diretório Público: O Flight utiliza um único ponto de entrada (
index.php
). Crie uma pastapublic/
para ele:mkdir public
-
index.php
Básico: Criepublic/index.php
com uma rota simples de “hello world”:<?php require '../vendor/autoload.php'; Flight::route('/', function () { echo 'Olá, Flight!'; }); Flight::start();
-
Executar o Servidor Interno: Teste sua configuração com o servidor de desenvolvimento do PHP:
php -S localhost:8000 -t public/
Acesse
http://localhost:8000
para ver “Olá, Flight!”.
Passo 2: Organize a Estrutura do Seu Projeto
Para uma configuração limpa, estruture seu projeto da seguinte forma:
flight-blog/
├── app/
│ ├── config/
│ └── views/
├── data/
├── public/
│ └── index.php
├── vendor/
└── composer.json
app/config/
: Arquivos de configuração (por exemplo, eventos, rotas).app/views/
: Templates para renderização de páginas.data/
: Arquivo JSON para armazenar postagens do blog.public/
: Raiz da web comindex.php
.
Passo 3: Instalar e Configurar o Latte
O Latte é um motor de templates leve que se integra bem com o Flight.
-
Instalar o Latte:
composer require latte/latte
-
Configurar o Latte no Flight: Atualize
public/index.php
para registrar o Latte como o motor de visualização:<?php require '../vendor/autoload.php'; use Latte\Engine; Flight::register('view', Engine::class, [], function ($latte) { $latte->setTempDirectory(__DIR__ . '/../cache/'); $latte->setLoader(new \Latte\Loaders\FileLoader(__DIR__ . '/../app/views/')); }); Flight::route('/', function () { Flight::view()->render('home.latte', ['title' => 'Meu Blog']); }); Flight::start();
-
Criar um Template de Layout: Em
app/views/layout.latte
:<!DOCTYPE html> <html> <head> <title>{$title}</title> </head> <body> <header> <h1>Meu Blog</h1> <nav> <a href="/">Home</a> | <a href="/create">Criar uma Postagem</a> </nav> </header> <main> {block content}{/block} </main> <footer> <p>© {date('Y')} Blog Flight</p> </footer> </body> </html>
-
Criar um Template de Página Inicial: Em
app/views/home.latte
:{extends 'layout.latte'} {block content} <h2>{$title}</h2> <ul> {foreach $posts as $post} <li><a href="/post/{$post['slug']}">{$post['title']}</a></li> {/foreach} </ul> {/block}
Reinicie o servidor se você saiu dele e visite
http://localhost:8000
para ver a página renderizada. -
Criar um Arquivo de Dados:
Use um arquivo JSON para simular um banco de dados para simplicidade.
Em
data/posts.json
:[ { "slug": "primeira-postagem", "title": "Minha Primeira Postagem", "content": "Esta é minha primeira postagem no blog com Flight PHP!" } ]
Passo 4: Definir Rotas
Separe suas rotas em um arquivo de configuração para melhor organização.
-
Criar
routes.php
: Emapp/config/routes.php
:<?php Flight::route('/', function () { Flight::view()->render('home.latte', ['title' => 'Meu Blog']); }); Flight::route('/post/@slug', function ($slug) { Flight::view()->render('post.latte', ['title' => 'Post: ' . $slug, 'slug' => $slug]); }); Flight::route('GET /create', function () { Flight::view()->render('create.latte', ['title' => 'Criar uma Postagem']); });
-
Atualizar
index.php
: Inclua o arquivo de rotas:<?php require '../vendor/autoload.php'; use Latte\Engine; Flight::register('view', Engine::class, [], function ($latte) { $latte->setTempDirectory(__DIR__ . '/../cache/'); $latte->setLoader(new \Latte\Loaders\FileLoader(__DIR__ . '/../app/views/')); }); require '../app/config/routes.php'; Flight::start();
Passo 5: Armazenar e Recuperar Postagens do Blog
Adicione os métodos para carregar e salvar postagens.
-
Adicionar um Método de Postagens: Em
index.php
, adicione um método para carregar postagens:Flight::map('posts', function () { $file = __DIR__ . '/../data/posts.json'; return json_decode(file_get_contents($file), true); });
-
Atualizar Rotas: Modifique
app/config/routes.php
para usar postagens:<?php Flight::route('/', function () { $posts = Flight::posts(); Flight::view()->render('home.latte', [ 'title' => 'Meu Blog', 'posts' => $posts ]); }); Flight::route('/post/@slug', function ($slug) { $posts = Flight::posts(); $post = array_filter($posts, fn($p) => $p['slug'] === $slug); $post = reset($post) ?: null; if (!$post) { Flight::notFound(); return; } Flight::view()->render('post.latte', [ 'title' => $post['title'], 'post' => $post ]); }); Flight::route('GET /create', function () { Flight::view()->render('create.latte', ['title' => 'Criar uma Postagem']); });
Passo 6: Criar Templates
Atualize seus templates para exibir postagens.
-
Página de Postagem (
app/views/post.latte
):{extends 'layout.latte'} {block content} <h2>{$post['title']}</h2> <div class="post-content"> <p>{$post['content']}</p> </div> {/block}
Passo 7: Adicionar Criação de Postagens
Manipule a submissão de formulário para adicionar novas postagens.
-
Criar Formulário (
app/views/create.latte
):{extends 'layout.latte'} {block content} <h2>{$title}</h2> <form method="POST" action="/create"> <div class="form-group"> <label for="title">Título:</label> <input type="text" name="title" id="title" required> </div> <div class="form-group"> <label for="content">Conteúdo:</label> <textarea name="content" id="content" required></textarea> </div> <button type="submit">Salvar Postagem</button> </form> {/block}
-
Adicionar Rota POST: Em
app/config/routes.php
:Flight::route('POST /create', function () { $request = Flight::request(); $title = $request->data['title']; $content = $request->data['content']; $slug = strtolower(str_replace(' ', '-', $title)); $posts = Flight::posts(); $posts[] = ['slug' => $slug, 'title' => $title, 'content' => $content]; file_put_contents(__DIR__ . '/../../data/posts.json', json_encode($posts, JSON_PRETTY_PRINT)); Flight::redirect('/'); });
-
Teste:
- Acesse
http://localhost:8000/create
. - Envie uma nova postagem (por exemplo, “Segunda Postagem” com algum conteúdo).
- Verifique a página inicial para vê-la listada.
- Acesse
Passo 8: Melhorar com Tratamento de Erros
Sobreponha o método notFound
para uma melhor experiência de 404.
Em index.php
:
Flight::map('notFound', function () {
Flight::view()->render('404.latte', ['title' => 'Página Não Encontrada']);
});
Crie app/views/404.latte
:
{extends 'layout.latte'}
{block content}
<h2>404 - {$title}</h2>
<p>Desculpe, essa página não existe!</p>
{/block}
Próximos Passos
- Adicionar Estilo: Use CSS em seus templates para uma aparência melhor.
- Banco de Dados: Substitua
posts.json
por um banco de dados como SQLite usando oPdoWrapper
. - Validação: Adicione verificações para slugs duplicados ou entradas vazias.
- Middleware: Implemente autenticação para criação de postagens.
Conclusão
Você construiu um blog simples com o Flight PHP! Este guia demonstra recursos principais como roteamento, templating com Latte e manipulação de envios de formulários—tudo isso mantendo as coisas leves. Explore a documentação do Flight para recursos mais avançados para levar seu blog adiante!
License
The MIT License (MIT)
Direitos de autor © 2024
@mikecao, @n0nag0n
É concedida permissão, gratuitamente, a qualquer pessoa que obtenha uma cópia deste software e documentação associada (arquivo PDF), 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 que as pessoas a quem o software é fornecido o façam, sujeito às seguintes condições:
O aviso de direitos de autor acima e este aviso de permissão devem ser incluídos em todas as cópias ou partes 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 COMERCIALIZAÇÃO, ADEQUAÇÃO A UM FIM ESPECÍFICO E NÃO VIOLAÇÃO. EM NENHUM CASO OS AUTORES OU DETENTORES DE DIREITOS AUTORAIS SERÃO RESPONSÁVEIS POR QUALQUER REIVINDICAÇÃO, DANOS OU OUTRA RESPONSABILIDADE, SEJA EM UMA AÇÃO DE CONTRATO, DELITO OU DE OUTRA FORMA, DECORRENTE DE, FORA DE OU EM CONEXÃO COM O SOFTWARE OU O USO OU OUTROS ACORDOS NO SOFTWARE.
About
O que é Flight?
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. Foi criado com simplicidade em mente e é escrito de uma forma que é fácil de entender e usar.
Flight é um ótimo framework para iniciantes que estão novos no PHP e querem aprender como construir aplicações web. Também é um excelente framework para desenvolvedores experientes que desejam mais controle sobre suas aplicações web. É projetado para construir facilmente uma API RESTful, uma aplicação web simples ou uma aplicação web complexa.
Início Rápido
Primeiro, instale-o com o Composer
composer require flightphp/core
ou você pode baixar um zip do repositório aqui. Então você teria um arquivo básico index.php
como o seguinte:
<?php
// se instalado com composer
require 'vendor/autoload.php';
// ou se instalado manualmente por arquivo zip
// require 'flight/Flight.php';
Flight::route('/', function() {
echo 'hello world!';
});
Flight::route('/json', function() {
Flight::json(['hello' => 'world']);
});
Flight::start();
É isso! Você tem uma aplicação básica Flight. Agora você pode executar este arquivo com php -S localhost:8000
e visitar http://localhost:8000
em seu navegador para ver a saída.
É rápido?
Sim! Flight é rápido. É um dos frameworks PHP mais rápidos disponíveis. Você pode ver todos os benchmarks em TechEmpower
Veja o benchmark abaixo com alguns outros frameworks PHP populares.
Framework | Reqs/seg em texto simples | Reqs/seg em JSON |
---|---|---|
Flight | 190,421 | 182,491 |
Yii | 145,749 | 131,434 |
Fat-Free | 139,238 | 133,952 |
Slim | 89,588 | 87,348 |
Phalcon | 95,911 | 87,675 |
Symfony | 65,053 | 63,237 |
Lumen | 40,572 | 39,700 |
Laravel | 26,657 | 26,901 |
CodeIgniter | 20,628 | 19,901 |
Aplicativo Skeleton/Boilerplate
Há um aplicativo de exemplo que pode ajudá-lo a começar com o Framework Flight. Vá para flightphp/skeleton para instruções sobre como começar! Você também pode visitar a página exemplos para se inspirar sobre algumas das coisas que você pode fazer com Flight.
Comunidade
Estamos no Matrix Chat
E no Discord
Contribuindo
Existem duas maneiras de você contribuir para o Flight:
- Você pode contribuir para o framework principal visitando o repositório principal.
- Você pode contribuir para a documentação. Este site de documentação está hospedado no Github. Se você notar um erro ou quiser melhorar algo, fique à vontade para corrigir e enviar um pull request! Tentamos nos manter atualizados, mas atualizações e traduções são bem-vindas.
Requisitos
Flight requer PHP 7.4 ou superior.
Nota: PHP 7.4 é suportado porque, no momento da redação (2024), o PHP 7.4 é a versão padrão para algumas distribuições Linux LTS. Forçar uma mudança para PHP >8 causaria muitos problemas para esses usuários. O framework também suporta PHP >8.
Licença
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
flightphp/cache
Classe de armazenamento em cache em arquivo PHP leve, simples e autônoma
Vantagens
- Leve, autônoma e simples
- Todo o código em um arquivo - sem drivers desnecessários.
- Seguro - cada arquivo de cache gerado possui um cabeçalho php com die, tornando o acesso direto impossível mesmo que alguém conheça o caminho e seu servidor não esteja configurado corretamente
- Bem documentado e testado
- Manipula a concorrência corretamente via flock
- Suporta PHP 7.4+
- Gratuito sob uma licença MIT
Este site de documentação está usando esta biblioteca para armazenar em cache cada uma das páginas!
Clique aqui para ver o código.
Instalação
Instale via composer:
composer require flightphp/cache
Uso
O uso é bastante simples. Isso salva um arquivo de cache no diretório de cache.
use flight\Cache;
$app = Flight::app();
// Você passa o diretório onde o cache será armazenado no construtor
$app->register('cache', Cache::class, [ __DIR__ . '/../cache/' ], function(Cache $cache) {
// Isso garante que o cache seja usado apenas quando estiver em modo de produção
// ENVIRONMENT é uma constante definida no seu arquivo de bootstrap ou em outro lugar no seu aplicativo
$cache->setDevMode(ENVIRONMENT === 'development');
});
Então você pode usá-lo no 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/flightphp/cache para a documentação completa e certifique-se de ver a pasta exemplos.
Awesome-plugins/permissions
FlightPHP/Permissões
Este é um módulo de permissões que pode ser usado em seus projetos se você tiver vários papéis em seu aplicativo e cada papel tiver funcionalidades um pouco diferentes. Este módulo permite que você defina permissões para cada papel e depois verifique se o usuário atual tem permissão para acessar uma determinada página ou realizar uma determinada ação.
Clique aqui para acessar o repositório no GitHub.
Instalação
Execute composer require flightphp/permissions
e pronto!
Uso
Primeiro, você precisa configurar suas permissões e, em seguida, informar ao seu aplicativo o que as permissões significam. Por fim, você verificará suas permissões com $Permissions->has()
, ->can()
ou is()
. has()
e can()
têm a mesma funcionalidade, mas têm nomes diferentes para tornar seu código mais legível.
Exemplo Básico
Vamos supor que você tenha um recurso em seu aplicativo que verifica se um usuário está conectado. Você pode criar um objeto de permissões assim:
// index.php
require 'vendor/autoload.php';
// algum código
// então você provavelmente tem algo que diz qual é o papel atual da pessoa
// provavelmente você tem algo que puxa o papel atual
// de uma variável de sessão que define isso
// depois que alguém faz o login, caso contrário eles terão um papel 'convidado' ou 'público'.
$current_role = 'admin';
// configurar permissões
$permission = new \flight\Permission($current_role);
$permission->defineRule('conectado', function($current_role) {
return $current_role !== 'convidado';
});
// Você provavelmente vai querer persistir este objeto em algum lugar do Flight
Flight::set('permissão', $permissão);
Então em um controlador em algum lugar, você pode ter algo assim.
<?php
// algum controlador
class AlgunsController {
public function algumaAção() {
$permissão = Flight::get('permissão');
if ($permissão->has('conectado')) {
// faça algo
} else {
// faça algo diferente
}
}
}
Você também pode usar isso para rastrear se têm permissão para fazer algo em seu aplicativo. Por exemplo, se você tiver uma forma dos usuários interagirem com postagens em seu software, você pode verificar se eles têm permissão para realizar certas ações.
$current_role = 'admin';
// configurar permissões
$permission = new \flight\Permission($current_role);
$permission->defineRule('postagem', function($current_role) {
if($current_role === 'admin') {
$permissões = ['criar', 'ler', 'atualizar', 'apagar'];
} else if($current_role === 'editor') {
$permissões = ['criar', 'ler', 'atualizar'];
} else if($current_role === 'autor') {
$permissões = ['criar', 'ler'];
} else if($current_role === 'contribuidor') {
$permissões = ['criar'];
} else {
$permissões = [];
}
return $permissões;
});
Flight::set('permissão', $permissão);
Então em um controlador em algum lugar...
class ControladorDePostagem {
public function criar() {
$permissão = Flight::get('permissão');
if ($permissão->can('postagem.criar')) {
// faça algo
} else {
// faça algo diferente
}
}
}
Injetando dependências
Você pode injetar dependências no fechamento que define as permissões. Isso é útil se você tiver algum tipo de alternância, id ou qualquer outro ponto de dados que deseja verificar. O mesmo funciona para chamadas de Tipo Classe->Método, exceto que você define os argumentos no método.
Fechamentos
$Permission->defineRule('pedido', function(string $current_role, MyDependency $MyDependency = null) {
// ... código
});
// em seu arquivo de controlador
public function criarPedido() {
$MyDependency = Flight::myDependency();
$permissão = Flight::get('permissão');
if ($permissão->can('pedido.criar', $MyDependency)) {
// faça algo
} else {
// faça algo diferente
}
}
Classes
namespace MyApp;
class Permissões {
public function pedido(string $current_role, MyDependency $MyDependency = null) {
// ... código
}
}
Atalho para definir permissões com classes
Você também pode usar classes para definir suas permissões. Isso é útil se você tiver muitas permissões e quiser manter seu código limpo. Você pode fazer algo assim:
<?php
// código de inicialização
$Permissões = new \flight\Permission($current_role);
$Permissões->defineRule('pedido', 'MyApp\Permissões->pedido');
// myapp/Permissões.php
namespace MyApp;
class Permissões {
public function pedido(string $current_role, int $id_usuario) {
// Pressupondo que você configurou isso antecipadamente
/** @var \flight\database\PdoWrapper $db */
$db = Flight::db();
$permissões_permitidas = [ 'ler' ]; // todos podem visualizar um pedido
if($current_role === 'gerente') {
$permissões_permitidas[] = 'criar'; // gerentes podem criar pedidos
}
$alguma_alternancia_especial_do_db = $db->fetchField('SELECT alguma_alternancia_especial FROM ajustes WHERE id = ?', [ $id_usuario ]);
if($alguma_alternancia_especial_do_db) {
$permissões_permitidas[] = 'atualizar'; // se o usuário tiver uma alternância especial, ele pode atualizar pedidos
}
if($current_role === 'admin') {
$permissões_permitidas[] = 'apagar'; // administradores podem excluir pedidos
}
return $permissões_permitidas;
}
}
A parte legal é que também há um atalho que você pode usar (que também pode ser cacheado!!!) onde você apenas diz à classe de permissões para mapear todos os métodos de uma classe em permissões. Portanto, se você tiver um método chamado pedido()
e um método chamado empresa()
, esses serão mapeados automaticamente para que você possa simplesmente executar $Permissões->has('pedido.ler')
ou `$Permissões->has('empresa.ler') e isso funcionará. Definir isso é muito difícil, então fique comigo aqui. Você só precisa fazer o seguinte:
Crie a classe de permissões que deseja agrupar.
class MinhasPermissões {
public function pedido(string $current_role, int $id_pedido = 0): array {
// código para determinar permissões
return $array_de_permissoes;
}
public function empresa(string $current_role, int $id_empresa): array {
// código para determinar permissões
return $array_de_permissoes;
}
}
Em seguida, torne as permissões descobríveis usando esta biblioteca.
$Permissões = new \flight\Permission($current_role);
$Permissões->defineRulesFromClassMethods(MyApp\Permissões::class);
Flight::set('permissões', $Permissões);
Finalmente, chame a permissão em sua base de código para verificar se o usuário tem permissão para realizar uma determinada permissão.
class AlgunsControlador {
public function criarPedido() {
if(Flight::get('permissões')->can('pedido.criar') === false) {
die('Você não pode criar um pedido. Desculpe!');
}
}
}
Cache
Para habilitar o cache, consulte a simples biblioteca wruczak/phpfilecache. Um exemplo de habilitação está abaixo.
// este $app pode fazer parte do seu código, ou
// você pode simplesmente passar null e ele irá
// puxar de Flight::app() no construtor
$app = Flight::app();
// Por enquanto, aceita isso como um cache de arquivo. Outros podem ser facilmente
// adicionados no futuro.
$Cache = new Wruczek\PhpFileCache\PhpFileCache;
$Permissões = new \flight\Permission($current_role, $app, $Cache);
$Permissões->defineRulesFromClassMethods(MyApp\Permissões::class, 3600); // 3600 é quantos segundos para armazenar em cache. Deixe isso de fora para não usar o cache
Awesome-plugins/simple_job_queue
Fila de Trabalho Simples
A Fila de Trabalho Simples é uma biblioteca que pode ser usada para processar trabalhos de forma assíncrona. Pode ser usada com beanstalkd, MySQL/MariaDB, SQLite e PostgreSQL.
Instalação
composer require n0nag0n/simple-job-queue
Uso
Para que isso funcione, você precisa de uma maneira de adicionar trabalhos à fila e uma maneira de processar os trabalhos (um trabalhador). Abaixo estão exemplos de como adicionar um trabalho à fila e como processar o trabalho.
Adicionando ao Flight
Adicionar isso ao Flight é simples e é feito usando o método register()
. Abaixo está um exemplo de como adicionar isso ao Flight.
<?php
require 'vendor/autoload.php';
// Mude ['mysql'] para ['beanstalkd'] se você quiser usar beanstalkd
Flight::register('queue', n0nag0n\Job_Queue::class, ['mysql'], function($Job_Queue) {
// se você já tiver uma conexão PDO em Flight::db();
$Job_Queue->addQueueConnection(Flight::db());
// ou se você estiver usando beanstalkd/Pheanstalk
$pheanstalk = Pheanstalk\Pheanstalk::create('127.0.0.1');
$Job_Queue->addQueueConnection($pheanstalk);
});
Adicionando um novo trabalho
Quando você adiciona um trabalho, precisa especificar um pipeline (fila). Isso é comparável a um canal no RabbitMQ ou um tubo no beanstalkd.
<?php
Flight::queue()->selectPipeline('send_important_emails');
Flight::queue()->addJob(json_encode([ 'something' => 'that', 'ends' => 'up', 'a' => 'string' ]));
Executando um trabalhador
Aqui está um arquivo de exemplo de como executar um trabalhador.
<?php
require 'vendor/autoload.php';
$Job_Queue = new n0nag0n\Job_Queue('mysql');
// Conexão PDO
$PDO = new PDO('mysql:dbname=testdb;host=127.0.0.1', 'user', 'pass');
$Job_Queue->addQueueConnection($PDO);
// ou se você estiver usando beanstalkd/Pheanstalk
$pheanstalk = Pheanstalk\Pheanstalk::create('127.0.0.1');
$Job_Queue->addQueueConnection($pheanstalk);
$Job_Queue->watchPipeline('send_important_emails');
while(true) {
$job = $Job_Queue->getNextJobAndReserve();
// ajuste para o que te faz dormir melhor à noite (para filas de banco de dados apenas, beanstalkd não precisa dessa instrução if)
if(empty($job)) {
usleep(500000);
continue;
}
echo "Processando {$job['id']}\n";
$payload = json_decode($job['payload'], true);
try {
$result = doSomethingThatDoesSomething($payload);
if($result === true) {
$Job_Queue->deleteJob($job);
} else {
// isso o tira da fila de prontos e o coloca em outra fila que pode ser coletada e "chutada" mais tarde.
$Job_Queue->buryJob($job);
}
} catch(Exception $e) {
$Job_Queue->buryJob($job);
}
}
Manipulando Processos Longos com Supervisord
O Supervisord é um sistema de controle de processos que garante que seus processos de trabalhadores permaneçam em execução continuamente. Aqui está um guia mais completo sobre como configurá-lo com seu trabalhador da Fila de Trabalho Simples:
Instalando o Supervisord
# No Ubuntu/Debian
sudo apt-get install supervisor
# No CentOS/RHEL
sudo yum install supervisor
# No macOS com Homebrew
brew install supervisor
Criando um Script de Trabalhador
Primeiro, salve seu código de trabalhador em um arquivo PHP dedicado:
<?php
require 'vendor/autoload.php';
$Job_Queue = new n0nag0n\Job_Queue('mysql');
// Conexão PDO
$PDO = new PDO('mysql:dbname=your_database;host=127.0.0.1', 'username', 'password');
$Job_Queue->addQueueConnection($PDO);
// Defina o pipeline a ser monitorado
$Job_Queue->watchPipeline('send_important_emails');
// Registre o início do trabalhador
echo date('Y-m-d H:i:s') . " - Trabalhador iniciado\n";
while(true) {
$job = $Job_Queue->getNextJobAndReserve();
if(empty($job)) {
usleep(500000); // Durma por 0,5 segundos
continue;
}
echo date('Y-m-d H:i:s') . " - Processando trabalho {$job['id']}\n";
$payload = json_decode($job['payload'], true);
try {
$result = doSomethingThatDoesSomething($payload);
if($result === true) {
$Job_Queue->deleteJob($job);
echo date('Y-m-d H:i:s') . " - Trabalho {$job['id']} completado com sucesso\n";
} else {
$Job_Queue->buryJob($job);
echo date('Y-m-d H:i:s') . " - Trabalho {$job['id']} falhou, enterrado\n";
}
} catch(Exception $e) {
$Job_Queue->buryJob($job);
echo date('Y-m-d H:i:s') . " - Exceção ao processar o trabalho {$job['id']}: {$e->getMessage()}\n";
}
}
Configurando o Supervisord
Crie um arquivo de configuração para seu trabalhador:
[program:email_worker]
command=php /path/to/worker.php
directory=/path/to/project
autostart=true
autorestart=true
startretries=3
stderr_logfile=/var/log/simple_job_queue_err.log
stdout_logfile=/var/log/simple_job_queue.log
user=www-data
numprocs=2
process_name=%(program_name)s_%(process_num)02d
Principais Opções de Configuração:
command
: O comando para executar seu trabalhadordirectory
: Diretório de trabalho para o trabalhadorautostart
: Iniciar automaticamente quando o supervisord iniciarautorestart
: Reiniciar automaticamente se o processo sairstartretries
: Número de tentativas para reiniciar se falharstderr_logfile
/stdout_logfile
: Localizações dos arquivos de loguser
: Usuário do sistema para executar o processonumprocs
: Número de instâncias de trabalhador a serem executadasprocess_name
: Formato de nomenclatura para vários processos de trabalhadores
Gerenciando Trabalhadores com Supervisorctl
Após criar ou modificar a configuração:
# Recarregar a configuração do supervisor
sudo supervisorctl reread
sudo supervisorctl update
# Controlar processos de trabalhadores específicos
sudo supervisorctl start email_worker:*
sudo supervisorctl stop email_worker:*
sudo supervisorctl restart email_worker:*
sudo supervisorctl status email_worker:*
Executando Múltiplos Pipelines
Para múltiplos pipelines, crie arquivos de trabalhadores e configurações separados:
[program:email_worker]
command=php /path/to/email_worker.php
# ... outras configurações ...
[program:notification_worker]
command=php /path/to/notification_worker.php
# ... outras configurações ...
Monitoramento e Logs
Verifique os logs para monitorar a atividade dos trabalhadores:
# Ver logs
sudo tail -f /var/log/simple_job_queue.log
# Verificar status
sudo supervisorctl status
Essa configuração garante que seus trabalhadores de tarefas continuem em execução mesmo após falhas, reinicializações de servidor ou outros problemas, tornando seu sistema de fila confiável para ambientes de produção.
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.
- Wruczek/PHP-File-Cache - Classe de armazenamento em arquivo PHP leve, simples e independente.
Debugging
Depurar é crucial quando você está desenvolvendo no seu ambiente local. Existem alguns plugins que podem aprimorar a sua experiência de depuração.
- tracy/tracy - Este é um manipulador de erros completo que pode ser usado com o Flight. Possui várias painéis que podem ajudá-lo a depurar a sua aplicação. Também é muito fácil de estender e adicionar os seus próprios painéis.
- flightphp/tracy-extensions - Usado com o manipulador de erros Tracy, este plugin adiciona alguns painéis extras para ajudar na depuração especificamente em projetos do Flight.
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.
- flightphp/core PdoWrapper - Wrapper PDO Oficial do Flight que faz parte do core. Este é um wrapper simples para ajudar a simplificar o processo de escrever consultas e executá-las. Não é um ORM.
- flightphp/active-record - ORM/Mapper Active Record oficial do Flight. Ótima pequena biblioteca para recuperar e armazenar dados facilmente no seu banco de dados.
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.
- Ghostff/Session - Gerenciador de Sessão PHP (não bloqueante, flash, segmento, criptografia de sessão). Usa o open_ssl do PHP para criptografar/criptografar opcionalmente os dados da sessão.
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.
- flightphp/core View - Esta é uma engine de templates muito básica que faz parte do core. Não é recomendado para uso se você tiver mais do que algumas páginas no seu projeto.
- latte/latte - Latte é uma engine de templates completa que é muito fácil de usar e se aproxima mais de uma sintaxe PHP do que Twig ou Smarty. Também é muito fácil de estender e adicionar os seus próprios filtros e funções.
Contribuindo
Tem um plugin que gostaria de compartilhar? Envie uma solicitação pull para adicioná-lo à lista!
Awesome-plugins/ghost_session
Ghostff/Session
Gerenciador de Sessões PHP (não bloqueante, flash, segmento, criptografia de sessão). Usa PHP open_ssl para criptografia/descriptografia opcional de dados da sessão. Suporta File, MySQL, Redis e Memcached.
Clique aqui para ver o código.
Instalação
Instale com o composer.
composer require ghostff/session
Configuração Básica
Você não precisa passar nada para usar as configurações padrão com sua sessão. Você pode ler sobre mais configurações no Github Readme.
use Ghostff\Session\Session;
require 'vendor/autoload.php';
$app = Flight::app();
$app->register('session', Session::class);
// uma coisa a lembrar é que você deve confirmar sua sessão em cada carregamento de página
// ou precisará executar auto_commit em sua configuração.
Exemplo Simples
Aqui está um exemplo simples de como você pode usar isso.
Flight::route('POST /login', function() {
$session = Flight::session();
// faça sua lógica de login aqui
// valide a senha, etc.
// se o login for bem-sucedido
$session->set('is_logged_in', true);
$session->set('user', $user);
// a cada vez que você escreve na sessão, você deve confirmá-la deliberadamente.
$session->commit();
});
// Esta verificação pode estar na lógica da página restrita ou encapsulada com middleware.
Flight::route('/some-restricted-page', function() {
$session = Flight::session();
if(!$session->get('is_logged_in')) {
Flight::redirect('/login');
}
// faça sua lógica de página restrita aqui
});
// a versão middleware
Flight::route('/some-restricted-page', function() {
// lógica da página regular
})->addMiddleware(function() {
$session = Flight::session();
if(!$session->get('is_logged_in')) {
Flight::redirect('/login');
}
});
Exemplo Mais Complexo
Aqui está um exemplo mais complexo de como você pode usar isso.
use Ghostff\Session\Session;
require 'vendor/autoload.php';
$app = Flight::app();
// defina um caminho personalizado para o seu arquivo de configuração de sessão e dê a ele uma string aleatória para o id da sessão
$app->register('session', Session::class, [ 'path/to/session_config.php', bin2hex(random_bytes(32)) ], function(Session $session) {
// ou você pode sobrescrever 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 "me desconectar de todos os dispositivos")
Session::CONFIG_DRIVER => Ghostff\Session\Drivers\MySql::class,
Session::CONFIG_ENCRYPT_DATA => true,
Session::CONFIG_SALT_KEY => hash('sha256', 'my-super-S3CR3T-salt'), // por favor, mude isto para ser algo diferente
Session::CONFIG_AUTO_COMMIT => true, // faça isso apenas se for necessário e/ou for difícil confirmar sua sessão.
// além disso, você poderia fazer Flight::after('start', function() { Flight::session()->commit(); });
Session::CONFIG_MYSQL_DS => [
'driver' => 'mysql', # Driver do banco de dados para PDO dns eg(mysql:host=...;dbname=...)
'host' => '127.0.0.1', # Host do banco de dados
'db_name' => 'my_app_database', # Nome do banco de dados
'db_table' => 'sessions', # Tabela do banco de dados
'db_user' => 'root', # Nome de usuário do banco de dados
'db_pass' => '', # Senha do banco de dados
'persistent_conn'=> false, # Evitar a sobrecarga de estabelecer uma nova conexão toda vez que um script precisa se comunicar com um banco de dados, resultando em um aplicativo web mais rápido. ENCONTRE O LADO TRASEIRO VOCÊ MESMO
]
]);
}
);
Ajuda! Meus Dados de Sessão Não Estão Persistindo!
Você está definindo seus dados de sessão e eles não estão persistindo entre solicitações? Você pode ter se esquecido de confirmar seus dados de sessão. Você pode fazer isso chamando $session->commit()
depois de definir seus dados de sessão.
Flight::route('POST /login', function() {
$session = Flight::session();
// faça sua lógica de login aqui
// valide a senha, etc.
// se o login for bem-sucedido
$session->set('is_logged_in', true);
$session->set('user', $user);
// a cada vez que você escreve na sessão, você deve confirmá-la deliberadamente.
$session->commit();
});
A outra maneira de contornar isso é quando você configura seu serviço de sessão, você deve definir auto_commit
como true
em sua configuração. Isso confirmará automaticamente seus dados de sessão após cada solicitação.
$app->register('session', Session::class, [ 'path/to/session_config.php', bin2hex(random_bytes(32)) ], function(Session $session) {
$session->updateConfiguration([
Session::CONFIG_AUTO_COMMIT => true,
]);
}
);
Além disso, você poderia fazer Flight::after('start', function() { Flight::session()->commit(); });
para confirmar seus dados de sessão após cada solicitação.
Documentação
Visite o Github Readme para documentação completa. As opções de configuração estão bem documentadas em default_config.php no próprio arquivo. O código é simples de entender se você quiser examinar este pacote por conta própria.
Awesome-plugins/pdo_wrapper
Awesome-plugins/migrations
Migrations
Uma migração para o seu projeto é o acompanhamento de todas as alterações de banco de dados envolvidas no seu projeto. byjg/php-migration é uma biblioteca central muito útil para começar.
Instalando
Biblioteca PHP
Se você quiser usar apenas a Biblioteca PHP em seu projeto:
composer require "byjg/migration"
Interface de Linha de Comando
A interface de linha de comando é autônoma e não requer instalação com seu projeto.
Você pode instalar globalmente e criar um link simbólico
composer require "byjg/migration-cli"
Por favor, visite byjg/migration-cli para obter mais informações sobre Migration CLI.
Bancos de dados suportados
Banco de dados | Driver | String de Conexão |
---|---|---|
Sqlite | pdo_sqlite | sqlite:///caminho/para/arquivo |
MySql/MariaDb | pdo_mysql | mysql://usuario:senha@hostname:porta/banco |
Postgres | pdo_pgsql | pgsql://usuario:senha@hostname:porta/banco |
Sql Server | pdo_dblib, pdo_sysbase Linux | dblib://usuario:senha@hostname:porta/banco |
Sql Server | pdo_sqlsrv Windows | sqlsrv://usuario:senha@hostname:porta/banco |
Como Funciona?
A Migração de Banco de Dados utiliza SQL PURO para gerenciar a versionamento do banco de dados. Para funcionar, você precisa de:
- Criar os Scripts SQL
- Gerenciar usando a Linha de Comando ou a API.
Os Scripts SQL
Os scripts são divididos em três conjuntos de scripts:
- O script BASE contém TODOS os comandos sql para criar um banco de dados novo;
- Os scripts UP contêm todos os comandos de migração sql para "subir" a versão do banco de dados;
- Os scripts DOWN contêm todos os comandos de migração sql para "descer" ou reverter a versão do banco de dados;
O diretório dos scripts é:
<diretório raiz>
|
+-- base.sql
|
+-- /migrations
|
+-- /up
|
+-- 00001.sql
+-- 00002.sql
+-- /down
|
+-- 00000.sql
+-- 00001.sql
- "base.sql" é o script base
- A pasta "up" contém os scripts para migrar para cima na versão. Por exemplo: 00002.sql é o script para mover o banco de dados da versão '1' para '2'.
- A pasta "down" contém os scripts para migrar para baixo na versão. Por exemplo: 00001.sql é o script para mover o banco de dados da versão '2' para '1'. A pasta "down" é opcional.
Ambiente de Desenvolvimento Múltiplo
Se você trabalha com vários desenvolvedores e múltiplas ramificações, pode ser muito difícil determinar qual é o próximo número.
Nesse caso, você tem o sufixo "-dev" após o número da versão.
Veja o cenário:
- O Desenvolvedor 1 cria uma ramificação e a versão mais recente é, por exemplo, 42.
- O Desenvolvedor 2 cria uma ramificação ao mesmo tempo e tem o mesmo número de versão do banco de dados.
Em ambos os casos, os desenvolvedores criarão um arquivo chamado 43-dev.sql. Ambos os desenvolvedores migrarão para CIMA e para BAIXO sem problemas e sua versão local será 43.
Mas o desenvolvedor 1 mesclou suas alterações e criou uma versão final 43.sql (git mv 43-dev.sql 43.sql
). Se o desenvolvedor 2
atualizar sua ramificação local, ele terá um arquivo 43.sql (do dev 1) e seu arquivo 43-dev.sql.
Se ele tentar migrar para CIMA ou para BAIXO,
o script de migração irá falhar e alertá-lo que há DUAS versões 43. Nesse caso, o desenvolvedor 2 terá que atualizar seu
arquivo para 44-dev.sql e continuar a trabalhar até mesclar suas alterações e gerar uma versão final.
Usando a API PHP e Integrando-a em seus projetos
O uso básico é
- Criar uma conexão com um objeto ConnectionManagement. Para mais informações, veja o componente "byjg/anydataset".
- Criar um objeto Migration com essa conexão e a pasta onde os scripts sql estão localizados.
- Use o comando apropriado para "resetar", "subir" ou "descer" os scripts de migração.
Veja um exemplo:
<?php
// Criar o URI de Conexão
// Veja mais: https://github.com/byjg/anydataset#connection-based-on-uri
$connectionUri = new \ByJG\Util\Uri('mysql://migrateuser:migratepwd@localhost/migratedatabase');
// Registrar o Banco de Dados ou Bancos de Dados que podem manipular esse URI:
\ByJG\DbMigration\Migration::registerDatabase(\ByJG\DbMigration\Database\MySqlDatabase::class);
// Criar a instância de Migração
$migration = new \ByJG\DbMigration\Migration($connectionUri, '.');
// Adicionar uma função de progresso de callback para receber informações da execução
$migration->addCallbackProgress(function ($action, $currentVersion, $fileInfo) {
echo "$action, $currentVersion, ${fileInfo['description']}\n";
});
// Restaurar o banco de dados usando o script "base.sql"
// e executar TODOS os scripts existentes para subir a versão do banco de dados para a versão mais recente
$migration->reset();
// Executar TODOS os scripts existentes para subir ou descer a versão do banco de dados
// da versão atual até o número $version;
// Se o número da versão não for especificado, migrar até a última versão do banco de dados
$migration->update($version = null);
O objeto Migration controla a versão do banco de dados.
Criando um controle de versão em seu projeto
<?php
// Registrar o Banco de Dados ou Bancos de Dados que podem manipular esse URI:
\ByJG\DbMigration\Migration::registerDatabase(\ByJG\DbMigration\Database\MySqlDatabase::class);
// Criar a instância de Migração
$migration = new \ByJG\DbMigration\Migration($connectionUri, '.');
// Este comando criará a tabela de versões em seu banco de dados
$migration->createVersion();
Obtendo a versão atual
<?php
$migration->getCurrentVersion();
Adicionar Callback para controlar o progresso
<?php
$migration->addCallbackProgress(function ($command, $version, $fileInfo) {
echo "Executando Comando: $command na versão $version - ${fileInfo['description']}, ${fileInfo['exists']}, ${fileInfo['file']}, ${fileInfo['checksum']}\n";
});
Obtendo a instância do Driver de Db
<?php
$migration->getDbDriver();
Para utilizá-lo, visite: https://github.com/byjg/anydataset-db
Evitando Migração Parcial (não disponível para MySQL)
Uma migração parcial é quando o script de migração é interrompido no meio do processo devido a um erro ou uma interrupção manual.
A tabela de migração ficará com o status partial up
ou partial down
e precisará ser corrigida manualmente antes que possa ser migrada novamente.
Para evitar essa situação, você pode especificar que a migração será executada em um contexto transacional.
Se o script de migração falhar, a transação será revertida e a tabela de migração será marcada como complete
e
a versão será a imediatamente anterior antes do script que causou o erro.
Para habilitar esse recurso, você precisa chamar o método withTransactionEnabled
passando true
como parâmetro:
<?php
$migration->withTransactionEnabled(true);
NOTA: Este recurso não está disponível para MySQL, pois não suporta comandos DDL dentro de uma transação. Se você usar este método com MySQL, a Migração irá ignorá-lo silenciosamente. Mais informações: https://dev.mysql.com/doc/refman/8.0/en/cannot-roll-back.html
Dicas para escrever migrações SQL para Postgres
Ao criar gatilhos e funções SQL
-- FAÇA
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- Verifique se empname e salary foram fornecidos
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname não pode ser nulo'; -- não importa se esses comentários estão em branco ou não
END IF; --
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% não pode ter salário nulo', NEW.empname; --
END IF; --
-- Quem trabalha para nós quando eles devem pagar por isso?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% não pode ter um salário negativo', NEW.empname; --
END IF; --
-- Lembre-se de quem mudou a folha de pagamento quando
NEW.last_date := current_timestamp; --
NEW.last_user := current_user; --
RETURN NEW; --
END; --
$emp_stamp$ LANGUAGE plpgsql;
-- NÃO FAÇA
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- Verifique se empname e salary foram fornecidos
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname não pode ser nulo';
END IF;
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% não pode ter salário nulo', NEW.empname;
END IF;
-- Quem trabalha para nós quando eles devem pagar por isso?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% não pode ter um salário negativo', NEW.empname;
END IF;
-- Lembre-se de quem mudou a folha de pagamento quando
NEW.last_date := current_timestamp;
NEW.last_user := current_user;
RETURN NEW;
END;
$emp_stamp$ LANGUAGE plpgsql;
Como a camada de abstração de banco de dados PDO
não pode executar lotes de instruções SQL,
quando byjg/migration
lê um arquivo de migração, ele tem que dividir todo o conteúdo do arquivo SQL nos ponto e vírgula e executar as instruções uma a uma. No entanto, há um tipo de instrução que pode ter múltiplos ponto e vírgula em seu corpo: funções.
Para ser capaz de analisar funções corretamente, byjg/migration
2.1.0 começou a dividir arquivos de migração na sequência ponto e vírgula + EOL
em vez de apenas no ponto e vírgula. Dessa forma, se você adicionar um comentário vazio após cada ponto e vírgula interno de uma definição de função, byjg/migration
poderá analisá-lo.
Infelizmente, se você esquecer de adicionar algum desses comentários, a biblioteca dividirá a instrução CREATE FUNCTION
em várias partes e a migração falhará.
Evitar o caractere de dois-pontos (:
)
-- FAÇA
CREATE TABLE bookings (
booking_id UUID PRIMARY KEY,
booked_at TIMESTAMPTZ NOT NULL CHECK (CAST(booked_at AS DATE) <= check_in),
check_in DATE NOT NULL
);
-- NÃO FAÇA
CREATE TABLE bookings (
booking_id UUID PRIMARY KEY,
booked_at TIMESTAMPTZ NOT NULL CHECK (booked_at::DATE <= check_in),
check_in DATE NOT NULL
);
Como o PDO
usa o caractere dois-pontos para prefixar parâmetros nomeados em instruções preparadas, seu uso causará problemas em outros contextos.
Por exemplo, as instruções PostgreSQL podem usar ::
para converter valores entre tipos. Por outro lado, o PDO
lerá isso como um parâmetro nomeado inválido em um contexto inválido e falhará ao tentar executá-lo.
A única maneira de corrigir essa inconsistência é evitar completamente os dois-pontos (neste caso, o PostgreSQL também tem uma sintaxe alternativa: CAST(value AS type)
).
Use um editor SQL
Finalmente, escrever migrações SQL manuais pode ser cansativo, mas é significativamente mais fácil se você usar um editor capaz de entender a sintaxe SQL, fornecendo autocompletar, introspectando seu esquema de banco de dados atual e/ou autoformatando seu código.
Lidar com diferentes migrações dentro de um esquema
Se você precisar criar diferentes scripts de migração e versões dentro do mesmo esquema, é possível mas é muito arriscado e eu não recomendo de forma alguma.
Para fazer isso, você precisa criar diferentes "tabelas de migração" passando o parâmetro para o construtor.
<?php
$migration = new \ByJG\DbMigration\Migration("db:/uri", "/path", true, "NEW_MIGRATION_TABLE_NAME");
Por razões de segurança, esse recurso não está disponível na linha de comando, mas você pode usar a variável de ambiente
MIGRATION_VERSION
para armazenar o nome.
Recomendamos fortemente não usar esse recurso. A recomendação é uma migração para um esquema.
Executando Testes Unitários
Testes unitários básicos podem ser executados com:
vendor/bin/phpunit
Executando testes de banco de dados
Executar testes de integração requer que você tenha os bancos de dados em funcionamento. Fornecemos um básico docker-compose.yml
e você
pode usá-lo para iniciar os bancos de dados para testes.
Executando os bancos de dados
docker-compose up -d postgres mysql mssql
Executar os testes
vendor/bin/phpunit
vendor/bin/phpunit tests/SqliteDatabase*
vendor/bin/phpunit tests/MysqlDatabase*
vendor/bin/phpunit tests/PostgresDatabase*
vendor/bin/phpunit tests/SqlServerDblibDatabase*
vendor/bin/phpunit tests/SqlServerSqlsrvDatabase*
Opcionalmente, você pode definir o host e a senha usados pelos testes unitários
export MYSQL_TEST_HOST=localhost # padrão é localhost
export MYSQL_PASSWORD=newpassword # use '.' se quiser ter uma senha nula
export PSQL_TEST_HOST=localhost # padrão é localhost
export PSQL_PASSWORD=newpassword # use '.' se quiser ter uma senha nula
export MSSQL_TEST_HOST=localhost # padrão é localhost
export MSSQL_PASSWORD=Pa55word
export SQLITE_TEST_HOST=/tmp/test.db # padrão é /tmp/test.db
Awesome-plugins/session
FlightPHP Sessão - Manipulador de Sessão Leve Baseado em Arquivo
Este é um plugin de manipulador de sessão leve e baseado em arquivo para o Flight PHP Framework. Ele fornece uma solução simples, mas poderosa para gerenciar sessões, com recursos como leituras de sessão não bloqueantes, criptografia opcional, funcionalidade de auto-confirmação e um modo de teste para desenvolvimento. Os dados da sessão são armazenados em arquivos, tornando-o ideal para aplicativos que não exigem um banco de dados.
Se você quiser usar um banco de dados, confira o plugin ghostff/session com muitas dessas mesmas características, mas com um backend de banco de dados.
Visite o repositório do Github para o código-fonte completo e detalhes.
Instalação
Instale o plugin via Composer:
composer require flightphp/session
Uso Básico
Aqui está um exemplo simples de como usar o plugin flightphp/session
em sua aplicação Flight:
require 'vendor/autoload.php';
use flight\Session;
$app = Flight::app();
// Registra o serviço de sessão
$app->register('session', Session::class);
// Exemplo de rota com uso de sessão
Flight::route('/login', function() {
$session = Flight::session();
$session->set('user_id', 123);
$session->set('username', 'johndoe');
$session->set('is_admin', false);
echo $session->get('username'); // Saída: johndoe
echo $session->get('preferences', 'default_theme'); // Saída: default_theme
if ($session->get('user_id')) {
Flight::json(['message' => 'Usuário está logado!', 'user_id' => $session->get('user_id')]);
}
});
Flight::route('/logout', function() {
$session = Flight::session();
$session->clear(); // Limpa todos os dados da sessão
Flight::json(['message' => 'Desconectado com sucesso']);
});
Flight::start();
Pontos-Chave
- Não-Bloqueante: Usa
read_and_close
para iniciar a sessão por padrão, evitando problemas de bloqueio de sessão. - Auto-Confirmação: Habilitado por padrão, então as alterações são salvas automaticamente no desligamento, a menos que desativado.
- Armazenamento em Arquivo: Sessões são armazenadas no diretório temporário do sistema sob
/flight_sessions
por padrão.
Configuração
Você pode personalizar o manipulador de sessão passando um array de opções ao registrá-lo:
$app->register('session', Session::class, [
'save_path' => '/custom/path/to/sessions', // Diretório para os arquivos de sessão
'encryption_key' => 'a-secure-32-byte-key-here', // Habilitar criptografia (32 bytes recomendado para AES-256-CBC)
'auto_commit' => false, // Desativar auto-confirmação para controle manual
'start_session' => true, // Iniciar sessão automaticamente (padrão: true)
'test_mode' => false // Habilitar modo de teste para desenvolvimento
]);
Opções de Configuração
Opção | Descrição | Valor Padrão |
---|---|---|
save_path |
Diretório onde os arquivos de sessão são armazenados | sys_get_temp_dir() . '/flight_sessions' |
encryption_key |
Chave para criptografia AES-256-CBC (opcional) | null (sem criptografia) |
auto_commit |
Salvar dados da sessão automaticamente ao desligar | true |
start_session |
Iniciar a sessão automaticamente | true |
test_mode |
Executar em modo de teste sem afetar as sessões do PHP | false |
test_session_id |
ID de sessão personalizado para o modo de teste (opcional) | Gerado aleatoriamente se não definido |
Uso Avançado
Confirmação Manual
Se você desativar a auto-confirmação, deverá confirmar manualmente as alterações:
$app->register('session', Session::class, ['auto_commit' => false]);
Flight::route('/update', function() {
$session = Flight::session();
$session->set('key', 'value');
$session->commit(); // Salva explicitamente as alterações
});
Segurança da Sessão com Criptografia
Habilite a criptografia para dados sensíveis:
$app->register('session', Session::class, [
'encryption_key' => 'your-32-byte-secret-key-here'
]);
Flight::route('/secure', function() {
$session = Flight::session();
$session->set('credit_card', '4111-1111-1111-1111'); // Criptografado automaticamente
echo $session->get('credit_card'); // Descriptografado na recuperação
});
Regeneração da Sessão
Regere a ID da sessão para segurança (por exemplo, após login):
Flight::route('/post-login', function() {
$session = Flight::session();
$session->regenerate(); // Nova ID, mantém os dados
// OU
$session->regenerate(true); // Nova ID, exclui dados antigos
});
Exemplo de Middleware
Proteja rotas com autenticação baseada em sessão:
Flight::route('/admin', function() {
Flight::json(['message' => 'Bem-vindo ao painel de administração']);
})->addMiddleware(function() {
$session = Flight::session();
if (!$session->get('is_admin')) {
Flight::halt(403, 'Acesso negado');
}
});
Este é apenas um exemplo simples de como usar isso em middleware. Para um exemplo mais detalhado, consulte a documentação de middleware.
Métodos
A classe Session
fornece estes métodos:
set(string $key, $value)
: Armazena um valor na sessão.get(string $key, $default = null)
: Recupera um valor, com um padrão opcional se a chave não existir.delete(string $key)
: Remove uma chave específica da sessão.clear()
: Exclui todos os dados da sessão.commit()
: Salva os dados atuais da sessão no sistema de arquivos.id()
: Retorna a ID da sessão atual.regenerate(bool $deleteOld = false)
: Regenera a ID da sessão, excluindo opcionalmente os dados antigos.
Todos os métodos, exceto get()
e id()
, retornam a instância Session
para encadeamento.
Por Que Usar Este Plugin?
- Leve: Sem dependências externas—apenas arquivos.
- Não-Bloqueante: Evita bloqueio de sessão com
read_and_close
por padrão. - Seguro: Suporta criptografia AES-256-CBC para dados sensíveis.
- Flexível: Opções de auto-confirmação, modo de teste e controle manual.
- Nativo ao Flight: Construído especificamente para o framework Flight.
Detalhes Técnicos
- Formato de Armazenamento: Os arquivos de sessão são prefixados com
sess_
e armazenados nosave_path
configurado. Dados criptografados usam um prefixoE
, texto não criptografado usaP
. - Criptografia: Usa AES-256-CBC com um IV aleatório por gravação de sessão quando uma
encryption_key
é fornecida. - Coleta de Lixo: Implementa o
SessionHandlerInterface::gc()
do PHP para limpar sessões expiradas.
Contribuindo
Contribuições são bem-vindas! Fork o repositório, faça suas alterações e envie um pull request. Relate bugs ou sugira recursos pelo rastreador de problemas do Github.
Licença
Este plugin é licenciado sob a Licença MIT. Consulte o repositório do Github para detalhes.
Awesome-plugins/runway
Pista
A Pista é uma aplicação CLI que ajuda a gerenciar suas aplicações Flight. Ela pode gerar controladores, exibir todas as rotas e mais. É baseada na excelente biblioteca adhocore/php-cli.
Clique aqui para visualizar o código.
Instalação
Instale com o composer.
composer require flightphp/runway
Configuração Básica
Na primeira execução da Pista, ela guiará você por um processo de configuração e criará um arquivo de configuração .runway.json
na raiz do seu projeto. Este arquivo conterá algumas configurações necessárias para que a Pista funcione corretamente.
Utilização
A Pista possui vários comandos que você pode usar para gerenciar sua aplicação Flight. Existem duas maneiras fáceis de usar a Pista.
- Se estiver usando o projeto esqueleto, você pode executar
php runway [comando]
a partir da raiz do seu projeto. - Se estiver usando a Pista como um pacote instalado via composer, você pode executar
vendor/bin/runway [comando]
a partir da raiz do seu projeto.
Para qualquer comando, você pode inserir a flag --help
para obter mais informações sobre como utilizar o comando.
php runway routes --help
Aqui estão alguns exemplos:
Gerar um Controlador
Com base na configuração em seu arquivo .runway.json
, a localização padrão gerará um controlador para você no diretório app/controllers/
.
php runway make:controller MeuControlador
Gerar um Modelo Active Record
Com base na configuração em seu arquivo .runway.json
, a localização padrão gerará um controlador para você no diretório app/records/
.
php runway make:record usuários
Se por acaso você tiver a tabela usuários
com o seguinte esquema: id
, nome
, email
, criado_em
, atualizado_em
, um arquivo semelhante ao seguinte será criado no arquivo app/records/RegistroUsuario.php
:
<?php
declare(strict_types=1);
namespace app\records;
/**
* Classe Active Record para a tabela de usuários.
* @link https://docs.flightphp.com/awesome-plugins/active-record
*
* @property int $id
* @property string $nome
* @property string $email
* @property string $criado_em
* @property string $atualizado_em
* // você também pode adicionar relacionamentos aqui uma vez que os definir no array $relações
* @property RegistroEmpresa $empresa Exemplo de um relacionamento
*/
class RegistroUsuario extends \flight\ActiveRecord
{
/**
* @var array $relações Define os relacionamentos para o modelo
* https://docs.flightphp.com/awesome-plugins/active-record#relationships
*/
protected array $relações = [];
/**
* Construtor
* @param mixed $conexãoBancoDados A conexão com o banco de dados
*/
public function __construct($conexãoBancoDados)
{
parent::__construct($conexãoBancoDados, 'usuários');
}
}
Exibir Todas as Rotas
Isso exibirá todas as rotas atualmente registradas com o Flight.
php runway routes
Se desejar visualizar apenas rotas específicas, você pode inserir uma flag para filtrar as rotas.
# Exibir apenas rotas GET
php runway routes --get
# Exibir apenas rotas POST
php runway routes --post
# etc.
Personalizando a Pista
Se você está criando um pacote para o Flight ou deseja adicionar seus próprios comandos personalizados ao seu projeto, pode fazer isso criando um diretório src/commands/
, flight/commands/
, app/commands/
ou commands/
para o seu projeto/pacote.
Para criar um comando, você simplesmente estende a classe AbstractBaseCommand
e implementa, no mínimo, um método __construct
e um método execute
.
<?php
declare(strict_types=1);
namespace flight\commands;
class ComandoExemplo extends AbstractBaseCommand
{
/**
* Construtor
*
* @param array<string,mixed> $config Configuração JSON de .runway-config.json
*/
public function __construct(array $config)
{
parent::__construct('make:example', 'Criar um exemplo para a documentação', $config);
$this->argument('<gif-engracado>', 'O nome do gif engraçado');
}
/**
* Executa a função
*
* @return void
*/
public function execute(string $controlador)
{
$io = $this->app()->io();
$io->info('Criando exemplo...');
// Faça algo aqui
$io->ok('Exemplo criado!');
}
}
Consulte a Documentação do adhocore/php-cli para mais informações sobre como criar seus próprios comandos personalizados em sua aplicação Flight!
Awesome-plugins/tracy_extensions
Tracy Painel de Extensões
Este é um conjunto de extensões para tornar o trabalho com o Flight um pouco mais rico.
- Flight - Analisar todas as variáveis do Flight.
- Banco de Dados - Analisar todas as consultas que foram executadas na página (se você iniciar corretamente a conexão com o banco de dados)
- Requisição - Analisar todas as variáveis
$_SERVER
e examinar todos os payloads globais ($_GET
,$_POST
,$_FILES
) - Sessão - Analisar todas as variáveis
$_SESSION
se as sessões estiverem ativas.
Este é o Painel
E cada painel exibe informações muito úteis sobre sua aplicação!
Clique aqui para visualizar o código.
Instalação
Execute composer require flightphp/tracy-extensions --dev
e está tudo pronto!
Configuração
Há muito pouca configuração que você precisa fazer para começar. Você precisará iniciar o depurador Tracy antes de usar isso https://tracy.nette.org/en/guide:
<?php
use Tracy\Debugger;
use flight\debug\tracy\TracyExtensionLoader;
// código de inicialização
require __DIR__ . '/vendor/autoload.php';
Debugger::enable();
// Talvez você precise especificar seu ambiente com Debugger::enable(Debugger::DEVELOPMENT)
// se você usa conexões de banco de dados em seu aplicativo, há
// um wrapper PDO obrigatório para usar APENAS EM DESENVOLVIMENTO (não em produção por favor!)
// Tem os mesmos parâmetros que 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, ela capturará o tempo, a consulta e os parâmetros
// Isso conecta os pontos
if(Debugger::$showBar === true) {
// Isto precisa ser falso ou Tracy não pode renderizar de fato :(
Flight::set('flight.content_length', false);
new TracyExtensionLoader(Flight::app());
}
// mais código
Flight::start();
Configuração Adicional
Dados da Sessão
Se você tiver um manipulador de sessão personalizado (como ghostff/session), você pode passar qualquer array de dados de sessão para Tracy e ele os exibirá automaticamente para você. Você passa com a chave session_data
no segundo parâmetro do construtor TracyExtensionLoader
.
use Ghostff\Session\Session;
require 'vendor/autoload.php';
$app = Flight::app();
$app->register('session', Session::class);
if(Debugger::$showBar === true) {
// Isto precisa ser falso ou Tracy não pode renderizar de fato :(
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 do Latte para analisar seus modelos. Você pode passar a instância do Latte para o construtor TracyExtensionLoader
com a chave latte
no segundo parâmetro.
use Latte\Engine;
require 'vendor/autoload.php';
$app = Flight::app();
$app->register('latte', Engine::class, [], function($latte) {
$latte->setTempDirectory(__DIR__ . '/temp');
// aqui é onde você adiciona o Painel do Latte ao Tracy
$latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);
});
if(Debugger::$showBar === true) {
// Isto precisa ser falso ou Tracy não pode renderizar de fato :(
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ê.
bdump($var)
- Isso irá despejar a variável na Barra do Tracy em um painel separado.dumpe($var)
- Isso irá despejar a variável e então cessar imediatamente.
Awesome-plugins/active_record
Flight Active Record
Um registro ativo é o mapeamento de uma entidade de banco de dados para um objeto PHP. Falando de forma simples, se você tiver uma tabela de usuários em seu banco de dados, você pode "traduzir" uma linha dessa tabela para uma classe User
e um objeto $user
em seu código. Veja exemplo básico.
Clique aqui para o repositório no GitHub.
Exemplo Básico
Vamos assumir que você tem 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á-la assim
parent::__construct($database_connection, 'users');
// ou assim
parent::__construct($database_connection, null, [ 'table' => 'users']);
}
}
Agora assista a mágica acontecer!
// para sqlite
$database_connection = new PDO('sqlite:test.db'); // isso é apenas um exemplo, você provavelmente usaria uma conexão de banco de dados real
// 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('uma senha legal');
$user->insert();
// ou $user->save();
echo $user->id; // 1
$user->name = 'Joseph Mamma';
$user->password = password_hash('uma nova senha legal!!!');
$user->insert();
// não pode usar $user->save() aqui ou ele achará que é uma atualização!
echo $user->id; // 2
E foi assim 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 quanto a uma certa condição?
$users = $user->like('name', '%mamma%')->findAll();
Veja quanto é divertido isso? Vamos instalá-lo e começar!
Instalação
Basta instalar com o Composer
composer require flightphp/active-record
Uso
Isso pode ser utilizado como uma biblioteca autônoma ou com o Framework PHP Flight. Totalmente a seu critério.
Autônomo
Basta garantir que você passe uma conexão PDO para o construtor.
$pdo_connection = new PDO('sqlite:test.db'); // isso é apenas um exemplo, você provavelmente usaria uma conexão de banco de dados real
$User = new User($pdo_connection);
Não quer sempre configurar sua conexão de banco de dados no construtor? Veja Gerenciamento de Conexão de Banco de Dados para outras ideias!
Registrar como um método no Flight
Se você estiver usando o Framework PHP Flight, 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á-la assim em um controlador, uma função, etc.
Flight::user()->find(1);
Métodos runway
runway é uma ferramenta CLI para o Flight que possui um comando personalizado para esta biblioteca.
# Uso
php runway make:record nome_da_tabela_do_banco_de_dados [nome_da_classe]
# Exemplo
php runway make:record users
Isso criará uma nova classe no diretório app/records/
como UserRecord.php
com o seguinte conteúdo:
<?php
declare(strict_types=1);
namespace app\records;
/**
* Classe ActiveRecord para a tabela users.
* @link https://docs.flightphp.com/awesome-plugins/active-record
*
* @property int $id
* @property string $username
* @property string $email
* @property string $password_hash
* @property string $created_dt
*/
class UserRecord extends \flight\ActiveRecord
{
/**
* @var array $relations Defina os relacionamentos para o modelo
* https://docs.flightphp.com/awesome-plugins/active-record#relationships
*/
protected array $relations = [
// 'nome_da_relação' => [ self::HAS_MANY, 'ClasseRelacionada', 'chave_estrangeira' ],
];
/**
* Construtor
* @param mixed $databaseConnection A conexão com o banco de dados
*/
public function __construct($databaseConnection)
{
parent::__construct($databaseConnection, 'users');
}
}
Funções CRUD
find($id = null) : boolean|ActiveRecord
Encontra um registro e atribui ao objeto atual. Se você passar um $id
de algum tipo, ele realizará uma busca na chave primária com esse valor. Se nada for passado, ele apenas 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 previamente
$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 tiver sido hidratado (buscado do banco de dados).
$user->find(1);
// se um registro for encontrado com dados...
$user->isHydrated(); // true
insert(): boolean|ActiveRecord
Insere o registro atual no banco de dados.
$user = new User($pdo_connection);
$user->name = 'demo';
$user->password = md5('demo');
$user->insert();
Chaves Primárias Baseadas em Texto
Se você tiver uma chave primária baseada em texto (como um UUID), pode definir o valor da chave primária antes de inserir de duas maneiras.
$user = new User($pdo_connection, [ 'primaryKey' => 'uuid' ]);
$user->uuid = 'algum-uuid';
$user->name = 'demo';
$user->password = md5('demo');
$user->insert(); // ou $user->save();
ou você pode fazer com que a chave primária seja gerada automaticamente para você por meio de eventos.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users', [ 'primaryKey' => 'uuid' ]);
// você também pode definir a chave primária assim em vez do array acima.
$this->primaryKey = 'uuid';
}
protected function beforeInsert(self $self) {
$self->uuid = uniqid(); // ou como você precisar gerar seus ids únicos
}
}
Se você não definir a chave primária antes de inserir, ela será definida como o rowid
e o
banco de dados a gerará para você, mas não persistirá porque esse campo pode não existir
em sua tabela. Por isso, é recomendável usar o evento para gerenciar isso automaticamente.
update(): boolean|ActiveRecord
Atualiza o registro atual no banco de dados.
$user->greaterThan('id', 0)->orderBy('id desc')->find();
$user->email = 'test@example.com';
$user->update();
save(): boolean|ActiveRecord
Insere ou atualiza o registro atual no banco de dados. Se o registro tiver um id, ele atualizará; caso contrário, ele irá inserir.
$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 foram definidas, instanciadas e possuem dados sujos para atualizar. (v0.4.0 e superior)
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 previamente.
$user->like('name', 'Bob%')->delete();
dirty(array $dirty = []): ActiveRecord
Dados sujos referem-se aos dados que foram alterados em um registro.
$user->greaterThan('id', 0)->orderBy('id desc')->find();
// nada está "sujo" até esse ponto.
$user->email = 'test@example.com'; // agora o e-mail é considerado "sujo" pois foi alterado.
$user->update();
// agora não há dados sujos porque foram atualizados e persistidos no banco de dados.
$user->password = password_hash('nova_senha'); // agora isso é sujo
$user->dirty(); // passando nada limpará todas as entradas sujas.
$user->update(); // nada será atualizado porque nada foi capturado como sujo.
$user->dirty([ 'name' => 'algo', 'password' => password_hash('uma senha diferente') ]);
$user->update(); // tanto nome quanto 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 nome quanto senha são atualizados.
isDirty(): boolean
(v0.4.0)
Retorna true
se o registro atual tiver sido alterado.
$user->greaterThan('id', 0)->orderBy('id desc')->find();
$user->email = 'test@email.com';
$user->isDirty(); // true
reset(bool $include_query_data = true): ActiveRecord
Redefine o registro atual para seu estado inicial. Isso é muito bom para usar em tipos de comportamento de loop.
Se você passar true
, isso também redefinirá os dados da consulta 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 folha limpa
$user_company->user_id = $user->id;
$user_company->company_id = $some_company_id;
$user_company->insert();
}
getBuiltSql(): string
(v0.4.1)
Depois de executar um método find()
, findAll()
, insert()
, update()
, ou save()
, você pode obter o SQL que foi construído e usá-lo para fins de depuração.
Métodos de Consulta SQL
select(string $field1 [, string $field2 ... ])
Você pode selecionar apenas algumas das colunas em uma tabela, se desejar (é mais eficiente em tabelas realmente largas com muitas colunas)
$user->select('id', 'name')->find();
from(string $table)
Você pode tecnicamente escolher outra tabela também! Por que não?!
$user->select('id', 'name')->from('user')->find();
join(string $table_name, string $join_condition)
Você pode até juntar a outra tabela no banco de dados.
$user->join('contacts', 'contacts.user_id = users.id')->find();
where(string $where_conditions)
Você pode definir alguns argumentos where personalizados (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 a ataques que são conhecidos como injeções de SQL. Existem muitos artigos online, por favor, pesquise "sql injection attacks php" e você encontrará muitos artigos sobre esse assunto. A maneira adequada de lidar com isso com esta biblioteca é, em vez desse método where()
, você faria algo mais parecido com $user->eq('id', $id)->eq('name', $name)->find();
Se você absolutamente precisa fazer isso, a biblioteca PDO
possui $pdo->quote($var)
para escapar para você. Somente após usar quote()
você pode usá-lo em uma declaração where()
.
group(string $group_by_statement)/groupBy(string $group_by_statement)
Agrupe seus resultados por uma condição particular.
$user->select('COUNT(*) as count')->groupBy('name')->findAll();
order(string $order_by_statement)/orderBy(string $order_by_statement)
Classifique a consulta retornada de uma certa maneira.
$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, será offset, limit como em 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();
Condições OR
É possível envolver suas condições em uma declaração OR. Isso é feito com o método startWrap()
e endWrap()
ou preenchendo o 3º parâmetro da condição após o campo e o valor.
// Método 1
$user->eq('id', 1)->startWrap()->eq('name', 'demo')->or()->eq('name', 'test')->endWrap('OR')->find();
// Isso será avaliado para `id = 1 AND (name = 'demo' OR name = 'test')`
// Método 2
$user->eq('id', 1)->eq('name', 'demo', 'OR')->find();
// Isso será avaliado para `id = 1 OR name = 'demo'`
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 extra na classe previamente.
Definir 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', // esta é a classe ActiveRecord "outra" que será referenciada
// obrigatório
// dependendo do tipo de relacionamento
// self::HAS_ONE = a chave estrangeira que referencia a junção
// self::HAS_MANY = a chave estrangeira que referencia a junção
// self::BELONGS_TO = a chave local que referencia a junção
'local_or_foreign_key',
// apenas FYI, isso também se junta apenas à chave primária do modelo "outro"
// opcional
[ 'eq' => [ 'client_id', 5 ], 'select' => 'COUNT(*) as count', 'limit' 5 ], // condições adicionais que você deseja ao juntar a relação
// $record->eq('client_id', 5)->select('COUNT(*) as count')->limit(5))
// opcional
'back_reference_name' // isso é se você quiser referenciar essa relação de volta para si mesma 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 temos as referências configuradas para que possamos 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 caminho inverso.
$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ão é?
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 apenas anexar ao objeto que então seria passado para, digamos, um modelo.
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 faz referência a ele como uma propriedade normal do objeto.
echo $user->page_view_count;
Eventos
Mais uma super incrível característica sobre esta biblioteca é sobre eventos. Eventos são acionados em determinados momentos com base em certos métodos que você chama. Eles são muito úteis para configurar dados automaticamente para você.
onConstruct(ActiveRecord $ActiveRecord, array &config)
Isso é realmente útil se você precisar definir uma conexão padrão ou algo assim.
// index.php ou bootstrap.php
Flight::register('db', 'PDO', [ 'sqlite:test.db' ]);
//
//
//
// User.php
class User extends flight\ActiveRecord {
protected function onConstruct(self $self, array &$config) { // não se esqueça da referência &
// você poderia fazer isso para definir automaticamente a conexão
$config['connection'] = Flight::db();
// ou isso
$self->transformAndPersistConnection(Flight::db());
// Você também pode definir o nome da tabela dessa maneira.
$config['table'] = 'users';
}
}
beforeFind(ActiveRecord $ActiveRecord)
Isso provavelmente só é útil se você precisar de uma manipulação de consulta toda vez.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeFind(self $self) {
// sempre execute id >= 0 se isso for do seu agrado
$self->gte('id', 0);
}
}
afterFind(ActiveRecord $ActiveRecord)
Esse é provavelmente mais útil se você sempre precisar executar alguma lógica toda vez que este registro for buscado. Você precisa descriptografar algo? Você precisa executar uma consulta de contagem personalizada a cada vez (não performático, mas tudo bem)?
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterFind(self $self) {
// descriptografando algo
$self->secret = yourDecryptFunction($self->secret, $some_key);
// talvez armazenando algo personalizado como uma consulta???
$self->setCustomData('view_count', $self->select('COUNT(*) count')->from('user_views')->eq('user_id', $self->id)['count'];
}
}
beforeFindAll(ActiveRecord $ActiveRecord)
Isso é provavelmente só útil se você precisar de uma manipulação de consulta toda vez.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeFindAll(self $self) {
// sempre execute id >= 0 se isso for do seu agrado
$self->gte('id', 0);
}
}
afterFindAll(array<int,ActiveRecord> $results)
Semelhante ao afterFind()
, mas você pode fazê-lo com todos os registros!
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterFindAll(array $results) {
foreach($results as $self) {
// faça algo legal como no afterFind()
}
}
}
beforeInsert(ActiveRecord $ActiveRecord)
Realmente útil se você precisar definir alguns valores padrão toda vez.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeInsert(self $self) {
// defina alguns padrões sensatos
if(!$self->created_date) {
$self->created_date = gmdate('Y-m-d');
}
if(!$self->password) {
$self->password = password_hash((string) microtime(true));
}
}
}
afterInsert(ActiveRecord $ActiveRecord)
Talvez você tenha um caso de uso para mudar dados após serem inseridos?
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterInsert(self $self) {
// você faz o que quiser
Flight::cache()->set('most_recent_insert_id', $self->id);
// ou qualquer outra coisa....
}
}
beforeUpdate(ActiveRecord $ActiveRecord)
Realmente útil se você precisar definir alguns valores padrão cada vez que uma atualização ocorrer.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeInsert(self $self) {
// defina alguns padrões sensatos
if(!$self->updated_date) {
$self->updated_date = gmdate('Y-m-d');
}
}
}
afterUpdate(ActiveRecord $ActiveRecord)
Talvez você tenha um caso de uso para mudar dados após serem atualizados?
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterInsert(self $self) {
// você faz o que quiser
Flight::cache()->set('most_recently_updated_user_id', $self->id);
// ou qualquer outra coisa....
}
}
beforeSave(ActiveRecord $ActiveRecord)/afterSave(ActiveRecord $ActiveRecord)
Isso é útil se você quiser que eventos aconteçam tanto quando inserções quanto atualizações ocorrerem. Vou poupar você da longa explicação, mas tenho certeza de que você pode imaginar o que é.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeSave(self $self) {
$self->last_updated = gmdate('Y-m-d H:i:s');
}
}
beforeDelete(ActiveRecord $ActiveRecord)/afterDelete(ActiveRecord $ActiveRecord)
Não tenho certeza do que você gostaria de fazer aqui, mas sem julgamentos! Vá em frente!
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeDelete(self $self) {
echo 'Ele foi um soldado valente... :cry-face:';
}
}
Gerenciamento de Conexão de Banco de Dados
Quando você estiver usando esta biblioteca, pode definir a conexão com o banco de dados de algumas maneiras diferentes. Você pode definir a conexão no construtor, pode defini-la via uma variável de configuração $config['connection']
ou pode defini-la via setDatabaseConnection()
(v0.4.1).
$pdo_connection = new PDO('sqlite:test.db'); // por exemplo
$user = new User($pdo_connection);
// ou
$user = new User(null, [ 'connection' => $pdo_connection ]);
// ou
$user = new User();
$user->setDatabaseConnection($pdo_connection);
Se você deseja evitar configurar sempre uma $database_connection
toda vez que chamar um registro ativo, existem maneiras de contornar isso!
// index.php ou bootstrap.php
// Defina isso como uma classe registrada no Flight
Flight::register('db', 'PDO', [ 'sqlite:test.db' ]);
// User.php
class User extends flight\ActiveRecord {
public function __construct(array $config = [])
{
$database_connection = $config['connection'] ?? Flight::db();
parent::__construct($database_connection, 'users', $config);
}
}
// E agora, nenhum argumento é necessário!
$user = new User();
Nota: Se você planeja fazer testes unitários, fazer assim pode adicionar alguns desafios aos testes unitários, mas, no geral, porque você pode injetar sua conexão com
setDatabaseConnection()
ou$config['connection']
, não é tão complicado.
Se você precisar atualizar a conexão do banco de dados, por exemplo, se estiver executando um script CLI de longa duração e precisar atualizar a conexão de tempos em tempos, pode redefinir a conexão com $your_record->setDatabaseConnection($pdo_connection)
.
Contribuindo
Por favor, contribua. :D
Configuração
Quando você contribuir, certifique-se de executar composer test-coverage
para manter 100% de cobertura de testes (isso não é verdadeiro teste unitário, mais como teste de integração).
Além disso, certifique-se de executar composer beautify
e composer phpcs
para corrigir quaisquer erros de linting.
Licença
MIT
Awesome-plugins/latte
Latte
Latte é um mecanismo de modelo completo que é muito fácil de usar e se sente mais próximo de uma sintaxe PHP do que Twig ou Smarty. Também é muito fácil de estender 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 nos seus modelos!
$latte->setTempDirectory(__DIR__ . '/../cache/');
// Diga ao Latte onde o diretório raiz para suas visualizações estará
// $app->get('flight.views.path') é definido no arquivo config.php
// Você também pode fazer algo como `__DIR__ . '/../views/'`
$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="en">
<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">
© 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 da 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 em todo o seu potencial!
Awesome-plugins/awesome_plugins
Plugins Incríveis
Flight é incrivelmente extensível. Existem vários plugins que podem ser usados para adicionar funcionalidade à sua aplicação Flight. Alguns são oficialmente suportados pela equipe Flight e outros são bibliotecas micro/lite para ajudá-lo a começar.
Documentação da API
A documentação da API é crucial para qualquer API. Ela ajuda os desenvolvedores a entender como interagir com sua API e o que esperar em troca. Existem algumas ferramentas disponíveis para ajudá-lo a gerar documentação da API para seus projetos Flight.
- FlightPHP OpenAPI Generator - Post do blog escrito por Daniel Schreiber sobre como usar a Especificação OpenAPI com FlightPHP para construir sua API usando uma abordagem primeiro a API.
- SwaggerUI - Swagger UI é uma ótima ferramenta para ajudá-lo a gerar documentação da API para seus projetos Flight. É muito fácil de usar e pode ser personalizado para atender às suas necessidades. Esta é a biblioteca PHP que o ajudará a gerar a documentação Swagger.
Autenticação/Autorização
Autenticação e Autorização são cruciais para qualquer aplicação que exija controles para determinar quem pode acessar o quê.
- oficial flightphp/permissions - Biblioteca oficial de Permissões Flight. Esta biblioteca é uma maneira simples de adicionar permissões de nível de usuário e aplicação à sua aplicação.
Cache
Cache é uma ótima maneira de acelerar sua aplicação. Existem várias bibliotecas de cache que podem ser usadas com Flight.
- oficial flightphp/cache - Classe de cache em arquivo PHP leve, simples e autônoma.
CLI
Aplicações CLI são uma ótima maneira de interagir com sua aplicação. Você pode usá-las para gerar controladores, exibir todas as rotas e mais.
- oficial flightphp/runway - Runway é uma aplicação CLI que o ajuda a gerenciar suas aplicações Flight.
Cookies
Cookies são uma ótima maneira de armazenar pequenos pedaços de dados no lado do cliente. Eles podem ser usados para armazenar preferências do usuário, configurações da aplicação e mais.
- overclokk/cookie - PHP Cookie é uma biblioteca PHP que fornece uma maneira simples e eficaz de gerenciar cookies.
Depuração
A depuração é crucial ao desenvolver em seu ambiente local. Existem alguns plugins que podem elevar sua experiência de depuração.
- tracy/tracy - Este é um manipulador de erros completo que pode ser usado com Flight. Ele 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.
- flightphp/tracy-extensions - Usado com o manipulador de erros Tracy, este plugin adiciona alguns painéis extras para ajudar especificamente na depuração de projetos Flight.
Bancos de Dados
Bancos de dados são a base da maioria das aplicações. Esta é a forma como você armazena e recupera dados. Algumas bibliotecas de banco de dados são apenas wrappers para escrever consultas e algumas são ORMs completos.
- oficial flightphp/core PdoWrapper - Wrapper PDO oficial do Flight que faz parte do núcleo. Este é um wrapper simples para ajudar a simplificar o processo de escrever consultas e executá-las. Não é um ORM.
- oficial flightphp/active-record - ORM/Mapper ActiveRecord do Flight oficial. Ótima biblioteca para recuperar e armazenar dados em seu banco de dados de forma fácil.
- byjg/php-migration - Plugin para manter o controle de todas as alterações no banco de dados para o seu projeto.
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 adequadamente 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 comprometer em seu repositório de código.
- defuse/php-encryption - Esta é uma biblioteca que pode ser usada para criptografar e descriptografar dados. Começar a usá-la é bastante simples para iniciar a criptografia e descriptografia de dados.
Fila de Trabalho
Filas de trabalho são muito úteis para processar tarefas de forma assíncrona. Isso pode incluir o envio de e-mails, processamento de imagens ou qualquer coisa que não precisa ser feita em tempo real.
- n0nag0n/simple-job-queue - Fila de Trabalho Simples é uma biblioteca que pode ser usada para processar trabalhos de forma assíncrona. Pode ser usada com beanstalkd, MySQL/MariaDB, SQLite e PostgreSQL.
Sessão
Sessões não são realmente úteis para APIs, mas para construir uma aplicação web, as sessões podem ser cruciais para manter o estado e as informações de login.
- oficial flightphp/session - Biblioteca oficial de Sessões Flight. Esta é uma biblioteca simples de sessão que pode ser usada para armazenar e recuperar dados de sessão. Ela utiliza o gerenciamento de sessões embutido do PHP.
- Ghostff/Session - Gerenciador de Sessões PHP (não bloqueante, flash, segmento, criptografia de sessão). Utiliza PHP open_ssl para criptografia/descriptografia opcional de dados de sessão.
Modelagem
Modelagem é essencial para qualquer aplicação web com uma interface de usuário. Existem vários mecanismos de modelagem que podem ser usados com Flight.
- obsoleto flightphp/core View - Este é um mecanismo de modelagem muito básico que faz parte do núcleo. Não é recomendado o uso se você tiver mais de algumas páginas em seu projeto.
- latte/latte - Latte é um mecanismo de modelagem completo que é muito fácil de usar e se sente mais próximo de uma sintaxe PHP do que Twig ou Smarty. Também é muito fácil de estender e adicionar seus próprios filtros e funções.
Contribuindo
Tem um plugin que gostaria de compartilhar? Envie um pedido de pull para adicioná-lo à lista!
Media
Mídia
Tentamos rastrear o que podemos dos diversos tipos de mídia na internet sobre o Flight. Veja abaixo diferentes recursos que você pode usar para aprender mais sobre o Flight.
Artigos e Escritos
- Defina, Gere e Implemente: Uma Abordagem Primeiro API com OpenAPI Generator e FlightPHP por Daniel Schreiber (2025)
- Melhores Micro Frameworks PHP para 2024 por n0nag0n (2024)
- Criando uma API RESTful com o Flight Framework por n0nag0n (2024)
- Construindo um Blog Simples com o Flight Parte 2 por n0nag0n (2024)
- Construindo um Blog Simples com o Flight Parte 1 por n0nag0n (2024)
- 🚀 Construa uma API CRUD Simples em PHP com o Flight Framework por soheil-khaledabadi (2024)
- Construindo uma Aplicação Web PHP com o Micro-framework Flight por Arthur C. Codex (2023)
- Melhores Frameworks PHP para Desenvolvimento Web em 2024 por Ravikiran A S (2023)
- Top 12 Frameworks PHP: Um Guia Abrangente para 2023 por marketing kbk (2023)
- 5 Frameworks PHP que Você (Provavelmente) Nunca Ouviu Falar por n0nag0n (2022)
- 12 principais frameworks PHP para desenvolvedores web considerarem em 2023 por Anna Monus (2022)
- Os Melhores Microframeworks PHP em um Servidor Cloud por Shahzeb Ahmed (2021)
- Framework PHP: Os 15 mais poderosos para seu desenvolvimento web por AHT Tech (2020)
- Roteamento PHP Fácil com FlightPHP por Lucas Conceição (2019)
- Experimentando um Novo Framework PHP (Flight) por Leon (2017)
- Configurando o FlightPHP para trabalhar com Backbonejs por Timothy Tocci (2015)
Vídeos e Tutoriais
- Crie uma API REST para Dispositivos IoT Usando PHP & FlightPHP - ESP32 API por IoT Craft Hub (2024)
- Vídeo Introdutório Simples do PHP Flight Framework por n0nag0n (2024)
- Defina o código de cabeçalho HTTP no Flightphp (3 Soluções!!) por Roel Van de Paar (2024)
- Tutorial do PHP Flight Framework. Projeto API super fácil! por n0nag0n (2022)
- Aplicação web CRUD com php e mysql e bootstrap usando flight por Devlopteca - Oscar Uh (2021)
- DevOps & SysAdmins: Regra de reescrita lighttpd para o microframework Flight PHP por Roel Van de Paar (2021)
- Tutorial REST API Flight PHP #PARTE2 INSERIR INFORMAÇÕES DA TABELA #Código (Tagalog) por Info Singkat Official (2020)
- Tutorial REST API Flight PHP #PARTE1 Informações #Código (Tagalog) por Info Singkat Official (2020)
- Como Criar uma API REST JSON EM PHP - Parte 2 por Codewife (2018)
- Como Criar uma API REST JSON EM PHP - Parte 1 por Codewife (2018)
- Teste Micro Frameworks PHP - Flight PHP, Lumen, Slim 3 e Laravel por Codemarket (2016)
- Tutorial 1 Flight PHP - Instalação por absagg (2014)
- Tutorial 2 Flight PHP - Rota parte 1 por absagg (2014)
Examples
Precisa de um início rápido?
Você tem duas opções para começar um novo projeto Flight:
- Boilerplate de Esqueleto Completo: Um exemplo mais completo com controladores e visões.
- Boilerplate de Esqueleto de Arquivo Único: Um arquivo único que inclui tudo o que você precisa para rodar seu aplicativo em um único arquivo simples.
Exemplos contribuídos pela comunidade:
- flightravel: FlightPHP com diretórios Laravel, com ferramentas PHP + Ações GH
- fleact - Um kit inicial do FlightPHP com integração ReactJS.
- flastro - Um kit inicial do FlightPHP com integração Astro.
- velt - Velt é um modelo de inicialização rápido e fácil em Svelte com um backend FlightPHP.
Precisa de Inspiração?
Embora esses não sejam oficialmente patrocinados pela equipe Flight, eles podem lhe dar ideias sobre como estruturar seus próprios projetos que são construídos com o Flight!
- Decay - Flight v3 com HTMX e SleekDB tudo sobre zumbis! (Demo)
- Exemplo de Blog Flight - Flight v3 com Middleware, Controladores, Active Record e Latte.
- API RESTful CRUD de Flight - Projeto simples de API CRUD usando o framework Flight, que fornece uma estrutura básica para novos usuários configurarem rapidamente um aplicativo PHP com operações CRUD e conectividade de banco de dados. O projeto demonstra como usar o Flight para desenvolvimento de API RESTful, tornando-se uma ferramenta de aprendizado ideal para iniciantes e um kit inicial útil para desenvolvedores mais experientes.
- Sistema de Gestão Escolar Flight - Flight v3
- Paste Bin com Comentários - Flight v3
- Aplicativo Básico de Esqueleto
- Exemplo de Wiki
- A Aplicação do Framework PHP IT-Innovator
- LittleEducationalCMS (Espanhol)
- API de Páginas Amarelas Italiana
- Sistema de Gerenciamento de Conteúdo Genérico (com....muito pouca documentação)
- Um pequeno framework php baseado no Flight e medoo.
- Exemplo de Aplicativo MVC
Quer Compartilhar Seu Próprio Exemplo?
Se você tem um projeto que gostaria de compartilhar, por favor envie um pull request para adicioná-lo a esta lista!
Install/install
Instalação
Baixar os arquivos
Certifique-se de ter o PHP instalado em seu sistema. Caso contrário, clique aqui para obter instruções sobre como instalá-lo em seu sistema.
Se estiver usando Composer, você pode executar o seguinte comando:
composer require flightphp/core
OU você pode baixar os arquivos diretamente e extrair para o diretório da web.
Configurar seu Servidor Web
Servidor de Desenvolvimento PHP Integrado
Esta é de longe a maneira mais simples de começar. Você pode usar o servidor integrado para executar sua aplicação e até mesmo usar SQLite para um banco de dados (desde que o sqlite3 esteja instalado em seu sistema) e não exigir muito! Basta executar o seguinte comando uma vez que o PHP estiver instalado:
php -S localhost:8000
Em seguida, abra seu navegador e vá para http://localhost:8000
.
Se quiser definir o diretório raiz do seu projeto como um diretório diferente (Por ex: seu projeto é ~/myproject
, mas sua raiz do documento é ~/myproject/public/
), você pode executar o seguinte comando uma vez que estiver no diretório ~/myproject
:
php -S localhost:8000 -t public/
Então, abra seu navegador e vá para http://localhost:8000
.
Apache
Certifique-se de que o Apache já esteja instalado em seu sistema. Caso contrário, pesquise como instalar o Apache em seu sistema.
Para o Apache, edite seu arquivo .htaccess
com o seguinte:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
Nota: Se precisar usar o flight em um subdiretório, adicione a linha
RewriteBase /subdir/
logo apósRewriteEngine On
.Nota: Se desejar proteger todos os arquivos do servidor, como um arquivo db ou env. Coloque isso em seu arquivo
.htaccess
:
RewriteEngine On
RewriteRule ^(.*)$ index.php
Nginx
Certifique-se de que o Nginx já esteja instalado em seu sistema. Caso contrário, pesquise como instalar o Nginx em seu sistema.
Para o Nginx, adicione o seguinte à declaração do seu servidor:
server {
location / {
try_files $uri $uri/ /index.php;
}
}
Crie seu arquivo index.php
<?php
// Se estiver usando o Composer, requer o autoloader.
require 'vendor/autoload.php';
// se não estiver usando o Composer, carregue o framework diretamente
// require 'flight/Flight.php';
// Em seguida, defina uma rota e atribua uma função para lidar com a solicitação.
Flight::route('/', function () {
echo 'olá mundo!';
});
// Por fim, inicie o framework.
Flight::start();
Instalando o PHP
Se você já tem o php
instalado em seu sistema, vá em frente e pule estas instruções e vá para a seção de download
Claro! Aqui estão as instruções para instalar o PHP no macOS, Windows 10/11, Ubuntu e Rocky Linux. Também incluirei detalhes sobre como instalar diferentes versões do PHP.
macOS
Instalando o PHP usando o Homebrew
-
Instalar o Homebrew (caso ainda não esteja instalado):
- Abra o Terminal e execute:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
- Abra o Terminal e execute:
-
Instalar o PHP:
- Instalar a versão mais recente:
brew install php
- Para instalar uma versão específica, por exemplo, PHP 8.1:
brew tap shivammathur/php brew install shivammathur/php/php@8.1
- Instalar a versão mais recente:
-
Alternar entre versões do PHP:
- Desvincule a versão atual e vincule a versão desejada:
brew unlink php brew link --overwrite --force php@8.1
- Verifique a versão instalada:
php -v
- Desvincule a versão atual e vincule a versão desejada:
Windows 10/11
Instalando o PHP manualmente
-
Baixar o PHP:
- Visite PHP for Windows e baixe a versão mais recente ou uma versão específica (por ex., 7.4, 8.0) como um arquivo zip não seguro para threads.
-
Extrair o PHP:
- Extraia o arquivo zip baixado para
C:\php
.
- Extraia o arquivo zip baixado para
-
Adicionar o PHP ao PATH do sistema:
- Vá para Propriedades do Sistema > Variáveis de Ambiente.
- Em Variáveis do Sistema, encontre Path e clique em Editar.
- Adicione o caminho
C:\php
(ou onde você extraiu o PHP). - Clique em OK para fechar todas as janelas.
-
Configurar o PHP:
- Copie
php.ini-development
paraphp.ini
. - Edite
php.ini
para configurar o PHP conforme necessário (por ex., definindoextension_dir
, habilitando extensões).
- Copie
-
Verificar a instalação do PHP:
- Abra o Prompt de Comando e execute:
php -v
- Abra o Prompt de Comando e execute:
Instalando Múltiplas Versões do PHP
-
Repita os passos acima para cada versão, colocando cada uma em um diretório separado (por ex.,
C:\php7
,C:\php8
). -
Alternar entre as versões ajustando a variável PATH do sistema para apontar para o diretório da versão desejada.
Ubuntu (20.04, 22.04, etc.)
Instalando o PHP usando apt
-
Atualizar listas de pacotes:
- Abra o Terminal e execute:
sudo apt update
- Abra o Terminal e execute:
-
Instalar o PHP:
- Instalar a versão mais recente do PHP:
sudo apt install php
- Para instalar uma versão específica, por exemplo, PHP 8.1:
sudo apt install php8.1
- Instalar a versão mais recente do PHP:
-
Instalar módulos adicionais (opcional):
- Por exemplo, para instalar suporte ao MySQL:
sudo apt install php8.1-mysql
- Por exemplo, para instalar suporte ao MySQL:
-
Alternar entre as versões do PHP:
- Usar
update-alternatives
:sudo update-alternatives --set php /usr/bin/php8.1
- Usar
-
Verificar a versão instalada:
- Execute:
php -v
- Execute:
Rocky Linux
Instalando o PHP usando yum/dnf
-
Ativar o repositório EPEL:
- Abra o Terminal e execute:
sudo dnf install epel-release
- Abra o Terminal e execute:
-
Instalar o repositório do Remi:
- Execute:
sudo dnf install https://rpms.remirepo.net/enterprise/remi-release-8.rpm sudo dnf module reset php
- Execute:
-
Instalar o PHP:
- Para instalar a versão padrão:
sudo dnf install php
- Para instalar uma versão específica, por exemplo, PHP 7.4:
sudo dnf module install php:remi-7.4
- Para instalar a versão padrão:
-
Alternar entre as versões do PHP:
- Usar o comando de módulo
dnf
:sudo dnf module reset php sudo dnf module enable php:remi-8.0 sudo dnf install php
- Usar o comando de módulo
-
Verificar a versão instalada:
- Execute:
php -v
- Execute:
Notas Gerais
- Para ambientes de desenvolvimento, é importante configurar as configurações do PHP conforme os requisitos do seu projeto.
- Ao alternar entre as versões do PHP, assegure-se de que todas as extensões relevantes do PHP estejam instaladas para a versão específica que pretende usar.
- Reinicie seu servidor web (Apache, Nginx, etc.) após alternar entre as versões do PHP ou atualizar as configurações para aplicar as alterações.
Guides
Guias
Flight PHP foi projetado para ser simples, mas poderoso, e nossos guias irão ajudá-lo a construir aplicações do mundo real passo a passo. Estes tutoriais práticos o guiarão por projetos completos para demonstrar como o Flight pode ser usado de forma eficaz.
Guias Oficiais
Construindo um Blog
Aprenda como criar um aplicativo de blog funcional com o Flight PHP. Este guia o orienta sobre:
- Configurar uma estrutura de projeto
- Trabalhar com templates usando Latte
- Implementar rotas para postagens
- Armazenar e recuperar dados
- Lidar com envios de formulários
- Tratamento básico de erros
Este tutorial é perfeito para iniciantes que desejam ver como todas as peças se encaixam em uma aplicação real.
Guias Não Oficiais
Embora esses guias não sejam oficialmente mantidos pela equipe do Flight, eles são recursos valiosos criados pela comunidade. Eles cobrem vários tópicos e casos de uso, fornecendo percepções adicionais sobre o uso do Flight PHP.
Criando uma API RESTful com o Flight Framework
Este guia o orienta na criação de uma API RESTful usando o framework Flight PHP. Ele cobre o básico sobre como configurar uma API, definir rotas e retornar respostas em JSON.
Construindo um Blog Simples
Este guia o orienta na criação de um blog básico usando o framework Flight PHP. Ele na verdade possui 2 partes: uma para cobrir o básico e a outra para abordar tópicos mais avançados e refinamentos para um blog pronto para produção.
- Construindo um Blog Simples com Flight - Parte 1 - Começando com um blog simples.
- Construindo um Blog Simples com Flight - Parte 2 - Refinando o blog para produção.
Construindo uma API de Pokémon em PHP: Um Guia para Iniciantes
Este guia divertido o orienta na criação de uma API de Pokémon simples usando o Flight PHP. Ele cobre o básico sobre como configurar uma API, definir rotas e retornar respostas em JSON.
Contribuindo
Tem uma ideia para um guia? Encontrou um erro? Nós acolhemos contribuições! Nossos guias são mantidos no repositório de documentação FlightPHP.
Se você construiu algo interessante com o Flight e deseja compartilhá-lo como um guia, por favor, envie um pull request. Compartilhar seu conhecimento ajuda a comunidade Flight a crescer.
Procurando Documentação da API?
Se você está procurando informações específicas sobre os recursos e métodos principais do Flight, confira a seção Aprender da nossa documentação.