Learn
Узнайте о Flight
Flight — это быстрый, простой, расширяемый фреймворк для PHP. Он достаточно универсален и может быть использован для создания любых веб-приложений. Он создан с учетом простоты и написан так, что легко понять и использовать.
Важные концепции фреймворка
Почему фреймворк?
Вот короткая статья о том, почему вам следует использовать фреймворк. Хорошо понимать преимущества использования фреймворка прежде чем начать его использовать.
Кроме того, отличный учебник был создан @lubiana. Хотя он не углубляется в детали Flight, это руководство поможет вам понять некоторые основные концепции, связанные с фреймворком, и почему их полезно использовать. Вы можете найти учебник здесь.
Flight в сравнении с другими фреймворками
Если вы переходите с другого фреймворка, такого как Laravel, Slim, Fat-Free или Symfony на Flight, эта страница поможет вам понять различия между ними.
Основные темы
Автозагрузка
Узнайте, как автозагружать свои собственные классы в вашем приложении.
Маршрутизация
Узнайте, как управлять маршрутами для вашего веб-приложения. Это также включает в себя группировку маршрутов, параметры маршрутов и промежуточное программное обеспечение.
Промежуточное ПО
Узнайте, как использовать промежуточное ПО для фильтрации запросов и ответов в вашем приложении.
Запросы
Узнайте, как обрабатывать запросы и ответы в вашем приложении.
Ответы
Узнайте, как отправлять ответы вашим пользователям.
События
Узнайте, как использовать систему событий для добавления пользовательских событий в ваше приложение.
HTML Шаблоны
Узнайте, как использовать встроенный движок представлений для рендеринга ваших HTML шаблонов.
Безопасность
Узнайте, как защитить ваше приложение от распространенных угроз безопасности.
Конфигурация
Узнайте, как настроить фреймворк для вашего приложения.
Расширение Flight
Узнайте, как расширять фреймворк, добавляя свои собственные методы и классы.
События и фильтрация
Узнайте, как использовать систему событий для добавления хуков к вашим методам и внутренним методам фреймворка.
Контейнер внедрения зависимостей
Узнайте, как использовать контейнеры внедрения зависимостей (DIC) для управления зависимостями вашего приложения.
API фреймворка
Узнайте о основных методах фреймворка.
Переход на v3
Совместимость с предыдущими версиями в основном была сохранена, но есть некоторые изменения, о которых вам следует знать при переходе с v2 на v3.
Устранение неполадок
Существует несколько распространенных проблем, с которыми вы можете столкнуться при использовании Flight. Эта страница поможет вам устранить эти проблемы.
Learn/stopping
Остановка
Вы можете остановить фреймворк в любой момент, вызвав метод halt
:
Flight::halt();
Вы также можете указать необязательный HTTP
код состояния и сообщение:
Flight::halt(200, 'Сейчас вернусь...');
Вызов halt
приведет к отмене любого содержимого ответа до этого момента. Если вы хотите остановить
фреймворк и вывести текущий ответ, используйте метод stop
:
Flight::stop();
Learn/errorhandling
Обработка Ошибок
Ошибки и Исключения
Все ошибки и исключения перехватываются Flight и передаются методу error
.
Поведение по умолчанию - отправлять общий ответ HTTP 500 Внутренняя Ошибка Сервера
с некоторой информацией об ошибке.
Вы можете переопределить это поведение для ваших собственных потребностей:
Flight::map('error', function (Throwable $error) {
// Обработать ошибку
echo $error->getTraceAsString();
});
По умолчанию ошибки не записываются в журнал веб-сервера. Вы можете включить это, изменив конфигурацию:
Flight::set('flight.log_errors', true);
Не Найдено
Когда URL не может быть найден, Flight вызывает метод notFound
. Поведение по
умолчанию - отправить ответ HTTP 404 Не Найдено
с простым сообщением.
Вы можете переопределить это поведение для ваших собственных потребностей:
Flight::map('notFound', function () {
// Обработать не найдено
});
Learn/flight_vs_laravel
Сравнение Flight и Laravel
Что такое Laravel?
Laravel - это полнофункциональный фреймворк с множеством функций и удивительной экосистемой, но это отражается на производительности и сложности. Цель Laravel - обеспечить разработчику наивысший уровень продуктивности и упростить выполнение общих задач. Laravel отличный выбор для разработчиков, которые стремятся создать полнофункциональное корпоративное веб-приложение. Однако это связано с определенными компромиссами, особенно в плане производительности и сложности. Начать работу с Laravel легко, но для освоения фреймворка потребуется некоторое время.
Часто разработчики чувствуют, что единственный способ решить проблемы в Laravel - использовать модули, в то время как на самом деле можно просто использовать другую библиотеку или написать свой собственный код.
Преимущества по сравнению с Flight
- В Laravel есть огромная экосистема разработчиков и модулей, которые могут использоваться для решения общих задач.
- Laravel имеет полнофункциональную ORM, которая может использоваться для взаимодействия с базой данных.
- Laravel имеет огромное количество документации и учебных пособий, которые помогут в изучении фреймворка.
- Laravel имеет встроенную систему аутентификации, которая обеспечит безопасность вашего приложения.
- У Laravel есть подкасты, конференции, встречи, видео и другие ресурсы, которые помогут в изучении фреймворка.
- Laravel ориентирован на опытных разработчиков, которые хотят создать полнофункциональное корпоративное веб-приложение.
Недостатки по сравнению с Flight
- В Laravel происходит гораздо больше "под капотом", чем в Flight. Это сказывается на колоссальных затратах в плане производительности. См. бенчмарки TechEmpower для получения дополнительной информации.
- Flight ориентирован на разработчика, который хочет создать легкое, быстрое и простое веб-приложение.
- Flight ориентирован на простоту и легкость использования.
- Одной из основных особенностей Flight является поддержание обратной совместимости. Laravel вызывает много недовольства при переходе между основными версиями.
- Flight предназначен для разработчиков, которые только начинают знакомиться с миром фреймворков.
- У Flight нет зависимостей, в то время как у Laravel ужасное количество зависимостей.
- Flight также способен создавать корпоративные приложения, но у него не так много шаблонного кода, как у Laravel. Это также потребует больше дисциплины со стороны разработчика для сохранения порядка и хорошей структуры.
- Flight дает разработчику больше контроля над приложением, в то время как у Laravel много магии "под капотом", что может вызывать недовольство.
Learn/migrating_to_v3
Миграция на v3
Обратная совместимость в целом была сохранена, но есть некоторые изменения, о которых вам следует знать при переходе с v2 на v3.
Поведение буферизации вывода (3.5.0)
Буферизация вывода - это процесс, при котором вывод, сгенерированный сценарием PHP, хранится в буфере (внутри PHP) перед отправкой клиенту. Это позволяет вам изменять вывод перед его отправкой клиенту.
В приложении MVC Контроллер является "менеджером" и управляет тем, что делает представление. Генерация вывода за пределами контроллера (или в случае Flight иногда анонимной функции) нарушает шаблон MVC. Это изменение призвано больше соответствовать шаблону MVC и сделать фреймворк более предсказуемым и легким в использовании.
В v2 буферизация вывода обрабатывалась таким образом, что не всегда закрывался собственный буфер вывода, что затрудняло unit тестирование и потоковую передачу. Для большинства пользователей это изменение на самом деле может вас не затронуть. Однако, если вы выводите содержимое вне вызываемых функций и контроллеров (например, в хуке), вам, скорее всего, придется столкнуться с проблемами. Вывод содержимого в хуках и до фактического выполнения фреймворка мог работать в прошлом, но это не сработает в будущем.
Где у вас могут возникнуть проблемы
// index.php
require 'vendor/autoload.php';
// просто пример
define('START_TIME', microtime(true));
function hello() {
echo 'Привет, мир';
}
Flight::map('hello', 'hello');
Flight::after('hello', function(){
// это на самом деле будет хорошо
echo '<p>Эта фраза Привет, мир была предоставлена буквой "П"</p>';
});
Flight::before('start', function(){
// такие вещи вызовут ошибку
echo '<html><head><title>Моя страница</title></head><body>';
});
Flight::route('/', function(){
// это на самом деле в порядке
echo 'Привет, мир';
// Это тоже должно быть в порядке
Flight::hello();
});
Flight::after('start', function(){
// это вызовет ошибку
echo '<div>Ваша страница загрузилась за '.(microtime(true) - START_TIME).' секунд</div></body></html>';
});
Включение поведения рендеринга v2
Можно ли сохранить ваш старый код без переписывания, чтобы он работал с v3? Да, вы можете! Вы можете включить поведение
рендеринга v2, установив параметр конфигурации flight.v2.output_buffering
в значение true
. Это позволит вам продолжать
использовать старое поведение рендеринга, но рекомендуется исправить его на будущее. В v4 фреймворка это будет удалено.
// index.php
require 'vendor/autoload.php';
Flight::set('flight.v2.output_buffering', true);
Flight::before('start', function(){
// Теперь это будет в порядке
echo '<html><head><title>Моя страница</title></head><body>';
});
// еще код
Изменения Диспетчера (3.7.0)
Если вы напрямую вызывали статические методы для Dispatcher
, такие как Dispatcher::invokeMethod()
, Dispatcher::execute()
, и т.д.,
вам потребуется обновить ваш код, чтобы не вызывать эти методы напрямую. Dispatcher
был преобразован в более объектно-ориентированный
для удобства использования контейнеров внедрения зависимостей. Если вам нужно вызвать метод аналогично тому, как делал Диспетчер,
вы можете вручную использовать что-то вроде $result = $class->$method(...$params);
или call_user_func_array()
вместо этого.
Изменения halt()
stop()
redirect()
и error()
(3.10.0)
Поведение по умолчанию до версии 3.10.0 заключалось в очистке как заголовков, так и тела ответа. Это было изменено на очистку
только тела ответа. Если вам нужно очистить также заголовки, вы можете использовать Flight::response()->clear()
.
Learn/configuration
Конфигурация
Вы можете настроить определенные поведения Flight, установив значения конфигурации
через метод set
.
Flight::set('flight.log_errors', true);
Доступные настройки конфигурации
Ниже приведен список всех доступных параметров конфигурации:
- flight.base_url
?string
- Переопределение базового URL запроса. (по умолчанию: null) - flight.case_sensitive
bool
- Учет регистра при сопоставлении URL. (по умолчанию: false) - flight.handle_errors
bool
- Разрешить Flight обрабатывать все ошибки внутренне. (по умолчанию: true) - flight.log_errors
bool
- Записывать ошибки в файл журнала сервера веб-сервера. (по умолчанию: false) - flight.views.path
string
- Каталог, содержащий файлы шаблонов представлений. (по умолчанию: ./views) - flight.views.extension
string
- Расширение файла шаблона представления. (по умолчанию: .php) - flight.content_length
bool
- Установить заголовокContent-Length
. (по умолчанию: true) - flight.v2.output_buffering
bool
- Использовать устаревшее буферизацию вывода. См. переход к v3. (по умолчанию: false)
Конфигурация загрузчика
Существует еще один параметр конфигурации для загрузчика. Это позволит вам
автоматически загружать классы с _
в имени класса.
// Включить загрузку классов с подчеркиваниями
// Установлено по умолчанию в true
Loader::$v2ClassLoading = false;
Переменные
Flight позволяет вам сохранять переменные, чтобы их можно было использовать в любом месте вашего приложения.
// Сохраните вашу переменную
Flight::set('id', 123);
// В другом месте в вашем приложении
$id = Flight::get('id');
Чтобы проверить, была ли установлена переменная, вы можете сделать следующее:
if (Flight::has('id')) {
// Сделайте что-то
}
Вы можете очистить переменную, сделав следующее:
// Очищает переменную id
Flight::clear('id');
// Очистить все переменные
Flight::clear();
Flight также использует переменные в целях конфигурации.
Flight::set('flight.log_errors', true);
Обработка ошибок
Ошибки и исключения
Все ошибки и исключения перехватываются Flight и передаются методу error
.
По умолчанию выполняется отправка общего ответа HTTP 500 Internal Server Error
с некоторой информацией об ошибке.
Вы можете изменить это поведение под свои нужды:
Flight::map('error', function (Throwable $error) {
// Обработать ошибку
echo $error->getTraceAsString();
});
По умолчанию ошибки не регистрируются на веб-сервере. Вы можете включить это, изменив конфигурацию:
Flight::set('flight.log_errors', true);
Не найдено
Когда URL не найден, Flight вызывает метод notFound
. По умолчанию
выполняется отправка ответа HTTP 404 Not Found
с простым сообщением.
Вы можете изменить это поведение под свои нужды:
Flight::map('notFound', function () {
// Обработать не найденный
});
Learn/security
Безопасность
Безопасность является важным аспектом веб-приложений. Вы хотите убедиться, что ваше приложение безопасно, и что данные ваших пользователей находятся в безопасности. Flight предоставляет ряд функций, которые помогут вам обезопасить ваши веб-приложения.
Заголовки
HTTP-заголовки — один из самых простых способов защитить ваши веб-приложения. Вы можете использовать заголовки, чтобы предотвратить кликджекинг, XSS и другие атаки. Есть несколько способов добавить эти заголовки в ваше приложение.
Два отличных веб-сайта для проверки безопасности ваших заголовков — это securityheaders.com и observatory.mozilla.org.
Добавить вручную
Вы можете вручную добавить эти заголовки, используя метод header
объекта Flight\Response
.
// Установите заголовок X-Frame-Options для предотвращения кликджекинга
Flight::response()->header('X-Frame-Options', 'SAMEORIGIN');
// Установите заголовок Content-Security-Policy для предотвращения XSS
// Примечание: этот заголовок может быть очень сложным, поэтому вам стоит
// посмотреть примеры в интернете для вашего приложения
Flight::response()->header("Content-Security-Policy", "default-src 'self'");
// Установите заголовок X-XSS-Protection для предотвращения XSS
Flight::response()->header('X-XSS-Protection', '1; mode=block');
// Установите заголовок X-Content-Type-Options для предотвращения MIME sniffing
Flight::response()->header('X-Content-Type-Options', 'nosniff');
// Установите заголовок Referrer-Policy для контроля над тем, сколько информации передается
Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');
// Установите заголовок Strict-Transport-Security для принудительного использования HTTPS
Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
// Установите заголовок Permissions-Policy для контроля над тем, какие функции и API могут быть использованы
Flight::response()->header('Permissions-Policy', 'geolocation=()');
Эти заголовки можно добавить в начало ваших файлов bootstrap.php
или index.php
.
Добавить как фильтр
Вы также можете добавить их в фильтр/хуки, как показано ниже:
// Добавьте заголовки в фильтре
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=()');
});
Добавить как промежуточное ПО
Вы также можете добавить их как класс промежуточного ПО. Это хороший способ сохранить ваш код чистым и организованным.
// 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 или где угодно, где у вас есть ваши маршруты
// К вашему сведению, эта пустая строка группируется как глобальное промежуточное ПО для
// всех маршрутов. Конечно, вы можете сделать то же самое и добавить
// это только для конкретных маршрутов.
Flight::group('', function(Router $router) {
$router->get('/users', [ 'UserController', 'getUsers' ]);
// больше маршрутов
}, [ new SecurityHeadersMiddleware() ]);
Подделка межсайтовых запросов (CSRF)
Подделка межсайтовых запросов (CSRF) — это тип атаки, при которой вредоносный веб-сайт может заставить браузер пользователя отправить запрос на ваш веб-сайт. Это может использоваться для выполнения действий на вашем сайте без ведома пользователя. Flight не предоставляет встроенного механизма защиты от CSRF, но вы можете легко реализовать свой собственный с помощью промежуточного ПО.
Настройка
Сначала вам нужно сгенерировать токен CSRF и сохранить его в сеансе пользователя. Вы можете использовать этот токен в ваших формах и проверять его, когда форма отправлена.
// Сгенерируйте токен CSRF и сохраните его в сеансе пользователя
// (предполагая, что вы создали объект сеанса и прикрепили его к Flight)
// смотрите документацию по сеансам для получения дополнительной информации
Flight::register('session', \Ghostff\Session\Session::class);
// Вам нужно сгенерировать только один токен на сеанс (чтобы он работал
// на нескольких вкладках и запросах для одного и того же пользователя)
if(Flight::session()->get('csrf_token') === null) {
Flight::session()->set('csrf_token', bin2hex(random_bytes(32)) );
}
<!-- Используйте токен CSRF в вашей форме -->
<form method="post">
<input type="hidden" name="csrf_token" value="<?= Flight::session()->get('csrf_token') ?>">
<!-- другие поля формы -->
</form>
Использование Latte
Вы также можете установить пользовательскую функцию для вывода токена CSRF в ваших шаблонах Latte.
// Установите пользовательскую функцию для вывода токена CSRF
// Примечание: Вид был сконфигурирован с Latte как движок представления
Flight::view()->addFunction('csrf', function() {
$csrfToken = Flight::session()->get('csrf_token');
return new \Latte\Runtime\Html('<input type="hidden" name="csrf_token" value="' . $csrfToken . '">');
});
И теперь в ваших шаблонах Latte вы можете использовать функцию csrf()
, чтобы вывести токен CSRF.
<form method="post">
{csrf()}
<!-- другие поля формы -->
</form>
Коротко и просто, верно?
Проверка токена CSRF
Вы можете проверить токен CSRF, используя фильтры событий:
// Этот промежуточное ПО проверяет, является ли запрос POST-запросом, и если да, проверяет, действителен ли токен CSRF
Flight::before('start', function() {
if(Flight::request()->method == 'POST') {
// захватите токен CSRF из значений формы
$token = Flight::request()->data->csrf_token;
if($token !== Flight::session()->get('csrf_token')) {
Flight::halt(403, 'Недействительный токен CSRF');
// или для JSON ответа
Flight::jsonHalt(['error' => 'Недействительный токен CSRF'], 403);
}
}
});
Или вы можете использовать класс промежуточного ПО:
// 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, 'Недействительный токен CSRF');
}
}
}
}
// index.php или где угодно, где у вас есть ваши маршруты
Flight::group('', function(Router $router) {
$router->get('/users', [ 'UserController', 'getUsers' ]);
// больше маршрутов
}, [ new CsrfMiddleware() ]);
Межсайтовый скриптинг (XSS)
Межсайтовый скриптинг (XSS) — это тип атаки, при которой злонамеренный веб-сайт может внедрить код на ваш сайт. Большинство этих возможностей возникают из значений формы, которые заполняют ваши конечные пользователи. Вы никогда не должны доверять выводам от ваших пользователей! Всегда предполагает, что все они — лучшие хакеры в мире. Они могут внедрить вредоносный JavaScript или HTML на вашу страницу. Этот код может использоваться для кражи информации у ваших пользователей или выполнения действий на вашем веб-сайте. Используя класс представления Flight, вы можете легко экранировать вывод, чтобы предотвратить атаки XSS.
// Предположим, что пользователь умен и пытается использовать это как свое имя
$name = '<script>alert("XSS")</script>';
// Это экранирует вывод
Flight::view()->set('name', $name);
// Это выведет: <script>alert("XSS")</script>
// Если вы используете что-то вроде Latte, зарегистрированное как ваш класс представления,
// это также будет автоматически экранировано.
Flight::view()->render('template', ['name' => $name]);
SQL-инъекция
SQL-инъекция — это тип атаки, при которой злонамеренный пользователь может внедрить SQL-код в вашу базу данных. Это может использоваться для кражи информации из вашей базы данных или выполнения действий с вашей базой данных. Опять же, вы никогда не должны доверять входным данным от ваших пользователей! Всегда предполагает, что они преследуют свои цели. Вы можете использовать подготовленные выражения в ваших объектах PDO
, чтобы предотвратить SQL-инъекции.
// Предполагая, что у вас зарегистрирован Flight::db() как ваш объект PDO
$statement = Flight::db()->prepare('SELECT * FROM users WHERE username = :username');
$statement->execute([':username' => $username]);
$users = $statement->fetchAll();
// Если вы используете класс PdoWrapper, это можно легко сделать в одной строке
$users = Flight::db()->fetchAll('SELECT * FROM users WHERE username = :username', [ 'username' => $username ]);
// Вы можете сделать то же самое с объектом PDO с ? плейсхолдерами
$statement = Flight::db()->fetchAll('SELECT * FROM users WHERE username = ?', [ $username ]);
// Просто пообещайте, что вы никогда не будете делать что-то подобное...
$users = Flight::db()->fetchAll("SELECT * FROM users WHERE username = '{$username}' LIMIT 5");
// потому что что, если $username = "' OR 1=1; -- ";
// После того, как запрос будет построен, он будет выглядеть так
// SELECT * FROM users WHERE username = '' OR 1=1; -- LIMIT 5
// Это выглядит странно, но это действительный запрос, который сработает. На самом деле,
// это очень распространенная атака SQL-инъекции, которая вернет всех пользователей.
CORS
Обмен ресурсами между разными источниками (CORS) — это механизм, который позволяет запрашивать многие ресурсы (например, шрифты, JavaScript и т. д.) на веб-странице с другого домена, отличного от домена, с которого этот ресурс был получен. Flight не имеет встроенной функциональности, но это можно легко обработать с помощью хука, который будет вызван перед тем, как будет вызван метод Flight::start()
.
// 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
{
// настройте здесь ваши разрешенные хосты.
$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 или где угодно, где у вас есть ваши маршруты
$CorsUtil = new CorsUtil();
// Это нужно выполнить до начала работы.
Flight::before('start', [ $CorsUtil, 'setupCors' ]);
Обработка ошибок
Скрывайте чувствительную информацию об ошибках в производственной среде, чтобы избежать утечки данных злоумышленникам.
// В вашем bootstrap.php или index.php
// в flightphp/skeleton это находится в app/config/config.php
$environment = ENVIRONMENT;
if ($environment === 'production') {
ini_set('display_errors', 0); // Отключить отображение ошибок
ini_set('log_errors', 1); // Записывать ошибки
ini_set('error_log', '/path/to/error.log');
}
// В ваших маршрутах или контроллерах
// Используйте Flight::halt() для контролируемых ответов на ошибки
Flight::halt(403, 'Доступ запрещен');
Санитария ввода
Никогда не доверяйте вводу от пользователя. Убирайте нежелательные данные перед обработкой, чтобы предотвратить внедрение вредоносных данных.
// Предположим, что есть запрос $_POST с $_POST['input'] и $_POST['email']
// Санировать строковый ввод
$clean_input = filter_var(Flight::request()->data->input, FILTER_SANITIZE_STRING);
// Санировать электронную почту
$clean_email = filter_var(Flight::request()->data->email, FILTER_SANITIZE_EMAIL);
Хэширование паролей
Храните пароли в безопасности и аккуратно их проверяйте, используя встроенные функции PHP.
$password = Flight::request()->data->password;
// Хэшировать пароль при хранении (например, во время регистрации)
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
// Проверить пароль (например, во время входа в систему)
if (password_verify($password, $stored_hash)) {
// Пароль совпадает
}
Ограничение частоты
Защитите себя от атак грубой силы, ограничив частоту запросов с помощью кеша.
// Предполагая, что у вас установлен и зарегистрирован flightphp/cache
// Использование flightphp/cache в промежуточном ПО
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, 'Слишком много запросов');
}
$cache->set($key, $attempts + 1, 60); // Сбросить через 60 секунд
});
Заключение
Безопасность является важным аспектом, и важно убедиться, что ваши веб-приложения безопасны. Flight предоставляет ряд функций, которые помогут вам защитить ваши веб-приложения, но важно всегда быть бдительным и убедиться, что вы делаете все возможное, чтобы защитить данные своих пользователей. Всегда предполагайте худшее и никогда не доверяйте вводу от ваших пользователей. Всегда экранируйте вывод и используйте подготовленные выражения, чтобы предотвратить SQL-инъекции. Всегда используйте промежуточное ПО для защиты ваших маршрутов от атак CSRF и CORS. Если вы сделаете все это, вы будете на пути к созданию безопасных веб-приложений.
Learn/overriding
Переопределение
Flight позволяет вам переопределять его стандартную функциональность под свои собственные потребности, не изменяя какой-либо код.
Например, когда Flight не может сопоставить URL с маршрутом, он вызывает метод notFound
,
который отправляет общий ответ HTTP 404
. Вы можете переопределить это поведение
используя метод map
:
Flight::map('notFound', function() {
// Показать пользовательскую страницу 404
include 'errors/404.html';
});
Flight также позволяет заменять основные компоненты фреймворка. Например, вы можете заменить стандартный класс Router на свой собственный класс:
// Зарегистрируйте свой собственный класс
Flight::register('router', MyRouter::class);
// Когда Flight загружает экземпляр Router, он загрузит ваш класс
$myrouter = Flight::router();
Однако методы фреймворка, такие как map
и register
, не могут быть переопределены. Если
вы попытаетесь это сделать, вы получите ошибку.
Learn/routing
Маршрутизация
Примечание: Хотите узнать больше о маршрутизации? Посмотрите страницу "почему фреймворк?" для более детального объяснения.
Базовая маршрутизация в Flight осуществляется путем сопоставления шаблона URL с функцией обратного вызова или массивом класса и метода.
Flight::route('/', function(){
echo 'привет, мир!';
});
Маршруты сопоставляются в порядке их определения. Первый маршрут, который соответствует запросу, будет вызван.
Обратные вызовы/Функции
Функция обратного вызова может быть любым вызываемым объектом. Поэтому вы можете использовать обычную функцию:
function hello() {
echo 'привет, мир!';
}
Flight::route('/', 'hello');
Классы
Вы также можете использовать статический метод класса:
class Greeting {
public static function hello() {
echo 'привет, мир!';
}
}
Flight::route('/', [ 'Greeting','hello' ]);
Или создав объект сначала, а затем вызвав метод:
// Greeting.php
class Greeting
{
public function __construct() {
$this->name = 'Джон Доу';
}
public function hello() {
echo "Привет, {$this->name}!";
}
}
// index.php
$greeting = new Greeting();
Flight::route('/', [ $greeting, 'hello' ]);
// Вы также можете сделать это без создания объекта сначала
// Примечание: аргументы не будут переданы в конструктор
Flight::route('/', [ 'Greeting', 'hello' ]);
// Дополнительно вы можете использовать эту более короткую синтаксис
Flight::route('/', 'Greeting->hello');
// или
Flight::route('/', Greeting::class.'->hello');
Внедрение зависимостей через DIC (Контейнер для внедрения зависимостей)
Если вы хотите использовать внедрение зависимостей через контейнер (PSR-11, PHP-DI, Dice и т. д.), единственный тип маршрутов, где это доступно, это либо непосредственное создание объекта самостоятельно и использование контейнера для создания вашего объекта, либо вы можете использовать строки для определения класса и метода для вызова. Вы можете перейти на страницу Внедрение зависимостей для большей информации.
Вот краткий пример:
use flight\database\PdoWrapper;
// Greeting.php
class Greeting
{
protected PdoWrapper $pdoWrapper;
public function __construct(PdoWrapper $pdoWrapper) {
$this->pdoWrapper = $pdoWrapper;
}
public function hello(int $id) {
// сделать что-то с $this->pdoWrapper
$name = $this->pdoWrapper->fetchField("SELECT name FROM users WHERE id = ?", [ $id ]);
echo "Привет, мир! Меня зовут {$name}!";
}
}
// index.php
// Настройте контейнер с любыми параметрами, которые вам нужны
// См. страницу Внедрения зависимостей для получения дополнительной информации о PSR-11
$dice = new \Dice\Dice();
// Не забудьте повторно присвоить переменной '$dice = '!!!!!
$dice = $dice->addRule('flight\database\PdoWrapper', [
'shared' => true,
'constructParams' => [
'mysql:host=localhost;dbname=test',
'root',
'password'
]
]);
// Зарегистрируйте обработчик контейнера
Flight::registerContainerHandler(function($class, $params) use ($dice) {
return $dice->create($class, $params);
});
// Маршруты как обычно
Flight::route('/hello/@id', [ 'Greeting', 'hello' ]);
// или
Flight::route('/hello/@id', 'Greeting->hello');
// или
Flight::route('/hello/@id', 'Greeting::hello');
Flight::start();
Маршрутизация по методу
По умолчанию шаблоны маршрутов сопоставляются со всеми методами запроса. Вы можете реагировать на конкретные методы, поставив идентификатор перед URL.
Flight::route('GET /', function () {
echo 'Я получил GET запрос.';
});
Flight::route('POST /', function () {
echo 'Я получил POST запрос.';
});
// Вы не можете использовать Flight::get() для маршрутов, так как это метод
// для получения переменных, а не создания маршрута.
// Flight::post('/', function() { /* код */ });
// Flight::patch('/', function() { /* код */ });
// Flight::put('/', function() { /* код */ });
// Flight::delete('/', function() { /* код */ });
Вы также можете сопоставить несколько методов с одним обратным вызовом, используя разделитель |
:
Flight::route('GET|POST /', function () {
echo 'Я получил либо GET, либо POST запрос.';
});
Дополнительно вы можете получить объект Router, который имеет некоторые вспомогательные методы для вашего использования:
$router = Flight::router();
// сопоставляет все методы
$router->map('/', function() {
echo 'привет, мир!';
});
// GET запрос
$router->get('/users', function() {
echo 'пользователи';
});
// $router->post();
// $router->put();
// $router->delete();
// $router->patch();
Регулярные выражения
Вы можете использовать регулярные выражения в своих маршрутах:
Flight::route('/user/[0-9]+', function () {
// Это будет соответствовать /user/1234
});
Хотя этот метод доступен, рекомендуется использовать именованные параметры или именованные параметры с регулярными выражениями, так как они более читаемы и проще в обслуживании.
Именованные параметры
Вы можете указать именованные параметры в своих маршрутах, которые будут переданы вашей функции обратного вызова. Это больше для читаемости маршрута, чем для чего-либо другого. Пожалуйста, смотрите раздел ниже о важной оговорке.
Flight::route('/@name/@id', function (string $name, string $id) {
echo "привет, $name ($id)!";
});
Вы также можете включить регулярные выражения с вашими именованными параметрами, используя
разделитель :
:
Flight::route('/@name/@id:[0-9]{3}', function (string $name, string $id) {
// Это будет соответствовать /bob/123
// Но не будет соответствовать /bob/12345
});
Примечание: Не поддерживается сопоставление групп regex
()
с позиционными параметрами. :'(
Важная оговорка
Хотя в приведенном выше примере кажется, что @name
непосредственно связан с переменной $name
, это не так. Порядок параметров в функции обратного вызова определяет, что будет передано ей. Так что если вы измените порядок параметров в функции обратного вызова, переменные также изменятся. Вот пример:
Flight::route('/@name/@id', function (string $id, string $name) {
echo "привет, $name ($id)!";
});
А если вы перейдете по следующему URL: /bob/123
, вывод будет привет, 123 (bob)!
.
Пожалуйста, будьте осторожны, когда настраиваете свои маршруты и функции обратного вызова.
Необязательные параметры
Вы можете указать именованные параметры, которые являются необязательными для сопоставления, обернув сегменты в круглые скобки.
Flight::route(
'/blog(/@year(/@month(/@day)))',
function(?string $year, ?string $month, ?string $day) {
// Это будет соответствовать следующим URL:
// /blog/2012/12/10
// /blog/2012/12
// /blog/2012
// /blog
}
);
Любые необязательные параметры, которые не совпадают, будут переданы как NULL
.
Шаблоны
Сопоставление происходит только по отдельным сегментам URL. Если вы хотите сопоставить несколько
сегментов, вы можете использовать подстановочный знак *
.
Flight::route('/blog/*', function () {
// Это будет соответствовать /blog/2000/02/01
});
Чтобы направить все запросы на одну функцию обратного вызова, вы можете сделать следующее:
Flight::route('*', function () {
// Сделать что-то
});
Передача
Вы можете передать выполнение следующему соответствующему маршруту, вернув true
из вашей
функции обратного вызова.
Flight::route('/user/@name', function (string $name) {
// Проверьте некоторое условие
if ($name !== "Bob") {
// Продолжить следующий маршрут
return true;
}
});
Flight::route('/user/*', function () {
// Это будет вызвано
});
Псевдоним маршрута
Вы можете присвоить псевдоним маршруту, чтобы URL можно было динамически генерировать позже в вашем коде (например, как шаблон).
Flight::route('/users/@id', function($id) { echo 'пользователь:'.$id; }, false, 'user_view');
// позже в коде где-то
Flight::getUrl('user_view', [ 'id' => 5 ]); // вернет '/users/5'
Это особенно полезно, если ваш URL по каким-то причинам изменился. В приведенном выше примере, предположим, что пользователи были перемещены на /admin/users/@id
вместо этого.
С псевд naming в месте, вам не нужно менять там, где вы ссылаетесь на псевдоним, потому что псевдоним теперь будет возвращать /admin/users/5
, как в
примере выше.
Псевдоним маршрута все еще работает в группах:
Flight::group('/users', function() {
Flight::route('/@id', function($id) { echo 'пользователь:'.$id; }, false, 'user_view');
});
// позже в коде где-то
Flight::getUrl('user_view', [ 'id' => 5 ]); // вернет '/users/5'
Информация о маршруте
Если вы хотите просмотреть информацию о соответствии маршрута, вы можете запросить, чтобы объект маршрута был передан вашей функции обратного вызова, передав true
в качестве третьего параметра в методе маршрута. Объект маршрута всегда будет последним параметром, переданным вашей функции обратного вызова.
Flight::route('/', function(\flight\net\Route $route) {
// Массив HTTP методов, с которыми было сопоставлено
$route->methods;
// Массив именованных параметров
$route->params;
// Сопоставление регулярного выражения
$route->regex;
// Содержит содержимое любого '*' использованного в шаблоне URL
$route->splat;
// Показывает путь URL....если вам это действительно нужно
$route->pattern;
// Показывает, какому промежуточному программному обеспечению это назначено
$route->middleware;
// Показывает псевдоним, назначенный этому маршруту
$route->alias;
}, true);
Группировка маршрутов
Могут быть случаи, когда вы хотите сгруппировать связанные маршруты вместе (например, /api/v1
).
Вы можете сделать это, используя метод group
:
Flight::group('/api/v1', function () {
Flight::route('/users', function () {
// Соответствует /api/v1/users
});
Flight::route('/posts', function () {
// Соответствует /api/v1/posts
});
});
Вы даже можете вложить группы групп:
Flight::group('/api', function () {
Flight::group('/v1', function () {
// Flight::get() получает переменные, он не устанавливает маршрут! См. контекст объекта ниже
Flight::route('GET /users', function () {
// Соответствует GET /api/v1/users
});
Flight::post('/posts', function () {
// Соответствует POST /api/v1/posts
});
Flight::put('/posts/1', function () {
// Соответствует PUT /api/v1/posts
});
});
Flight::group('/v2', function () {
// Flight::get() получает переменные, он не устанавливает маршрут! См. контекст объекта ниже
Flight::route('GET /users', function () {
// Соответствует GET /api/v2/users
});
});
});
Группировка с контекстом объекта
Вы все еще можете использовать группировку маршрутов с объектом Engine
следующими способами:
$app = new \flight\Engine();
$app->group('/api/v1', function (Router $router) {
// используйте переменную $router
$router->get('/users', function () {
// Соответствует GET /api/v1/users
});
$router->post('/posts', function () {
// Соответствует POST /api/v1/posts
});
});
Ресурсная маршрутизация
Вы можете создать набор маршрутов для ресурса, используя метод resource
. Это создаст
набор маршрутов для ресурса, который соответствует RESTful соглашениям.
Чтобы создать ресурс, сделайте следующее:
Flight::resource('/users', UsersController::class);
И что произойдет в фоновом режиме, так это то, что он создаст следующие маршруты:
[
'index' => 'GET ',
'create' => 'GET /create',
'store' => 'POST ',
'show' => 'GET /@id',
'edit' => 'GET /@id/edit',
'update' => 'PUT /@id',
'destroy' => 'DELETE /@id'
]
И ваш контроллер будет выглядеть так:
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
{
}
}
Примечание: Вы можете просмотреть вновь добавленные маршруты с помощью
runway
, запустивphp runway routes
.
Настройка ресурсных маршрутов
Существует несколько вариантов настройки ресурсных маршрутов.
Псевдоним базы
Вы можете настроить aliasBase
. По умолчанию псевдоним является последней частью указанного URL.
Например, /users/
приведет к aliasBase
равному users
. Когда эти маршруты созданы,
псевдонимы будут users.index
, users.create
и т. д. Если вы хотите изменить псевдоним, установите aliasBase
на желаемое значение.
Flight::resource('/users', UsersController::class, [ 'aliasBase' => 'user' ]);
Только и Исключено
Вы также можете указать, какие маршруты вы хотите создать, используя параметры only
и except
.
Flight::resource('/users', UsersController::class, [ 'only' => [ 'index', 'show' ] ]);
Flight::resource('/users', UsersController::class, [ 'except' => [ 'create', 'store', 'edit', 'update', 'destroy' ] ]);
Это, по сути, параметры белого и черного списков, поэтому вы можете указать, какие маршруты вы хотите создать.
Промежуточное ПО
Вы также можете указать промежуточное ПО, которое будет выполняться на каждом из маршрутов, созданных методом resource
.
Flight::resource('/users', UsersController::class, [ 'middleware' => [ MyAuthMiddleware::class ] ]);
Потоковая передача
Теперь вы можете потоково передавать ответы клиенту, используя метод streamWithHeaders()
.
Это полезно для отправки больших файлов, длительных процессов или генерации больших ответов.
Потоковая маршрутизация обрабатывается немного иначе, чем обычный маршрут.
Примечание: Потоковая передача ответов доступна только в том случае, если у вас установлено
flight.v2.output_buffering
значение false.
Потоковая передача с ручными заголовками
Вы можете потоково передавать ответ клиенту, используя метод stream()
на маршруте. Если вы
это сделаете, вы должны установить все методы вручную перед тем, как что-либо вывести клиенту.
Это делается с помощью функции header()
PHP или метода Flight::response()->setRealHeader()
.
Flight::route('/@filename', function($filename) {
// очевидно, вам нужно будет санировать путь и прочее.
$fileNameSafe = basename($filename);
// Если у вас есть дополнительные заголовки, которые нужно установить здесь после выполнения маршрута,
// вы должны определить их перед тем, как что-либо вывести.
// Все они должны быть вызваны через функцию header() или
// вызов метода Flight::response()->setRealHeader()
header('Content-Disposition: attachment; filename="'.$fileNameSafe.'"');
// или
Flight::response()->setRealHeader('Content-Disposition', 'attachment; filename="'.$fileNameSafe.'"');
$fileData = file_get_contents('/some/path/to/files/'.$fileNameSafe);
// Обработка ошибок и прочее
if(empty($fileData)) {
Flight::halt(404, 'Файл не найден');
}
// вручную установите длину содержимого, если хотите
header('Content-Length: '.filesize($filename));
// Передать данные клиенту
echo $fileData;
// Это магическая строка
})->stream();
Потоковая передача с заголовками
Вы также можете использовать метод streamWithHeaders()
, чтобы установить заголовки перед началом потоковой передачи.
Flight::route('/stream-users', function() {
// вы можете добавить любые дополнительные заголовки, которые хотите здесь
// вы просто должны использовать header() или Flight::response()->setRealHeader()
// как бы вы ни получали свои данные, просто в качестве примера...
$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 ',';
}
// Это необходимо для отправки данных клиенту
ob_flush();
}
echo '}';
// Так вы установите заголовки перед началом потоковой передачи.
})->streamWithHeaders([
'Content-Type' => 'application/json',
'Content-Disposition' => 'attachment; filename="users.json"',
// необязательный код состояния, по умолчанию 200
'status' => 200
]);
Learn/flight_vs_symfony
Сравнение Flight и Symfony
Что такое Symfony?
Symfony - набор многоразовых компонентов PHP и фреймворк PHP для веб-проектов.
Стандартный фундамент, на котором строятся лучшие приложения на PHP. Выберите любые из 50 доступных автономных компонентов для ваших собственных приложений.
Ускорьте создание и поддержку ваших веб-приложений на PHP. Заканчивайте повторяющиеся задачи по кодированию и наслаждайтесь возможностью контролировать ваш код.
Преимущества по сравнению с Flight
- Symfony имеет огромную экосистему разработчиков и модулей, которые могут использоваться для решения общих проблем.
- Symfony имеет полнофункциональный ORM (Doctrine), который можно использовать для взаимодействия с вашей базой данных.
- Symfony имеет большое количество документации и учебных пособий, которые могут быть использованы для изучения фреймворка.
- Symfony имеет подкасты, конференции, встречи, видео и другие ресурсы, которые можно использовать для изучения фреймворка.
- Symfony ориентирован на опытного разработчика, который стремится создать полнофункциональное корпоративное веб-приложение.
Недостатки по сравнению с Flight
- Symfony имеет гораздо больше происходящего "под капотом", чем Flight. Это происходит за счет радикальных затрат в терминах производительности. См. бенчмарки TechEmpower для более подробной информации
- Flight ориентирован на разработчика, который стремится создать легкое, быстрое и простое в использовании веб-приложение.
- Flight ориентирован на простоту и удобство использования.
- Одной из основных особенностей Flight является то, что он делает все возможное для поддержания обратной совместимости.
- У Flight нет зависимостей, в то время как у Symfony есть целый ряд зависимостей
- Flight предназначен для разработчиков, которые впервые погружаются в мир фреймворков.
- Flight также может обрабатывать корпоративные приложения, но у него не так много примеров и учебных пособий, как у Symfony. Также для поддержания порядка и хорошей структуры разработчику потребуется больше дисциплины.
- Flight дает разработчику больший контроль над приложением, в то время как Symfony может скрыто использовать магию за кулисами.
Learn/flight_vs_another_framework
Сравнение Flight с другим фреймворком
Если вы переходите с другого фреймворка, такого как Laravel, Slim, Fat-Free или Symfony, на Flight, эта страница поможет вам понять различия между ними.
Laravel
Laravel - это полнофункциональный фреймворк со всеми плюшками и удивительной экосистемой, сосредоточенной на разработчике, но за счет производительности и сложности.
Slim
Slim - это микро-фреймворк, похожий на Flight. Он разработан с упором на легкость использования, но может быть немного сложнее, чем Flight.
Fat-Free
Fat-Free - это полностековый фреймворк в намного меньшем объеме. Хотя в нем есть все необходимые инструменты, его архитектура данных может усложнить некоторые проекты более, чем это необходимо.
Symfony
Symfony - модульный фреймворк корпоративного уровня, разработанный для гибкости и масштабируемости. Для меньших проектов или новых разработчиков Symfony может быть немного подавляющим.
Learn/variables
Переменные
Flight позволяет вам сохранять переменные, чтобы они могли быть использованы в любом месте вашего приложения.
// Сохраняет вашу переменную
Flight::set('id', 123);
// В другом месте вашего приложения
$id = Flight::get('id');
Для проверки установленной переменной вы можете сделать следующее:
if (Flight::has('id')) {
// Сделать что-то
}
Вы можете очистить переменную, выполнив:
// Очищает переменную id
Flight::clear('id');
// Очищает все переменные
Flight::clear();
Flight также использует переменные для целей конфигурации.
Flight::set('flight.log_errors', true);
Learn/dependency_injection_container
Контейнер внедрения зависимостей
Вступление
Контейнер внедрения зависимостей (DIC) является мощным инструментом, который позволяет управлять зависимостями вашего приложения. Это ключевая концепция в современных PHP фреймворках и используется для управления созданием и настройкой объектов. Некоторые примеры DIC библиотек: Dice, Pimple, PHP-DI, и league/container.
DIC - это удобный способ сказать, что он позволяет создавать и управлять вашими классами в централизованном месте. Это полезно, когда вам нужно передавать один и тот же объект в несколько классов (например, в ваши контроллеры). Простой пример может помочь лучше понять это.
Основной пример
В старом подходе это могло выглядеть примерно так:
require 'vendor/autoload.php';
// класс для управления пользователями в базе данных
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();
Вы видите из приведенного выше кода, что мы создаем новый объект PDO
и передаем его
в наш класс UserController
. Это нормально для небольших приложений, но по мере
роста вашего приложения вы обнаружите, что создаете тот же объект PDO
в нескольких
местах. Вот где пригодится DIC.
Вот тот же пример с использованием DIC (используя Dice):
require 'vendor/autoload.php';
// тот же класс, что и выше. Ничего не изменилось
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());
}
}
// создаем новый контейнер
$container = new \Dice\Dice;
// не забудьте переприсвоить его самому себе, как показано ниже!
$container = $container->addRule('PDO', [
// shared означает, что каждый раз будет возвращаться один и тот же объект
'shared' => true,
'constructParams' => ['mysql:host=localhost;dbname=test', 'user', 'pass' ]
]);
// Это регистрирует обработчик контейнера, чтобы Flight знал, что его использовать.
Flight::registerContainerHandler(function($class, $params) use ($container) {
return $container->create($class, $params);
});
// теперь мы можем использовать контейнер для создания нашего UserController
Flight::route('/user/@id', [ 'UserController', 'view' ]);
// или, альтернативно, вы можете определить маршрут так
Flight::route('/user/@id', 'UserController->view');
// или
Flight::route('/user/@id', 'UserController::view');
Flight::start();
Возможно, вы подумаете, что в пример было добавлено много лишнего кода.
Магия заключается в том, что когда у вас есть другой контроллер, который нуждается в объекте PDO
.
// Если все ваши контроллеры имеют конструктор, который нуждается в объекте PDO
// каждый из нижеуказанных маршрутов автоматически будет его использовать!!!
Flight::route('/company/@id', 'CompanyController->view');
Flight::route('/organization/@id', 'OrganizationController->view');
Flight::route('/category/@id', 'CategoryController->view');
Flight::route('/settings', 'SettingsController->view');
Дополнительный бонус использования DIC заключается в том, что тестирование модулей становится намного проще. Вы можете создать имитационный объект и передать его в ваш класс. Это огромное преимущество при написании тестов для вашего приложения!
PSR-11
Flight также может использовать любой совместимый с PSR-11 контейнер. Это означает, что вы можете использовать любой контейнер, который реализует интерфейс PSR-11. Вот пример использования контейнера PSR-11 от League:
require 'vendor/autoload.php';
// тот же класс UserController, что и выше
$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();
Хотя это может быть немного более многословным, чем предыдущий пример с Dice, все равно делает свою работу с теми же преимуществами!
Пользовательский обработчик DIC
Вы также можете создать свой собственный обработчик DIC. Это полезно, если у вас есть пользовательский контейнер, который вы хотите использовать и который не является PSR-11 (Dice). См. основной пример для того, как это сделать.
Кроме того, имеются некоторые полезные значения по умолчанию, которые сделают вашу жизнь проще при использовании Flight.
Экземпляр Engine
Если вы используете экземпляр Engine
в своих контроллерах/промежуточных уровнях, вот
как вы можете его настроить:
// Где-то в вашем файле инициализации
$engine = Flight::app();
$container = new \Dice\Dice;
$container = $container->addRule('*', [
'substitutions' => [
// Здесь передается экземпляр
Engine::class => $engine
]
]);
$engine->registerContainerHandler(function($class, $params) use ($container) {
return $container->create($class, $params);
});
// Теперь вы можете использовать экземпляр Engine в ваших контроллерах/промежуточных уровнях
class MyController {
public function __construct(Engine $app) {
$this->app = $app;
}
public function index() {
$this->app->render('index');
}
}
Добавление других классов
Если у вас есть другие классы, которые вы хотите добавить в контейнер, с Dice это легко, так как они будут автоматически разрешены контейнером. Вот пример:
$container = new \Dice\Dice;
// Если вам не нужно внедрять что-либо в ваш класс
// вам не нужно ничего определять!
Flight::registerContainerHandler(function($class, $params) use ($container) {
return $container->create($class, $params);
});
class MyCustomClass {
public function parseThing() {
return 'thing';
}
}
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
Промежуточное программное обеспечение маршрута
Flight поддерживает промежуточное программное обеспечение маршрута и группы маршрутов. Промежуточное программное обеспечение - это функция, которая выполняется до (или после) обратного вызова маршрута. Это отличный способ добавить проверки аутентификации API в ваш код или проверить, имеет ли пользователь разрешение на доступ к маршруту.
Основное промежуточное программное обеспечение
Вот простой пример:
// Если вы предоставляете только безымянную функцию, она будет выполнена перед обратным вызовом маршрута.
// нет "после" промежуточных функций, за исключением классов (см. ниже)
Flight::route('/path', function() { echo ' Вот я!'; })->addMiddleware(function() {
echo 'Сначала промежуточное программное обеспечение!';
});
Flight::start();
// Это выведет "Сначала промежуточное программное обеспечение! Вот я!"
Существуют некоторые очень важные заметки о промежуточном программном обеспечении, о которых вам следует знать перед тем, как использовать их:
- Промежуточные функции выполняются в порядке их добавления к маршруту. Выполнение аналогично тому, как это делает Slim Framework.
- 'before' выполняются в порядке добавления, 'after' выполняются в обратном порядке.
- Если ваша функция промежуточного программного обеспечения возвращает false, вся работа останавливается и генерируется ошибка 403 Forbidden. Вероятно, вам захочется об этом более гармонично позаботиться с помощью
Flight::redirect()
или чего-то подобного. - Если вам нужны параметры из вашего маршрута, они будут переданы в виде одного массива в вашу функцию промежуточного программного обеспечения (
function($params) { ... }
илиpublic function before($params) {}
). Причина в том, что вы можете структурировать параметры в группы, и в некоторых из этих групп ваши параметры могут фактически появиться в другом порядке, что нарушило бы функцию промежуточного программного обеспечения, обращаясь к неправильному параметру. Таким образом, вы можете обращаться к ним по имени, а не по позиции. - Если вы передаете только имя промежуточного программного обеспечения, оно будет автоматически выполнено контейнером внедрения зависимостей и промежуточное программное обеспечение будет выполнено с необходимыми параметрами. Если у вас не зарегистрирован контейнер внедрения зависимостей, он передаст в
__construct()
экземплярflight\Engine
.
Классы промежуточного программного обеспечения
Промежуточное программное обеспечение можно зарегистрировать также как класс. Если вам нужна функциональность "после", вы должны использовать класс.
class MyMiddleware {
public function before($params) {
echo 'Сначала промежуточное программное обеспечение!';
}
public function after($params) {
echo 'Последнее промежуточное программное обеспечение!';
}
}
$MyMiddleware = new MyMiddleware();
Flight::route('/path', function() { echo ' Вот я! '; })->addMiddleware($MyMiddleware); // также ->addMiddleware([ $MyMiddleware, $MyMiddleware2 ]);
Flight::start();
// Это выведет "Сначала промежуточное программное обеспечение! Вот я! Последнее промежуточное программное обеспечение!"
Обработка ошибок промежуточного программного обеспечения
Предположим, у вас есть промежуточное программное обеспечение аутентификации и вы хотите перенаправить пользователя на страницу входа, если он не аутентифицирован. У вас есть несколько вариантов действий:
- Вы можете вернуть false из функции промежуточного программного обеспечения, и Flight автоматически вернет ошибку 403 Forbidden, но без настроек.
- Вы можете перенаправить пользователя на страницу входа, используя
Flight::redirect()
. - Вы можете создать пользовательскую ошибку в промежуточном программном обеспечении и остановить выполнение маршрута.
Простой пример
Вот простой пример с возвратом false:
class MyMiddleware {
public function before($params) {
if (isset($_SESSION['user']) === false) {
return false;
}
// поскольку это верно, всё продолжает идти
}
}
Пример перенаправления
Вот пример перенаправления пользователя на страницу входа:
class MyMiddleware {
public function before($params) {
if (isset($_SESSION['user']) === false) {
Flight::redirect('/login');
exit;
}
}
}
Пример пользовательской ошибки
Предположим, вам нужно сгенерировать JSON-ошибку, потому что вы создаете API. Вы можете сделать это следующим образом:
class MyMiddleware {
public function before($params) {
$authorization = Flight::request()->headers['Authorization'];
if(empty($authorization)) {
Flight::jsonHalt(['error' => 'Необходимо войти в систему, чтобы получить доступ к этой странице.'], 403);
// или
Flight::json(['error' => 'Необходимо войти в систему, чтобы получить доступ к этой странице.'], 403);
exit;
// или
Flight::halt(403, json_encode(['error' => 'Необходимо войти в систему, чтобы получить доступ к этой странице.']);
}
}
}
Группирование промежуточного программного обеспечения
Вы можете добавить группу маршрутов, и затем у каждого маршрута в этой группе будет также применяться то же промежуточное программное обеспечение. Это полезно, если вам нужно сгруппировать несколько маршрутов, например при помощи промежуточного программного обеспечения аутентификации для проверки ключа API в заголовке.
// добавлено в конце метода группы
Flight::group('/api', function() {
// Этот "пустой" маршрут на самом деле совпадет с /api
Flight::route('', function() { echo 'api'; }, false, 'api');
// Это совпадет с /api/users
Flight::route('/users', function() { echo 'users'; }, false, 'users');
// Это совпадет с /api/users/1234
Flight::route('/users/@id', function($id) { echo 'пользователь:'.$id; }, false, 'user_view');
}, [ new ApiAuthMiddleware() ]);
Если вы хотите применить глобальное промежуточное программное обеспечение ко всем вашим маршрутам, вы можете добавить "пустую" группу:
// добавлено в конце метода группы
Flight::group('', function() {
// Это все еще /users
Flight::route('/users', function() { echo 'users'; }, false, 'users');
// А это все еще /users/1234
Flight::route('/users/@id', function($id) { echo 'пользователь:'.$id; }, false, 'user_view');
}, [ new ApiAuthMiddleware() ]);
Learn/filtering
Фильтрация
Flight позволяет вам фильтровать методы до и после их вызова. Нет заранее определенных хуков, которые нужно запоминать. Вы можете фильтровать любые из методов фреймворка по умолчанию а также любые пользовательские методы, которые вы объявили.
Функция фильтра выглядит следующим образом:
function (array &$params, string &$output): bool {
// Код фильтрации
}
Используя передаваемые переменные, вы можете изменять входные параметры и/или вывод.
Вы можете запустить фильтр перед вызовом метода следующим образом:
Flight::before('start', function (array &$params, string &$output): bool {
// Сделать что-то
});
Вы можете запустить фильтр после вызова метода следующим образом:
Flight::after('start', function (array &$params, string &$output): bool {
// Сделать что-то
});
Вы можете добавлять столько фильтров, сколько захотите, к любому методу. Они будут вызваны в порядке объявления.
Вот пример процесса фильтрации:
// Объявляем пользовательский метод
Flight::map('hello', function (string $name) {
return "Hello, $name!";
});
// Добавляем фильтр до
Flight::before('hello', function (array &$params, string &$output): bool {
// Изменяем параметр
$params[0] = 'Fred';
return true;
});
// Добавляем фильтр после
Flight::after('hello', function (array &$params, string &$output): bool {
// Изменяем вывод
$output .= " Have a nice day!";
return true;
});
// Вызываем пользовательский метод
echo Flight::hello('Bob');
Это должно вывести:
Hello Fred! Have a nice day!
Если у вас определено несколько фильтров, вы можете прервать цепочку, вернув false
в любой из ваших фильтров:
Flight::before('start', function (array &$params, string &$output): bool {
echo 'one';
return true;
});
Flight::before('start', function (array &$params, string &$output): bool {
echo 'two';
// Это прервет цепочку
return false;
});
// Этот фильтр не будет вызван
Flight::before('start', function (array &$params, string &$output): bool {
echo 'three';
return true;
});
Обратите внимание, что основные методы, такие как map
и register
, не могут быть фильтрованы, потому что
они вызываются непосредственно и не динамически вызываются.
Learn/requests
Запросы
Flight инкапсулирует HTTP-запрос в единый объект, к которому можно получить доступ следующим образом:
$request = Flight::request();
Типичные Сценарии Использования
Когда вы работаете с запросом в веб-приложении, обычно вы хотите извлечь заголовок или параметр $_GET
или $_POST
, или, возможно, даже сырое тело запроса. Flight предоставляет простой интерфейс для выполнения всех этих действий.
Вот пример получения параметра строки запроса:
Flight::route('/search', function(){
$keyword = Flight::request()->query['keyword'];
echo "Вы ищете: $keyword";
// запрашиваем базу данных или что-то еще с $keyword
});
Вот пример формы с методом POST:
Flight::route('POST /submit', function(){
$name = Flight::request()->data['name'];
$email = Flight::request()->data['email'];
echo "Вы отправили: $name, $email";
// сохранить в базе данных или что-то еще с $name и $email
});
Свойства Объекта Запроса
Объект запроса предоставляет следующие свойства:
- body - Сырое тело HTTP-запроса
- url - Запрашиваемый URL
- base - Родительский подкаталог URL
- method - Метод запроса (GET, POST, PUT, DELETE)
- referrer - URL-адрес реферера
- ip - IP-адрес клиента
- ajax - Является ли запрос AJAX-запросом
- scheme - Протокол сервера (http, https)
- user_agent - Информация о браузере
- type - Тип содержимого
- length - Длина содержимого
- query - Параметры строки запроса
- data - Пост-данные или JSON-данные
- cookies - Данные cookie
- files - Загруз файлы
- secure - Является ли соединение безопасным
- accept - Параметры принятия HTTP
- proxy_ip - IP-адрес прокси-клиента. Сканирует массив
$_SERVER
на наличиеHTTP_CLIENT_IP
,HTTP_X_FORWARDED_FOR
,HTTP_X_FORWARDED
,HTTP_X_CLUSTER_CLIENT_IP
,HTTP_FORWARDED_FOR
,HTTP_FORWARDED
в указанном порядке. - host - Имя хоста запроса
Вы можете получить доступ к свойствам query
, data
, cookies
и files
как к массивам или объектам.
Итак, чтобы получить параметр строки запроса, вы можете сделать:
$id = Flight::request()->query['id'];
Или вы можете сделать:
$id = Flight::request()->query->id;
СЫРОЕ Тело Запроса
Чтобы получить сырое тело HTTP-запроса, например, при работе с запросами PUT, вы можете сделать:
$body = Flight::request()->getBody();
JSON Входные Данные
Если вы отправляете запрос с типом application/json
и данными {"id": 123}
, они будут доступны через свойство data
:
$id = Flight::request()->data->id;
$_GET
Вы можете получить доступ к массиву $_GET
через свойство query
:
$id = Flight::request()->query['id'];
$_POST
Вы можете получить доступ к массиву $_POST
через свойство data
:
$id = Flight::request()->data['id'];
$_COOKIE
Вы можете получить доступ к массиву $_COOKIE
через свойство cookies
:
$myCookieValue = Flight::request()->cookies['myCookieName'];
$_SERVER
Существует удобный способ доступа к массиву $_SERVER
через метод getVar()
:
$host = Flight::request()->getVar['HTTP_HOST'];
Доступ к Загруженным Файлам через $_FILES
Вы можете получить доступ к загруженным файлам через свойство files
:
$uploadedFile = Flight::request()->files['myFile'];
Обработка Загрузки Файлов
Вы можете обрабатывать загрузку файлов с помощью фреймворка с помощью некоторых вспомогательных методов. В основном это сводится к извлечению данных файла из запроса и перемещению их в новое местоположение.
Flight::route('POST /upload', function(){
// Если у вас было поле ввода, такое как <input type="file" name="myFile">
$uploadedFileData = Flight::request()->getUploadedFiles();
$uploadedFile = $uploadedFileData['myFile'];
$uploadedFile->moveTo('/path/to/uploads/' . $uploadedFile->getClientFilename());
});
Если вы загрузили несколько файлов, вы можете пройтись по ним в цикле:
Flight::route('POST /upload', function(){
// Если у вас было поле ввода, такое как <input type="file" name="myFiles[]">
$uploadedFiles = Flight::request()->getUploadedFiles()['myFiles'];
foreach ($uploadedFiles as $uploadedFile) {
$uploadedFile->moveTo('/path/to/uploads/' . $uploadedFile->getClientFilename());
}
});
Заметка по Безопасности: Всегда валидируйте и очищайте ввод пользователя, особенно при работе с загрузками файлов. Всегда валидируйте типы расширений, которые вы разрешите загружать, но вы также должны валидировать "магические байты" файла, чтобы убедиться, что это действительно тот тип файла, который пользователь заявляет, что это. Существуют статьи и библиотеки для помощи в этом.
Заголовки Запроса
Вы можете получить доступ к заголовкам запроса с помощью метода getHeader()
или getHeaders()
:
// Возможно, вам нужен заголовок авторизации
$host = Flight::request()->getHeader('Authorization');
// или
$host = Flight::request()->header('Authorization');
// Если вам нужно получить все заголовки
$headers = Flight::request()->getHeaders();
// или
$headers = Flight::request()->headers();
Тело Запроса
Вы можете получить доступ к сырому телу запроса, используя метод getBody()
:
$body = Flight::request()->getBody();
Метод Запроса
Вы можете получить доступ к методу запроса, используя свойство method
или метод getMethod()
:
$method = Flight::request()->method; // фактически вызывает getMethod()
$method = Flight::request()->getMethod();
Примечание: Метод getMethod()
сначала извлекает метод из $_SERVER['REQUEST_METHOD']
, затем он может быть переопределен
с помощью $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']
, если он существует, или $_REQUEST['_method']
, если он существует.
URL Запросов
Существуют несколько вспомогательных методов для сборки частей URL для вашего удобства.
Полный URL
Вы можете получить доступ к полному URL запроса, используя метод getFullUrl()
:
$url = Flight::request()->getFullUrl();
// https://example.com/some/path?foo=bar
Базовый URL
Вы можете получить доступ к базовому URL, используя метод getBaseUrl()
:
$url = Flight::request()->getBaseUrl();
// Обратите внимание, нет конечного слэша.
// https://example.com
Парсинг Запросов
Вы можете передать URL в метод parseQuery()
, чтобы разобрать строку запроса в ассоциативный массив:
$query = Flight::request()->parseQuery('https://example.com/some/path?foo=bar');
// ['foo' => 'bar']
Learn/frameworkmethods
Методы фреймворка
Flight разработан для удобства использования и понимания. Ниже приведен полный набор методов для фреймворка. Он состоит из основных методов, которые являются обычными статическими методами, и расширяемых методов, которые являются отображенными методами, которые могут быть отфильтрованы или переопределены.
Основные Методы
Flight::map(строка $name, callable $callback, bool $pass_route = false) // Создает пользовательский метод фреймворка.
Flight::register(строка $name, строка $class, массив $params = [], ?callable $callback = null) // Регистрирует класс для метода фреймворка.
Flight::before(строка $name, callable $callback) // Добавляет фильтр перед методом фреймворка.
Flight::after(строка $name, callable $callback) // Добавляет фильтр после метода фреймворка.
Flight::path(строка $path) // Добавляет путь для автозагрузки классов.
Flight::get(строка $key) // Получает переменную.
Flight::set(строка $key, смешанный $value) // Устанавливает переменную.
Flight::has(строка $key) // Проверяет, установлена ли переменная.
Flight::clear(массив|строка $key = []) // Очищает переменную.
Flight::init() // Инициализирует фреймворк к его настройкам по умолчанию.
Flight::app() // Получает экземпляр объекта приложения.
Расширяемые Методы
Flight::start() // Запускает фреймворк.
Flight::stop() // Останавливает фреймворк и отправляет ответ.
Flight::halt(int $code = 200, строка $message = '') // Останавливает фреймворк с необязательным кодом состояния и сообщением.
Flight::route(строка $pattern, callable $callback, bool $pass_route = false) // Сопоставляет шаблон URL с обратным вызовом.
Flight::group(строка $pattern, callable $callback) // Создает группировку для URL, шаблон должен быть строкой.
Flight::redirect(строка $url, int $code) // Перенаправляет на другой URL.
Flight::render(строка $file, массив $data, ?строка $key = null) // Рендерит файл шаблона.
Flight::error(Throwable $error) // Отправляет ответ HTTP 500.
Flight::notFound() // Отправляет ответ HTTP 404.
Flight::etag(строка $id, строка $type = 'string') // Выполняет кэширование HTTP ETag.
Flight::lastModified(int $time) // Выполняет кэширование HTTP Last-Modified.
Flight::json(смешанный $data, int $code = 200, bool $encode = true, строка $charset = 'utf8', int $option) // Отправляет ответ JSON.
Flight::jsonp(смешанный $data, строка $param = 'jsonp', int $code = 200, bool $encode = true, строка $charset = 'utf8', int $option) // Отправляет ответ JSONP.
Любые пользовательские методы, добавленные с помощью map
и register
, также могут быть отфильтрованы.
Learn/api
Методы API фреймворка
Flight разработан для того, чтобы быть простым в использовании и понимании. Ниже представлен полный набор методов для фреймворка. Он состоит из основных методов, которые являются обычными статическими методами, и расширяемых методов, которые являются сопоставленными методами, которые можно фильтровать или переопределять.
Основные методы
Эти методы являются основными для фреймворка и не могут быть переопределены.
Flight::map(string $name, callable $callback, bool $pass_route = false) // Создает пользовательский метод фреймворка.
Flight::register(string $name, string $class, array $params = [], ?callable $callback = null) // Регистрирует класс для метода фреймворка.
Flight::unregister(string $name) // Удаляет регистрацию класса для метода фреймворка.
Flight::before(string $name, callable $callback) // Добавляет фильтр перед методом фреймворка.
Flight::after(string $name, callable $callback) // Добавляет фильтр после метода фреймворка.
Flight::path(string $path) // Добавляет путь для автозагрузки классов.
Flight::get(string $key) // Получает переменную, установленную с помощью Flight::set().
Flight::set(string $key, mixed $value) // Устанавливает переменную внутри движка Flight.
Flight::has(string $key) // Проверяет, установлена ли переменная.
Flight::clear(array|string $key = []) // Очищает переменную.
Flight::init() // Инициализирует фреймворк с его значениями по умолчанию.
Flight::app() // Получает экземпляр объекта приложения
Flight::request() // Получает экземпляр объекта запроса
Flight::response() // Получает экземпляр объекта ответа
Flight::router() // Получает экземпляр объекта маршрутизатора
Flight::view() // Получает экземпляр объекта представления
Расширяемые методы
Flight::start() // Запускает фреймворк.
Flight::stop() // Останавливает фреймворк и отправляет ответ.
Flight::halt(int $code = 200, string $message = '') // Останавливает фреймворк с необязательным кодом состояния и сообщением.
Flight::route(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Сопоставляет шаблон URL с колбеком.
Flight::post(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Сопоставляет шаблон URL POST-запроса с колбеком.
Flight::put(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Сопоставляет шаблон URL PUT-запроса с колбеком.
Flight::patch(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Сопоставляет шаблон URL PATCH-запроса с колбеком.
Flight::delete(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Сопоставляет шаблон URL DELETE-запроса с колбеком.
Flight::group(string $pattern, callable $callback) // Создает группировку для URL, шаблон должен быть строкой.
Flight::getUrl(string $name, array $params = []) // Генерирует URL на основе псевдонима маршрута.
Flight::redirect(string $url, int $code) // Перенаправляет на другой URL.
Flight::download(string $filePath) // Загружает файл.
Flight::render(string $file, array $data, ?string $key = null) // Отображает файл шаблона.
Flight::error(Throwable $error) // Отправляет ответ HTTP 500.
Flight::notFound() // Отправляет ответ HTTP 404.
Flight::etag(string $id, string $type = 'string') // Выполняет HTTP-кэширование ETag.
Flight::lastModified(int $time) // Выполняет HTTP-кэширование для последнего изменения.
Flight::json(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Отправляет JSON-ответ.
Flight::jsonp(mixed $data, string $param = 'jsonp', int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Отправляет JSONP-ответ.
Flight::jsonHalt(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Отправляет JSON-ответ и останавливает фреймворк.
Flight::onEvent(string $event, callable $callback) // Регистрирует слушатель событий.
Flight::triggerEvent(string $event, ...$args) // Вызывает событие.
Любые пользовательские методы, добавленные с помощью map
и register
, также могут быть отфильтрованы. Для примеров того, как сопоставить эти методы, смотрите руководство Расширение Flight.
Learn/why_frameworks
Почему фреймворк?
Некоторые программисты решительно против использования фреймворков. Они утверждают, что фреймворки избыточны, медленны и сложны в изучении. Они говорят, что фреймворки не нужны, и что можно писать лучший код без них. Конечно, есть несколько обоснованных аргументов против использования фреймворков. Однако, есть также много преимуществ в использовании фреймворков.
Причины использования фреймворка
Вот несколько причин, почему вам может захотеться рассмотреть использование фреймворка:
- Быстрая разработка: Фреймворки предоставляют много функциональности из коробки. Это означает, что вы можете создавать веб-приложения быстрее. Вам не нужно писать столько кода, потому что фреймворк предоставляет много функциональности, которая вам нужна.
- Согласованность: Фреймворки предоставляют последовательный способ сделать вещи. Это облегчает понимание работы кода и упрощает другим разработчикам понимание вашего кода. Если у вас есть скрипт за скриптом, вы можете потерять согласованность между скриптами, особенно если вы работаете с командой разработчиков.
- Безопасность: Фреймворки предоставляют функции безопасности, которые помогают защитить ваши веб-приложения от распространенных угроз безопасности. Это означает, что вам не нужно беспокоиться о безопасности, потому что фреймворк заботится о большей части этого за вас.
- Сообщество: У фреймворков есть большие сообщества разработчиков, которые вносят свой вклад во фреймворк. Это означает, что вы можете получить помощь от других разработчиков, когда у вас возникают вопросы или проблемы. Это также означает, что доступно множество ресурсов для помощи в изучении использования фреймворка.
- Лучшие практики: Фреймворки созданы с использованием лучших практик. Это означает, что вы можете учиться у фреймворка и использовать те же лучшие практики в своем собственном коде. Это может помочь вам стать лучшим программистом. Иногда вы не знаете, чего не знаете, и это может вам плохо повлиять в конечном итоге.
- Расширяемость: Фреймворки разработаны для расширения. Это означает, что вы можете добавлять свою собственную функциональность в фреймворк. Это позволяет создавать веб-приложения, которые соответствуют вашим конкретным потребностям.
Flight - это микрофреймворк. Это означает, что он небольшой и легкий. Он не предоставляет так много функциональности, как более крупные фреймворки, такие как Laravel или Symfony. Однако он предоставляет много функциональности, которая вам нужна для создания веб-приложений. Его также легко изучить и использовать. Это делает его хорошим выбором для быстрого и простого создания веб-приложений. Если вы новичок в фреймворках, Flight - отличный фреймворк для начала. Он поможет вам узнать о преимуществах использования фреймворков, не перегружая вас слишком сложностью. После того как у вас будет опыт работы с Flight, будет легче перейти на более сложные фреймворки, такие как Laravel или Symfony, однако Flight все равно может создать успешное надежное приложение.
Что такое маршрутизация?
Маршрутизация является основой фреймворка Flight, но что это такое? Маршрутизация - это процесс принятия URL и сопоставления его с определенной функцией в вашем коде.
Таким образом вы можете заставить ваш веб-сайт делать разные вещи в зависимости от запрошенного URL. Например, вы могли бы показать профиль пользователя, когда
они посещают /user/1234
, но показать список всех пользователей, когда они посещают /users
. Все это делается через маршрутизацию.
Это может работать так:
- Пользователь заходит в ваш браузер и вводит
http://example.com/user/1234
. - Сервер получает запрос, смотрит на URL и передает его в ваш код приложения Flight.
- Предположим, в вашем коде Flight у вас есть что-то вроде
Flight::route('/user/@id', [ 'UserController', 'viewUserProfile' ]);
. Ваш код приложения Flight смотрит на URL и видит, что он соответствует определенному маршруту, затем выполняет код, который вы определили для этого маршрута. - Маршрутизатор Flight затем запустится и вызовет метод
viewUserProfile($id)
в классеUserController
, передавая1234
в аргумент$id
метода. - Код в вашем методе
viewUserProfile()
затем будет выполняться и делать то, что вы ему сказали делать. Вы можете закончить выводом некоторого HTML для страницы профиля пользователя, или если это RESTful API, вы можете вывести JSON-ответ с информацией о пользователе. - Flight завернет это в красивый бантик, сгенерирует заголовки ответа и отправит обратно в браузер пользователя.
- Пользователь будет полон радости и даст себе теплый объятие!
И зачем это важно?
Иметь правильный централизованный маршрутизатор может действительно сильно облегчить вашу жизнь! Просто сначала это может быть трудно увидеть. Вот несколько причин:
- Централизованная маршрутизация: Вы можете хранить все свои маршруты в одном месте. Это позволяет легче видеть, какие маршруты у вас есть и что они делают. Также это упрощает их изменение, если вам это понадобится.
- Параметры маршрута: Вы можете использовать параметры маршрута, чтобы передавать данные в ваши методы маршрутов. Это отличный способ держать ваш код чистым и организованным.
- Группировка маршрутов: Вы можете группировать маршруты вместе. Это отлично для организации вашего кода и для применения middleware к группе маршрутов.
- Псевдонимы маршрутов: Вы можете присвоить псевдоним маршруту, чтобы URL мог динамически генерироваться позже в вашем коде (например, как шаблон). Например, вместо хардкода
/user/1234
в вашем коде, вы можете обратиться к псевдонимуuser_view
и передатьid
как параметр. Это замечательно в случае, если вы решите изменить его на/admin/user/1234
позднее. Вам не придется изменять все свои жестко закодированные URL, просто URL, привязанный к маршруту. - Промежуточное ПО маршрута: Вы можете добавлять промежуточное программное обеспечение к вашим маршрутам. Промежуточное программное обеспечение невероятно мощно в добавлении определенных поведений к вашему приложению, таких как аутентификация того, что определенный пользователь может получить доступ к маршруту или группе маршрутов.
Я уверен, что вы знакомы со способом создания веб-сайта, описанным скрипт за скриптом. Может быть у вас есть файл под названием index.php
, который содержит множество условных операторов if
для проверки URL, а затем запуска определенной функции на основе URL. Это форма маршрутизации, но она не очень организована и может
быстро выйти из-под контроля. Система маршрутизации Flight - это гораздо более организованный и мощный способ управления маршрутами.
Это?
// /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);
}
// и так далее...
или это?
// index.php
Flight::route('/user/@id', [ 'UserController', 'viewUserProfile' ]);
Flight::route('/user/@id/edit', [ 'UserController', 'editUserProfile' ]);
// Возможно, в вашем app/controllers/UserController.php
class UserController {
public function viewUserProfile($id) {
// сделать что-то
}
public function editUserProfile($id) {
// сделать что-то
}
}
Надеюсь, теперь вы начинаете видеть преимущества использования централизованной системы маршрутизации. Это намного проще управлять и понимать в долгосрочной перспективе!
Запросы и ответы
Flight предоставляет простой и легкий способ обработки запросов и ответов. Это ядро функционала веб-фреймворка. Он принимает запрос от браузера пользователя, обрабатывает его, а затем отправляет ответ. Именно так вы можете создавать веб-приложения, которые показывают профиль пользователя, позволяют пользователю войти в систему или опубликовать новый блог.
Запросы
Запрос - это то, что браузер пользователя отправляет на ваш сервер при посещении вашего веб-сайта. Этот запрос содержит информацию о том, что хочет сделать пользователь. Например, он может содержать информацию о том, какой URL пользователь хочет посетить, какие данные пользователь хочет отправить на ваш сервер, или какие данные пользователь хочет получить от вашего сервера. Важно знать, что запрос только для чтения. Вы не можете изменить запрос, но можете читать его.
Flight предоставляет простой способ получить доступ к информации о запросе. Вы можете получить доступ к информации о запросе, используя метод Flight::request()
Метод возвращает объект Request
, который содержит информацию о запросе. Вы можете использовать этот объект для доступа к информации о запросе, такой как URL, метод или данные,
которые пользователь отправил на ваш сервер.
Ответы
Ответ - это то, что ваш сервер отправляет обратно на браузер пользователя при посещении вашего веб-сайта. Этот ответ содержит информацию о том, что ваш сервер хочет сделать. Например, он может содержать информацию о том, какие данные ваш сервер хочет отправить пользователю, какие данные ваш сервер хочет получить от пользователя, или какие данные ваш сервер хочет сохранить на компьютере пользователя.
Flight предоставляет простой способ отправить ответ браузеру пользователя. Вы можете отправить ответ, используя метод Flight::response()
Метод принимает объект Response
в качестве аргумента и отправляет ответ браузеру пользователя. Вы можете использовать этот объект, чтобы отправить ответ браузеру пользователя,
такой как HTML, JSON или файл. Flight помогает автоматически генерировать некоторые части ответа, чтобы сделать вещи легкими, но в конечном итоге у вас есть
контроль над тем, что вы отправляете обратно пользователю.
Learn/httpcaching
Кэширование HTTP
Flight обеспечивает встроенную поддержку кэширования на уровне HTTP. Если условие кэширования
выполнено, Flight вернет ответ HTTP 304 Not Modified
. В следующий раз
когда клиент запросит тот же ресурс, их попросят использовать локально
кэшированную версию.
Последнее изменение
Вы можете использовать метод lastModified
и передать временную метку UNIX для установки даты
и времени последнего изменения страницы. Клиент будет продолжать использовать свой кэш до тех пор,
пока значение последнего изменения не изменится.
Flight::route('/news', function () {
Flight::lastModified(1234567890);
echo 'Этот контент будет кэширован.';
});
ETag
Кэширование ETag
аналогично Last-Modified
, за исключением того, что вы можете указать любой идентификатор
для ресурса:
Flight::route('/news', function () {
Flight::etag('my-unique-id');
echo 'Этот контент будет кэширован.';
});
Имейте в виду, что вызов методов lastModified
или etag
устанавливает и проверяет
значение кэша. Если значение кэша совпадает между запросами, Flight немедленно
отправит ответ HTTP 304
и прекратит обработку.
Learn/responses
Ответы
Flight помогает генерировать часть заголовков ответа для вас, но вы контролируете, что отправляете обратно пользователю. Иногда вы можете получить доступ к объекту Response
напрямую, но чаще всего вы будете использовать экземпляр Flight
для отправки ответа.
Отправка базового ответа
Flight использует ob_start() для буферизации вывода. Это означает, что вы можете использовать echo
или print
, чтобы отправить ответ пользователю, и Flight захватит его и отправит обратно пользователю с соответствующими заголовками.
// Это отправит "Hello, World!" в браузер пользователя
Flight::route('/', function() {
echo "Hello, World!";
});
// HTTP/1.1 200 OK
// Content-Type: text/html
//
// Hello, World!
В качестве альтернативы вы можете вызвать метод write()
, чтобы добавить в тело тоже.
// Это отправит "Hello, World!" в браузер пользователя
Flight::route('/', function() {
// многословно, но иногда работает, когда вам это нужно
Flight::response()->write("Hello, World!");
// если вы хотите получить тело, которое вы установили на этот момент
// вы можете сделать это так
$body = Flight::response()->getBody();
});
Код состояния
Вы можете установить код состояния ответа, используя метод status
:
Flight::route('/@id', function($id) {
if($id == 123) {
Flight::response()->status(200);
echo "Hello, World!";
} else {
Flight::response()->status(403);
echo "Forbidden";
}
});
Если вы хотите получить текущий код состояния, вы можете использовать метод status
без аргументов:
Flight::response()->status(); // 200
Установка тела ответа
Вы можете установить тело ответа, используя метод write
, однако если вы используете echo или print,
это будет захвачено и отправлено как тело ответа через буферизацию вывода.
Flight::route('/', function() {
Flight::response()->write("Hello, World!");
});
// то же самое что и
Flight::route('/', function() {
echo "Hello, World!";
});
Очистка тела ответа
Если вы хотите очистить тело ответа, вы можете использовать метод clearBody
:
Flight::route('/', function() {
if($someCondition) {
Flight::response()->write("Hello, World!");
} else {
Flight::response()->clearBody();
}
});
Запуск обратного вызова на теле ответа
Вы можете запустить обратный вызов на теле ответа с помощью метода addResponseBodyCallback
:
Flight::route('/users', function() {
$db = Flight::db();
$users = $db->fetchAll("SELECT * FROM users");
Flight::render('users_table', ['users' => $users]);
});
// Это сожмет все ответы для любого маршрута
Flight::response()->addResponseBodyCallback(function($body) {
return gzencode($body, 9);
});
Вы можете добавить несколько обратных вызовов, и они будут выполняться в порядке их добавления. Поскольку это может принимать любой callable, он может принимать массив классов [ $class, 'method' ]
, замыкание $strReplace = function($body) { str_replace('hi', 'there', $body); };
, или имя функции 'minify'
, если у вас есть функция, чтобы минимизировать ваш html код, например.
Примечание: Обратные вызовы маршрутов не будут работать, если вы используете параметр конфигурации flight.v2.output_buffering
.
Специфический обратный вызов маршрута
Если вы хотите, чтобы это применялось только к конкретному маршруту, вы можете добавить обратный вызов в сам маршрут:
Flight::route('/users', function() {
$db = Flight::db();
$users = $db->fetchAll("SELECT * FROM users");
Flight::render('users_table', ['users' => $users]);
// Это сожмет только ответ для этого маршрута
Flight::response()->addResponseBodyCallback(function($body) {
return gzencode($body, 9);
});
});
Опция Middleware
Вы также можете использовать промежуточное ПО, чтобы применить обратный вызов ко всем маршрутам через промежуточное ПО:
// MinifyMiddleware.php
class MinifyMiddleware {
public function before() {
// Примените обратный вызов здесь к объекту response().
Flight::response()->addResponseBodyCallback(function($body) {
return $this->minify($body);
});
}
protected function minify(string $body): string {
// как-то уменьшить тело
return $body;
}
}
// index.php
Flight::group('/users', function() {
Flight::route('', function() { /* ... */ });
Flight::route('/@id', function($id) { /* ... */ });
}, [ new MinifyMiddleware() ]);
Установка заголовка ответа
Вы можете установить заголовок, например тип контента ответа, используя метод header
:
// Это отправит "Hello, World!" в браузер пользователя в виде обычного текста
Flight::route('/', function() {
Flight::response()->header('Content-Type', 'text/plain');
// или
Flight::response()->setHeader('Content-Type', 'text/plain');
echo "Hello, World!";
});
JSON
Flight предоставляет поддержку для отправки JSON и JSONP ответов. Чтобы отправить JSON ответ, вы передаете некоторые данные для кодирования в JSON:
Flight::json(['id' => 123]);
Примечание: По умолчанию Flight будет отправлять заголовок
Content-Type: application/json
с ответом. Он также будет использовать константыJSON_THROW_ON_ERROR
иJSON_UNESCAPED_SLASHES
при кодировании JSON.
JSON с кодом состояния
Вы также можете передать код состояния в качестве второго аргумента:
Flight::json(['id' => 123], 201);
JSON с форматированием
Вы также можете передать аргумент на последней позиции, чтобы включить форматирование:
Flight::json(['id' => 123], 200, true, 'utf-8', JSON_PRETTY_PRINT);
Если вы изменяете параметры, переданные в Flight::json()
, и хотите более простую синтаксис, вы можете
просто переопределить метод JSON:
Flight::map('json', function($data, $code = 200, $options = 0) {
Flight::_json($data, $code, true, 'utf-8', $options);
}
// И теперь его можно использовать так
Flight::json(['id' => 123], 200, JSON_PRETTY_PRINT);
JSON и остановка выполнения (v3.10.0)
Если вы хотите отправить JSON ответ и остановить выполнение, вы можете использовать метод jsonHalt
.
Это полезно в случаях, когда вы проверяете, возможно, какой-то тип авторизации, и если
пользователь не авторизован, вы можете сразу отправить JSON ответ, очистить содержимое текущего тела и остановить выполнение.
Flight::route('/users', function() {
$authorized = someAuthorizationCheck();
// Проверьте, авторизован ли пользователь
if($authorized === false) {
Flight::jsonHalt(['error' => 'Unauthorized'], 401);
}
// Продолжайте с остальной частью маршрута
});
До версии v3.10.0 вам приходилось делать что-то вроде этого:
Flight::route('/users', function() {
$authorized = someAuthorizationCheck();
// Проверьте, авторизован ли пользователь
if($authorized === false) {
Flight::halt(401, json_encode(['error' => 'Unauthorized']));
}
// Продолжайте с остальной частью маршрута
});
JSONP
Для запросов JSONP вы можете опционально передать название параметра запроса, который вы используете для определения вашей функции обратного вызова:
Flight::jsonp(['id' => 123], 'q');
Таким образом, при выполнении GET-запроса с использованием ?q=my_func
, вы должны получить вывод:
my_func({"id":123});
Если вы не передаете название параметра запроса, по умолчанию оно будет установлено в jsonp
.
Перенаправление на другой URL
Вы можете перенаправить текущий запрос, используя метод redirect()
и передавая
новый URL:
Flight::redirect('/new/location');
По умолчанию Flight отправляет статус-код HTTP 303 ("See Other"). Вы можете опционально установить пользовательский код:
Flight::redirect('/new/location', 401);
Остановка
Вы можете остановить работу фреймворка в любой момент, вызвав метод halt
:
Flight::halt();
Вы также можете указать необязательный код состояния HTTP и сообщение:
Flight::halt(200, 'Be right back...');
Вызов halt
отменит любое содержимое ответа до этого момента. Если вы хотите остановить
фреймворк и вывести текущий ответ, используйте метод stop
:
Flight::stop();
Очистка данных ответа
Вы можете очистить тело ответа и заголовки, используя метод clear()
. Это очистит
все заголовки, назначенные ответу, очистит тело ответа и установит код состояния в 200
.
Flight::response()->clear();
Очистка только тела ответа
Если вы хотите очистить только тело ответа, вы можете использовать метод clearBody()
:
// Это всё равно сохранит любые заголовки, установленные на объекте response().
Flight::response()->clearBody();
HTTP кеширование
Flight предоставляет встроенную поддержку HTTP кеширования. Если условие кеширования
выполнено, Flight вернёт ответ HTTP 304 Not Modified
. В следующий раз, когда клиент
запросит тот же ресурс, ему будет предложено использовать свою локальную
кэшированную версию.
Кеширование на уровне маршрута
Если вы хотите кешировать весь ваш ответ, вы можете использовать метод cache()
и передать
время для кеширования.
// Это будет кешировать ответ на 5 минут
Flight::route('/news', function () {
Flight::response()->cache(time() + 300);
echo 'Этот контент будет кеширован.';
});
// Или вы можете использовать строку, которую вы передали бы
// методу strtotime()
Flight::route('/news', function () {
Flight::response()->cache('+5 minutes');
echo 'Этот контент будет кеширован.';
});
Время последнего изменения
Вы можете использовать метод lastModified
и передать метку времени UNIX, чтобы установить дату
и время, когда страница была в последний раз изменена. Клиент будет продолжать использовать
свой кеш, пока значение времени последнего изменения не изменится.
Flight::route('/news', function () {
Flight::lastModified(1234567890);
echo 'Этот контент будет кеширован.';
});
ETag
Кеширование ETag
аналогично Last-Modified
, за исключением того, что вы можете
указать любое уникальное значение для ресурса:
Flight::route('/news', function () {
Flight::etag('my-unique-id');
echo 'Этот контент будет кеширован.';
});
Имейте в виду, что вызов как lastModified
, так и etag
установит и проверит
значение кеша. Если значение кеша одинаково между запросами, Flight сразу
отправит ответ HTTP 304
и остановит обработку.
Загрузка файла (v3.12.0)
Существует вспомогательный метод для загрузки файла. Вы можете использовать метод download
и передать путь.
Flight::route('/download', function () {
Flight::download('/path/to/file.txt');
});
Learn/frameworkinstance
Экземпляр фреймворка
Вместо того чтобы запускать Flight как глобальный статический класс, вы можете опционально запустить его как экземпляр объекта.
require 'flight/autoload.php';
$app = Flight::app();
$app->route('/', function () {
echo 'hello world!';
});
$app->start();
Таким образом, вместо вызова статического метода, вы вызывали бы метод экземпляра с тем же именем на объекте Engine.
Learn/redirects
Редиректы
Можно перенаправить текущий запрос, используя метод redirect
и передав новый URL:
Flight::redirect('/новое/местоположение');
По умолчанию Flight отправляет статусный код HTTP 303. Можно дополнительно установить пользовательский код:
Flight::redirect('/новое/местоположение', 401);
Learn/events
Система событий в Flight PHP (v3.15.0+)
Flight PHP вводит легкую и интуитивную систему событий, которая позволяет вам регистрировать и запускать пользовательские события в вашем приложении. С добавлением Flight::onEvent()
и Flight::triggerEvent()
вы теперь можете подключаться к ключевым моментам жизненного цикла вашего приложения или определять собственные события, чтобы сделать ваш код более модульным и расширяемым. Эти методы являются частью настраиваемых методов Flight, что означает, что вы можете переопределить их поведение в соответствии с вашими потребностями.
В этом руководстве приведено все, что вам нужно знать, чтобы начать работу с событиями, включая причины их ценности, как их использовать и практические примеры, которые помогут новичкам понять их мощь.
Почему использовать события?
События позволяют вам разделить различные части вашего приложения так, чтобы они не зависели слишком сильно друг от друга. Это разделение, часто называемое разделением, упрощает обновление, расширение или отладку вашего кода. Вместо того чтобы писать все в одном большом фрагменте, вы можете разделить свою логику на более мелкие, независимые части, которые реагируют на определенные действия (события).
Представьте, что вы создаете приложение для блога:
- Когда пользователь оставляет комментарий, вы можете захотеть:
- Сохранить комментарий в базе данных.
- Отправить электронное письмо владельцу блога.
- Зафиксировать действие для безопасности.
Без событий вы бы напихали все это в одну функцию. С происшествиями вы можете разделить это: одна часть сохраняет комментарий, другая запускает событие типа 'comment.posted'
, а отдельные слушатели обрабатывают электронную почту и ведение журналов. Это делает ваш код более чистым и позволяет добавлять или удалять функции (например, уведомления), не трогая основную логику.
Обычные применения
- Ведение журнала: Записывайте действия, такие как вход в систему или ошибки, без загромождения основного кода.
- Уведомления: Отправляйте электронные письма или оповещения, когда что-то происходит.
- Обновления: Обновляйте кеши или уведомляйте другие системы о изменениях.
Регистрация слушателей событий
Чтобы прослушивать событие, используйте Flight::onEvent()
. Этот метод позволяет вам определить, что должно произойти, когда событие происходит.
Синтаксис
Flight::onEvent(string $event, callable $callback): void
$event
: Имя вашего события (например,'user.login'
).$callback
: Функция, которая будет запущена, когда событие будет вызвано.
Как это работает
Вы "подписываетесь" на событие, сообщая Flight, что делать, когда оно происходит. Коллбек может принимать аргументы, переданные из триггера события.
Система событий Flight является синхронной, что означает, что каждый слушатель события выполняется последовательно, один за другим. Когда вы инициируете событие, все зарегистрированные слушатели для этого события будут выполнены до завершения, прежде чем ваш код продолжит работу. Это важно понимать, так как это отличается от асинхронных систем событий, где слушатели могут работать параллельно или в более позднее время.
Простой пример
Flight::onEvent('user.login', function ($username) {
echo "Добро пожаловать обратно, $username!";
});
Здесь, когда событие 'user.login'
инициируется, оно поприветствует пользователя по имени.
Ключевые моменты
- Вы можете добавить несколько слушателей к одному и тому же событию — они будут работать в порядке, в котором вы их зарегистрировали.
- Коллбек может быть функцией, анонимной функцией или методом класса.
Инициация событий
Чтобы событие произошло, используйте Flight::triggerEvent()
. Это сообщает Flight выполнить всех слушателей, зарегистрированных для этого события, передавая любые данные, которые вы предоставляете.
Синтаксис
Flight::triggerEvent(string $event, ...$args): void
$event
: Имя события, которое вы инициируете (должно соответствовать зарегистрированному событию)....$args
: Необязательные аргументы для передачи слушателям (может быть любое количество аргументов).
Простой пример
$username = 'alice';
Flight::triggerEvent('user.login', $username);
Это инициирует событие 'user.login'
и отправляет 'alice'
слушателю, который мы определили ранее, что приведет к выводу: Добро пожаловать обратно, alice!
.
Ключевые моменты
- Если слушатели не зарегистрированы, ничего не произойдет — ваше приложение не сломается.
- Используйте оператор распространения (
...
), чтобы гибко передавать несколько аргументов.
Регистрация слушателей событий
...
Остановка дальнейших слушателей:
Если слушатель возвращает false
, никакие дополнительные слушатели для этого события выполняться не будут. Это позволяет вам остановить цепочку событий на основе определенных условий. Помните, что порядок слушателей имеет значение, поскольку первый, кто вернет false
, остановит последующих от работы.
Пример:
Flight::onEvent('user.login', function ($username) {
if (isBanned($username)) {
logoutUser($username);
return false; // Останавливает последующих слушателей
}
});
Flight::onEvent('user.login', function ($username) {
sendWelcomeEmail($username); // это никогда не отправляется
});
Переопределение методов событий
Flight::onEvent()
и Flight::triggerEvent()
доступны для расширения, что означает, что вы можете перес définir, как они работают. Это прекрасно для продвинутых пользователей, которые хотят настроить систему событий, например, добавив ведение журнала или изменив, как события отправляются.
Пример: Настройка onEvent
Flight::map('onEvent', function (string $event, callable $callback) {
// Записывать каждую регистрацию события
error_log("Добавлен новый слушатель события для: $event");
// Вызвать стандартное поведение (предполагая наличие внутренней системы событий)
Flight::_onEvent($event, $callback);
});
Теперь каждый раз, когда вы регистрируете событие, оно записывается перед продолжением.
Почему переопределять?
- Добавить отладку или мониторинг.
- Ограничить события в определенных средах (например, отключить в тестировании).
- Интеграция с другой библиотекой событий.
Куда поместить ваши события
Как новичок, вы можете задаться вопросом: где мне регистрировать все эти события в моем приложении? Простота Flight означает, что нет строгого правила — вы можете разместить их там, где это имеет смысл для вашего проекта. Тем не менее, поддержание их организованными помогает вам поддерживать код по мере роста вашего приложения. Вот некоторые практические варианты и лучшие практики, адаптированные к легковесной природе Flight:
Вариант 1: В вашем основном index.php
Для небольших приложений или быстрых прототипов вы можете зарегистрировать события прямо в вашем файле index.php
наряду с вашими маршрутами. Это держит все в одном месте, что допустимо, когда простота является вашим приоритетом.
require 'vendor/autoload.php';
// Регистрация событий
Flight::onEvent('user.login', function ($username) {
error_log("$username вошел в систему в " . date('Y-m-d H:i:s'));
});
// Определение маршрутов
Flight::route('/login', function () {
$username = 'bob';
Flight::triggerEvent('user.login', $username);
echo "Вошли в систему!";
});
Flight::start();
- Плюсы: Просто, нет лишних файлов, отлично для маленьких проектов.
- Минусы: Может стать беспорядочным, когда ваше приложение расширяется с большим количеством событий и маршрутов.
Вариант 2: Отдельный файл events.php
Для несколько более крупного приложения подумайте о перемещении регистраций событий в специальный файл, такой как app/config/events.php
. Включите этот файл в ваш index.php
до ваших маршрутов. Это имитирует, как маршруты часто организованы в app/config/routes.php
в проектах Flight.
// app/config/events.php
Flight::onEvent('user.login', function ($username) {
error_log("$username вошел в систему в " . date('Y-m-d H:i:s'));
});
Flight::onEvent('user.registered', function ($email, $name) {
echo "Электронное письмо отправлено на $email: Добро пожаловать, $name!";
});
// index.php
require 'vendor/autoload.php';
require 'app/config/events.php';
Flight::route('/login', function () {
$username = 'bob';
Flight::triggerEvent('user.login', $username);
echo "Вошли в систему!";
});
Flight::start();
- Плюсы: Держит
index.php
сосредоточенным на маршрутах, организует события логически, легко находить и редактировать. - Минусы: Добавляет небольшую структурированность, что может показаться излишним для очень маленьких приложений.
Вариант 3: Ближе к месту их триггера
Другой подход - регистрировать события рядом с тем, где они триггерятся, например, внутри контроллера или определения маршрута. Это хорошо работает, если событие специфично для одной части вашего приложения.
Flight::route('/signup', function () {
// Регистрация события здесь
Flight::onEvent('user.registered', function ($email) {
echo "Уведомление об приветствии отправлено на $email!";
});
$email = 'jane@example.com';
Flight::triggerEvent('user.registered', $email);
echo "Зарегистрировано!";
});
- Плюсы: Держит связанный код вместе, хорошо для изолированных функций.
- Минусы: Рассекает регистрации событий, что усложняет возможности видеть все события сразу; риски дубликатов регистраций, если не быть осторожным.
Лучшие практики для Flight
- Начните с простого: Для крошечных приложений размещайте события в
index.php
. Это быстро и соответствует минимализму Flight. - Развивайтесь разумно: По мере расширения вашего приложения (например, более 5-10 событий) используйте файл
app/config/events.php
. Это естественный шаг вверх, подобно организации маршрутов, и держит ваш код аккуратным без добавления сложных фреймворков. - Избегайте чрезмерной инженерии: Не создавайте полный «менеджер событий» или директорию, если ваше приложение не стало огромным — Flight процветает на простоте, поэтому держите его легким.
Советы: группировка по назначению
В events.php
группируйте связанные события (например, все события, связанные с пользователем, вместе) с комментариями для ясности:
// app/config/events.php
// События пользователя
Flight::onEvent('user.login', function ($username) {
error_log("$username вошел в систему");
});
Flight::onEvent('user.registered', function ($email) {
echo "Добро пожаловать на $email!";
});
// События страницы
Flight::onEvent('page.updated', function ($pageId) {
unset($_SESSION['pages'][$pageId]);
});
Эта структура хорошо масштабируется и остается удобной для новичков.
Примеры для новичков
Давайте рассмотрим несколько реальных сценариев, чтобы показать, как работают события и почему они полезны.
Пример 1: Ведение журнала входа пользователя
// Шаг 1: Зарегистрируйте слушателя
Flight::onEvent('user.login', function ($username) {
$time = date('Y-m-d H:i:s');
error_log("$username вошел в систему в $time");
});
// Шаг 2: Запустить это в вашем приложении
Flight::route('/login', function () {
$username = 'bob'; // Предположим, это из формы
Flight::triggerEvent('user.login', $username);
echo "Привет, $username!";
});
Почему это полезно: Код входа не должен знать о ведении журнала — он просто инициирует событие. Позже вы можете добавить больше слушателей (например, отправить приветственное письмо) без изменения маршрута.
Пример 2: Уведомление о новых пользователях
// Слушатель для новых регистраций
Flight::onEvent('user.registered', function ($email, $name) {
// Симулировать отправку письма
echo "Электронное письмо отправлено на $email: Добро пожаловать, $name!";
});
// Инициировать, когда кто-то подписывается
Flight::route('/signup', function () {
$email = 'jane@example.com';
$name = 'Jane';
Flight::triggerEvent('user.registered', $email, $name);
echo "Спасибо за регистрацию!";
});
Почему это полезно: Логика регистрации сосредоточена на создании пользователя, в то время как событие обрабатывает уведомления. Вы можете добавить другие слушатели (например, вести журнал регистрации) позже.
Пример 3: Очистка кеша
// Слушатель для очистки кеша
Flight::onEvent('page.updated', function ($pageId) {
unset($_SESSION['pages'][$pageId]); // Очистить сессионный кеш, если применимо
echo "Кеш очищен для страницы $pageId.";
});
// Инициировать, когда страница редактируется
Flight::route('/edit-page/(@id)', function ($pageId) {
// Предположим, что мы обновили страницу
Flight::triggerEvent('page.updated', $pageId);
echo "Страница $pageId обновлена.";
});
Почему это полезно: Код редактирования не заботится о кешировании — он просто сигнализирует об обновлении. Другие части приложения могут реагировать по мере необходимости.
Лучшие практики
- Ясно именуйте события: Используйте конкретные имена, такие как
'user.login'
или'page.updated'
, чтобы было очевидно, что они делают. - Держите слушателей простыми: Не помещайте медленные или сложные задачи в слушателей — держите приложение быстрым.
- Тестируйте свои события: Инициируйте их вручную, чтобы убедиться, что слушатели работают правильно.
- Используйте события разумно: Они отличны для разделения, но слишком много может усложнить ваш код — используйте их, когда это имеет смысл.
Система событий в Flight PHP с Flight::onEvent()
и Flight::triggerEvent()
предоставляет вам простой, но мощный способ создания гибких приложений. Позволяя различным частям вашего приложения взаимодействовать друг с другом через события, вы можете держать код организованным, повторно используемым и простым в расширении. Будь то ведение журнала действий, отправка уведомлений или управление обновлениями, события помогают делать это, не запутывая вашу логику. Кроме того, с возможностью переопределения этих методов у вас есть свобода настраивать систему под ваши нужды. Начните с небольшого события и посмотрите, как это преобразует структуру вашего приложения!
Встроенные события
Flight PHP поставляется с несколькими встроенными событиями, которые вы можете использовать для подключения к жизненному циклу фреймворка. Эти события инициируются в определенные моменты в цикле запроса/ответа, что позволяет вам выполнять пользовательскую логику, когда происходят определенные действия.
Список встроенных событий
flight.request.received
: Инициируется, когда запрос принимается, обрабатывается и разбирается.flight.route.middleware.before
: Инициируется после выполнения промежуточного программного обеспечения до.flight.route.middleware.after
: Инициируется после выполнения промежуточного программного обеспечения после.flight.route.executed
: Инициируется после выполнения и обработки маршрута.flight.response.sent
: Инициируется после отправки ответа клиенту.
Learn/views
Виды
Flight по умолчанию предоставляет некоторые базовые функции шаблонизации. Чтобы отобразить вид
шаблона, вызовите метод render
с именем файла шаблона и необязательными
данными шаблона:
Flight::render('hello.php', ['name' => 'Bob']);
Данные шаблона, которые вы передаете, автоматически встраиваются в шаблон и могут
быть обращены как локальная переменная. Файлы шаблонов - это просто файлы PHP. Если
содержимое файла шаблона hello.php
выглядит так:
Привет, <?= $name ?>!
То вывод будет:
Привет, Bob!
Вы также можете вручную устанавливать переменные представления с помощью метода set:
Flight::view()->set('name', 'Bob');
Переменная name
теперь доступна во всех ваших представлениях. Поэтому вы просто можете сделать:
Flight::render('hello');
Обратите внимание, что при указании имени шаблона в методе render вы можете
пропустить расширение .php
.
По умолчанию Flight будет искать каталог views
для файлов шаблонов. Вы можете
задать альтернативный путь для ваших шаблонов, установив следующую конфигурацию:
Flight::set('flight.views.path', '/путь/к/views');
Макеты
Часто веб-сайты имеют один файл шаблона макета с переменным
содержимым. Чтобы отобразить содержимое для использования в макете, вы можете передать
необязательный параметр в метод render
.
Flight::render('header', ['heading' => 'Привет'], 'headerContent');
Flight::render('body', ['body' => 'Мир'], 'bodyContent');
Ваше представление затем будет иметь сохраненные переменные с именами headerContent
и bodyContent
.
Затем вы можете отобразить ваш макет так:
Flight::render('layout', ['title' => 'Домашняя страница']);
Если файлы шаблонов выглядят так:
header.php
:
<h1><?= $heading ?></h1>
body.php
:
<div><?= $body ?></div>
layout.php
:
<html>
<head>
<title><?= $title ?></title>
</head>
<body>
<?= $headerContent ?>
<?= $bodyContent ?>
</body>
</html>
Вывод будет:
<html>
<head>
<title>Домашняя страница</title>
</head>
<body>
<h1>Привет</h1>
<div>Мир</div>
</body>
</html>
Пользовательские представления
Flight позволяет заменить стандартный движок представлений просто зарегистрировав свой собственный класс представлений. Вот как вы можете использовать Smarty шаблонный движок для ваших представлений:
// Загрузка библиотеки Smarty
require './Smarty/libs/Smarty.class.php';
// Регистрация Smarty как класса представления
// Также передайте функцию обратного вызова для настройки Smarty при загрузке
Flight::register('view', Smarty::class, [], function (Smarty $smarty) {
$smarty->setTemplateDir('./templates/');
$smarty->setCompileDir('./templates_c/');
$smarty->setConfigDir('./config/');
$smarty->setCacheDir('./cache/');
});
// Назначение данных шаблона
Flight::view()->assign('name', 'Bob');
// Отображение шаблона
Flight::view()->display('hello.tpl');
Для полноты вы также должны переопределить стандартный метод render Flight:
Flight::map('render', function(string $template, array $data): void {
Flight::view()->assign($data);
Flight::view()->display($template);
});
Learn/templates
HTML Views and Templates
Flight предоставляет базовую функциональность шаблонизации по умолчанию.
Flight позволяет вам заменить движок представлений по умолчанию, просто зарегистрировав свой собственный класс представления. Прокрутите вниз, чтобы увидеть примеры использования Smarty, Latte, Blade и других!
Встроенный движок представлений
Чтобы отобразить шаблон представления, вызовите метод render
с именем файла шаблона и необязательными данными шаблона:
Flight::render('hello.php', ['name' => 'Bob']);
Данные шаблона, которые вы передаете, автоматически встраиваются в шаблон и могут ссылаться на локальную переменную. Шаблоны представляют собой просто файлы PHP. Если содержимое файла шаблона hello.php
выглядит так:
Hello, <?= $name ?>!
Вывод будет:
Hello, Bob!
Вы также можете вручную установить переменные представления, используя метод set:
Flight::view()->set('name', 'Bob');
Переменная name
теперь доступна во всех ваших представлениях. Так вы можете просто сделать:
Flight::render('hello');
Обратите внимание, что при указании имени шаблона в методе render, вы можете опустить расширение .php
.
По умолчанию Flight будет искать директорию views
для файлов шаблонов. Вы можете установить альтернативный путь для ваших шаблонов, установив следующую конфигурацию:
Flight::set('flight.views.path', '/path/to/views');
Макеты
Обычно для веб-сайтов используется один файл шаблона макета с изменяемым содержимым. Чтобы отобразить содержимое, которое будет использоваться в макете, вы можете передать необязательный параметр в метод render
.
Flight::render('header', ['heading' => 'Hello'], 'headerContent');
Flight::render('body', ['body' => 'World'], 'bodyContent');
Ваше представление будет содержать сохраненные переменные, называемые headerContent
и bodyContent
. Вы можете отобразить ваш макет, сделав следующее:
Flight::render('layout', ['title' => 'Home Page']);
Если файлы шаблонов выглядят так:
header.php
:
<h1><?= $heading ?></h1>
body.php
:
<div><?= $body ?></div>
layout.php
:
<html>
<head>
<title><?= $title ?></title>
</head>
<body>
<?= $headerContent ?>
<?= $bodyContent ?>
</body>
</html>
Вывод будет:
<html>
<head>
<title>Home Page</title>
</head>
<body>
<h1>Hello</h1>
<div>World</div>
</body>
</html>
Smarty
Вот как вы можете использовать Smarty движок шаблонизации для ваших представлений:
// Загрузить библиотеку Smarty
require './Smarty/libs/Smarty.class.php';
// Зарегистрировать Smarty как класс представления
// Также передайте функцию обратного вызова для настройки Smarty при загрузке
Flight::register('view', Smarty::class, [], function (Smarty $smarty) {
$smarty->setTemplateDir('./templates/');
$smarty->setCompileDir('./templates_c/');
$smarty->setConfigDir('./config/');
$smarty->setCacheDir('./cache/');
});
// Назначить данные шаблона
Flight::view()->assign('name', 'Bob');
// Отобразить шаблон
Flight::view()->display('hello.tpl');
Для полноты картины вы также должны переопределить метод render по умолчанию в Flight:
Flight::map('render', function(string $template, array $data): void {
Flight::view()->assign($data);
Flight::view()->display($template);
});
Latte
Вот как вы можете использовать Latte движок шаблонизации для ваших представлений:
// Зарегистрировать Latte как класс представления
// Также передайте функцию обратного вызова для настройки Latte при загрузке
Flight::register('view', Latte\Engine::class, [], function (Latte\Engine $latte) {
// Здесь Latte будет кэшировать ваши шаблоны для повышения скорости
// Одно из интересных свойств Latte в том, что он автоматически обновляет ваш
// кэш, когда вы вносите изменения в ваши шаблоны!
$latte->setTempDirectory(__DIR__ . '/../cache/');
// Укажите Latte, где будет находиться корневая директория для ваших представлений.
$latte->setLoader(new \Latte\Loaders\FileLoader(__DIR__ . '/../views/'));
});
// И заверните, чтобы вы могли правильно использовать Flight::render()
Flight::map('render', function(string $template, array $data): void {
// Это как $latte_engine->render($template, $data);
echo Flight::view()->render($template, $data);
});
Blade
Вот как вы можете использовать Blade движок шаблонизации для ваших представлений:
Сначала вам нужно установить библиотеку BladeOne через Composer:
composer require eftec/bladeone
Затем вы можете настроить BladeOne как класс представления в Flight:
<?php
// Загрузить библиотеку BladeOne
use eftec\bladeone\BladeOne;
// Зарегистрировать BladeOne как класс представления
// Также передайте функцию обратного вызова для настройки BladeOne при загрузке
Flight::register('view', BladeOne::class, [], function (BladeOne $blade) {
$views = __DIR__ . '/../views';
$cache = __DIR__ . '/../cache';
$blade->setPath($views);
$blade->setCompiledPath($cache);
});
// Назначить данные шаблона
Flight::view()->share('name', 'Bob');
// Отобразить шаблон
echo Flight::view()->run('hello', []);
Для полноты картины вы также должны переопределить метод render по умолчанию в Flight:
<?php
Flight::map('render', function(string $template, array $data): void {
echo Flight::view()->run($template, $data);
});
В этом примере файл шаблона hello.blade.php может выглядеть так:
<?php
Hello, {{ $name }}!
Вывод будет:
Hello, Bob!
Следуя этим шагам, вы можете интегрировать движок шаблонизации Blade с Flight и использовать его для отображения ваших представлений.
Learn/flight_vs_fat_free
Сравнение Flight и Fat-Free
Что такое Fat-Free?
Fat-Free (ласково известный как F3) - это мощный, но простой в использовании микро-фреймворк на PHP, разработанный для помощи в создании динамичных и надежных веб-приложений - быстро!
Flight сравнивается с Fat-Free во многих аспектах и, вероятно, ближе всего по функциональности и простоте. У Fat-Free есть много функций, которых нет у Flight, но у него также есть много функций, которые есть у Flight. Fat-Free начинает устаревать и уже не так популярен, как раньше.
Обновления становятся менее частыми, и сообщество не так активно, как раньше. Код достаточно прост, но иногда отсутствие дисциплины в синтаксисе может затруднить его чтение и понимание. Он работает для PHP 8.3, но сам код все еще выглядит так, будто он создан для PHP 5.3.
Преимущества по сравнению с Flight
- У Fat-Free больше звезд на GitHub, чем у Flight.
- У Fat-Free есть хорошая документация, но не хватает ясности в некоторых аспектах.
- У Fat-Free есть некоторые ресурсы, такие как видеоуроки на YouTube и онлайн статьи, которые можно использовать для изучения фреймворка.
- У Fat-Free встроены некоторые полезные плагины, которые иногда бывают полезными.
- У Fat-Free есть встроенный ORM под названием Mapper, который можно использовать для взаимодействия с базой данных. У Flight есть active-record.
- У Fat-Free есть Сессии, Кеширование и локализация встроенные. Для Flight вам нужно использовать сторонние библиотеки, но это описано в документации.
- У Fat-Free есть небольшая группа плагинов, созданных сообществом, которые можно использовать для расширения фреймворка. У Flight некоторые из них описаны в документации и на страницах примеров.
- Fat-Free, как и Flight, не имеет зависимостей.
- Fat-Free, как и Flight, нацелен на то, чтобы разработчик имел контроль над своим приложением и простой опыт разработчика.
- Fat-Free поддерживает обратную совместимость, как и Flight (частично, потому что обновления становятся менее частыми).
- Fat-Free, как и Flight, предназначен для разработчиков, которые впервые познают мир фреймворков.
- У Fat-Free есть встроенный шаблонизатор, который более надежен, чем шаблонизатор Flight. Flight рекомендует использовать Latte для этого.
- У Fat-Free есть уникальная команда типа "route" для CLI, где вы можете создавать приложения CLI в самом Fat-Free и обращаться к ним, как к запросам
GET
. Flight делает это с помощью runway.
Недостатки по сравнению с Flight
- У Fat-Free есть некоторые тесты реализации, и даже есть свой собственный тестовый класс, который очень прост. Однако он не проходит полное модульное тестирование, как Flight.
- Вам придется использовать поисковик типа Google, чтобы действительно искать на сайте документации.
- У Flight есть темный режим на своем сайте документации. (эпичный эффект)
- У Fat-Free есть модули, поддержка для которых находится в жалком состоянии.
- У Flight есть простой PdoWrapper, который немного проще, чем встроенный класс
DB\SQL
в Fat-Free. - У Flight есть плагин для разрешений, который может использоваться для обеспечения безопасности вашего приложения. Slim требует использовать стороннюю библиотеку.
- У Flight есть ORM под названием active-record, который больше напоминает ORM, чем Mapper в Fat-Free. Основное преимущество
active-record
заключается в том, что вы можете определять отношения между записями для автоматических объединений, тогда как Mapper в Fat-Free требует создания SQL представлений. - Как ни странно, у Fat-Free нет корневого пространства имен. У Flight имена пространств имен используются во избежание конфликтов с вашим собственным кодом. класс
Cache
- самый большой нарушитель здесь. - У Fat-Free нет промежуточного ПО (middleware). Вместо этого есть
beforeroute
иafterroute
хуки, которые могут использоваться для фильтрации запросов и ответов в контроллерах. - У Fat-Free нельзя группировать маршруты.
- У Fat-Free есть обработчик контейнера внедрения зависимостей, но документация по использованию его крайне скудна.
- Отладка может стать немного сложной, поскольку практически все хранится в так называемом
HIVE
Learn/extending
Расширение
Flight предназначен для того, чтобы быть расширяемым фреймворком. Фреймворк поставляется с набором стандартных методов и компонентов, но он позволяет вам сопоставлять свои собственные методы, регистрировать свои собственные классы или даже переопределять существующие классы и методы.
Если вы ищете DIC (Контейнер Для Внедрения Зависимостей), переходите на страницу Контейнер Для Внедрения Зависимостей.
Сопоставление методов
Чтобы сопоставить свой собственный простой метод, вы используете функцию map
:
// Сопоставьте свой метод
Flight::map('hello', function (string $name) {
echo "hello $name!";
});
// Вызовите свой собственный метод
Flight::hello('Bob');
Хотя сделать простые пользовательские методы возможно, настоятельно рекомендуется просто создавать стандартные функции в PHP. Это имеет автозаполнение в IDE и легче читается. Эквивалент приведённого выше кода будет:
function hello(string $name) {
echo "hello $name!";
}
hello('Bob');
Это используется больше, когда вам нужно передать переменные в ваш метод, чтобы получить ожидаемое
значение. Использование метода register()
, как показано ниже, больше для передачи конфигурации
и последующего вызова вашего предварительно настроенного класса.
Регистрация классов
Чтобы зарегистрировать свой собственный класс и настроить его, вы используете функцию register
:
// Зарегистрируйте ваш класс
Flight::register('user', User::class);
// Получите экземпляр вашего класса
$user = Flight::user();
Метод регистрации также позволяет вам передавать параметры в конструктор вашего класса. Таким образом, когда вы загружаете свой пользовательский класс, он будет предварительно инициализирован. Вы можете определить параметры конструктора, передав дополнительный массив. Вот пример загрузки соединения с базой данных:
// Зарегистрируйте класс с параметрами конструктора
Flight::register('db', PDO::class, ['mysql:host=localhost;dbname=test', 'user', 'pass']);
// Получите экземпляр вашего класса
// Это создаст объект с определёнными параметрами
//
// new PDO('mysql:host=localhost;dbname=test','user','pass');
//
$db = Flight::db();
// и если вам это понадобится позже в вашем коде, вы просто снова вызываете тот же метод
class SomeController {
public function __construct() {
$this->db = Flight::db();
}
}
Если вы передадите дополнительный параметр обратного вызова, он будет выполнен немедленно после создания класса. Это позволяет вам выполнять любые процедуры настройки для вашего нового объекта. Функция обратного вызова принимает один параметр, экземпляр нового объекта.
// Обратный вызов будет передан объекту, который был создан
Flight::register(
'db',
PDO::class,
['mysql:host=localhost;dbname=test', 'user', 'pass'],
function (PDO $db) {
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
);
По умолчанию, каждый раз, когда вы загружаете ваш класс, вы получаете общий экземпляр.
Чтобы получить новый экземпляр класса, просто передайте false
в качестве параметра:
// Общий экземпляр класса
$shared = Flight::db();
// Новый экземпляр класса
$new = Flight::db(false);
Имейте в виду, что сопоставленные методы имеют приоритет над зарегистрированными классами. Если вы объявите оба с тем же именем, будет вызван только сопоставленный метод.
Ведение журнала
Flight не имеет встроенной системы ведения журналов, однако, использовать библиотеку ведения журналов с Flight очень просто. Вот пример использования библиотеки Monolog:
// index.php или bootstrap.php
// Зарегистрируйте логгер с 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));
});
Теперь, когда он зарегистрирован, вы можете использовать его в своём приложении:
// В вашем контроллере или маршруте
Flight::log()->warning('Это сообщение предупреждения');
Это зафиксирует сообщение в файл журнала, который вы указали. А что, если вы хотите зафиксировать что-то, когда происходит ошибка? Вы можете использовать метод error
:
// В вашем контроллере или маршруте
Flight::map('error', function(Throwable $ex) {
Flight::log()->error($ex->getMessage());
// Отобразите свою пользовательскую страницу ошибки
include 'errors/500.html';
});
Вы также можете создать базовую систему APM (Мониторинг Производительности Приложения)
используя методы before
и after
:
// В вашем 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('Запрос '.Flight::request()->url.' занял ' . round($end - $start, 4) . ' секунд');
// Вы также можете добавить свои заголовки запроса или ответа
// чтобы зафиксировать их (будьте осторожны, так как это будет
// много данных, если у вас много запросов)
Flight::log()->info('Заголовки запроса: ' . json_encode(Flight::request()->headers));
Flight::log()->info('Заголовки ответа: ' . json_encode(Flight::response()->headers));
});
Переопределение методов фреймворка
Flight позволяет вам переопределять его стандартную функциональность в соответствии с вашими потребностями, не внося изменения в код. Вы можете просмотреть все методы, которые можно переопределить здесь.
Например, когда Flight не может сопоставить URL с маршрутом, он вызывает метод notFound
,
который отправляет общий ответ HTTP 404
. Вы можете переопределить это поведение,
используя метод map
:
Flight::map('notFound', function() {
// Отобразите пользовательскую страницу 404
include 'errors/404.html';
});
Flight также позволяет вам заменять основные компоненты фреймворка. Например, вы можете заменить класс Router по умолчанию на свой собственный пользовательский класс:
// Зарегистрируйте свой собственный класс
Flight::register('router', MyRouter::class);
// Когда Flight загружает экземпляр Router, он загрузит ваш класс
$myrouter = Flight::router();
Однако методы фреймворка, такие как map
и register
, не могут быть переопределены. Вы получите ошибку, если попытаетесь это сделать.
Learn/json
JSON
Flight предоставляет поддержку для отправки JSON и JSONP ответов. Чтобы отправить ответ в формате JSON, вы передаете данные, которые будут закодированы в JSON:
Flight::json(['id' => 123]);
Для запросов JSONP вы можете, опционально, передать имя параметра запроса, которое вы используете для определения функции обратного вызова:
Flight::jsonp(['id' => 123], 'q');
Таким образом, при выполнении GET-запроса с использованием ?q=my_func
, вы должны получить следующий вывод:
my_func({"id":123});
Если вы не передаете имя параметра запроса, оно будет по умолчанию установлено на jsonp
.
Learn/flight_vs_slim
Сравнение Flight и Slim
Что такое Slim?
Slim - это PHP микрофреймворк, который помогает вам быстро писать простые, но мощные веб-приложения и API.
Многие идеи для некоторых функций v3 Flight фактически были взяты из Slim. Группировка маршрутов и выполнение промежуточного ПО в определенном порядке - это две функции, которые были вдохновлены Slim. Slim v3 был создан в стремлении к простоте, но есть смешанные отзывы относительно v4.
Достоинства по сравнению с Flight
- У Slim большее сообщество разработчиков, которые в свою очередь создают удобные модули, чтобы помочь вам не изобретать велосипед.
- Slim следует многим интерфейсам и стандартам, которые распространены в сообществе PHP, что повышает совместимость.
- У Slim есть приличная документация и учебные пособия, которые можно использовать для изучения фреймворка (хотя это не сравнить с Laravel или Symfony).
- У Slim есть различные ресурсы, такие как видеоуроки на YouTube и онлайн-статьи, которые можно использовать для изучения фреймворка.
- Slim разрешает использовать любые компоненты, которые вы хотите, чтобы обрабатывать основные функции маршрутизации, поскольку он соответствует PSR-7.
Недостатки по сравнению с Flight
- Удивительно, но Slim не так быстр, как можно было бы подумать о микро-фреймворке. Смотрите бенчмарки TechEmpower для получения дополнительной информации.
- Flight ориентирован на разработчика, который хочет создать легкое, быстрое и простое в использовании веб-приложение.
- У Flight нет зависимостей, тогда как у Slim есть несколько зависимостей, которые вам нужно установить.
- Flight ориентирован на простоту и удобство использования.
- Одной из основных особенностей Flight является то, что он делает все возможное для поддержания обратной совместимости. Slim v3 превратился в v4 за счет нарушения обратной совместимости.
- Flight предназначен для разработчиков, которые впервые рискнули попробовать фреймворки.
- Flight также может использоваться для приложений уровня предприятия, но у него нет такого количества примеров и учебных пособий, как у Slim. Также разработчику понадобится больше дисциплины, чтобы поддерживать организованность и хорошую структуру.
- Flight дает разработчику больший контроль над приложением, тогда как Slim может использовать некоторую магию за кулисами.
- У Flight есть простой PdoWrapper, который можно использовать для взаимодействия с вашей базой данных. Для Slim вам нужно использовать стороннюю библиотеку.
- У Flight есть плагин permissions, который можно использовать для обеспечения безопасности вашего приложения. Для Slim вам нужно использовать стороннюю библиотеку.
- У Flight есть ORM под названием active-record, который можно использовать для взаимодействия с базой данных. Для Slim вам нужно использовать стороннюю библиотеку.
- У Flight есть приложение CLI под названием runway, которое можно использовать для запуска вашего приложения из командной строки. У Slim такого нет.
Learn/autoloading
Автозагрузка
Автозагрузка - это концепция в PHP, где вы указываете каталог или каталоги для загрузки классов. Это намного более выгодно, чем использование require
или include
для загрузки классов. Это также требование для использования пакетов Composer.
По умолчанию любой класс Flight
автоматически загружается благодаря composer. Однако, если вы хотите загружать свои собственные классы, вы можете использовать метод Flight::path()
для указания каталога для загрузки классов.
Основной пример
Допустим, у нас есть древо каталогов, похожее на следующее:
# Пример пути
/home/user/project/my-flight-project/
├── app
│ ├── cache
│ ├── config
│ ├── controllers - содержит контроллеры для этого проекта
│ ├── translations
│ ├── UTILS - содержит классы только для этого приложения (это все заглавные буквы специально для примера позже)
│ └── views
└── public
└── css
└── js
└── index.php
Вы могли заметить, что это та же структура файлов, что и на этом сайте документации.
Вы можете указать каждый каталог для загрузки вот так:
/**
* public/index.php
*/
// Добавить путь к автозагрузчику
Flight::path(__DIR__.'/../app/controllers/');
Flight::path(__DIR__.'/../app/utils/');
/**
* app/controllers/MyController.php
*/
// не требуется использование пространства имен
// Рекомендуется, чтобы все загружаемые классы были в стиле Pascal (каждое слово с заглавной буквы, без пробелов)
// Начиная с версии 3.7.2, вы можете использовать Pascal_Snake_Case для имен ваших классов, запустив Loader::setV2ClassLoading(false);
class MyController {
public function index() {
// сделать что-то
}
}
Пространства имен
Если у вас есть пространства имен, на самом деле становится очень легко это реализовать. Вы должны использовать метод Flight::path()
для указания корневого каталога (не корневого документа или папки public/
) вашего приложения.
/**
* public/index.php
*/
// Добавить путь к автозагрузчику
Flight::path(__DIR__.'/../');
Теперь вот как может выглядеть ваш контроллер. Посмотрите на пример ниже, но обратите внимание на комментарии для важной информации.
/**
* app/controllers/MyController.php
*/
// пространства имен обязательны
// пространства имен такие же, как структура каталогов
// пространства имен должны следовать тому же регистру, что и структура каталогов
// пространства и каталоги не могут иметь нижние подчеркивания (если не установлено Loader::setV2ClassLoading(false))
namespace app\controllers;
// Рекомендуется, чтобы все загружаемые классы были в стиле Pascal (каждое слово с заглавной буквы, без пробелов)
// Начиная с версии 3.7.2, вы можете использовать Pascal_Snake_Case для имен ваших классов, запустив Loader::setV2ClassLoading(false);
class MyController {
public function index() {
// сделать что-то
}
}
И если вы хотите автоматически загрузить класс в каталоге utils, вы сделали бы практически то же самое:
/**
* app/UTILS/ArrayHelperUtil.php
*/
// пространство имен должно соответствовать структуре каталога и регистру (обратите внимание на каталог UTILS в верхнем регистре
// как в дереве файлов выше)
namespace app\UTILS;
class ArrayHelperUtil {
public function changeArrayCase(array $array) {
// сделать что-то
}
}
Подчеркивания в именах классов
Начиная с версии 3.7.2, вы можете использовать Pascal_Snake_Case для имен ваших классов, запустив Loader::setV2ClassLoading(false);
.
Это позволит вам использовать подчеркивания в именах ваших классов.
Это не рекомендуется, но это доступно для тех, кто нуждается в этом.
/**
* public/index.php
*/
// Добавить путь к автозагрузчику
Flight::path(__DIR__.'/../app/controllers/');
Flight::path(__DIR__.'/../app/utils/');
Loader::setV2ClassLoading(false);
/**
* app/controllers/My_Controller.php
*/
// не требуется использование пространства имен
class My_Controller {
public function index() {
// сделать что-то
}
}
Learn/troubleshooting
Решение проблем
Эта страница поможет вам устранить общие проблемы, с которыми вы можете столкнуться при использовании Flight.
Общие проблемы
404 Страница не найдена или непредвиденное поведение маршрута
Если вы видите ошибку 404 Страница не найдена (но вы клянетесь своей жизнью, что она действительно там и это не опечатка), на самом деле это может быть проблема с возвращением значения в конечной точке маршрута вместо простого его вывода. Причина этого намеренная, но некоторым разработчикам это может ускользнуть.
Flight::route('/hello', function(){
// Это может вызвать ошибку 404 Страница не найдена
return 'Hello World';
});
// Что вам, вероятно, нужно
Flight::route('/hello', function(){
echo 'Hello World';
});
Причина заключается в том, что в маршрутизаторе встроен специальный механизм, который обрабатывает возвращаемый вывод как инструкцию "перейти к следующему маршруту". Вы можете ознакомиться с этим поведением в разделе Маршрутизация.
Класс не найден (автозагрузка не работает)
Могут быть несколько причин, почему это происходит. Ниже приведены некоторые примеры, но убедитесь также, что вы изучили раздел автозагрузка.
Некорректное имя файла
Самая распространенная причина в том, что имя класса не совпадает с именем файла.
Если у вас есть класс с именем MyClass
, то файл должен иметь имя MyClass.php
. Если у вас есть класс с именем MyClass
и файл назван myclass.php
,
то автозагрузчик не сможет его найти.
Некорректное пространство имен
Если вы используете пространства имен, то пространство имен должно соответствовать структуре каталогов.
// код
// если ваш MyController находится в каталоге app/controllers и у него есть пространство имен
// это не сработает.
Flight::route('/hello', 'MyController->hello');
// вам нужно выбрать один из этих вариантов
Flight::route('/hello', 'app\controllers\MyController->hello');
// или если у вас есть оператор use вверху
use app\controllers\MyController;
Flight::route('/hello', [ MyController::class, 'hello' ]);
// также можно написать
Flight::route('/hello', MyController::class.'->hello');
// или так...
Flight::route('/hello', [ 'app\controllers\MyController', 'hello' ]);
path()
не определен
В контрольном приложении это определяется внутри файла config.php
, но для того чтобы ваши классы были найдены, убедитесь, что метод path()
определен (вероятно, к корневому каталогу вашего каталога) прежде чем пытаться его использовать.
// Добавьте путь к автозагрузчику
Flight::path(__DIR__.'/../');
Install
Установка
Загрузите файлы.
Если вы используете Composer, вы можете запустить следующую команду:
composer require flightphp/core
ИЛИ вы можете скачать файлы напрямую и извлечь их в ваш веб-каталог.
Настройка вашего веб-сервера.
Apache
Для Apache отредактируйте ваш файл .htaccess
следующим образом:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
Примечание: Если вам нужно использовать flight в подкаталоге, добавьте строку
RewriteBase /subdir/
сразу послеRewriteEngine On
.Примечание: Если вы хотите защитить все файлы сервера, такие как файл db или env. Поместите это в свой файл
.htaccess
:
RewriteEngine On
RewriteRule ^(.*)$ index.php
Nginx
Для Nginx добавьте следующее в ваше объявление сервера:
server {
location / {
try_files $uri $uri/ /index.php;
}
}
Создайте файл index.php
.
<?php
// Если вы используете Composer, требуется автозагрузчик.
require 'vendor/autoload.php';
// если вы не используете Composer, загружайте фреймворк напрямую
// require 'flight/Flight.php';
// Затем определите маршрут и назначьте функцию для обработки запроса.
Flight::route('/', function () {
echo 'hello world!';
});
// Наконец, запустите фреймворк.
Flight::start();
Guides/blog
Создание простого блога с Flight PHP
Этот гид проведет вас через создание базового блога с использованием фреймворка Flight PHP. Вы настроите проект, определите маршруты, управляйте постами с помощью JSON и отображайте их с помощью шаблонизатора Latte — все это демонстрирует простоту и гибкость Flight. В конце у вас будет функциональный блог с домашней страницей, страницами отдельных постов и формой для создания.
Предварительные требования
- PHP 7.4+: Установлен на вашей системе.
- Composer: Для управления зависимостями.
- Текстовый редактор: Любой редактор, например, VS Code или PHPStorm.
- Базовые знания PHP и веб-разработки.
Шаг 1: Настройте свой проект
Начните с создания новой директории проекта и установки Flight через Composer.
-
Создайте директорию:
mkdir flight-blog cd flight-blog
-
Установите Flight:
composer require flightphp/core
-
Создайте публичную директорию: Flight использует одну точку входа (
index.php
). Создайте папкуpublic/
для этого:mkdir public
-
Базовый
index.php
: Создайтеpublic/index.php
с простым маршрутом "hello world":<?php require '../vendor/autoload.php'; Flight::route('/', function () { echo 'Привет, Flight!'; }); Flight::start();
-
Запустите встроенный сервер: Проверьте вашу настройку с помощью веб-сервера разработки PHP:
php -S localhost:8000 -t public/
Посетите
http://localhost:8000
, чтобы увидеть "Привет, Flight!".
Шаг 2: Организуйте структуру вашего проекта
Для чистой настройки структурируйте ваш проект следующим образом:
flight-blog/
├── app/
│ ├── config/
│ └── views/
├── data/
├── public/
│ └── index.php
├── vendor/
└── composer.json
app/config/
: Файлы конфигурации (например, события, маршруты).app/views/
: Шаблоны для отображения страниц.data/
: JSON-файл для хранения постов блога.public/
: Веб-корень сindex.php
.
Шаг 3: Установите и настройте Latte
Latte — это легкий шаблонизатор, который хорошо интегрируется с Flight.
-
Установите Latte:
composer require latte/latte
-
Настройте Latte в Flight: Обновите
public/index.php
, чтобы зарегистрировать Latte как движок представлений:<?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' => 'Мой блог']); }); Flight::start();
-
Создайте шаблон разметки: В
app/views/layout.latte
:<!DOCTYPE html> <html> <head> <title>{$title}</title> </head> <body> <header> <h1>Мой блог</h1> <nav> <a href="/">Главная</a> | <a href="/create">Создать пост</a> </nav> </header> <main> {block content}{/block} </main> <footer> <p>© {date('Y')} Блог Flight</p> </footer> </body> </html>
-
Создайте шаблон для главной страницы: В
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}
Перезапустите сервер, если вы вышли из него, и посетите
http://localhost:8000
, чтобы увидеть отрендеренную страницу. -
Создайте файл данных:
Используйте JSON-файл, чтобы смоделировать базу данных для простоты.
В
data/posts.json
:[ { "slug": "first-post", "title": "Мой первый пост", "content": "Это мой самый первый пост в блоге с Flight PHP!" } ]
Шаг 4: Определите маршруты
Отделите ваши маршруты в файл конфигурации для лучшей организации.
-
Создание
routes.php
: Вapp/config/routes.php
:<?php Flight::route('/', function () { Flight::view()->render('home.latte', ['title' => 'Мой блог']); }); Flight::route('/post/@slug', function ($slug) { Flight::view()->render('post.latte', ['title' => 'Пост: ' . $slug, 'slug' => $slug]); }); Flight::route('GET /create', function () { Flight::view()->render('create.latte', ['title' => 'Создать пост']); });
-
Обновите
index.php
: Включите файл маршрутов:<?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();
Шаг 5: Хранение и получение постов блога
Добавьте методы для загрузки и сохранения постов.
-
Добавьте метод для постов: В
index.php
добавьте метод для загрузки постов:Flight::map('posts', function () { $file = __DIR__ . '/../data/posts.json'; return json_decode(file_get_contents($file), true); });
-
Обновите маршруты: Измените
app/config/routes.php
, чтобы использовать посты:<?php Flight::route('/', function () { $posts = Flight::posts(); Flight::view()->render('home.latte', [ 'title' => 'Мой блог', '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' => 'Создать пост']); });
Шаг 6: Создание шаблонов
Обновите ваши шаблоны для отображения постов.
-
Страница поста (
app/views/post.latte
):{extends 'layout.latte'} {block content} <h2>{$post['title']}</h2> <div class="post-content"> <p>{$post['content']}</p> </div> {/block}
Шаг 7: Добавление создания постов
Обработайте отправку формы для добавления новых постов.
-
Создайте форму (
app/views/create.latte
):{extends 'layout.latte'} {block content} <h2>{$title}</h2> <form method="POST" action="/create"> <div class="form-group"> <label for="title">Заголовок:</label> <input type="text" name="title" id="title" required> </div> <div class="form-group"> <label for="content">Содержимое:</label> <textarea name="content" id="content" required></textarea> </div> <button type="submit">Сохранить пост</button> </form> {/block}
-
Добавьте маршрут POST: В
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('/'); });
-
Проверьте это:
- Посетите
http://localhost:8000/create
. - Отправьте новый пост (например, "Второй пост" с некоторым содержимым).
- Проверьте главную страницу, чтобы увидеть его в списке.
- Посетите
Шаг 8: Улучшите обработку ошибок
Переопределите метод notFound
для лучшего опыта 404.
В index.php
:
Flight::map('notFound', function () {
Flight::view()->render('404.latte', ['title' => 'Страница не найдена']);
});
Создайте app/views/404.latte
:
{extends 'layout.latte'}
{block content}
<h2>404 - {$title}</h2>
<p>Извините, этой страницы не существует!</p>
{/block}
Следующие шаги
- Добавить стили: Используйте CSS в ваших шаблонах для лучшего внешнего вида.
- База данных: Замените
posts.json
на базу данных, такую как SQLite, используяPdoWrapper
. - Валидация: Добавьте проверки на дублирующиеся слаги или пустые вводы.
- Промежуточное ПО: Реализуйте аутентификацию для создания постов.
Заключение
Вы создали простой блог с Flight PHP! Этот гид демонстрирует основные функции, такие как маршрутизация, шаблонизация с помощью Latte и обработка отправок форм — при этом все оставаясь легковесным. Изучите документацию Flight для более сложных функций, чтобы развить ваш блог дальше!
License
Лицензия MIT (MIT)
Авторское право © 2024
@mikecao, @n0nag0n
Настоящим предоставляется разрешение на бесплатное использование любому лицу, получившему копию данного программного обеспечения и сопроводительной документации (далее - "Программное обеспечение"), без ограничений, включая право использовать, копировать, изменять, объединять, публиковать, распространять, подлицензировать и/или продавать копии Программного обеспечения и разрешать лицам, которым предоставляется Программное обеспечение, сделать то же самое, при соблюдении следующих условий:
Вышеприведенное уведомление об авторском праве и это уведомление о разрешении должны быть включены во все копии или существенные части Программного обеспечения.
ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ "КАК ЕСТЬ", БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ ГАРАНТИЯМИ ТОВАРНОГО СОСТОЯНИЯ, ПРИГОДНОСТИ ДЛЯ КОНКРЕТНОЙ ЦЕЛИ И НЕНАРУШЕНИЯ. НИ В КОЕМ СЛУЧАЕ АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ НЕСУТ ОТВЕТСТВЕННОСТИ ПО ОТВЕТСТВЕННОСТИ, ВЫТЕКАЮЩЕЙ ИЗ ДОГОВОРА, ДЕЛИКТА ИЛИ ИНАЧЕ, В СВЯЗИ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ ИЛИ ИСПОЛЬЗОВАНИЕМ ИЛИ ДРУГИМИ ОБРАЩЕНИЯМИ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ.
About
Что такое Flight?
Flight — это быстрый, простой и расширяемый фреймворк для PHP. Он довольно универсален и может быть использован для создания любого типа веб-приложения. Он разработан с учетом простоты и написан так, чтобы его было легко понять и использовать.
Flight — отличный фреймворк для начинающих, которые новички в PHP и хотят научиться создавать веб-приложения. Это также отличный фреймворк для опытных разработчиков, которые хотят больше контроля над своими веб-приложениями. Он спроектирован для легкого создания RESTful API, простого веб-приложения или сложного веб-приложения.
Быстрый старт
Сначала установите его с помощью Composer
composer require flightphp/core
или вы можете скачать zip-архив репозитория здесь. Затем у вас будет базовый файл index.php
, похожий на следующий:
<?php
// если установлен с помощью composer
require 'vendor/autoload.php';
// или если установлен вручную через zip-файл
// require 'flight/Flight.php';
Flight::route('/', function() {
echo 'привет, мир!';
});
Flight::route('/json', function() {
Flight::json(['привет' => 'мир']);
});
Flight::start();
Вот и все! У вас есть базовое приложение на Flight. Теперь вы можете запустить этот файл с помощью php -S localhost:8000
и посетить http://localhost:8000
в вашем браузере, чтобы увидеть вывод.
Это быстро?
Да! Flight быстрый. Это один из самых быстрых фреймворков PHP, доступных на данный момент. Вы можете увидеть все тесты на TechEmpower
Смотрите тест ниже с некоторыми другими популярными фреймворками PHP.
Фреймворк | Plaintext Reqs/sec | JSON Reqs/sec |
---|---|---|
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 |
Skeleton/Boilerplate App
Существует пример приложения, который может помочь вам начать работу с фреймворком Flight. Перейдите к flightphp/skeleton для инструкций о том, как начать! Вы также можете посетить страницу примеры для вдохновения на некоторые вещи, которые вы можете сделать с Flight.
Сообщество
Мы в Matrix Chat
И в Discord
Участие
Существуют два способа, которыми вы можете внести свой вклад в Flight:
- Вы можете внести свой вклад в ядро фреймворка, посетив основной репозиторий.
- Вы можете внести свой вклад в документацию. Этот веб-сайт документации размещен на Github. Если вы заметите ошибку или хотите улучшить что-то, не стесняйтесь исправить это и отправить запрос на изменение! Мы стараемся следить за всем, но обновления и переводы языков приветствуются.
Требования
Flight требует PHP 7.4 или выше.
Примечание: PHP 7.4 поддерживается, потому что на данный момент (2024) PHP 7.4 является версией по умолчанию для некоторых LTS-дистрибутивов Linux. Принуждение к переходу на PHP >8 вызовет много недовольства у этих пользователей. Фреймворк также поддерживает PHP >8.
Лицензия
Flight выпускается под лицензией MIT.
Awesome-plugins/php_cookie
Cookies
overclokk/cookie это простая библиотека для управления куки в вашем приложении.
Установка
Установка проста с помощью composer.
composer require overclokk/cookie
Использование
Использование так же просто, как регистрация нового метода в классе Flight.
use Overclokk\Cookie\Cookie;
/*
* Установите в вашем файле bootstrap или public/index.php
*/
Flight::register('cookie', Cookie::class);
/**
* ExampleController.php
*/
class ExampleController {
public function login() {
// Установить куки
// вам нужно, чтобы это было false, чтобы получить новый экземпляр
// используйте комментарий ниже, если хотите автозаполнение
/** @var \Overclokk\Cookie\Cookie $cookie */
$cookie = Flight::cookie(false);
$cookie->set(
'stay_logged_in', // имя куки
'1', // значение, которое вы хотите установить
86400, // количество секунд, на которое должно длиться куки
'/', // путь, по которому куки будут доступны
'example.com', // домен, на котором будут доступны куки
true, // куки будут передаваться только через безопасное соединение HTTPS
true // куки будут доступны только через протокол HTTP
);
// необязательно, если вы хотите сохранить значения по умолчанию
// и иметь быстрый способ установить куки на длительное время
$cookie->forever('stay_logged_in', '1');
}
public function home() {
// Проверить, есть ли у вас куки
if (Flight::cookie()->has('stay_logged_in')) {
// поместите их в область панели управления, например.
Flight::redirect('/dashboard');
}
}
}
Awesome-plugins/php_encryption
Шифрование PHP
defuse/php-encryption - это библиотека, которая может быть использована для шифрования и дешифрования данных. Начать использование довольно просто для начала шифрования и дешифрования данных. У них есть отличное руководство, которое помогает объяснить основы использования библиотеки, а также важные аспекты безопасности, касающиеся шифрования.
Установка
Установка проста с помощью композитора.
composer require defuse/php-encryption
Настройка
Затем вам нужно сгенерировать ключ шифрования.
vendor/bin/generate-defuse-key
Это выдаст ключ, который вам нужно будет хранить в надежном месте. Вы можете сохранить ключ в вашем файле app/config/config.php
в массиве внизу файла. Хотя это не идеальное место, это хотя бы что-то.
Использование
Теперь, когда у вас есть библиотека и ключ шифрования, вы можете начать шифровать и дешифровать данные.
use Defuse\Crypto\Crypto;
use Defuse\Crypto\Key;
/*
* Set in your bootstrap or public/index.php file
*/
// Метод шифрования
Flight::map('encrypt', function($raw_data) {
$encryption_key = /* $config['encryption_key'] or a file_get_contents of where you put the key */;
return Crypto::encrypt($raw_data, Key::loadFromAsciiSafeString($encryption_key));
});
// Метод дешифрования
Flight::map('decrypt', function($encrypted_data) {
$encryption_key = /* $config['encryption_key'] or a file_get_contents of where you put the key */;
try {
$raw_data = Crypto::decrypt($encrypted_data, Key::loadFromAsciiSafeString($encryption_key));
} catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
// Атака! Загружен неверный ключ или зашифрованный текст был изменен с момента его создания -- либо поврежден в базе данных, либо намеренно изменен Злодеем, пытающимся провести атаку.
// ... обработайте этот случай так, чтобы он подходил для вашего приложения ...
}
return $raw_data;
});
Flight::route('/encrypt', function() {
$encrypted_data = Flight::encrypt('Это секрет');
echo $encrypted_data;
});
Flight::route('/decrypt', function() {
$encrypted_data = '...'; // Получите зашифрованные данные откуда-нибудь
$decrypted_data = Flight::decrypt($encrypted_data);
echo $decrypted_data;
});
Awesome-plugins/php_file_cache
flightphp/cache
Лёгкий, простой и автономный класс кэширования PHP в файле
Преимущества
- Лёгкий, автономный и простой
- Весь код в одном файле - никаких бесполезных драйверов.
- Безопасный - каждый сгенерированный файл кэша имеет заголовок php с die, что делает прямой доступ невозможным, даже если кто-то знает путь и ваш сервер неправильно настроен.
- Хорошо документирован и протестирован
- Корректно обрабатывает параллелизм через flock
- Поддерживает PHP 7.4+
- Бесплатно по лицензии MIT
Этот сайт документации использует эту библиотеку для кэширования каждой из страниц!
Нажмите здесь, чтобы просмотреть код.
Установка
Установите через composer:
composer require flightphp/cache
Использование
Использование довольно простое. Это сохраняет файл кэша в директории кэша.
use flight\Cache;
$app = Flight::app();
// Вы передаёте директорию, в которой будет храниться кэш, в конструктор
$app->register('cache', Cache::class, [ __DIR__ . '/../cache/' ], function(Cache $cache) {
// Это гарантирует, что кэш используется только в режиме продакшн
// ENVIRONMENT - это константа, которая устанавливается в вашем файле начальной загрузки или в другом месте вашего приложения
$cache->setDevMode(ENVIRONMENT === 'development');
});
Затем вы можете использовать его в своём коде так:
// Получить экземпляр кэша
$cache = Flight::cache();
$data = $cache->refreshIfExpired('simple-cache-test', function () {
return date("H:i:s"); // вернуть данные для кэширования
}, 10); // 10 секунд
// или
$data = $cache->retrieve('simple-cache-test');
if(empty($data)) {
$data = date("H:i:s");
$cache->store('simple-cache-test', $data, 10); // 10 секунд
}
Документация
Посетите https://github.com/flightphp/cache для полной документации и убедитесь, что вы посмотрели папку примеры.
Awesome-plugins/permissions
FlightPHP/Права доступа
Это модуль разрешений, который можно использовать в ваших проектах, если у вас есть несколько ролей в вашем приложении, и каждая роль имеет немного разную функциональность. Этот модуль позволяет определить разрешения для каждой роли, а затем проверить, имеет ли текущий пользователь разрешение на доступ к определенной странице или выполнение определенного действия.
Нажмите сюда для репозитория на GitHub.
Установка
Запустите composer require flightphp/permissions
и вы готовы к работе!
Использование
Сначала вам нужно настроить ваши разрешения, затем сообщить вашему приложению, что означают эти разрешения. В конечном итоге вы проверите ваши разрешения с помощью $Permissions->has()
, ->can()
или is()
. has()
и can()
имеют одинаковую функциональность, но названы по-разному, чтобы сделать ваш код более читаемым.
Базовый пример
Давайте предположим, что у вас есть функция в вашем приложении, которая проверяет, вошел ли пользователь в систему. Вы можете создать объект разрешений следующим образом:
// index.php
require 'vendor/autoload.php';
// некоторый код
// затем у вас вероятно есть что-то, что говорит вам, какая текущая роль у человека
// скорее всего у вас есть что-то, откуда вы извлекаете текущую роль
// из переменной сеанса, которая определяет это
// после входа в систему у кого-то должна быть роль 'guest' или 'public'.
$current_role = 'admin';
// настройка разрешений
$permission = new \flight\Permission($current_role);
$permission->defineRule('loggedIn', function($current_role) {
return $current_role !== 'guest';
});
// Вам вероятно захочется сохранить этот объект где-то в Flight
Flight::set('permission', $permission);
Затем в контроллере где-то вы можете иметь что-то вроде этого.
<?php
// некоторый контроллер
class SomeController {
public function someAction() {
$permission = Flight::get('permission');
if ($permission->has('loggedIn')) {
// сделать что-то
} else {
// сделать что-то другое
}
}
}
Вы также можете использовать это для отслеживания, есть ли у них разрешение на выполнение определенного действия в вашем приложении. Например, если у вас есть способ, как пользователи могут взаимодействовать с публикацией в вашем программном обеспечении, вы можете проверить, имеют ли они разрешение на выполнение определенных действий.
$current_role = 'admin';
// настройка разрешений
$permission = new \flight\Permission($current_role);
$permission->defineRule('post', function($current_role) {
if($current_role === 'admin') {
$permissions = ['create', 'read', 'update', 'delete'];
} else if($current_role === 'editor') {
$permissions = ['create', 'read', 'update'];
} else if($current_role === 'author') {
$permissions = ['create', 'read'];
} else if($current_role === 'contributor') {
$permissions = ['create'];
} else {
$permissions = [];
}
return $permissions;
});
Flight::set('permission', $permission);
Затем где-то в контроллере...
class PostController {
public function create() {
$permission = Flight::get('permission');
if ($permission->can('post.create')) {
// сделать что-то
} else {
// сделать что-то еще
}
}
}
Внедрение зависимостей
Вы можете внедрять зависимости в замыкание, которое определяет разрешения. Это полезно, если у вас есть какой-то переключатель, идентификатор или любая другая точка данных, которую вы хотите проверить. То же самое работает для вызовов вида Class->Method, за исключением того, что аргументы определяются в методе.
Замыкания
$Permission->defineRule('order', function(string $current_role, MyDependency $MyDependency = null) {
// ... код
});
// в вашем файле контроллера
public function createOrder() {
$MyDependency = Flight::myDependency();
$permission = Flight::get('permission');
if ($permission->can('order.create', $MyDependency)) {
// сделать что-то
} else {
// сделать что-то еще
}
}
Классы
namespace MyApp;
class Permissions {
public function order(string $current_role, MyDependency $MyDependency = null) {
// ... код
}
}
Сокращение для установки разрешений с использованием классов
Вы также можете использовать классы для определения ваших разрешений. Это полезно, если у вас много разрешений, и вы хотите, чтобы ваш код был чистым. Вы можете сделать что-то вроде этого:
<?php
// код инициализации
$Permissions = new \flight\Permission($current_role);
$Permissions->defineRule('order', 'MyApp\Permissions->order');
// myapp/Permissions.php
namespace MyApp;
class Permissions {
public function order(string $current_role, int $user_id) {
// Предположим, что вы это настроили заранее
/** @var \flight\database\PdoWrapper $db */
$db = Flight::db();
$allowed_permissions = [ 'read' ]; // каждый может просматривать заказ
if($current_role === 'manager') {
$allowed_permissions[] = 'create'; // менеджеры могут создавать заказы
}
$some_special_toggle_from_db = $db->fetchField('SELECT some_special_toggle FROM settings WHERE id = ?', [ $user_id ]);
if($some_special_toggle_from_db) {
$allowed_permissions[] = 'update'; // если у пользователя есть особый переключатель, он может обновлять заказы
}
if($current_role === 'admin') {
$allowed_permissions[] = 'delete'; // администраторы могут удалять заказы
}
return $allowed_permissions;
}
}
Здесь примечательно то, что есть также сокращение, которое можно использовать (которое также может быть кешировано!!!), где вы просто говорите классу разрешений сопоставить все методы в классе в разрешения. Поэтому, если у вас есть метод с именем order()
и метод с именем company()
, они будут автоматически сопоставлены, и вы сможете просто выполнить $Permissions->has('order.read')
или $Permissions->has('company.read')
, и это сработает. Определение этого очень сложно, так что держитесь здесь со мной. Просто вам нужно сделать это:
Создайте класс разрешений, которые вы хотите сгруппировать вместе.
class MyPermissions {
public function order(string $current_role, int $order_id = 0): array {
// код определения разрешений
return $permissions_array;
}
public function company(string $current_role, int $company_id): array {
// код определения разрешений
return $permissions_array;
}
}
Затем сделайте разрешения обнаруживаемыми с использованием этой библиотеки.
$Permissions = new \flight\Permission($current_role);
$Permissions->defineRulesFromClassMethods(MyApp\Permissions::class);
Flight::set('permissions', $Permissions);
Наконец, вызовите разрешение в вашей кодовой базе, чтобы проверить, разрешено ли пользователю выполнение заданного разрешения.
class SomeController {
public function createOrder() {
if(Flight::get('permissions')->can('order.create') === false) {
die('Вы не можете создать заказ. Извините!');
}
}
}
Кеширование
Для включения кэширования, см. простую библиотеку wruczak/phpfilecache. Пример включения приведен ниже.
// этот $app может быть частью вашего кода, или
// вы можете просто передать null, и он извлечет из Flight::app() в конструкторе
$app = Flight::app();
// Теперь для этого принимается файловое кэширование. Другие могут легко
// быть добавлены в будущем.
$Cache = new Wruczek\PhpFileCache\PhpFileCache;
$Permissions = new \flight\Permission($current_role, $app, $Cache);
$Permissions->defineRulesFromClassMethods(MyApp\Permissions::class, 3600); // 3600 - это сколько секунд кэшировать это. Оставьте это, чтобы не использовать кэширование
И впереди!
Awesome-plugins/simple_job_queue
Простой очередь задач
Простой очередь задач - это библиотека, которая может использоваться для обработки задач асинхронно. Она может быть использована с beanstalkd, MySQL/MariaDB, SQLite и PostgreSQL.
Установка
composer require n0nag0n/simple-job-queue
Использование
Чтобы это работало, вам нужен способ добавлять задачи в очередь и способ обрабатывать задачи (рабочий процесс). Ниже приведены примеры того, как добавить задачу в очередь и как обработать задачу.
Добавление в Flight
Добавить это в Flight просто, и это делается с помощью метода register()
. Ниже приведен пример того, как добавить это в Flight.
<?php
require 'vendor/autoload.php';
// Замените ['mysql'] на ['beanstalkd'], если хотите использовать beanstalkd
Flight::register('queue', n0nag0n\Job_Queue::class, ['mysql'], function($Job_Queue) {
// если у вас уже есть соединение PDO на Flight::db();
$Job_Queue->addQueueConnection(Flight::db());
// или если вы используете beanstalkd/Pheanstalk
$pheanstalk = Pheanstalk\Pheanstalk::create('127.0.0.1');
$Job_Queue->addQueueConnection($pheanstalk);
});
Добавление новой задачи
Когда вы добавляете задачу, вам нужно указать конвейер (очередь). Это сравнимо с каналом в RabbitMQ или трубой в beanstalkd.
<?php
Flight::queue()->selectPipeline('send_important_emails');
Flight::queue()->addJob(json_encode([ 'something' => 'that', 'ends' => 'up', 'a' => 'string' ]));
Запуск рабочего процесса
Вот пример файла того, как запустить рабочего процесса.
<?php
require 'vendor/autoload.php';
$Job_Queue = new n0nag0n\Job_Queue('mysql');
// Соединение PDO
$PDO = new PDO('mysql:dbname=testdb;host=127.0.0.1', 'user', 'pass');
$Job_Queue->addQueueConnection($PDO);
// или если вы используете 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();
// настройте это так, как вам будет спокойнее (только для очередей базы данных, beanstalkd не нуждается в этом условии)
if(empty($job)) {
usleep(500000);
continue;
}
echo "Обработка {$job['id']}\n";
$payload = json_decode($job['payload'], true);
try {
$result = doSomethingThatDoesSomething($payload);
if($result === true) {
$Job_Queue->deleteJob($job);
} else {
// это убирает его из очереди готовых и помещает в другую очередь, которую можно будет забрать и «ударить» позже.
$Job_Queue->buryJob($job);
}
} catch(Exception $e) {
$Job_Queue->buryJob($job);
}
}
Обработка длительных процессов с помощью Supervisord
Supervisord - это система управления процессами, которая обеспечивает постоянную работу ваших процессов рабочих. Вот более полное руководство по настройке его с вашим рабочим процессом Простой очереди задач:
Установка Supervisord
# На Ubuntu/Debian
sudo apt-get install supervisor
# На CentOS/RHEL
sudo yum install supervisor
# На macOS с Homebrew
brew install supervisor
Создание скрипта рабочего процесса
Сначала сохраните ваш код рабочего процесса в отдельный файл PHP:
<?php
require 'vendor/autoload.php';
$Job_Queue = new n0nag0n\Job_Queue('mysql');
// Соединение PDO
$PDO = new PDO('mysql:dbname=your_database;host=127.0.0.1', 'username', 'password');
$Job_Queue->addQueueConnection($PDO);
// Установите конвейер для наблюдения
$Job_Queue->watchPipeline('send_important_emails');
// Запись начала работы рабочего процесса
echo date('Y-m-d H:i:s') . " - Рабочий процесс запущен\n";
while(true) {
$job = $Job_Queue->getNextJobAndReserve();
if(empty($job)) {
usleep(500000); // Спите 0,5 секунды
continue;
}
echo date('Y-m-d H:i:s') . " - Обработка задачи {$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') . " - Задача {$job['id']} успешно завершена\n";
} else {
$Job_Queue->buryJob($job);
echo date('Y-m-d H:i:s') . " - Задача {$job['id']} не удалась, похоронена\n";
}
} catch(Exception $e) {
$Job_Queue->buryJob($job);
echo date('Y-m-d H:i:s') . " - Исключение при обработке задачи {$job['id']}: {$e->getMessage()}\n";
}
}
Настройка Supervisord
Создайте файл конфигурации для вашего рабочего процесса:
[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
Основные параметры конфигурации:
command
: Команда для запуска вашего рабочего процессаdirectory
: Рабочая директория для рабочего процессаautostart
: Автоматически запускать при запуске supervisordautorestart
: Автоматически перезапускать, если процесс выходитstartretries
: Количество попыток перезапуска, если он завершится с ошибкойstderr_logfile
/stdout_logfile
: Пути к файлам журналовuser
: Системный пользователь, от имени которого будет запущен процессnumprocs
: Количество экземпляров рабочего процесса для запускаprocess_name
: Форматирование имен для нескольких процессов рабочих
Управление рабочими процессами с помощью Supervisorctl
После создания или изменения конфигурации:
# Перезагрузить конфигурацию супервайзера
sudo supervisorctl reread
sudo supervisorctl update
# Управление конкретными процессами рабочих
sudo supervisorctl start email_worker:*
sudo supervisorctl stop email_worker:*
sudo supervisorctl restart email_worker:*
sudo supervisorctl status email_worker:*
Запуск нескольких конвейеров
Для нескольких конвейеров создайте отдельные файлы рабочих процессов и конфигурации:
[program:email_worker]
command=php /path/to/email_worker.php
# ... другие настройки ...
[program:notification_worker]
command=php /path/to/notification_worker.php
# ... другие настройки ...
Мониторинг и журналы
Проверьте журналы для мониторинга активности рабочих процессов:
# Просмотр журналов
sudo tail -f /var/log/simple_job_queue.log
# Проверка состояния
sudo supervisorctl status
Эта настройка гарантирует, что ваши рабочие процессы задач продолжают работать даже после сбоев, перезагрузок сервера или других проблем, делая вашу систему очередей надежной для производственных сред.
Awesome-plugins/index
Удивительные плагины
Flight невероятно расширяем. Есть ряд плагинов, которые можно использовать для добавления функциональности в ваше приложение Flight. Некоторые официально поддерживаются командой Flight, а другие являются микро/лайт библиотеками, чтобы помочь вам начать.
Кэширование
Кэширование - отличный способ ускорить ваше приложение. Есть несколько библиотек кэширования, которые можно использовать с Flight.
- Wruczek/PHP-File-Cache - Легкий, простой и автономный класс кэширования в файле PHP
Отладка
Отладка критически важна при разработке в локальной среде. Есть несколько плагинов, которые могут улучшить ваш опыт отладки.
- tracy/tracy - Это полнофункциональный обработчик ошибок, который можно использовать с Flight. У него есть несколько панелей, которые могут помочь вам отлаживать ваше приложение. Также очень легко расширяем и добавляете собственные панели.
- flightphp/tracy-extensions - Используется с обработчиком ошибок Tracy, этот плагин добавляет несколько дополнительных панелей для улучшения отладки специально для проектов Flight.
Базы данных
Базы данных являются основой для большинства приложений. Это то, как вы сохраняете и извлекаете данные. Некоторые библиотеки баз данных просто обертки для написания запросов, а некоторые - полноценные ORM.
- flightphp/core PdoWrapper - Официальная обертка Flight PDO, которая является частью ядра. Это простая обертка, которая помогает упростить процесс написания и выполнения запросов. Это не ORM.
- flightphp/active-record - Официальная библиотека Flight ActiveRecord ORM/Mapper. Отличная библиотека для легкого получения и хранения данных в вашей базе данных.
Сессии
Сессии действительно не очень полезны для API, но для создания веб-приложения они могут быть критически важны для поддержания состояния и информации о входе.
- Ghostff/Session - Менеджер сессий PHP (неблокирующий, flash, сегмент, шифрование сессий). Использует PHP open_ssl для дополнительного шифрования/дешифрования данных сессии.
Шаблонизация
Шаблонизация является основой любого веб-приложения с интерфейсом пользователя. Есть несколько шаблонизаторов, которые можно использовать с Flight.
- flightphp/core View - Это очень простой шаблонизатор, который является частью ядра. Не рекомендуется использовать его, если у вас больше чем несколько страниц в вашем проекте.
- latte/latte - Latte - это полнофункциональный шаблонизатор, который очень легко использовать и ближе к синтаксису PHP, чем Twig или Smarty. Также очень легко расширяем и добавляете свои собственные фильтры и функции.
Вклад
У вас есть плагин, который вы хотели бы поделиться? Отправьте запрос на добавление его в список!
Awesome-plugins/ghost_session
Ghostff/Session
Менеджер сессий PHP (неблокирующий, flash, сегмент, шифрование сессий). Использует PHP open_ssl для необязательной шифровки/расшифровки данных сессии. Поддерживает File, MySQL, Redis и Memcached.
Нажмите здесь, чтобы просмотреть код.
Установка
Установите с помощью composer.
composer require ghostff/session
Базовая конфигурация
Вы не обязаны передавать ничего, чтобы использовать настройки по умолчанию с вашей сессией. Вы можете прочитать о дополнительных настройках в Github Readme.
use Ghostff\Session\Session;
require 'vendor/autoload.php';
$app = Flight::app();
$app->register('session', Session::class);
// одно, что следует помнить, это то, что вы должны фиксировать вашу сессию при каждом загрузке страницы
// или вам нужно будет запустить auto_commit в вашей конфигурации.
Простой пример
Вот простой пример того, как вы можете использовать это.
Flight::route('POST /login', function() {
$session = Flight::session();
// выполните вашу логику входа здесь
// проверьте пароль и т.д.
// если вход успешен
$session->set('is_logged_in', true);
$session->set('user', $user);
// всякий раз, когда вы записываете в сессию, вы должны фиксировать это намеренно.
$session->commit();
});
// Эта проверка может быть в логике защищенной страницы или обернута в промежуточное ПО.
Flight::route('/some-restricted-page', function() {
$session = Flight::session();
if(!$session->get('is_logged_in')) {
Flight::redirect('/login');
}
// выполните свою логику защищенной страницы здесь
});
// версия с промежуточным ПО
Flight::route('/some-restricted-page', function() {
// обычная логика страницы
})->addMiddleware(function() {
$session = Flight::session();
if(!$session->get('is_logged_in')) {
Flight::redirect('/login');
}
});
Более сложный пример
Вот более сложный пример того, как вы можете использовать это.
use Ghostff\Session\Session;
require 'vendor/autoload.php';
$app = Flight::app();
// установите пользовательский путь к вашему файлу конфигурации сессии и дайте ему случайную строку для идентификатора сессии
$app->register('session', Session::class, [ 'path/to/session_config.php', bin2hex(random_bytes(32)) ], function(Session $session) {
// или вы можете вручную переопределить параметры конфигурации
$session->updateConfiguration([
// если вы хотите хранить данные своей сессии в базе данных (хорошо, если вы хотите, чтобы что-то вроде "выйти из всех устройств")
Session::CONFIG_DRIVER => Ghostff\Session\Drivers\MySql::class,
Session::CONFIG_ENCRYPT_DATA => true,
Session::CONFIG_SALT_KEY => hash('sha256', 'my-super-S3CR3T-salt'), // пожалуйста, измените это на что-то другое
Session::CONFIG_AUTO_COMMIT => true, // делайте это только если это требуется и/или трудно вызвать commit() для вашей сессии.
// дополнительно вы можете сделать Flight::after('start', function() { Flight::session()->commit(); });
Session::CONFIG_MYSQL_DS => [
'driver' => 'mysql', # Драйвер базы данных для PDO dns, например (mysql:host=...;dbname=...)
'host' => '127.0.0.1', # Хост базы данных
'db_name' => 'my_app_database', # Имя базы данных
'db_table' => 'sessions', # Таблица базы данных
'db_user' => 'root', # Имя пользователя базы данных
'db_pass' => '', # Пароль базы данных
'persistent_conn'=> false, # Избегайте накладных расходов на установление нового соединения каждый раз, когда скрипту необходимо взаимодействовать с базой данных, что приводит к более быстрому веб-приложению. НИЖНЯЯ СТОРОНА ВЫ БУДЕТЕ ИСКАТЬ САМИ
]
]);
}
);
Помогите! Мои данные сессии не сохраняются!
Вы устанавливаете данные своей сессии, и они не сохраняются между запросами? Возможно, вы забыли зафиксировать данные вашей сессии. Вы можете сделать это, вызвав $session->commit()
после того, как установили данные вашей сессии.
Flight::route('POST /login', function() {
$session = Flight::session();
// выполните вашу логику входа здесь
// проверьте пароль и т.д.
// если вход успешен
$session->set('is_logged_in', true);
$session->set('user', $user);
// всякий раз, когда вы записываете в сессию, вы должны фиксировать это намеренно.
$session->commit();
});
Другой способ обойти это - когда вы настраиваете свой сервис сессий, вы должны установить auto_commit
в true
в вашей конфигурации. Это автоматически зафиксирует данные вашей сессии после каждого запроса.
$app->register('session', Session::class, [ 'path/to/session_config.php', bin2hex(random_bytes(32)) ], function(Session $session) {
$session->updateConfiguration([
Session::CONFIG_AUTO_COMMIT => true,
]);
}
);
Кроме того, вы можете сделать Flight::after('start', function() { Flight::session()->commit(); });
, чтобы фиксировать данные вашей сессии после каждого запроса.
Документация
Посетите Github Readme для полной документации. Параметры конфигурации хорошо задокументированы в default_config.php файле. Код прост для понимания, если вы хотите просмотреть этот пакет сами.
Awesome-plugins/pdo_wrapper
PdoWrapper Класс помощника PDO
Flight поставляется с классом помощника для PDO. Он позволяет легко выполнять запросы к базе данных с использованием всех этих заморочек с подготовкой/выполнением/fetchAll(). Это значительно упрощает ваш способ выполнения запросов к базе данных. Каждая строка результата возвращается как класс Flight Collection, который позволяет вам получать доступ к данным с использованием синтаксиса массива или объекта.
Регистрация класса помощника PDO
// Регистрация класса помощника PDO
Flight::register('db', \flight\database\PdoWrapper::class, ['mysql:host=localhost;dbname=cool_db_name', 'user', 'pass', [
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'utf8mb4\'',
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_STRINGIFY_FETCHES => false,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]
]);
Применение
Этот объект расширяет PDO, поэтому все обычные методы PDO доступны. Для упрощения выполнения запросов к базе данных были добавлены следующие методы:
runQuery(string $sql, array $params = []): PDOStatement
Используйте это для INSERTS, UPDATES или если вы планируете использовать SELECT в цикле while
$db = Flight::db();
$statement = $db->runQuery("SELECT * FROM table WHERE something = ?", [ $something ]);
while($row = $statement->fetch()) {
// ...
}
// Или запись в базу данных
$db->runQuery("INSERT INTO table (name) VALUES (?)", [ $name ]);
$db->runQuery("UPDATE table SET name = ? WHERE id = ?", [ $name, $id ]);
fetchField(string $sql, array $params = []): mixed
Извлекает первое поле из запроса
$db = Flight::db();
$count = $db->fetchField("SELECT COUNT(*) FROM table WHERE something = ?", [ $something ]);
fetchRow(string $sql, array $params = []): array
Извлекает одну строку из запроса
$db = Flight::db();
$row = $db->fetchRow("SELECT id, name FROM table WHERE id = ?", [ $id ]);
echo $row['name'];
// или
echo $row->name;
fetchAll(string $sql, array $params = []): array
Извлекает все строки из запроса
$db = Flight::db();
$rows = $db->fetchAll("SELECT id, name FROM table WHERE something = ?", [ $something ]);
foreach($rows as $row) {
echo $row['name'];
// или
echo $row->name;
}
Примечание к синтаксису IN()
Здесь также есть удобная оболочка для оператора IN()
. Просто передайте один вопросительный знак в качестве заполнителя для IN()
, а затем массив значений. Вот пример того, как это может выглядеть:
$db = Flight::db();
$name = 'Bob';
$company_ids = [1,2,3,4,5];
$rows = $db->fetchAll("SELECT id, name FROM table WHERE name = ? AND company_id IN (?)", [ $name, $company_ids ]);
Полный пример
// Пример маршрута и как использовать эту оболочку
Flight::route('/users', function () {
// Получить всех пользователей
$users = Flight::db()->fetchAll('SELECT * FROM users');
// Отправить всех пользователей
$statement = Flight::db()->runQuery('SELECT * FROM users');
while ($user = $statement->fetch()) {
echo $user['name'];
// или echo $user->name;
}
// Получить одного пользователя
$user = Flight::db()->fetchRow('SELECT * FROM users WHERE id = ?', [123]);
// Получить одно значение
$count = Flight::db()->fetchField('SELECT COUNT(*) FROM users');
// Особый синтаксис IN() для помощи (убедитесь, что IN написано заглавными буквами)
$users = Flight::db()->fetchAll('SELECT * FROM users WHERE id IN (?)', [[1,2,3,4,5]]);
// вы также можете сделать так
$users = Flight::db()->fetchAll('SELECT * FROM users WHERE id IN (?)', [ '1,2,3,4,5']);
// Вставить нового пользователя
Flight::db()->runQuery("INSERT INTO users (name, email) VALUES (?, ?)", ['Bob', 'bob@example.com']);
$insert_id = Flight::db()->lastInsertId();
// Обновить пользователя
Flight::db()->runQuery("UPDATE users SET name = ? WHERE id = ?", ['Bob', 123]);
// Удалить пользователя
Flight::db()->runQuery("DELETE FROM users WHERE id = ?", [123]);
// Получить количество затронутых строк
$statement = Flight::db()->runQuery("UPDATE users SET name = ? WHERE name = ?", ['Bob', 'Sally']);
$affected_rows = $statement->rowCount();
});
Awesome-plugins/migrations
Миграции
Миграция для вашего проекта отслеживает все изменения базы данных, связанные с вашим проектом. byjg/php-migration — это действительно полезная основная библиотека, с которой вы можете начать.
Установка
PHP библиотека
Если вы хотите использовать только PHP библиотеку в вашем проекте:
composer require "byjg/migration"
Интерфейс командной строки
Интерфейс командной строки является отдельным и не требует установки вместе с вашим проектом.
Вы можете установить его глобально и создать символическую ссылку.
composer require "byjg/migration-cli"
Пожалуйста, посетите byjg/migration-cli, чтобы получить больше информации о Migration CLI.
Поддерживаемые базы данных
База данных | Драйвер | Строка соединения |
---|---|---|
Sqlite | pdo_sqlite | sqlite:///path/to/file |
MySql/MariaDb | pdo_mysql | mysql://username:password@hostname:port/database |
Postgres | pdo_pgsql | pgsql://username:password@hostname:port/database |
Sql Server | pdo_dblib, pdo_sysbase Linux | dblib://username:password@hostname:port/database |
Sql Server | pdo_sqlsrv Windows | sqlsrv://username:password@hostname:port/database |
Как это работает?
Миграция базы данных использует ЧИСТЫЙ SQL для управления версионностью базы данных. Чтобы это заработало, вам необходимо:
- Создать SQL скрипты
- Управлять, используя командную строку или API.
SQL скрипты
Скрипты делятся на три группы:
- БАЗОВЫЙ скрипт содержит ВСЕ sql команды для создания новой базы данных;
- UP скрипты содержат все sql миграционные команды для "повышения" версии базы данных;
- DOWN скрипты содержат все sql миграционные команды для "понижения" или возврата версии базы данных;
Директория скриптов:
<root dir>
|
+-- base.sql
|
+-- /migrations
|
+-- /up
|
+-- 00001.sql
+-- 00002.sql
+-- /down
|
+-- 00000.sql
+-- 00001.sql
- "base.sql" — это базовый скрипт
- Папка "up" содержит скрипты для миграции вверх. Например: 00002.sql — это скрипт для перемещения базы данных с версии '1' на '2'.
- Папка "down" содержит скрипты для миграции вниз. Например: 00001.sql — это скрипт для перемещения базы данных с версии '2' на '1'. Папка "down" является необязательной.
Многоразвивающая среда
Если вы работаете с несколькими разработчиками и несколькими ветками, будет сложно определить, какое число следующее.
В этом случае вы добавляете суффикс "-dev" после номера версии.
Смотрите сценарий:
- Разработчик 1 создает ветку, и самая последняя версия, например, 42.
- Разработчик 2 создаёт ветку одновременно и имеет тот же номер версии базы данных.
В обоих случаях разработчики создадут файл под названием 43-dev.sql. Оба разработчика будут мигрировать UP и DOWN без проблем, и ваша локальная версия будет 43.
Но разработчик 1 объединил ваши изменения и создал окончательную версию 43.sql (git mv 43-dev.sql 43.sql
). Если разработчик 2 обновит свою локальную ветку, он получит файл 43.sql (от dev 1) и ваш файл 43-dev.sql. Если он попытается мигрировать UP или DOWN, скрипт миграции упадет и предупредит его, что есть ДВЕ версии 43. В этом случае разработчик 2 должен будет обновить ваш файл до 44-dev.sql и продолжить работать, пока не объединит ваши изменения и не сгенерирует окончательную версию.
Использование PHP API и интеграция его в ваши проекты
Основное использование:
- Создайте объект ConnectionManagement для соединения. Для получения дополнительной информации смотрите компонент "byjg/anydataset".
- Создайте объект миграции с этим соединением и папкой, в которой находятся sql-скрипты.
- Используйте соответствующую команду для "сброса", "up" или "down" миграционных скриптов.
Смотрите пример:
<?php
// Создайте URI соединения
// Подробнее: https://github.com/byjg/anydataset#connection-based-on-uri
$connectionUri = new \ByJG\Util\Uri('mysql://migrateuser:migratepwd@localhost/migratedatabase');
// Зарегистрируйте Базу данных или Базы данных, которые могут обрабатывать этот URI:
\ByJG\DbMigration\Migration::registerDatabase(\ByJG\DbMigration\Database\MySqlDatabase::class);
// Создайте экземпляр миграции
$migration = new \ByJG\DbMigration\Migration($connectionUri, '.');
// Добавьте функцию обратного вызова прогресса для получения информации о выполнении
$migration->addCallbackProgress(function ($action, $currentVersion, $fileInfo) {
echo "$action, $currentVersion, ${fileInfo['description']}\n";
});
// Восстановите базу данных с использованием скрипта "base.sql"
// и выполните ВСЕ существующие скрипты для повышения версии базы данных до последней версии
$migration->reset();
// Выполните ВСЕ существующие скрипты для вверх или вниз версии базы данных
// от текущей версии до номера $version;
// Если номер версии не указан, мигрируйте до последней версии базы данных
$migration->update($version = null);
Объект миграции контролирует версию базы данных.
Создание контроля версий в вашем проекте
<?php
// Зарегистрируйте Базу данных или Базы данных, которые могут обрабатывать этот URI:
\ByJG\DbMigration\Migration::registerDatabase(\ByJG\DbMigration\Database\MySqlDatabase::class);
// Создайте экземпляр миграции
$migration = new \ByJG\DbMigration\Migration($connectionUri, '.');
// Эта команда создаст таблицу версий в вашей базе данных
$migration->createVersion();
Получение текущей версии
<?php
$migration->getCurrentVersion();
Добавить обратный вызов для контроля прогресса
<?php
$migration->addCallbackProgress(function ($command, $version, $fileInfo) {
echo "Выполняем команду: $command на версии $version - ${fileInfo['description']}, ${fileInfo['exists']}, ${fileInfo['file']}, ${fileInfo['checksum']}\n";
});
Получение экземпляра драйвера Db
<?php
$migration->getDbDriver();
Чтобы использовать это, пожалуйста, посетите: https://github.com/byjg/anydataset-db
Избежание частичной миграции (недоступно для MySQL)
Частичная миграция возникает, когда скрипт миграции прерывается в середине процесса из-за ошибки или ручного прерывания.
Таблица миграции будет иметь статус partial up
или partial down
, и ее необходимо исправить вручную перед тем, как снова мигрировать.
Чтобы избежать этой ситуации, вы можете указать, что миграция будет выполняться в транзакционном контексте. Если скрипт миграции не удастся выполнить, транзакция будет отменена, а таблица миграции будет отмечена как complete
, и версия будет сразу же предыдущей версией перед скриптом, который вызвал ошибку.
Чтобы включить эту функцию, вам необходимо вызвать метод withTransactionEnabled
, передав true
как параметр:
<?php
$migration->withTransactionEnabled(true);
ПРИМЕЧАНИЕ: Эта функция недоступна для MySQL, так как она не поддерживает DDL команды внутри транзакции. Если вы используете этот метод с MySQL, миграция проигнорирует его без уведомления. Дополнительная информация: https://dev.mysql.com/doc/refman/8.0/en/cannot-roll-back.html
Советы по написанию SQL миграций для Postgres
О создании триггеров и SQL функций
-- ДЕЛАЙТЕ
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- Проверьте, что empname и зарплата указаны
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname не может быть пустым'; -- не имеет значения, пустые ли эти комментарии
END IF; --
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% не может иметь пустую зарплату', NEW.empname; --
END IF; --
-- Кто работает на нас, когда они должны за это платить?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% не может иметь отрицательную зарплату', NEW.empname; --
END IF; --
-- Запомните, кто изменял зарплату, когда
NEW.last_date := current_timestamp; --
NEW.last_user := current_user; --
RETURN NEW; --
END; --
$emp_stamp$ LANGUAGE plpgsql;
-- НЕ ДЕЛАЙТЕ
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- Проверьте, что empname и зарплата указаны
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname не может быть пустым';
END IF;
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% не может иметь пустую зарплату', NEW.empname;
END IF;
-- Кто работает на нас, когда они должны за это платить?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% не может иметь отрицательную зарплату', NEW.empname;
END IF;
-- Запомните, кто изменял зарплату, когда
NEW.last_date := current_timestamp;
NEW.last_user := current_user;
RETURN NEW;
END;
$emp_stamp$ LANGUAGE plpgsql;
Поскольку уровень абстракции базы данных PDO
не может выполнять пачки SQL операторов, когда byjg/migration
читает файл миграции, он должен разбить все содержимое SQL файла по точкам с запятой и выполнять операторы один за другим. Однако существует один вид оператора, который может содержать несколько точек с запятой между его телом: функции.
Чтобы иметь возможность корректно парсить функции, byjg/migration
2.1.0 начал разбивать файлы миграции по последовательности точка с запятой + EOL
вместо обычной точки с запятой. Таким образом, если вы добавите пустой комментарий после каждой внутренней точки с запятой в определении функции, byjg/migration
сможет корректно его разобрать.
К сожалению, если вы забудете добавить хотя бы один из этих комментариев, библиотека разобьет оператор CREATE FUNCTION
на несколько частей, и миграция потерпит неудачу.
Избегайте символа двоеточия (:
)
-- ДЕЛАЙТЕ
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
);
-- НЕ ДЕЛАЙТЕ
CREATE TABLE bookings (
booking_id UUID PRIMARY KEY,
booked_at TIMESTAMPTZ NOT NULL CHECK (booked_at::DATE <= check_in),
check_in DATE NOT NULL
);
Поскольку PDO
использует двоеточие, чтобы обозначать именованные параметры в подготовленных операторов, его использование вызовет проблемы в других контекстах.
Например, операторы PostgreSQL могут использовать ::
для приведения значений между типами. С другой стороны, PDO
воспримет это как недопустимый именованный параметр в недопустимом контексте и выдаст ошибку, когда попытается его выполнить.
Единственный способ исправить это несоответствие — это полностью избегать двоеточий (в этом случае у PostgreSQL также есть альтернативный синтаксис: CAST(value AS type)
).
Используйте SQL редактор
Наконец, написание ручных SQL миграций может быть утомительным, но это значительно проще, если вы используете редактор, способный понимать синтаксис SQL, предоставляющий автозавершение, интуитивно исследующий вашу текущую схему базы данных и/или автоматически форматирующий ваш код.
Обработка различных миграций внутри одной схемы
Если вам нужно создать различные миграционные скрипты и версии в одной схеме, это возможно, но слишком рискованно, и я категорически не рекомендую это.
Для этого вам нужно создать разные "миграционные таблицы", передавая параметр в конструктор.
<?php
$migration = new \ByJG\DbMigration\Migration("db:/uri", "/path", true, "NEW_MIGRATION_TABLE_NAME");
По соображениям безопасности эта функция недоступна в командной строке, но вы можете использовать переменную среды MIGRATION_VERSION
, чтобы сохранить имя.
Мы действительно рекомендуем не использовать эту функцию. Рекомендация — одна миграция для одной схемы.
Запуск модульных тестов
Основные модульные тесты можно запустить с помощью:
vendor/bin/phpunit
Запуск тестов базы данных
Запуск интеграционных тестов требует, чтобы базы данных были запущены и работали. Мы предоставили базовый docker-compose.yml
, и вы можете использовать его для запуска баз данных для тестов.
Запуск баз данных
docker-compose up -d postgres mysql mssql
Запуск тестов
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*
Опционально вы можете установить хост и пароль, используемые модульными тестами.
export MYSQL_TEST_HOST=localhost # по умолчанию localhost
export MYSQL_PASSWORD=newpassword # используйте '.' если хотите иметь пустой пароль
export PSQL_TEST_HOST=localhost # по умолчанию localhost
export PSQL_PASSWORD=newpassword # используйте '.' если хотите иметь пустой пароль
export MSSQL_TEST_HOST=localhost # по умолчанию localhost
export MSSQL_PASSWORD=Pa55word
export SQLITE_TEST_HOST=/tmp/test.db # по умолчанию /tmp/test.db
Awesome-plugins/session
FlightPHP Сессия - Легковесный файловый обработчик сессий
Это легковесный, файловый плагин обработчика сессий для Flight PHP Framework. Он предоставляет простое, но мощное решение для управления сессиями с такими функциями, как неблокирующее чтение сессий, опциональное шифрование, функция автоматического коммита и тестовый режим для разработки. Данные сессии хранятся в файлах, что делает его идеальным для приложений, которые не требуют базу данных.
Если вы хотите использовать базу данных, ознакомьтесь с плагином ghostff/session, который имеет многие из этих функций, но с базой данных.
Посетите Github репозиторий для полного исходного кода и деталей.
Установка
Установите плагин через Composer:
composer require flightphp/session
Основное использование
Вот простой пример того, как использовать плагин flightphp/session
в вашем приложении Flight:
require 'vendor/autoload.php';
use flight\Session;
$app = Flight::app();
// Регистрация сервиса сессии
$app->register('session', Session::class);
// Пример маршрута с использованием сессии
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'); // Вывод: johndoe
echo $session->get('preferences', 'default_theme'); // Вывод: default_theme
if ($session->get('user_id')) {
Flight::json(['message' => 'Пользователь вошел в систему!', 'user_id' => $session->get('user_id')]);
}
});
Flight::route('/logout', function() {
$session = Flight::session();
$session->clear(); // Очистить все данные сессии
Flight::json(['message' => 'Успешный выход']);
});
Flight::start();
Ключевые моменты
- Неблокирующий: По умолчанию использует
read_and_close
для начала сессии, предотвращая проблемы блокировки сессий. - Авто-коммит: Включен по умолчанию, поэтому изменения сохраняются автоматически при завершении работы, если не отключены.
- Файловое хранилище: Сессии хранятся в системном временном каталоге по умолчанию в
/flight_sessions
.
Конфигурация
Вы можете настроить обработчик сессий, передавая массив параметров при регистрации:
$app->register('session', Session::class, [
'save_path' => '/custom/path/to/sessions', // Каталог для файлов сессий
'encryption_key' => 'a-secure-32-byte-key-here', // Включить шифрование (рекомендуется 32 байта для AES-256-CBC)
'auto_commit' => false, // Отключить автоматический коммит для ручного управления
'start_session' => true, // Автоматически начинать сессию (по умолчанию: true)
'test_mode' => false // Включить тестовый режим для разработки
]);
Параметры конфигурации
Параметр | Описание | Значение по умолчанию |
---|---|---|
save_path |
Каталог, где хранятся файлы сессий | sys_get_temp_dir() . '/flight_sessions' |
encryption_key |
Ключ для шифрования AES-256-CBC (опционально) | null (без шифрования) |
auto_commit |
Автоматически сохранять данные сессии при завершении работы | true |
start_session |
Автоматически начинать сессию | true |
test_mode |
Запуск в тестовом режиме без влияния на PHP сессии | false |
test_session_id |
Пользовательский идентификатор сессии для тестового режима (опционально) | Генерируется случайно, если не установлен |
Расширенное использование
Ручной коммит
Если вы отключите авто-коммит, вы должны вручную коммитить изменения:
$app->register('session', Session::class, ['auto_commit' => false]);
Flight::route('/update', function() {
$session = Flight::session();
$session->set('key', 'value');
$session->commit(); // Явно сохранить изменения
});
Безопасность сессии с шифрованием
Включите шифрование для конфиденциальных данных:
$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'); // Шифруется автоматически
echo $session->get('credit_card'); // Дешифруется при получении
});
Регенация сессии
Регенерируйте идентификатор сессии для безопасности (например, после входа в систему):
Flight::route('/post-login', function() {
$session = Flight::session();
$session->regenerate(); // Новый ID, сохранить данные
// ИЛИ
$session->regenerate(true); // Новый ID, удалить старые данные
});
Пример промежуточного программного обеспечения
Защитите маршруты с помощью аутентификации на основе сессий:
Flight::route('/admin', function() {
Flight::json(['message' => 'Добро пожаловать в панель управления']);
})->addMiddleware(function() {
$session = Flight::session();
if (!$session->get('is_admin')) {
Flight::halt(403, 'Доступ запрещен');
}
});
Это просто простой пример того, как использовать это в промежуточном программном обеспечении. Для более глубокого примера ознакомьтесь с документацией о промежуточном программном обеспечении.
Методы
Класс Session
предоставляет эти методы:
set(string $key, $value)
: Сохраняет значение в сессии.get(string $key, $default = null)
: Извлекает значение с опциональным значением по умолчанию, если ключ не существует.delete(string $key)
: Удаляет конкретный ключ из сессии.clear()
: Удаляет все данные сессии.commit()
: Сохраняет текущие данные сессии в файловой системе.id()
: Возвращает текущий идентификатор сессии.regenerate(bool $deleteOld = false)
: Регенерирует идентификатор сессии, опционально удаляя старые данные.
Все методы, кроме get()
и id()
, возвращают экземпляр Session
для связывания.
Зачем использовать этот плагин?
- Легковесный: Нет внешних зависимостей — только файлы.
- Неблокирующий: Избегает блокировки сессий с помощью
read_and_close
по умолчанию. - Безопасный: Поддерживает шифрование AES-256-CBC для конфиденциальных данных.
- Гибкий: Авто-коммит, тестовый режим и параметры ручного управления.
- Нативный для Flight: Построен специально для фреймворка Flight.
Технические детали
- Формат хранения: Файлы сессий имеют префикс
sess_
и хранятся в настроенномsave_path
. Шифрованные данные используют префиксE
, открытые данные используютP
. - Шифрование: Использует AES-256-CBC с случайным IV для каждой записи сессии, когда предоставлен
encryption_key
. - Сбор мусора: Реализует
SessionHandlerInterface::gc()
PHP для очистки истекших сессий.
Участие
Вклад приветствуется! Сделайте форк репозитория, внесите изменения и отправьте пулл-запрос. Сообщайте об ошибках или предлагайте функции через трекер проблем GitHub.
Лицензия
Этот плагин лицензирован под MIT License. См. Github репозиторий для получения деталей.
Awesome-plugins/runway
Взлетная полоса
Взлетная полоса — это приложение CLI, которое помогает управлять приложениями Flight. Он может генерировать контроллеры, отображать все маршруты и многое другое. Он основан на отличной библиотеке adhocore/php-cli.
Нажмите здесь, чтобы просмотреть код.
Установка
Установите через composer.
composer require flightphp/runway
Основная настройка
Первый раз, когда вы запускаете Взлетную полосу, она проведет вас через процесс настройки и создаст файл конфигурации .runway.json
в корне вашего проекта. Этот файл будет содержать несколько необходимых конфигураций для работы Взлетной полосы должным образом.
Использование
У Взлетной полосы есть несколько команд, которые вы можете использовать для управления вашим приложением Flight. Есть два простых способа использования Взлетной полосы.
- Если вы используете каркас проекта, вы можете запустить
php runway [команда]
из корня вашего проекта. - Если вы используете Взлетную полосу как пакет, установленный через composer, вы можете запустить
vendor/bin/runway [команда]
из корня вашего проекта.
Для любой команды вы можете передать флаг --help
, чтобы получить больше информации о том, как использовать команду.
php runway routes --help
Вот несколько примеров:
Создание контроллера
На основе конфигурации в вашем файле .runway.json
по умолчанию контроллер будет создан для вас в каталоге app/controllers/
.
php runway make:controller MyController
Создание модели Active Record
На основе конфигурации в вашем файле .runway.json
по умолчанию модель будет создана для вас в каталоге app/records/
.
php runway make:record users
Если у вас, например, есть таблица users
с такой схемой: id
, name
, email
, created_at
, updated_at
, будет создан файл, подобный следующему, в файле app/records/UserRecord.php
:
<?php
declare(strict_types=1);
namespace app\records;
/**
* Класс Active Record для таблицы пользователей.
* @link https://docs.flightphp.com/awesome-plugins/active-record
*
* @property int $id
* @property string $name
* @property string $email
* @property string $created_at
* @property string $updated_at
* // здесь вы также можете добавить отношения после их определения в массиве $relations
* @property CompanyRecord $company Пример отношения
*/
class UserRecord extends \flight\ActiveRecord
{
/**
* @var array $relations Установите отношения для модели
* https://docs.flightphp.com/awesome-plugins/active-record#relationships
*/
protected array $relations = [];
/**
* Конструктор
* @param mixed $databaseConnection Соединение с базой данных
*/
public function __construct($databaseConnection)
{
parent::__construct($databaseConnection, 'users');
}
}
Отображение всех маршрутов
Это отобразит все маршруты, которые в настоящее время зарегистрированы в Flight.
php runway routes
Если вы хотите просмотреть только определенные маршруты, вы можете передать флаг для фильтрации маршрутов.
# Отобразить только GET маршруты
php runway routes --get
# Отобразить только POST маршруты
php runway routes --post
# и т.д.
Настройка Взлетной полосы
Если вы создаете пакет для Flight или хотите добавить свои собственные команды в свой проект, вы можете сделать это, создав каталог src/commands/
, flight/commands/
, app/commands/
или commands/
для вашего проекта/пакета.
Для создания команды просто расширьте класс AbstractBaseCommand
и реализуйте, как минимум, метод __construct
и метод execute
.
<?php
declare(strict_types=1);
namespace flight\commands;
class ExampleCommand extends AbstractBaseCommand
{
/**
* Конструктор
*
* @param array<string,mixed> $config JSON конфигурация из .runway-config.json
*/
public function __construct(array $config)
{
parent::__construct('make:example', 'Создать пример для документации', $config);
$this->argument('<funny-gif>', 'Имя смешной гифки');
}
/**
* Выполняет функцию
*
* @return void
*/
public function execute(string $controller)
{
$io = $this->app()->io();
$io->info('Создание примера...');
// Сделайте здесь что-то
$io->ok('Пример создан!');
}
}
Смотрите Документация adhocore/php-cli для получения дополнительной информации о том, как создавать свои собственные команды в вашем приложении Flight!
Awesome-plugins/tracy_extensions
Tracy Flight Panel Extensions
Это набор расширений, чтобы работа с Flight была немного более обширной.
- Flight - Анализ всех переменных Flight.
- Database - Анализ всех запросов, выполняемых на странице (если правильно инициировать соединение с базой данных).
- Request - Анализ всех переменных
$_SERVER
и изучение всех глобальных нагрузок ($_GET
,$_POST
,$_FILES
). - Session - Анализ всех переменных
$_SESSION
, если сеансы активны.
Это Панель
И каждая панель отображает очень полезную информацию о вашем приложении!
Нажмите здесь, чтобы просмотреть код.
Установка
Запустите composer require flightphp/tracy-extensions --dev
и вы на правильном пути!
Конфигурация
Вам нужно сделать очень небольшую настройку, чтобы начать использовать это. Вам нужно инициировать отладчик Tracy перед использованием этого https://tracy.nette.org/en/guide:
<?php
use Tracy\Debugger;
use flight\debug\tracy\TracyExtensionLoader;
// код инициализации
require __DIR__ . '/vendor/autoload.php';
Debugger::enable();
// Возможно, вам нужно указать ваше окружение с помощью Debugger::enable(Debugger::DEVELOPMENT)
// если вы используете подключения к базе данных в вашем приложении, есть
// обязательная оболочка PDO для использования ТОЛЬКО В РАЗРАБОТКЕ (пожалуйста, не в продакшн!)
// У него те же параметры, что и обычное соединение PDO
$pdo = new PdoQueryCapture('sqlite:test.db', 'user', 'pass');
// или если вы присоединяете это к фреймворку Flight
Flight::register('db', PdoQueryCapture::class, ['sqlite:test.db', 'user', 'pass']);
// теперь при каждом запросе он будет записывать время, запрос и параметры
// Это соединяет точки
if(Debugger::$showBar === true) {
// Это должно быть false, иначе Tracy не сможет отображаться :(
Flight::set('flight.content_length', false);
new TracyExtensionLoader(Flight::app());
}
// еще код
Flight::start();
Дополнительная Конфигурация
Данные сеанса
Если у вас есть пользовательский обработчик сеансов (например, ghostff/session), вы можете передать любой массив данных сеанса в Tracy, и он автоматически выведет его для вас. Вы передаете это с ключом session_data
вторым параметром конструктора TracyExtensionLoader
.
use Ghostff\Session\Session;
require 'vendor/autoload.php';
$app = Flight::app();
$app->register('session', Session::class);
if(Debugger::$showBar === true) {
// Это должно быть false, иначе Tracy не сможет отображаться :(
Flight::set('flight.content_length', false);
new TracyExtensionLoader(Flight::app(), [ 'session_data' => Flight::session()->getAll() ]);
}
// маршруты и другие вещи...
Flight::start();
Latte
Если у вас установлен Latte в вашем проекте, вы можете использовать панель Latte для анализа ваших шаблонов. Вы можете передать экземпляр Latte в конструктор TracyExtensionLoader
с ключом latte
вторым параметром.
use Latte\Engine;
require 'vendor/autoload.php';
$app = Flight::app();
$app->register('latte', Engine::class, [], function($latte) {
$latte->setTempDirectory(__DIR__ . '/temp');
// здесь вы добавляете Панель Latte в Tracy
$latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);
});
if(Debugger::$showBar === true) {
// Это должно быть false, иначе Tracy не сможет отображаться :(
Flight::set('flight.content_length', false);
new TracyExtensionLoader(Flight::app());
}
Awesome-plugins/tracy
Tracy
Трейси - удивительный обработчик ошибок, который можно использовать с Flight. У него есть ряд панелей, которые могут помочь вам отлаживать ваше приложение. Он также очень легок в расширении и добавлении собственных панелей. Команда Flight создала несколько панелей специально для проектов Flight с плагином flightphp/tracy-extensions.
Установка
Установите с помощью composer. И вам действительно захочется установить это без версии для разработчиков, так как у Трейси есть компонент обработки ошибок для продакшена.
composer require tracy/tracy
Базовая конфигурация
Есть некоторые базовые параметры конфигурации, чтобы начать. Вы можете узнать больше о них в Документации по Tracy.
require 'vendor/autoload.php';
use Tracy\Debugger;
// Включение Tracy
Debugger::enable();
// Debugger::enable(Debugger::DEVELOPMENT) // иногда вам придется быть явным (также Debugger::PRODUCTION)
// Debugger::enable('23.75.345.200'); // также можно предоставить массив IP-адресов
// Здесь будут регистрироваться ошибки и исключения. Убедитесь, что этот каталог существует и доступен для записи.
Debugger::$logDirectory = __DIR__ . '/../log/';
Debugger::$strictMode = true; // показывать все ошибки
// Debugger::$strictMode = E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED; // все ошибки, кроме устаревших уведомлений
if (Debugger::$showBar) {
$app->set('flight.content_length', false); // если панель отладки видима, тогда длина содержимого не может быть установлена Flight
// Это специфично для Расширения Трейси для Flight, если вы его включили
// в противном случае закомментируйте это.
new TracyExtensionLoader($app);
}
Полезные советы
Когда вы отлаживаете свой код, есть несколько очень полезных функций для вывода данных для вас.
bdump($var)
- Это выведет переменную на панель Трейси в отдельной панели.dumpe($var)
- Это выведет переменную, а затем немедленно завершит работу.
Awesome-plugins/active_record
Flight Active Record
Активная запись – это отображение сущности базы данных на объект PHP. Проще говоря, если у вас есть таблица пользователей в вашей базе данных, вы можете "перевести" строку в этой таблице на класс User
и объект $user
в вашем коде. См. основной пример.
Нажмите здесь для просмотра репозитория на GitHub.
Основной пример
Предположим, у вас есть следующая таблица:
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT,
password TEXT
);
Теперь вы можете настроить новый класс для представления этой таблицы:
/**
* Класс ActiveRecord обычно единственное число
*
* Настоятельно рекомендуется добавлять свойства таблицы в виде комментариев здесь
*
* @property int $id
* @property string $name
* @property string $password
*/
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
// вы можете установить это таким образом
parent::__construct($database_connection, 'users');
// или таким образом
parent::__construct($database_connection, null, [ 'table' => 'users']);
}
}
Теперь смотрите, как происходит магия!
// для sqlite
$database_connection = new PDO('sqlite:test.db'); // это просто для примера, вы, вероятно, используете реальное подключение к базе данных
// для mysql
$database_connection = new PDO('mysql:host=localhost;dbname=test_db&charset=utf8bm4', 'username', 'password');
// или mysqli
$database_connection = new mysqli('localhost', 'username', 'password', 'test_db');
// или mysqli с созданием без объекта
$database_connection = mysqli_connect('localhost', 'username', 'password', 'test_db');
$user = new User($database_connection);
$user->name = 'Bobby Tables';
$user->password = password_hash('некоторый классный пароль');
$user->insert();
// или $user->save();
echo $user->id; // 1
$user->name = 'Joseph Mamma';
$user->password = password_hash('некоторый классный пароль снова!!!');
$user->insert();
// нельзя использовать $user->save() здесь, иначе он подумает, что это обновление!
echo $user->id; // 2
И добавление нового пользователя было так же просто! Теперь, когда в базе данных есть строка пользователя, как вы можете ее извлечь?
$user->find(1); // найти id = 1 в базе данных и вернуть его.
echo $user->name; // 'Bobby Tables'
А что если вы хотите найти всех пользователей?
$users = $user->findAll();
Что насчет определенного условия?
$users = $user->like('name', '%mamma%')->findAll();
Видите, как это весело? Давайте установим это и начнем!
Установка
Просто установите с помощью Composer
composer require flightphp/active-record
Использование
Это можно использовать как отдельную библиотеку или с PHP-фреймворком Flight. Полностью вам решать.
Отдельно
Просто убедитесь, что вы передаете подключение PDO в конструктор.
$pdo_connection = new PDO('sqlite:test.db'); // это просто для примера, вы, вероятно, используете реальное подключение к базе данных
$User = new User($pdo_connection);
Не хотите всегда устанавливать ваше подключение к базе данных в конструкторе? См. Управление подключением к базе данных для других идей!
Зарегистрировать как метод в Flight
Если вы используете PHP-фреймворк Flight, вы можете зарегистрировать класс ActiveRecord как сервис, но вам честно не обязательно это делать.
Flight::register('user', 'User', [ $pdo_connection ]);
// тогда вы можете использовать это так в контроллере, функции и т.д.
Flight::user()->find(1);
Методы runway
runway – это CLI инструмент для Flight, который имеет специальную команду для этой библиотеки.
# Использование
php runway make:record database_table_name [class_name]
# Пример
php runway make:record users
Это создаст новый класс в директории app/records/
как UserRecord.php
со следующим содержимым:
<?php
declare(strict_types=1);
namespace app\records;
/**
* Класс ActiveRecord для таблицы пользователей.
* @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 Установите отношения для модели
* https://docs.flightphp.com/awesome-plugins/active-record#relationships
*/
protected array $relations = [
// 'relation_name' => [ self::HAS_MANY, 'RelatedClass', 'foreign_key' ],
];
/**
* Конструктор
* @param mixed $databaseConnection Подключение к базе данных
*/
public function __construct($databaseConnection)
{
parent::__construct($databaseConnection, 'users');
}
}
Функции CRUD
find($id = null) : boolean|ActiveRecord
Находит одну запись и присваивает ее текущему объекту. Если вы передаете $id
какого-либо рода, это будет выполнять поиск по первичному ключу с этим значением. Если ничего не передается, это просто найдет первую запись в таблице.
Кроме того, вы можете передать ему другие вспомогательные методы для запроса к вашей таблице.
// найти запись с некоторыми условиями заранее
$user->notNull('password')->orderBy('id DESC')->find();
// найти запись по конкретному id
$id = 123;
$user->find($id);
findAll(): array<int,ActiveRecord>
Находит все записи в таблице, которую вы указываете.
$user->findAll();
isHydrated(): boolean
(v0.4.0)
Возвращает true
, если текущая запись была гидратирована (извлечена из базы данных).
$user->find(1);
// если запись найдена с данными...
$user->isHydrated(); // true
insert(): boolean|ActiveRecord
Вставляет текущую запись в базу данных.
$user = new User($pdo_connection);
$user->name = 'demo';
$user->password = md5('demo');
$user->insert();
Первичные ключи на текстовой основе
Если у вас есть первичный ключ на текстовой основе (например, UUID), вы можете установить значение первичного ключа перед вставкой одним из двух способов.
$user = new User($pdo_connection, [ 'primaryKey' => 'uuid' ]);
$user->uuid = 'some-uuid';
$user->name = 'demo';
$user->password = md5('demo');
$user->insert(); // или $user->save();
или вы можете позволить первичному ключу автоматически генерироваться для вас через события.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users', [ 'primaryKey' => 'uuid' ]);
// вы также можете установить первичный ключ таким образом вместо массива выше.
$this->primaryKey = 'uuid';
}
protected function beforeInsert(self $self) {
$self->uuid = uniqid(); // или как вам нужно сгенерировать ваши уникальные идентификаторы
}
}
Если вы не установите первичный ключ перед вставкой, он будет установлен на rowid
, и база данных сгенерирует его для вас, но он не будет храниться, потому что это поле может не существовать в вашей таблице. Вот почему рекомендуется использовать событие для автоматического управления этим.
update(): boolean|ActiveRecord
Обновляет текущую запись в базе данных.
$user->greaterThan('id', 0)->orderBy('id desc')->find();
$user->email = 'test@example.com';
$user->update();
save(): boolean|ActiveRecord
Вставляет или обновляет текущую запись в базе данных. Если у записи есть id, она будет обновлена, в противном случае будет вставлена.
$user = new User($pdo_connection);
$user->name = 'demo';
$user->password = md5('demo');
$user->save();
Примечание: Если у вас есть определенные отношения в классе, они также будут рекурсивно сохранены, если они были определены, созданы и имеют измененные данные для обновления. (v0.4.0 и выше)
delete(): boolean
Удаляет текущую запись из базы данных.
$user->gt('id', 0)->orderBy('id desc')->find();
$user->delete();
Вы также можете удалить несколько записей, исполняя поиск заранее.
$user->like('name', 'Bob%')->delete();
dirty(array $dirty = []): ActiveRecord
Грязные данные относятся к данным, которые были изменены в записи.
$user->greaterThan('id', 0)->orderBy('id desc')->find();
// на данный момент ничего не "грязное".
$user->email = 'test@example.com'; // теперь email считается "грязным", поскольку он был изменен.
$user->update();
// теперь нет грязных данных, так как они были обновлены и сохранены в базе данных
$user->password = password_hash('newpassword'); // теперь это грязное
$user->dirty(); // ничего не передав, вы очистите все грязные записи.
$user->update(); // ничего не обновится, потому что ничего не было захвачено как грязное.
$user->dirty([ 'name' => 'что-то', 'password' => password_hash('другой пароль') ]);
$user->update(); // обновлены как имя, так и пароль.
copyFrom(array $data): ActiveRecord
(v0.4.0)
Это псевдоним для метода dirty()
. Немного более понятно, что вы делаете.
$user->copyFrom([ 'name' => 'что-то', 'password' => password_hash('другой пароль') ]);
$user->update(); // обновлены как имя, так и пароль.
isDirty(): boolean
(v0.4.0)
Возвращает true
, если текущая запись была изменена.
$user->greaterThan('id', 0)->orderBy('id desc')->find();
$user->email = 'test@email.com';
$user->isDirty(); // true
reset(bool $include_query_data = true): ActiveRecord
Сбрасывает текущую запись в ее начальное состояние. Это действительно полезно использовать в поведениях типа цикла.
Если вы передадите true
, он также сбросит данные запроса, которые использовались для поиска текущего объекта (поведение по умолчанию).
$users = $user->greaterThan('id', 0)->orderBy('id desc')->find();
$user_company = new UserCompany($pdo_connection);
foreach($users as $user) {
$user_company->reset(); // начинаем с чистого листа
$user_company->user_id = $user->id;
$user_company->company_id = $some_company_id;
$user_company->insert();
}
getBuiltSql(): string
(v0.4.1)
После выполнения метода find()
, findAll()
, insert()
, update()
или save()
вы можете получить SQL, который был построен, и использовать его для отладки.
Методы SQL Запросов
select(string $field1 [, string $field2 ... ])
Вы можете выбрать только несколько столбцов в таблице, если хотите (это более производительно на действительно широких таблицах с большим количеством столбцов)
$user->select('id', 'name')->find();
from(string $table)
Вы вполне можете выбрать другую таблицу! Почему бы и нет?!
$user->select('id', 'name')->from('user')->find();
join(string $table_name, string $join_condition)
Вы даже можете присоединиться к другой таблице в базе данных.
$user->join('contacts', 'contacts.user_id = users.id')->find();
where(string $where_conditions)
Вы можете установить некоторые пользовательские аргументы where (вы не можете устанавливать параметры в этом условии where)
$user->where('id=1 AND name="demo"')->find();
Примечание по безопасности - Вам может возникнуть желание сделать что-то вроде $user->where("id = '{$id}' AND name = '{$name}'")->find();
. Пожалуйста, НЕ ДЕЛАЙТЕ ЭТОГО!!! Это подвержено тому, что называется атаками SQL-инъекций. В Интернете есть много статей, пожалуйста, поищите "sql injection attacks php", и вы найдёте много статей на эту тему. Правильный способ обработки этого с помощью этой библиотеки вместо этого метода where()
, вы бы сделали что-то более вроде $user->eq('id', $id)->eq('name', $name)->find();
Если вам абсолютно необходимо это делать, библиотека PDO
имеет $pdo->quote($var)
, чтобы экранировать это для вас. Только после того, как вы используете quote()
, вы можете использовать это в операторе where()
.
group(string $group_by_statement)/groupBy(string $group_by_statement)
Группируйте ваши результаты по определенному условию.
$user->select('COUNT(*) as count')->groupBy('name')->findAll();
order(string $order_by_statement)/orderBy(string $order_by_statement)
Сортируйте возвращаемый запрос определённым образом.
$user->orderBy('name DESC')->find();
limit(string $limit)/limit(int $offset, int $limit)
Ограничьте количество возвращаемых записей. Если задано второе целое число, оно будет сдвинуто, ограничение также как в SQL.
$user->orderby('name DESC')->limit(0, 10)->findAll();
Условия WHERE
equal(string $field, mixed $value) / eq(string $field, mixed $value)
Где field = $value
$user->eq('id', 1)->find();
notEqual(string $field, mixed $value) / ne(string $field, mixed $value)
Где field <> $value
$user->ne('id', 1)->find();
isNull(string $field)
Где field IS NULL
$user->isNull('id')->find();
isNotNull(string $field) / notNull(string $field)
Где field IS NOT NULL
$user->isNotNull('id')->find();
greaterThan(string $field, mixed $value) / gt(string $field, mixed $value)
Где field > $value
$user->gt('id', 1)->find();
lessThan(string $field, mixed $value) / lt(string $field, mixed $value)
Где field < $value
$user->lt('id', 1)->find();
greaterThanOrEqual(string $field, mixed $value) / ge(string $field, mixed $value) / gte(string $field, mixed $value)
Где field >= $value
$user->ge('id', 1)->find();
lessThanOrEqual(string $field, mixed $value) / le(string $field, mixed $value) / lte(string $field, mixed $value)
Где field <= $value
$user->le('id', 1)->find();
like(string $field, mixed $value) / notLike(string $field, mixed $value)
Где field LIKE $value
или field NOT LIKE $value
$user->like('name', 'de')->find();
in(string $field, array $values) / notIn(string $field, array $values)
Где field IN($value)
или field NOT IN($value)
$user->in('id', [1, 2])->find();
between(string $field, array $values)
Где field BETWEEN $value AND $value1
$user->between('id', [1, 2])->find();
Условия ИЛИ
Возможно, вы захотите обернуть ваши условия в оператор ИЛИ. Это делается либо с помощью методов startWrap()
и endWrap()
, либо путем заполнения 3-го параметра условия после поля и значения.
// Метод 1
$user->eq('id', 1)->startWrap()->eq('name', 'demo')->or()->eq('name', 'test')->endWrap('OR')->find();
// Это будет эквивалентно `id = 1 AND (name = 'demo' OR name = 'test')`
// Метод 2
$user->eq('id', 1)->eq('name', 'demo', 'OR')->find();
// Это будет эквивалентно `id = 1 OR name = 'demo'`
Отношения
Вы можете установить несколько видов отношений, используя эту библиотеку. Вы можете установить отношения один->много и один->один между таблицами. Это требует небольшой дополнительной настройки в классе заранее.
Установка массива $relations
не сложна, но угадать правильный синтаксис может быть сложно.
protected array $relations = [
// вы можете назвать ключ как угодно. Имя ActiveRecord, вероятно, хорошее. Например: user, contact, client
'user' => [
// обязательно
// self::HAS_MANY, self::HAS_ONE, self::BELONGS_TO
self::HAS_ONE, // это тип отношения
// обязательно
'Some_Class', // это "другой" ActiveRecord, на который это будет ссылаться
// обязательно
// в зависимости от типа отношения
// self::HAS_ONE = внешний ключ, который ссылается на соединение
// self::HAS_MANY = внешний ключ, который ссылается на соединение
// self::BELONGS_TO = локальный ключ, который ссылается на соединение
'local_or_foreign_key',
// просто FYI, это также соединяется только с первичным ключом "другой" модели
// опционально
[ 'eq' => [ 'client_id', 5 ], 'select' => 'COUNT(*) as count', 'limit' 5 ], // дополнительные условия, которые вы хотите при соединении с отношением
// $record->eq('client_id', 5)->select('COUNT(*) as count')->limit(5))
// опционально
'back_reference_name' // это если вы хотите ссылаться на это отношение обратно на себя, например: $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');
}
}
Теперь у нас есть установленные ссылки, так что мы можем использовать их очень легко!
$user = new User($pdo_connection);
// найдите самого последнего пользователя.
$user->notNull('id')->orderBy('id desc')->find();
// получите контакты, используя отношение:
foreach($user->contacts as $contact) {
echo $contact->id;
}
// или мы можем пойти другим путем.
$contact = new Contact();
// найдите один контакт
$contact->find();
// получите пользователя, используя отношение:
echo $contact->user->name; // это имя пользователя
Классно, да?
Установка пользовательских данных
Иногда вам может потребоваться прикрепить что-то уникальное к вашей ActiveRecord, например, пользовательский расчет, который, возможно, будет легче просто прикрепить к объекту, который затем будет передан, скажем, шаблону.
setCustomData(string $field, mixed $value)
Вы прикрепляете пользовательские данные с помощью метода setCustomData()
.
$user->setCustomData('page_view_count', $page_view_count);
А затем вы просто ссылаетесь на это, как на обычное свойство объекта.
echo $user->page_view_count;
События
Одним из супер классных преимуществ этой библиотеки являются события. События срабатывают в определенные моменты на основе определенных методов, которые вы вызываете. Они очень полезны для автоматической настройки данных для вас.
onConstruct(ActiveRecord $ActiveRecord, array &config)
Это действительно полезно, если вам нужно установить подключение по умолчанию или что-то подобное.
// index.php или bootstrap.php
Flight::register('db', 'PDO', [ 'sqlite:test.db' ]);
//
//
//
// User.php
class User extends flight\ActiveRecord {
protected function onConstruct(self $self, array &$config) { // не забывайте о референции &
// вы можете сделать это, чтобы автоматически установить подключение
$config['connection'] = Flight::db();
// или это
$self->transformAndPersistConnection(Flight::db());
// Вы также можете установить имя таблицы таким образом.
$config['table'] = 'users';
}
}
beforeFind(ActiveRecord $ActiveRecord)
Это, вероятно, будет полезно, только если вам нужно манипулировать запросом каждый раз.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeFind(self $self) {
// всегда выполняйте id >= 0, если это ваше желание
$self->gte('id', 0);
}
}
afterFind(ActiveRecord $ActiveRecord)
Эта один, вероятно, более полезна, если вам всегда нужно выполнять какую-либо логику каждый раз, когда эта запись извлекается. Нужно ли вам расшифровать что-то? Вам нужно запустить пользовательский запрос на подсчет каждый раз (невыгодно, но что ж)?
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterFind(self $self) {
// расшифровка чего-то
$self->secret = yourDecryptFunction($self->secret, $some_key);
// возможно, сохранение чего-то пользовательского, как запрос???
$self->setCustomData('view_count', $self->select('COUNT(*) count')->from('user_views')->eq('user_id', $self->id)['count']);
}
}
beforeFindAll(ActiveRecord $ActiveRecord)
Это, вероятно, будет полезно, только если вам нужно манипулировать запросом каждый раз.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeFindAll(self $self) {
// всегда выполняйте id >= 0, если это ваше желание
$self->gte('id', 0);
}
}
afterFindAll(array<int,ActiveRecord> $results)
Похоже на afterFind()
, но вы можете делать это со всеми записями сразу!
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterFindAll(array $results) {
foreach($results as $self) {
// сделайте что-то классное, как и в afterFind()
}
}
}
beforeInsert(ActiveRecord $ActiveRecord)
Действительно полезно, если вам нужно установить некоторые значения по умолчанию каждый раз.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeInsert(self $self) {
// задайте некоторые надежные значения по умолчанию
if(!$self->created_date) {
$self->created_date = gmdate('Y-m-d');
}
if(!$self->password) {
$self->password = password_hash((string) microtime(true));
}
}
}
afterInsert(ActiveRecord $ActiveRecord)
Возможно, у вас есть случай использования для изменения данных послеINSERT?
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterInsert(self $self) {
// делайте что хотите
Flight::cache()->set('most_recent_insert_id', $self->id);
// или что-то еще...
}
}
beforeUpdate(ActiveRecord $ActiveRecord)
Действительно полезно, если вам нужно установить некоторые значения по умолчанию каждый раз при обновлении.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeUpdate(self $self) {
// задайте некоторые надежные значения по умолчанию
if(!$self->updated_date) {
$self->updated_date = gmdate('Y-m-d');
}
}
}
afterUpdate(ActiveRecord $ActiveRecord)
Возможно, у вас есть случай использования для изменения данных после его обновления?
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterUpdate(self $self) {
// делайте что хотите
Flight::cache()->set('most_recently_updated_user_id', $self->id);
// или что-то еще...
}
}
beforeSave(ActiveRecord $ActiveRecord)/afterSave(ActiveRecord $ActiveRecord)
Это полезно, если вы хотите, чтобы события происходили как при вставках, так и при обновлениях. Я сэкономлю вам длинное объяснение, но я уверен, что вы можете догадаться, что это такое.
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)
Не уверен, что вы хотите сделать здесь, но никаких предвзятостей! Дерзайте!
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeDelete(self $self) {
echo 'Он был смелым солдатом... :cry-face:';
}
}
Управление подключением к базе данных
Когда вы используете эту библиотеку, вы можете установить подключение к базе данных несколькими способами. Вы можете установить подключение в конструкторе, вы можете установить его через переменную конфигурации $config['connection']
или вы можете установить его через setDatabaseConnection()
(v0.4.1).
$pdo_connection = new PDO('sqlite:test.db'); // например
$user = new User($pdo_connection);
// или
$user = new User(null, [ 'connection' => $pdo_connection ]);
// или
$user = new User();
$user->setDatabaseConnection($pdo_connection);
Если вы хотите избежать постоянного указания $database_connection
каждый раз, когда вы вызываете активную запись, есть способы обойти это!
// index.php или bootstrap.php
// Установите это как зарегистрированный класс в 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);
}
}
// И теперь, без аргументов не требуется!
$user = new User();
Примечание: Если вы планируете unit-тестирование, делать это может создать некоторые проблемы с unit-тестами, но в целом, потому что вы можете инъектировать ваше подключение с помощью
setDatabaseConnection()
или$config['connection']
, это не слишком плохо.
Если вам нужно обновить подключение к базе данных, например, если вы запускаете долгое CLI-скрипт и вам нужно периодически обновлять подключение, вы можете переустановить соединение с помощью $your_record->setDatabaseConnection($pdo_connection)
.
Участие
Пожалуйста, сделайте это. :D
Настройка
Когда вы участвуете, убедитесь, что вы выполняете команду composer test-coverage
, чтобы поддерживать 100% покрытие тестами (это не истинное покрытие юнит-тестами, больше похоже на интеграционное тестирование).
Также убедитесь, что вы выполняете composer beautify
и composer phpcs
, чтобы исправить любые ошибки линтинга.
Лицензия
MIT
Awesome-plugins/latte
Latte
Latte - это полнофункциональный движок шаблонов, который очень прост в использовании и ближе к синтаксису PHP, чем Twig или Smarty. Также очень легко расширяем и добавляем собственные фильтры и функции.
Установка
Установите с помощью composer.
composer require latte/latte
Основная конфигурация
Есть несколько основных опций конфигурации, с которых можно начать. Вы можете узнать больше о них в Документации по Latte.
use Latte\Engine as LatteEngine;
require 'vendor/autoload.php';
$app = Flight::app();
$app->register('latte', LatteEngine::class, [], function(LatteEngine $latte) use ($app) {
// Здесь Latte будет кэшировать ваши шаблоны для ускорения работы
// Одна из хороших вещей в Latte заключается в том, что он автоматически обновляет ваш
// кэш при внесении изменений в ваши шаблоны!
$latte->setTempDirectory(__DIR__ . '/../cache/');
// Скажите Latte, где будет базовый каталог для ваших представлений.
// $app->get('flight.views.path') устанавливается в файле config.php
// Вы также можете просто сделать что-то вроде `__DIR__ . '/../views/'`
$latte->setLoader(new \Latte\Loaders\FileLoader($app->get('flight.views.path')));
});
Простой пример макета
Вот простой пример файла макета. Этот файл будет использоваться для оформления всех ваших других представлений.
<!-- app/views/layout.latte -->
<!doctype html>
<html lang="en">
<head>
<title>{$title ? $title . ' - '}Мое приложение</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<nav>
<!-- ваши элементы навигации здесь -->
</nav>
</header>
<div id="content">
<!-- Вот где волшебство -->
{block content}{/block}
</div>
<div id="footer">
© Авторские права
</div>
</body>
</html>
И теперь у нас есть ваш файл, который будет отображаться внутри этого блока контента:
<!-- app/views/home.latte -->
<!-- Это говорит Latte, что этот файл "внутри" файла layout.latte -->
{extends layout.latte}
<!-- Это содержимое, которое будет отображаться в макете внутри блока контента -->
{block content}
<h1>Домашняя страница</h1>
<p>Добро пожаловать в мое приложение!</p>
{/block}
Затем, когда вы будете рендерить это внутри вашей функции или контроллера, вы сделаете что-то вроде этого:
// простой маршрут
Flight::route('/', function () {
Flight::latte()->render('home.latte', [
'title' => 'Домашняя страница'
]);
});
// или если вы используете контроллер
Flight::route('/', [HomeController::class, 'index']);
// HomeController.php
class HomeController
{
public function index()
{
Flight::latte()->render('home.latte', [
'title' => 'Домашняя страница'
]);
}
}
Смотрите Документацию по Latte для получения дополнительной информации о том, как использовать Latte в полной мере!
Awesome-plugins/awesome_plugins
Потрясающие Плагины
Flight невероятно расширяемый. Есть множество плагинов, которые могут быть использованы для добавления функциональности в ваше приложение Flight. Некоторые официально поддерживаются командой Flight, а другие являются микро/легкими библиотеками, чтобы помочь вам начать.
Документация API
Документация API имеет решающее значение для любого API. Она помогает разработчикам понять, как взаимодействовать с вашим API и чего ожидать в ответ. Существуют несколько инструментов, доступных для помощи в генерации документации API для ваших проектов Flight.
- FlightPHP OpenAPI Generator - Запись в блоге, написанная Даниэлем Шрайбером о том, как использовать спецификацию OpenAPI с FlightPHP для создания вашего API с использованием подхода "API сначала".
- SwaggerUI - Swagger UI - отличный инструмент для помощи в генерации документации API для ваших проектов Flight. Он очень прост в использовании и может быть настроен под ваши Bedürfnisse. Это PHP-библиотека, которая помогает вам генерировать документацию Swagger.
Аутентификация/Авторизация
Аутентификация и Авторизация имеют решающее значение для любого приложения, которое требует контроля доступа.
- официальный flightphp/permissions - Официальная библиотека разрешений Flight. Эта библиотека является простым способом добавления разрешений на уровне пользователя и приложения в ваше приложение.
Кэширование
Кэширование - отличный способ ускорить ваше приложение. Существует несколько библиотек кэширования, которые могут быть использованы с Flight.
- официальный flightphp/cache - Легкий, простой и автономный класс кэширования PHP в файле
CLI
CLI-приложения - отличный способ взаимодействовать с вашим приложением. Вы можете использовать их для генерации контроллеров, отображения всех маршрутов и многого другого.
- официальный flightphp/runway - Runway - это CLI-приложение, которое помогает вам управлять вашими приложениями Flight.
Куки
Куки - отличный способ хранения небольших данных на стороне клиента. Их можно использовать для хранения пользовательских предпочтений, настроек приложения и многого другого.
- overclokk/cookie - PHP Cookie - это библиотека PHP, которая предоставляет простой и эффективный способ управления куками.
Отладка
Отладка имеет решающее значение при разработке в вашей локальной среде. Существует несколько плагинов, которые могут улучшить ваш опыт отладки.
- tracy/tracy - Это полнофункциональный обработчик ошибок, который может использоваться с Flight. У него есть несколько панелей, которые могут помочь вам отлаживать ваше приложение. Также очень легко расширять и добавлять свои собственные панели.
- flightphp/tracy-extensions - Используется с обработчиком ошибок Tracy, этот плагин добавляет несколько дополнительных панелей для помощи с отладкой, специально для проектов Flight.
Базы данных
Базы данных являются основой большинства приложений. Это то, как вы храните и извлекаете данные. Некоторые библиотека баз данных просто обертки для написания запросов, а некоторые являются полноценными ORM.
- официальный flightphp/core PdoWrapper - Официальная обертка Flight PDO, которая является частью ядра. Это простая обертка, чтобы упростить процесс написания запросов и их выполнения. Это не ORM.
- официальный flightphp/active-record - Официальный ORM/Mapper Flight ActiveRecord. Отличная маленькая библиотека для легкого извлечения и хранения данных в вашей базе данных.
- byjg/php-migration - Плагин для отслеживания всех изменений базы данных для вашего проекта.
Шифрование
Шифрование имеет решающее значение для любого приложения, которое хранит конфиденциальные данные. Шифрование и дешифрование данных не так уж сложно, но правильное хранение ключа шифрования может быть сложным. Самое важное - никогда не хранить ваш ключ шифрования в публичном каталоге или не коммитить его в ваше репозиторий кода.
- defuse/php-encryption - Это библиотека, которая может быть использована для шифрования и дешифрования данных. Запуск и работа с ней довольно просты для начала шифрования и дешифрования данных.
Очередь заданий
Очереди заданий очень полезны для асинхронной обработки задач. Это может быть отправка электронных писем, обработка изображений или что угодно, что не требует выполнения в реальном времени.
- n0nag0n/simple-job-queue - Простая очередь заданий - это библиотека, которая может быть использована для обработки заданий асинхронно. Она может быть использована с beanstalkd, MySQL/MariaDB, SQLite и PostgreSQL.
Сессия
Сессии не очень полезны для API, но для создания веб-приложения сессии могут быть решающими для поддержания состояния и информации о входе.
- официальный flightphp/session - Официальная библиотека сессий Flight. Это простая библиотека сессий, которая может использоваться для хранения и извлечения данных сессии. Она использует встроенную обработку сессий PHP.
- Ghostff/Session - Менеджер сессий PHP (некоммутируемый, флеш, сегмент, шифрование сессий). Использует PHP open_ssl для опционального шифрования/дешифрования данных сессии.
Шаблонизация
Шаблонизация является основой любого веб-приложения с пользовательским интерфейсом. Существует несколько движков шаблонизации, которые могут быть использованы с Flight.
- устаревшая flightphp/core View - Это очень базовый движок шаблонизации, который является частью ядра. Его не рекомендуется использовать, если у вас больше нескольких страниц в проекте.
- latte/latte - Latte - это полнофункциональный движок шаблонизации, который очень прост в использовании и выглядит ближе к синтаксису PHP, чем Twig или Smarty. Его также очень легко расширять и добавлять свои собственные фильтры и функции.
Участие
Есть плагин, которым вы хотели бы поделиться? Отправьте запрос на внесение изменений, чтобы добавить его в список!
Media
Медиа
Мы постарались собрать все, что можем, о различных типах медиа в интернете, связанных с Flight. Ниже приведены различные ресурсы, которые вы можете использовать, чтобы узнать больше о Flight.
Статьи и материалы
- Определение, создание и внедрение: API-ориентированный подход с OpenAPI Generator и FlightPHP от Даниэль Шрайбер (2025)
- Лучшие микрофреймворки PHP на 2024 год от n0nag0n (2024)
- Создание RESTful API с помощью Flight Framework от n0nag0n (2024)
- Создание простого блога с помощью Flight Часть 2 от n0nag0n (2024)
- Создание простого блога с помощью Flight Часть 1 от n0nag0n (2024)
- 🚀 Создание простого CRUD API на PHP с помощью Flight Framework от soheil-khaledabadi (2024)
- Создание веб-приложения на PHP с помощью Flight Micro-framework от Артур С. Кодекс (2023)
- Лучшие фреймворки PHP для веб-разработки в 2024 году от Равикиран А С (2023)
- Топ 12 фреймворков PHP: Всеобъемлющее руководство на 2023 год от marketing kbk (2023)
- 5 фреймворков PHP, о которых вы, возможно, не слышали от n0nag0n (2022)
- 12 лучших фреймворков PHP для веб-разработчиков, которые стоит рассмотреть в 2023 году от Анны Монус (2022)
- Лучшие микрофреймворки PHP на облачном сервере от Шахзеб Ахмед (2021)
- PHP фреймворк: Топ 15 мощных для вашей веб-разработки от AHT Tech (2020)
- Легкая маршрутизация на PHP с помощью FlightPHP от Лукас Консейсао (2019)
- Тестирование нового PHP фреймворка (Flight) от Леона (2017)
- Настройка FlightPHP для работы с Backbonejs от Тимоти Токки (2015)
Видеоролики и руководства
- Создание REST API для IoT-устройств с использованием PHP и FlightPHP - ESP32 API от IoT Craft Hub (2024)
- Простой вводный видеоролик о PHP Flight Framework от n0nag0n (2024)
- Установка заголовка HTTP-кода в Flightphp (3 решения!!) от Роэль Ван де Пара (2024)
- Учебник по PHP Flight Framework. Суперлегкий API проект! от n0nag0n (2022)
- Веб-приложение CRUD с помощью PHP и MySQL и Bootstrap, используя Flight от Devlopteca - Оскар Ух (2021)
- DevOps и системные администраторы: Правило переписывания Lighttpd для микрофреймворка Flight PHP от Роэль Ван де Пара (2021)
- Учебник REST API Flight PHP #ЧАСТЬ2 ВСТАВКА ТАБЛИЦЫ Инфо #Код (Тагалог) от Info Singkat Official (2020)
- Учебник REST API Flight PHP #ЧАСТЬ1 Инфо #Код (Тагалог) от Info Singkat Official (2020)
- Как создать JSON REST API на PHP - Часть 2 от Codewife (2018)
- Как создать JSON REST API на PHP - Часть 1 от Codewife (2018)
- Тестирование микрофреймворков PHP - Flight PHP, Lumen, Slim 3 и Laravel от Codemarket (2016)
- Учебник 1 Flight PHP - Установка от absagg (2014)
- Учебник 2 Flight PHP - Маршрут часть 1 от absagg (2014)
Examples
Нужен быстрый старт?
У вас есть два варианта, чтобы начать новый проект на Flight:
- Полный скелетный каркас: более полный пример с контроллерами и представлениями.
- Скелетный каркас в одном файле: один файл, который включает все, что нужно для запуска вашего приложения в одном простом файле.
Примеры, предложенные сообществом:
- flightravel: FlightPHP с директориями Laravel, с инструментами PHP + GH Actions
- fleact - Стартовый комплект FlightPHP с интеграцией ReactJS.
- flastro - Стартовый комплект FlightPHP с интеграцией Astro.
- velt - Velt - это быстрый и простой шаблон Svelte с бэкендом на FlightPHP.
Нужна немного вдохновения?
Хотя эти проекты не официально спонсированы командой Flight, они могут дать вам идеи о том, как структурировать собственные проекты, построенные с использованием Flight!
- Decay - Flight v3 с HTMX и SleekDB, все о зомби! (Демо)
- Пример блога на Flight - Flight v3 с Middleware, контроллерами, Active Record и Latte.
- Flight CRUD RESTful API - Простой проект CRUD API с использованием фреймворка Flight, который предоставляет базовую структуру для новых пользователей, чтобы быстро настроить PHP-приложение с операциями CRUD и подключением к базе данных. Проект демонстрирует, как использовать Flight для разработки RESTful API, что делает его идеальным учебным инструментом для начинающих и полезным стартовым комплектом для более опытных разработчиков.
- Система управления школой на Flight - Flight v3
- Paste Bin с комментариями - Flight v3
- Базовое приложение Skeleton
- Пример Wiki
- Приложение PHP фреймворка IT-Innovator
- LittleEducationalCMS (испанский)
- API итальянских жёлтых страниц
- Универсальная система управления контентом (с....очень мало документации)
- Маленький php фреймворк на основе Flight и medoo.
- Пример MVC приложения
Хотите поделиться своим примером?
Если у вас есть проект, которым вы хотите поделиться, пожалуйста, отправьте запрос на слияние, чтобы добавить его в этот список!
Install/install
Установка
Загрузка файлов
Убедитесь, что у вас установлен PHP на вашей системе. Если нет, нажмите здесь для получения инструкций по установке на вашей системе.
Если вы используете Composer, вы можете выполнить следующую команду:
composer require flightphp/core
ИЛИ вы можете скачать файлы напрямую и извлечь их в ваш каталог веб-сайта.
Настройка вашего веб-сервера
Встроенный веб-сервер PHP
Это, безусловно, самый простой способ запустить приложение и использовать даже SQLite для базы данных. Просто выполните следующую команду после установки PHP:
php -S localhost:8000
Затем откройте свой браузер и перейдите по адресу http://localhost:8000
.
Если вы хотите изменить корневой каталог вашего проекта на другой каталог (например, ваш проект - ~/myproject
, а корневой каталог -~/myproject/public/
), вы можете выполнить следующую команду после того, как вы находитесь в каталоге ~/myproject
:
php -S localhost:8000 -t public/
Затем откройте свой браузер и перейдите по адресу http://localhost:8000
.
Apache
Убедитесь, что Apache уже установлен на вашей системе. Если нет, найдите, как установить Apache на вашей системе через поиск в Google.
Для Apache измените ваш файл .htaccess
следующим образом:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
Примечание: Если вам нужно использовать flight в подкаталоге, добавьте строку
RewriteBase /subdir/
сразу послеRewriteEngine On
.Примечание: Если вы хотите защитить все файлы сервера, такие как файл db или env. Поместите это в ваш файл
.htaccess
:
RewriteEngine On
RewriteRule ^(.*)$ index.php
Nginx
Убедитесь, что Nginx уже установлен на вашей системе. Если нет, найдите, как установить Nginx на вашей системе через поиск в Google.
Для Nginx добавьте следующее в ваше объявление сервера:
server {
location / {
try_files $uri $uri/ /index.php;
}
}
Создание вашего файла index.php
<?php
// Если вы используете Composer, подключите автозагрузчик.
require 'vendor/autoload.php';
// если вы не используете Composer, загрузите фреймворк непосредственно
// require 'flight/Flight.php';
// Затем определите маршрут и назначьте функцию для обработки запроса.
Flight::route('/', function () {
echo 'hello world!';
});
// Наконец, запустите фреймворк.
Flight::start();
Установка PHP
Если у вас уже установлен php
на вашей системе, переходите к разделу загрузки файлов.
Успехов! Вот инструкции по установке PHP на macOS, Windows 10/11, Ubuntu и Rocky Linux. Я также предоставлю информацию о том, как установить различные версии PHP.
macOS
Установка PHP с помощью Homebrew
-
Установите Homebrew (если его нет):
- Откройте терминал и выполните:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
- Откройте терминал и выполните:
-
Установите PHP:
- Установите последнюю версию:
brew install php
- Чтобы установить конкретную версию, например, PHP 8.1:
brew tap shivammathur/php brew install shivammathur/php/php@8.1
- Установите последнюю версию:
-
Переключение между версиями PHP:
- Отсоедините текущую версию и привяжите желаемую версию:
brew unlink php brew link --overwrite --force php@8.1
- Проверьте установленную версию:
php -v
- Отсоедините текущую версию и привяжите желаемую версию:
Windows 10/11
Установка PHP вручную
-
Скачайте PHP:
- Перейдите на PHP для Windows и загрузите последнюю или конкретную версию (например, 7.4, 8.0) как zip-архив без поддержки потоков.
-
Разархивируйте PHP:
- Разархивируйте загруженный zip-файл в
C:\php
.
- Разархивируйте загруженный zip-файл в
-
Добавьте PHP в системный путь:
- Перейдите в Свойства системы > Переменные среды.
- В разделе Системные переменные найдите Path и нажмите Изменить.
- Добавьте путь
C:\php
(или куда бы вы не разархивировали PHP). - Нажмите OK, чтобы закрыть все окна.
-
Настройте PHP:
- Скопируйте
php.ini-development
вphp.ini
. - Отредактируйте
php.ini
для настройки PHP по необходимости (например, установкаextension_dir
, включение расширений).
- Скопируйте
-
Проверьте установку PHP:
- Откройте командную строку и выполните:
php -v
- Откройте командную строку и выполните:
Установка нескольких версий PHP
-
Повторите вышеперечисленные шаги для каждой версии, помещая каждую в отдельный каталог (например,
C:\php7
,C:\php8
). -
Переключайтесь между версиями, изменяя системную переменную PATH, указывающую на каталог нужной версии.
Ubuntu (20.04, 22.04 и др.)
Установка PHP с помощью apt
-
Обновите списки пакетов:
- Откройте терминал и выполните:
sudo apt update
- Откройте терминал и выполните:
-
Установите PHP:
- Установите последнюю версию PHP:
sudo apt install php
- Чтобы установить конкретную версию, например, PHP 8.1:
sudo apt install php8.1
- Установите последнюю версию PHP:
-
Установите дополнительные модули (по желанию):
- Например, для установки поддержки MySQL:
sudo apt install php8.1-mysql
- Например, для установки поддержки MySQL:
-
Переключение между версиями PHP:
- Используйте
update-alternatives
:sudo update-alternatives --set php /usr/bin/php8.1
- Используйте
-
Проверьте установленную версию:
- Выполните:
php -v
- Выполните:
Rocky Linux
Установка PHP с помощью yum/dnf
-
Включите репозиторий EPEL:
- Откройте терминал и выполните:
sudo dnf install epel-release
- Откройте терминал и выполните:
-
Установите репозиторий Remi:
- Выполните:
sudo dnf install https://rpms.remirepo.net/enterprise/remi-release-8.rpm sudo dnf module reset php
- Выполните:
-
Установите PHP:
- Чтобы установить версию по умолчанию:
sudo dnf install php
- Чтобы установить конкретную версию, например, PHP 7.4:
sudo dnf module install php:remi-7.4
- Чтобы установить версию по умолчанию:
-
Переключение между версиями PHP:
- Используйте команду
dnf module
:sudo dnf module reset php sudo dnf module enable php:remi-8.0 sudo dnf install php
- Используйте команду
-
Проверьте установленную версию:
- Выполните:
php -v
- Выполните:
Общие заметки
- Для сред разработки важно настроить параметры PHP в соответствии с требованиями вашего проекта.
- При переключении версий PHP убедитесь, что все необходимые расширения PHP установлены для конкретной версии, которую вы собираетесь использовать.
- Перезапустите ваш веб-сервер (Apache, Nginx и т. д.) после смены версий PHP или обновления конфигураций, чтобы применить изменения.
Guides
Гиды
Flight PHP разработан, чтобы быть простым, но мощным, а наши гайды помогут вам шаг за шагом создавать реальные приложения. Эти практические учебники проведут вас через полные проекты, чтобы продемонстрировать, как можно эффективно использовать Flight.
Официальные Гайды
Создание блога
Узнайте, как создать функциональное блог-приложение с помощью Flight PHP. Этот гайд проведет вас через:
- Настройка структуры проекта
- Работа с шаблонами с использованием Latte
- Реализация маршрутов для постов
- Хранение и получение данных
- Обработка отправок форм
- Основная обработка ошибок
Этот учебник идеально подходит для начинающих, которые хотят увидеть, как все элементы сочетаются в реальном приложении.
Неофициальные Гайды
Хотя эти гайды не поддерживаются официально командой Flight, они являются ценными ресурсами, созданными сообществом. Они охватывают различные темы и сценарии использования, предоставляя дополнительные сведения о использовании Flight PHP.
Создание RESTful API с помощью Flight Framework
Этот гайд проводит вас через создание RESTful API с использованием фреймворка Flight PHP. Он охватывает основы настройки API, определения маршрутов и возврата JSON-ответов.
Создание простого блога
Этот гайд проводит вас через создание базового блога с использованием фреймворка Flight PHP. Он фактически состоит из 2 частей: одна охватывает основы, а другая более продвинутые темы и доработки для блога, готового к производству.
- Создание простого блога с помощью Flight - Часть 1 - Начало работы с простым блогом.
- Создание простого блога с помощью Flight - Часть 2 - Доработка блога для производства.
Создание Pokémon API на PHP: Руководство для начинающих
Этот интересный гайд проводит вас через создание простого Pokémon API с использованием Flight PHP. Он охватывает основы настройки API, определения маршрутов и возврата JSON-ответов.
Вклад
Есть идея для гайда? Нашли ошибку? Мы приветствуем вклад! Наши гайды поддерживаются в репозитории документации FlightPHP.
Если вы создали что-то интересное с помощью Flight и хотите поделиться этим в качестве гайда, пожалуйста, отправьте запрос на внесение изменений. Деление своими знаниями способствует росту сообщества Flight.
Ищете документацию по API?
Если вы ищете специфическую информацию о ключевых функциях и методах Flight, ознакомьтесь с разделом "Обучение" нашей документации.