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
플라이트 vs 라라벨
라라벨이란?
라라벨은 모든 기능과 놀라운 개발자 중심 생태계를 갖춘 풀 기능의 프레임워크이지만, 성능 및 복잡성 면에서 비용이 발생합니다. 라라벨의 목표는 개발자가 최고 수준의 생산성을 가지고 일반적인 작업을 쉽게 만들 수 있게 하는 것입니다. 라라벨은 풀 기능의 기업 웹 응용 프로그램을 구축하려는 개발자에게 좋은 선택지입니다. 이는 성능 및 복잡성 측면에서 어떤 희생을 감수해야 한다는 것을 의미합니다. 라라벨의 초보부터 학습하는 것은 쉬울 수 있지만, 프레임워크에 대한 능숙함을 키우는 데는 시간이 걸릴 수 있습니다.
또한 라라벨 모듈이 매우 많기 때문에 개발자들은 종종 문제를 해결하는 유일한 방법이 이러한 모듈을 통해인 것처럼 느낄 때가 있습니다. 실제로 다른 라이브러리를 사용하거나 직접 코드를 작성할 수 있습니다.
플라이트와 비교한 장점
- 라라벨은 일반적인 문제를 해결하는 데 사용할 수 있는 개발자 및 모듈의 거대한 생태계를 갖추고 있습니다.
- 라라벨은 데이터베이스와 상호 작용할 수 있는 풀 기능의 ORM을 갖추고 있습니다.
- 라라벨은 프레임워크를 배우는 데 사용할 수 있는 엄청난 양의 문서 및 자습서를 보유하고 있습니다.
- 라라벨은 응용 프로그램을 보호하는 데 사용할 수 있는 내장된 인증 시스템을 갖추고 있습니다.
- 라라벨은 프레임워크를 학습하는 데 사용할 수 있는 팟캐스트, 컨퍼런스, 미팅, 비디오 및 기타 자원을 갖추고 있습니다.
- 라라벨은 풀 기능의 기업 웹 애플리케이션을 구축하려는 숙련된 개발자를 대상으로 합니다.
플라이트와 비교한 단점
- 라라벨은 플라이트보다 하부에서 무언가 더 많이 실행됩니다. 이는 성능 면에서 극적인 비용이 발생합니다. 자세한 내용은 TechEmpower 벤치마크를 참조하십시오.
- 플라이트는 가벼우며 빠르고 사용하기 쉬운 웹 애플리케이션을 구축하려는 개발자를 대상으로 합니다.
- 플라이트는 단순함과 사용의 용이성을 목표로 합니다.
- 플라이트의 핵심 기능 중 하나는 역호환성을 유지하려 노력한다는 것입니다. 라라벨은 주요 버전 간에 많은 다툼을 야기시킵니다.
- 플라이트는 프레임워크의 세계로 처음 진입하는 개발자를 대상으로 합니다.
- 플라이트는 의존성이 없으며, 반면에 라라벨은 지독한 수준의 의존성을 갖고 있습니다.
- 플라이트는 기업 수준의 응용 프로그램도 수행할 수 있지만, 라라벨만큼의 보일러플레이트 코드가 없습니다. 그러나 개발자가 더 많은 정돈 및 잘 구조화된 코드를 유지하기 위해 더 많은 노력을 기울여야 합니다.
- 플라이트는 개발자에게 응용 프로그램에 대한 더 많은 제어권을 부여하고, 반면 라라벨은 뒷면에 숨겨진 많은 마법을 제공하여 답답할 수 있습니다.
Learn/migrating_to_v3
v3로 이전하기
대부분의 경우 하위 호환성이 유지되었지만, v2에서 v3로 이전할 때 알아야 할 몇 가지 변경 사항이 있습니다.
출력 버퍼링 동작 (3.5.0)
출력 버퍼링은 PHP 스크립트에서 생성된 출력물이 클라이언트로 전송되기 전에 PHP 내부 버퍼에 저장되는 프로세스입니다. 이를 통해 클라이언트로 전송되기 전에 출력물을 수정할 수 있습니다.
MVC 애플리케이션에서 컨트롤러는 "매니저"이며 뷰의 동작을 관리합니다. 컨트롤러 외부에서 출력물이 생성되는 것(또는 Flight의 경우 때로는 익명 함수 내에)는 MVC 패턴을 깨뜨립니다. 이 변경은 MVC 패턴에 더 일치하도록 하고 프레임워크를 더 예측 가능하고 사용하기 쉽도록 만드는 것입니다.
v2에서 출력 버퍼링은 자체 출력 버퍼를 일관되게 닫지 않는 방식으로 처리되었으며, 이는 단위 테스트와 스트리밍을 더 어렵게 만들었습니다. 대부분의 사용자에게는 이 변경이 실제로 영향을 미치지 않을 수 있습니다. 그러나 콜러블 및 컨트롤러 외부에서 콘텐츠를 에코하는 경우(예: 훅 내에서) 문제가 발생할 수 있습니다. 훅에서 콘텐츠를 에코하거나 프레임워크가 실제로 실행되기 전에 콘텐츠를 에코하는 것이 과거에는 작동했을 수 있지만 앞으로는 작동하지 않을 것입니다.
문제가 발생할 수 있는 곳
// index.php
require 'vendor/autoload.php';
// 예시일 뿐
define('START_TIME', microtime(true));
function hello() {
echo 'Hello World';
}
Flight::map('hello', 'hello');
Flight::after('hello', function(){
// 실제로 이것은 문제가 없을 것입니다
echo '<p>This Hello World phrase was brought to you by the letter "H"</p>';
});
Flight::before('start', function(){
// 이러한 것들은 오류를 일으킬 것입니다
echo '<html><head><title>My Page</title></head><body>';
});
Flight::route('/', function(){
// 실제로 이것은 괜찮습니다
echo 'Hello World';
// 이것도 아주 괜찮을 것입니다
Flight::hello();
});
Flight::after('start', function(){
// 이것은 오류를 일으킬 것입니다
echo '<div>Your page loaded in '.(microtime(true) - START_TIME).' seconds</div></body></html>';
});
v2 렌더링 동작 활성화
v3로 만들어진 코드를 다시 작성하지 않고 이전 방식을 계속 사용할 수 있을까요? 네, 가능합니다! flight.v2.output_buffering
구성 옵션을 true
로 설정하여 v2 렌더링 동작을 활성화할 수 있습니다. 이를 통해 이전 렌더링 동작을 계속 사용할 수 있지만 앞으로 수정하는 것이 좋습니다. 프레임워크의 v4에서는 이것이 제거될 것입니다.
// index.php
require 'vendor/autoload.php';
Flight::set('flight.v2.output_buffering', true);
Flight::before('start', function(){
// 이제 이것은 아주 잘 작동할 것입니다
echo '<html><head><title>My Page</title></head><body>';
});
// 더 많은 코드
Dispatcher 변경 사항 (3.7.0)
Dispatcher::invokeMethod()
, Dispatcher::execute()
등과 같은 Dispatcher
의 정적 메소드를 직접 호출했다면 코드를 업데이트하여 이러한 메소드를 직접 호출하지 않아야 합니다. Dispatcher
는 보다 객체 지향적으로 변환되어 의존성 주입 컨테이너를 보다 쉽게 사용할 수 있도록 변경되었습니다. 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을 통해 설정 값을 설정함으로써 Flight의 특정 동작을 사용자 정의할 수 있습니다.
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입니다.
수동 추가
Flight\Response
객체의 header
메서드를 사용하여 이러한 헤더를 수동으로 추가할 수 있습니다.
// 클릭재킹을 방지하기 위해 X-Frame-Options 헤더 설정
Flight::response()->header('X-Frame-Options', 'SAMEORIGIN');
// XSS를 방지하기 위해 Content-Security-Policy 헤더 설정
// 참고: 이 헤더는 매우 복잡해질 수 있으므로
// 귀하의 애플리케이션을 위한 인터넷의 예제를 참조하세요.
Flight::response()->header("Content-Security-Policy", "default-src 'self'");
// XSS를 방지하기 위해 X-XSS-Protection 헤더 설정
Flight::response()->header('X-XSS-Protection', '1; mode=block');
// MIME 스니핑을 방지하기 위해 X-Content-Type-Options 헤더 설정
Flight::response()->header('X-Content-Type-Options', 'nosniff');
// 리퍼러 정보가 얼마나 많이 전송되는지 제어하기 위해 Referrer-Policy 헤더 설정
Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');
// HTTPS를 강제하기 위해 Strict-Transport-Security 헤더 설정
Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
// 어떤 기능과 API를 사용할 수 있는지 제어하기 위해 Permissions-Policy 헤더 설정
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 사용하기
Latte 템플릿에서 CSRF 토큰을 출력하는 사용자 정의 함수를 설정할 수도 있습니다.
// 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
객체에서 prepared statements를 사용하면 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();
// 이는 start가 실행되기 전에 실행되어야 합니다.
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['input']와 $_POST['email']이 있는 $_POST 요청을 가정합니다.
// 문자열 입력 정리
$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 'hello world!';
});
경로는 정의된 순서대로 일치합니다. 요청과 일치하는 첫 번째 경로가 호출됩니다.
콜백/함수
콜백은 호출 가능한 모든 객체일 수 있습니다. 그래서 일반 함수를 사용할 수 있습니다:
function hello() {
echo 'hello world!';
}
Flight::route('/', 'hello');
클래스
클래스의 정적 메서드를 사용할 수도 있습니다:
class Greeting {
public static function hello() {
echo 'hello world!';
}
}
Flight::route('/', [ 'Greeting','hello' ]);
먼저 객체를 생성하고 메서드를 호출할 수도 있습니다:
// Greeting.php
class Greeting
{
public function __construct() {
$this->name = 'John Doe';
}
public function hello() {
echo "Hello, {$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 "Hello, world! My name is {$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 'hello world!';
});
// GET 요청
$router->get('/users', function() {
echo 'users';
});
// $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와는 일치하지 않습니다
});
참고: 포지셔널 매개변수와 함께 정규 표현식 그룹
()
는 지원되지 않습니다. :'(
중요한 경고
위의 예제에서 @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 'user:'.$id; }, false, 'user_view');
// 코드의 나중에
Flight::getUrl('user_view', [ 'id' => 5 ]); // '/users/5'를 반환합니다
URL이 변경될 경우 특히 유용합니다. 위의 예제에서 사용자가 /admin/users/@id
로 이동되었다고 가정해 봅시다.
별칭이 설정되어 있으면 별칭이 /admin/users/5
를 반환하기 때문에 별칭을 참조하는 모든 곳에서 변경할 필요가 없습니다.
경로 별칭은 그룹에서 여전히 작동합니다:
Flight::group('/users', function() {
Flight::route('/@id', function($id) { echo 'user:'.$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
어떤 경로를 생성할지 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
플라이트 대 시미포니
시미포니란 무엇인가요?
Symfony은 웹 프로젝트용 PHP 프레임워크 및 재사용 가능한 PHP 구성 요소 세트입니다.
최고의 PHP 응용프로그램이 구축되는 표준 기반입니다. 50 개의 독립형 구성 요소 중에서 필요한 애플리케이션에 사용할 수 있습니다.
PHP 웹 애플리케이션의 생성 및 유지 관리 속도를 높이세요. 반복되는 코딩 작업을 줄이고 코드를 제어하는 강력함을 누려보세요.
플라이트와 비교한 장점
- 시미포니는 공통 문제를 해결하는 데 사용할 수 있는 개발자 및 모듈의 거대한 생태계를 보유하고 있습니다.
- 시미포니에는 데이터베이스와 상호 작용할 수 있는 완전한 기능의 ORM(Doctrine)이 있습니다.
- 시미포니에는 프레임워크 학습에 사용할 수 있는 방대한 양의 문서 및 자습서가 있습니다.
- 시미포니에는 프레임워크 학습에 사용할 수 있는 팟캐스트, 컨퍼런스, 미팅, 비디오 및 기타 리소스가 있습니다.
- 시미포니는 풀 기능을 갖춘 엔터프라이즈 웹 애플리케이션을 구축하려는 경험이 풍부한 개발자를 위해 고안되었습니다.
플라이트와 비교한 단점
- 시미포니는 플라이트보다 하위 수준에서 수행되는 작업이 훨씬 많습니다. 이는 성능 측면에서 극적인 비용이 발생합니다. 자세한 내용은 TechEmpower benchmarks를 참조하세요.
- 플라이트는 가볍고 빠르며 사용하기 쉬운 웹 애플리케이션을 구축하려는 개발자를 대상으로 합니다.
- 플라이트는 단순함과 사용 편의성을 지향합니다.
- 플라이트의 핵심 기능 중 하나는 되도록 역 호환성을 유지하려고 합니다.
- 플라이트에는 의존성이 없지만, 시미포니에는 많은 의존성이 있습니다.
- 플라이트는 처음 프레임워크의 세계로 진입하는 개발자들을 대상으로 합니다.
- 플라이트는 엔터프라이즈 수준의 애플리케이션도 수행할 수 있지만, 시미포니만큼 많은 예제와 자습서가 제공되지 않습니다. 또한, 개발자가 조직화하고 잘 구조화하는 데 더 많은 dis선이 필요합니다.
- 플라이트는 개발자에게 애플리케이션에 대한 더 많은 제어권을 제공하는 반면, 시미포니는 종종 백그라운드에서 어떤 마법을 실행할 수도 있습니다.
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 인터페이스를 구현하는 어떤 컨테이너든 사용할 수 있다는 것을 의미합니다. 다음은 League의 PSR-11 컨테이너를 사용하는 예제입니다:
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이 아닌 사용자 지정 컨테이너를 사용하려는 경우 유용합니다. 기본 예제에서 이를 수행하는 방법을 확인하세요.
추가로, 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()
를 사용하여 사용자를 로그인 페이지로 리디렉션할 수 있습니다.- 미들웨어에서 사용자 정의 오류를 생성하고 라우트 실행을 중지할 수 있습니다.
기본 예제
다음은 간단한 return false; 예제입니다:
class MyMiddleware {
public function before($params) {
if (isset($_SESSION['user']) === false) {
return false;
}
// true이므로 계속 진행됩니다.
}
}
리디렉션 예제
사용자를 로그인 페이지로 리디렉션하는 예제입니다:
class MyMiddleware {
public function before($params) {
if (isset($_SESSION['user']) === false) {
Flight::redirect('/login');
exit;
}
}
}
사용자 정의 오류 예제
API를 구축하기 때문에 JSON 오류를 throw해야 한다고 가정해봅시다. 이렇게 할 수 있습니다:
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 "안녕, $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 .= " 좋은 하루 보내세요!";
return true;
});
// 사용자 정의 메서드 호출
echo Flight::hello('Bob');
다음이 표시되어야 합니다:
안녕, Fred! 좋은 하루 보내세요!
여러 필터를 정의했는데 체인을 중단하려면 필터 함수 중에 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 - POST 데이터 또는 JSON 데이터
- cookies - 쿠키 데이터
- files - 업로드된 파일
- secure - 연결이 안전한지 여부
- accept - HTTP 수락 매개변수
- proxy_ip - 클라이언트의 프록시 IP 주소.
HTTP_CLIENT_IP
,HTTP_X_FORWARDED_FOR
,HTTP_X_FORWARDED
,HTTP_X_CLUSTER_CLIENT_IP
,HTTP_FORWARDED_FOR
,HTTP_FORWARDED
에 대해$_SERVER
배열을 스캔합니다. - host - 요청 호스트 이름
query
, data
, cookies
, 및 files
속성을 배열 또는 객체로 접근할 수 있습니다.
쿼리 문자열 매개변수를 가져오려면 다음과 같이 하면 됩니다:
$id = Flight::request()->query['id'];
또는 다음과 같이 할 수 있습니다:
$id = Flight::request()->query->id;
원시 요청 본문
예를 들어 PUT 요청을 처리할 때 원시 HTTP 요청 본문을 가져오려면 다음과 같이 하면 됩니다:
$body = Flight::request()->getBody();
JSON 입력
application/json
유형과 데이터 {"id": 123}
로 요청을 전송하면 data
속성에서 이용 가능합니다:
$id = Flight::request()->data->id;
$_GET
query
속성을 통해 $_GET
배열에 접근할 수 있습니다:
$id = Flight::request()->query['id'];
$_POST
data
속성을 통해 $_POST
배열에 접근할 수 있습니다:
$id = Flight::request()->data['id'];
$_COOKIE
cookies
속성을 통해 $_COOKIE
배열에 접근할 수 있습니다:
$myCookieValue = Flight::request()->cookies['myCookieName'];
$_SERVER
getVar()
메서드를 통해 $_SERVER
배열에 접근하는 단축키가 있습니다:
$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()
메서드를 사용하여 요청 헤더에 접근할 수 있습니다:
// Authorization 헤더가 필요할 수 있습니다
$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
getFullUrl()
메서드를 사용하여 전체 요청 URL에 접근할 수 있습니다:
$url = Flight::request()->getFullUrl();
// https://example.com/some/path?foo=bar
기본 URL
getBaseUrl()
메서드를 사용하여 기본 URL에 접근할 수 있습니다:
$url = Flight::request()->getBaseUrl();
// 주의: 슬래시가 없습니다.
// https://example.com
쿼리 파싱
parseQuery()
메서드에 URL을 전달하여 쿼리 문자열을 연관 배열로 파싱할 수 있습니다:
$query = Flight::request()->parseQuery('https://example.com/some/path?foo=bar');
// ['foo' => 'bar']
Learn/frameworkmethods
# 프레임워크 메소드
Flight은 사용하기 쉽고 이해하기 쉽게 설계되었습니다. 다음은 프레임워크의 완전한
메소드 세트입니다. 이는 일반적인 정적 메소드인 코어 메소드와 필터링하거나 재정의할 수 있는
매핑된 메소드인 확장 가능한 메소드로 구성되어 있습니다.
## 코어 메소드
```php
Flight::map(string $name, callable $callback, bool $pass_route = false) // 사용자 정의 프레임워크 메소드를 생성합니다.
Flight::register(string $name, string $class, array $params = [], ?callable $callback = null) // 클래스를 프레임워크 메소드에 등록합니다.
Flight::before(string $name, callable $callback) // 프레임워크 메소드 앞에 필터를 추가합니다.
Flight::after(string $name, callable $callback) // 프레임워크 메소드 뒤에 필터를 추가합니다.
Flight::path(string $path) // 클래스의 자동로딩을 위한 경로를 추가합니다.
Flight::get(string $key) // 변수를 가져옵니다.
Flight::set(string $key, mixed $value) // 변수를 설정합니다.
Flight::has(string $key) // 변수가 설정되어 있는지 확인합니다.
Flight::clear(array|string $key = []) // 변수를 지웁니다.
Flight::init() // 프레임워크를 기본 설정으로 초기화합니다.
Flight::app() // 어플리케이션 객체 인스턴스를 가져옵니다.
확장 가능한 메소드
Flight::start() // 프레임워크를 시작합니다.
Flight::stop() // 프레임워크를 중지하고 응답을 보냅니다.
Flight::halt(int $code = 200, string $message = '') // 선택적 상태 코드와 메시지로 프레임워크를 중지합니다.
Flight::route(string $pattern, callable $callback, bool $pass_route = false) // URL 패턴을 콜백에 매핑합니다.
Flight::group(string $pattern, callable $callback) // URL을 위한 그룹을 만듭니다. 패턴은 문자열이어야 합니다.
Flight::redirect(string $url, int $code) // 다른 URL로 리다이렉트합니다.
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') // ETag HTTP 캐싱을 수행합니다.
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 응답을 보냅니다.
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 = '') // POST 요청 URL 패턴을 콜백에 매핑합니다.
Flight::put(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // PUT 요청 URL 패턴을 콜백에 매핑합니다.
Flight::patch(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // PATCH 요청 URL 패턴을 콜백에 매핑합니다.
Flight::delete(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // DELETE 요청 URL 패턴을 콜백에 매핑합니다.
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') // ETag HTTP 캐싱을 수행합니다.
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 라우터는 'UserController' 클래스의 'viewUserProfile($id)' 메서드를 호출하여 메서드 내의
$id
인수로1234
를 전달합니다. viewUserProfile()
메서드의 코드가 실행되어 코드를 실행하고 사용자 프로필 페이지를 위한 HTML을 출력할 수 있으며, 이것이 RESTful API인 경우 사용자 정보와 함께 JSON 응답을 출력할 수도 있습니다.- Flight는 깔끔하게 감싸고 응답 헤더를 생성하여 사용자 브라우저로 보내줍니다.
- 사용자는 기쁨에 충만하고 따뜻한 포옹을 합니다!
그리고 왜 중요한가요?
적절한 중앙 집중형 라우터를 가지고 있다면 실제로 여러분의 삶이 크게 쉬워질 수 있습니다! 처음에는 약간 복잡해 보일 수도 있습니다. 다음은 몇 가지 이유입니다:
- 중앙 집중식 라우팅: 모든 라우트 정보를 한 곳에 유지할 수 있습니다. 어떤 라우트가 있고 그들이 무엇을 하는지 보는 것이 쉽습니다. 필요한 경우 라우트를 쉽게 변경할 수 있습니다.
- 라우트 매개변수: 라우트 매개변수를 사용하여 데이터를 라우트 메서드로 전달할 수 있습니다. 이것은 코드를 깨끗하고 정리된 상태로 유지하는 좋은 방법입니다.
- 라우트 그룹: 라우트를 그룹화할 수 있습니다. 코드를 정리하고 middleware를 라우트 그룹에 적용하는 데 유용합니다.
- 라우트 별칭: 라우트에 별칭을 지정하여 URL을 동적으로 생성할 수 있습니다. 예를 들어, 코드에서 '/user/1234'를 직접 코딩하는 대신 'user_view'와 같은 별칭을 참조하고 매개변수로 'id'를 전달할 수 있습니다. 나중에 '/user/1234'를 '/admin/user/1234'로 변경해야 하는 경우 모든 하드 코딩된 URL을 변경할 필요가 없습니다. 라우트에 연결된 URL만 변경하면 되므로 훌륭합니다.
- 라우트 미들웨어: 라우트에 미들웨어를 추가할 수 있습니다. 미들웨어는 특정 동작을 추가하는 데 매우 강력합니다. 예를 들어 특정 사용자가 라우트나 라우트 그룹에 액세스할 수 있는지 작업을 인증하는 데 사용됩니다.
아마도 'index.php'에 여러분의 웹사이트를 만드는 스크립트별 방법에 익숙하실 것입니다. 'index.php'라는 파일이 있고 URL을 확인하고 URL에 따라 특정 함수를 실행하는 많은 'if' 문이 있는 것일 것입니다. 이것은 라우팅 방식 중 하나일 수 있지만 조직화되지 않았으며 빠르게 복잡해질 수 있습니다. 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);
}
// etc...
아니면 이렇게 하시겠습니까?
// 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
응답을 반환합니다. 클라이언트가 동일한 리소스를 요청할 때, 그들은 로컬로 캐시된 버전을 사용하라는 메시지를 받게 됩니다.
Last-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 "금지됨";
}
});
현재 상태 코드를 가져오려면 인수 없이 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]);
});
// 모든 경로에 대해 모든 응답을 gzip으로 압축합니다
Flight::response()->addResponseBodyCallback(function($body) {
return gzencode($body, 9);
});
여러 개의 콜백을 추가할 수 있으며 추가된 순서대로 실행됩니다. callable를 수용할 수 있으므로, 클래스 배열 [ $class, 'method' ]
, 클로저 $strReplace = function($body) { str_replace('hi', 'there', $body); };
또는 예를 들어 HTML 코드를 축소하는 함수를 가진 경우 함수 이름 'minify'
를 수용할 수 있습니다.
참고: flight.v2.output_buffering
구성 옵션을 사용하는 경우 라우트 콜백은 작동하지 않습니다.
특정 경로 콜백
특정 경로에만 적용되도록 하려면, 경로 자체에서 콜백을 추가할 수 있습니다:
Flight::route('/users', function() {
$db = Flight::db();
$users = $db->fetchAll("SELECT * FROM users");
Flight::render('users_table', ['users' => $users]);
// 이 경로에 대해서만 응답을 gzip으로 압축합니다
Flight::response()->addResponseBodyCallback(function($body) {
return gzencode($body, 9);
});
});
미들웨어 옵션
미들웨어를 사용하여 모든 경로에 콜백을 적용할 수도 있습니다:
// MinifyMiddleware.php
class MinifyMiddleware {
public function before() {
// 응답() 객체에서 여기에서 콜백을 적용합니다.
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 인코딩 시JSON_THROW_ON_ERROR
및JSON_UNESCAPED_SLASHES
상수를 사용합니다.
상태 코드가 포함된 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');
따라서 ?q=my_func
를 사용하여 GET 요청을 보낼 때, 다음과 같은 출력이 예상됩니다:
my_func({"id":123});
쿼리 매개변수 이름을 전달하지 않으면 기본값은 jsonp
가 됩니다.
다른 URL로 리디렉션
새로운 URL을 전달하여 현재 요청을 리디렉션할 수 있습니다:
Flight::redirect('/new/location');
기본적으로 Flight는 HTTP 303 ("See Other") 상태 코드를 보냅니다. 선택적으로 사용자 정의 코드를 설정할 수 있습니다:
Flight::redirect('/new/location', 401);
중지
중지 메서드를 호출하여 프레임워크를 언제든지 멈출 수 있습니다:
Flight::halt();
선택적으로 HTTP
상태 코드와 메시지를 지정할 수 있습니다:
Flight::halt(200, '잠시 후에 돌아옵니다...');
halt
를 호출하면 그 시점까지의 모든 응답 내용이 삭제됩니다. 프레임워크를 중지하고 현재 응답을 출력하려면 stop
메서드를 사용합니다:
Flight::stop();
응답 데이터 지우기
clear()
메서드를 사용하여 응답 본문과 헤더를 지울 수 있습니다. 이 메서드는 응답에 할당된 모든 헤더를 지우고 응답 본문을 지우며 상태 코드를 200
으로 설정합니다.
Flight::response()->clear();
응답 본문만 지우기
응답 본문만 지우고 싶다면 clearBody()
메서드를 사용할 수 있습니다:
// 응답() 객체에 설정된 모든 헤더는 여전히 유지됩니다.
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 '이 콘텐츠는 캐시됩니다.';
});
마지막 수정
페이지가 마지막으로 수정된 날짜와 시간을 설정하기 위해 UNIX 타임스탬프를 전달하여 lastModified
메서드를 사용할 수 있습니다. 클라이언트는 마지막 수정된 값이 변경될 때까지 캐시를 계속 사용합니다.
Flight::route('/news', function () {
Flight::lastModified(1234567890);
echo '이 콘텐츠는 캐시됩니다.';
});
ETag
ETag
캐싱은 Last-Modified
와 유사하지만 리소스에 대해 원하는 모든 ID를 지정할 수 있습니다:
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 '안녕 세계!';
});
$app->start();
정적 메서드를 호출하는 대신 Engine 객체의 동일한 이름을 가진 인스턴스 메서드를 호출합니다.
Learn/redirects
리다이렉트
새 URL을 전달하여 redirect
메서드를 사용하여 현재 요청을 리다이렉트할 수 있습니다:
Flight::redirect('/new/location');
기본적으로 Flight는 HTTP 303 상태 코드를 보냅니다. 선택적으로 사용자 정의 코드를 설정할 수 있습니다:
Flight::redirect('/new/location', 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 "Welcome back, $username!";
});
여기서 'user.login'
이벤트가 트리거되면 사용자의 이름으로 인사합니다.
주요 사항
- 같은 이벤트에 여러 리스너를 추가할 수 있으며, 등록한 순서대로 실행됩니다.
- 콜백은 함수, 익명 함수 또는 클래스의 메서드가 될 수 있습니다.
이벤트 트리거하기
이벤트가 발생하도록 하려면 Flight::triggerEvent()
를 사용하세요. 이 메서드는 Flight에 등록된 모든 리스너를 실행하도록 지시하며, 제공하는 데이터를 전달합니다.
구문
Flight::triggerEvent(string $event, ...$args): void
$event
: 트리거하려는 이벤트 이름(등록된 이벤트와 일치해야 함)....$args
: 리스너에 전달할 선택적 인수 (아무 개수의 인수가 될 수 있음).
간단한 예제
$username = 'alice';
Flight::triggerEvent('user.login', $username);
이것은 'user.login'
이벤트를 트리거하고, 이전에 정의한 리스너에 'alice'
를 전달하여 Welcome back, 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()
는 확장 가능하며, 작동 방식을 재정의할 수 있습니다. 이는 로그 추가나 이벤트 배포 방식을 변경하고 싶어하는 고급 사용자를 위한 훌륭한 기능입니다.
예제: 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 logged in at " . date('Y-m-d H:i:s'));
});
// 라우트 정의
Flight::route('/login', function () {
$username = 'bob';
Flight::triggerEvent('user.login', $username);
echo "Logged in!";
});
Flight::start();
- 장점: 간단함, 추가 파일 없음, 작은 프로젝트에 적합.
- 단점: 앱이 커지면서 더 많은 이벤트와 경로가 생기면 혼잡해질 수 있음.
옵션 2: 별도의 events.php
파일
약간 더 큰 앱의 경우 이벤트 등록을 app/config/events.php
와 같은 전용 파일로 이동하는 것을 고려하세요. 이 파일을 index.php
에서 라우트 전에 포함하세요. 이는 Flight 프로젝트에서 라우트가 종종 app/config/routes.php
에 조직되는 방식과 유사합니다.
// app/config/events.php
Flight::onEvent('user.login', function ($username) {
error_log("$username logged in at " . date('Y-m-d H:i:s'));
});
Flight::onEvent('user.registered', function ($email, $name) {
echo "Email sent to $email: Welcome, $name!";
});
// index.php
require 'vendor/autoload.php';
require 'app/config/events.php';
Flight::route('/login', function () {
$username = 'bob';
Flight::triggerEvent('user.login', $username);
echo "Logged in!";
});
Flight::start();
- 장점:
index.php
를 라우팅에 집중하게 하고, 이벤트를 논리적으로 조직하여 찾고 편집하기 쉬움. - 단점: 약간의 구조를 추가하며, 매우 작은 앱에서는 과한 느낌이 들 수 있음.
옵션 3: 트리거되는 위치 가까이
또 다른 접근법은 이벤트가 트리거되는 곳 근처에 이벤트를 등록하는 것입니다. 예를 들어, 컨트롤러나 경로 정의 내부에 배치하는 것입니다. 이 방법은 이벤트가 앱의 특정 부분에만 해당할 경우 잘 작동합니다.
Flight::route('/signup', function () {
// 여기에서 이벤트 등록
Flight::onEvent('user.registered', function ($email) {
echo "Welcome email sent to $email!";
});
$email = 'jane@example.com';
Flight::triggerEvent('user.registered', $email);
echo "Signed up!";
});
- 장점: 연관된 코드를 함께 유지, 격리된 기능에 좋음.
- 단점: 이벤트 등록이 흩어질 수 있어 모든 이벤트를 한눈에 보기 어려워지고 조심하지 않으면 중복 등록 위험도 있음.
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 logged in");
});
Flight::onEvent('user.registered', function ($email) {
echo "Welcome to $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 logged in at $time");
});
// 2단계: 앱에서 트리거
Flight::route('/login', function () {
$username = 'bob'; // 폼에서 오는 것처럼 가정
Flight::triggerEvent('user.login', $username);
echo "Hi, $username!";
});
유용한 이유: 로그인 코드는 로깅에 대해 알 필요가 없습니다—그저 이벤트를 트리거할 뿐입니다. 나중에 더 많은 리스너(예: 환영 이메일 전송)를 추가할 수 있으며 라우트를 변경할 필요가 없습니다.
예제 2: 신규 사용자 알림
// 신규 등록 리스너
Flight::onEvent('user.registered', function ($email, $name) {
// 이메일 전송 시뮬레이션
echo "Email sent to $email: Welcome, $name!";
});
// 누군가가 가입할 때 트리거
Flight::route('/signup', function () {
$email = 'jane@example.com';
$name = 'Jane';
Flight::triggerEvent('user.registered', $email, $name);
echo "Thanks for signing up!";
});
유용한 이유: 가입 로직은 사용자를 생성하는 데 집중하고, 이벤트는 알림을 처리합니다. 나중에 더 많은 리스너(예: 가입 기록)를 추가할 수 있습니다.
예제 3: 캐시 지우기
// 캐시 지우기 리스너
Flight::onEvent('page.updated', function ($pageId) {
unset($_SESSION['pages'][$pageId]); // 적용 가능한 경우 세션 캐시 지우기
echo "Cache cleared for page $pageId.";
});
// 페이지가 편집될 때 트리거
Flight::route('/edit-page/(@id)', function ($pageId) {
// 페이지가 업데이트되었다고 가정
Flight::triggerEvent('page.updated', $pageId);
echo "Page $pageId updated.";
});
유용한 이유: 편집 코드는 캐시에 대해 알 필요가 없습니다—그저 업데이트를 신호 보낼 뿐입니다. 앱의 다른 부분은 필요에 따라 반응할 수 있습니다.
모범 사례
- 이벤트 명확하게 이름 짓기:
'user.login'
또는'page.updated'
와 같은 구체적인 이름을 사용하여 어떤 일을 하는지 명확하게 하세요. - 리스너를 간단하게 유지: 느리거나 복잡한 작업을 리스너에 두지 마세요—앱을 빠르게 유지하세요.
- 이벤트 테스트: 수동으로 트리거하여 리스너가 예상대로 작동하는지 확인하세요.
- 이벤트를 현명하게 사용하세요: 이벤트는 디커플링에 훌륭하지만, 너무 많이 사용하면 코드를 이해하기 어려워질 수 있습니다—상황에 맞게 사용하세요.
Flight::onEvent()
과 Flight::triggerEvent()
를 통해 Flight PHP의 이벤트 시스템은 유연한 애플리케이션 구축을 위한 단순하면서도 강력한 방법을 제공합니다. 애플리케이션의 서로 다른 부분이 이벤트를 통해 소통할 수 있도록 하여 코드를 조직적이고 재사용 가능하며 쉽게 확장할 수 있도록 합니다. 로그 작업 수행, 알림 전송 또는 업데이트 관리와 관계없이 이벤트는 논리를 얽히게 하지 않고 이를 처리하는 데 도움을 줍니다. 또한 이러한 메서드를 재정의 할 수 있는 능력을 통해 필요에 맞게 시스템을 조정할 수 있는 자유를 누릴 수 있습니다. 단일 이벤트로 작게 시작하고, 그것이 앱 구조를 어떻게 변형시키는지 지켜보세요!
내장 이벤트
Flight PHP는 프레임워크의 생명 주기에서 특정 시점에 후킹할 수 있는 몇 가지 내장 이벤트를 제공합니다. 이러한 이벤트는 요청/응답 주기의 특정 지점에서 트리거되어 특정 작업이 발생할 때 사용자 정의 논리를 실행할 수 있게 해줍니다.
내장 이벤트 목록
flight.request.received
: 요청이 수신, 구문 분석 및 처리될 때 트리거됨.flight.route.middleware.before
: 이전 미들웨어가 실행된 후 트리거됨.flight.route.middleware.after
: 이후 미들웨어가 실행된 후 트리거됨.flight.route.executed
: 라우트가 실행되고 처리된 후 트리거됨.flight.response.sent
: 응답이 클라이언트에 전송된 후 트리거됨.
Learn/views
뷰(Views)
Flight는 기본적인 템플릿 기능을 제공합니다. 뷰
템플릿을 표시하려면 템플릿 파일의 이름과 선택적인
템플릿 데이터를 사용하여 render
메소드를 호출하십시오:
Flight::render('hello.php', ['name' => 'Bob']);
전달한 템플릿 데이터는 자동으로 템플릿에 주입되며
로컬 변수처럼 참조할 수 있습니다. 템플릿 파일은 단순히 PHP 파일입니다. hello.php
템플릿 파일의 내용이 다음과 같다면:
Hello, <?= $name ?>!
출력은 다음과 같을 것입니다:
Hello, Bob!
뷰 변수를 수동으로 설정할 수도 있습니다.
Flight::view()->set('name', 'Bob');
변수 name
은 이제 모든 뷰에서 사용할 수 있습니다. 따라서 다음과 같이 할 수 있습니다:
Flight::render('hello');
render
메소드에서 템플릿 이름을 지정할 때 .php
확장자를 생략할 수 있음을 유의하십시오.
Flight는 기본적으로 템플릿 파일을 위해 views
디렉토리를 찾습니다. 다음 구성을 설정하여 템플릿의 대체 경로를 설정할 수 있습니다:
Flight::set('flight.views.path', '/path/to/views');
레이아웃(Layouts)
웹 사이트에 단일 레이아웃 템플릿 파일이 있는 것이 일반적입니다. 레이아웃에 사용할 콘텐츠를 렌더링하려면 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>
사용자 정의 뷰(Custom Views)
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');
완전성을 위해 Flight의 기본 render
메소드를 재정의해야 합니다:
Flight::map('render', function(string $template, array $data): void {
Flight::view()->assign($data);
Flight::view()->display($template);
});
Learn/templates
HTML 뷰 및 템플릿
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');
완전성을 위해 Flight의 기본 render 메서드를 오버라이드하는 것도 잊지 마세요:
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 템플릿 엔진을 뷰에서 사용하는 방법입니다:
먼저, Composer를 통해 BladeOne 라이브러리를 설치해야 합니다:
composer require eftec/bladeone
그런 다음, Flight에서 BladeOne을 뷰 클래스로 구성할 수 있습니다:
<?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', []);
완전성을 위해 Flight의 기본 render 메서드를 오버라이드하는 것도 잊지 마세요:
<?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 vs Fat-Free
펫-프리란 무엇인가요?
펫-프리 (애칭으로 F3로도 불립니다)은 빠르게 동적이고 탄탄한 웹 애플리케이션을 구축하는 데 도움이 되는 강력하면서 쉽게 사용할 수 있는 PHP 마이크로 프레임워크입니다.
플라이트는 펫-프리와 여러 면에서 비교되며 기능과 간단함 측면에서 아마 가장 가까운 친척일 것입니다. 펫-프리에는 플라이트에 없는 기능들이 많이 있지만, 플라이트에는 없는 기능들도 많습니다. 펫-프리는 나이가 드러나기 시작하고 한때 그랬던 만큼 인기가 떨어졌습니다.
업데이트가 점차 적어지고 커뮤니티도 한때처럼 활발하지는 않습니다. 코드는 충분히 간단하지만, 때로는 문법의 엄격성 부족으로 읽고 이해하기 어려울 수 있습니다. PHP 8.3에서 작동하지만 코드 자체는 여전히 PHP 5.3에서 작성된 것처럼 보입니다.
Flight와 비교한 장점들
- 펫-프리에는 GitHub에서 더 많은 별이 달렸습니다.
- 펫-프리에는 일부 명확하지 않은 영역들을 제외하고 어느 정도 괜찮은 문서가 있습니다.
- 펫-프리에는 프레임워크를 배울 수 있는 YouTube 튜토리얼과 온라인 기사 같은 여러 리소스들이 있습니다.
- 펫-프리에는 때때로 유용한 일부 도움이 되는 플러그인들이 내장되어 있습니다.
- 펫-프리에는 데이터베이스와 상호 작용할 수 있는 Mapper라는 내장 ORM이 있습니다. 플라이트에는 액티브 레코드가 있습니다.
- 펫-프리에는 세션, 캐싱 및 지역화가 내장되어 있습니다. 플라이트는 제3자 라이브러리를 사용해야 하지만, 이는 문서에서 다룹니다.
- 펫-프리에는 프레임워크를 확장할 수 있는 일부 커뮤니티 생성 플러그인이 있습니다. Flight는 문서와 예제 페이지에 명시되어 있습니다.
- 펫-프리와 마찬가지로 플라이트는 의존성이 없습니다.
- 펫-프리와 마찬가지로 플라이트는 개발자가 애플리케이션을 제어하고 간단한 개발 경험을 제공하는 데 초점을 맞춘 것입니다.
- 펫-프리는 업데이트가 좀 더 적어진다는 걸 빼고 플라이트와 마찬가지로 하위 호환성을 유지하고 있습니다.
- 펫-프리와 마찬가지로 플라이트는 프레임워크의 세계로 처음 발을 딛는 개발자들을 위한 것입니다.
- 펫-프리에는 플라이트의 템플릿 엔진보다 강력한 내장된 템플릿 엔진이 있습니다. 플라이트는 이를 수행하기 위해 Latte를 권장합니다.
- 펫-프리에는 CLI 유형의 "루트" 명령어가 있어 펫-프리 자체에서 CLI 앱을 구축하고 이를
GET
요청과 유사하게 다룰 수 있습니다. 플라이트는 이를 runway로 수행합니다.
Flight와 비교한 단점들
- 펫-프리에는 몇 가지 구현 테스트가 있으며 매우 기본적인 자체 테스트 클래스가 있습니다. 그러나, 플라이트만큼 100%의 단위 테스트가 되지는 않습니다.
- 실제로 문서 사이트를 검색하려면 Google과 같은 검색 엔진을 사용해야 합니다.
- 플라이트에는 문서 사이트에 다크 모드가 있습니다. (마이크 드랍)
- 펫-프리에는 유지보수가 소홀한 모듈들이 있습니다.
- 플라이트에는 펫-프리의 내장
DB\SQL
클래스보다 조금 더 간단한 PdoWrapper가 있습니다. - 플라이트에는 애플리케이션을 보호하는 데 사용할 수 있는 권한 플러그인이 있습니다. Slim은 제3자 라이브러리를 사용해야 합니다.
- 플라이트에는 펫-프리의 Mapper보다 ORM으로서 더 느껴지는 액티브 레코드가 있습니다.
active-record
의 추가적인 이점은 레코드 간의 관계를 정의하여 자동 조인을 수행할 수 있다는 점인데, 펫-프리의 Mapper는 SQL 뷰를 생성해야 합니다. - 놀랍게도, 펫-프리에는 루트 네임스페이스가 없습니다. 플라이트는 코드 충돌을 방지하기 위해 완전히 네임스페이스가 지정되어 있습니다. 여기서
Cache
클래스가 가장 큰 문제입니다. - 펫-프리에는 미들웨어가 없습니다. 대신, 컨트롤러에서 요청과 응답을 필터링하는 데 사용되는
beforeroute
와afterroute
후크가 있습니다. - 펫-프리는 라우트를 그룹화할 수 없습니다.
- 펫-프리에는 의존성 주입 컨테이너 핸들러가 있지만, 사용 방법에 대한 문서가 매우 부족합니다.
- 디버깅은 'HIVE'라고 불리는 곳에 거의 모든 것이 저장되기 때문에 약간 까다로울 수 있습니다.
Learn/extending
확장
Flight는 확장 가능한 프레임워크로 설계되었습니다. 이 프레임워크는 기본 메서드 및 구성 요소 집합과 함께 제공되지만, 여러분이 자신의 메서드를 매핑하고, 자신의 클래스를 등록하거나, 기존 클래스와 메서드를 오버라이드할 수 있도록 허용합니다.
DIC(의존성 주입 컨테이너)를 찾고 계신다면 Dependency Injection Container 페이지로 이동하세요.
메서드 매핑
자신만의 간단한 사용자 정의 메서드를 매핑하려면 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();
register 메서드는 클래스 생성자에 매개변수를 전달할 수 있도록 허용합니다. 따라서 사용자 정의 클래스를 로드할 때, 생성자가 미리 초기화되어 올 것입니다. 추가 배열을 전달하여 생성자 매개변수를 정의할 수 있습니다. 데이터베이스 연결을 로드하는 예제는 다음과 같습니다:
// 생성자 매개변수와 함께 클래스 등록
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';
});
before
및 after
메서드를 사용하여 기본 APM(애플리케이션 성능 모니터링) 시스템을 만들 수도 있습니다:
// 부트스트랩 파일에서
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는 또한 프레임워크의 핵심 구성 요소를 교체할 수 있도록 합니다. 예를 들어, 기본 라우터 클래스를 사용자 정의 클래스로 교체할 수 있습니다:
// 사용자 정의 클래스 등록
Flight::register('router', MyRouter::class);
// Flight가 라우터 인스턴스를 로드할 때, 여러분의 클래스를 로드합니다
$myrouter = Flight::router();
그러나 map
및 register
와 같은 프레임워크 메서드는 오버라이드할 수 없습니다. 그렇게 시도하면 오류가 발생합니다.
Learn/json
JSON
Flight은 JSON 및 JSONP 응답을 보내는 데 지원을 제공합니다. JSON 응답을 보내려면 JSON으로 인코딩 할 데이터를 전달하면 됩니다:
Flight::json(['id' => 123]);
JSONP 요청의 경우, 콜백 함수를 정의하는 데 사용하는 쿼리 매개변수 이름을 선택적으로 전달할 수 있습니다:
Flight::jsonp(['id' => 123], 'q');
따라서 ?q=my_func
를 사용하여 GET 요청을 보낼 때 아래 출력을 받아야 합니다:
my_func({"id":123});
쿼리 매개변수 이름을 전달하지 않으면 jsonp
로 기본 설정됩니다.
Learn/flight_vs_slim
Flight vs 슬림
슬림이란 무엇인가요?
슬림은 심플하지만 강력한 웹 애플리케이션 및 API를 빠르게 작성할 수 있도록 도와주는 PHP 미니 프레임워크입니다.
Flight의 일부 v3 기능에 대한 영감은 실제로 슬림에서 왔습니다. 라우트 그룹화 및 미들웨어를 특정 순서대로 실행하는 것은 슬림에서 영감을 받은 기능 중 두 가지입니다. 슬림 v3은 간결함을 목표로 하였지만, v4에 대해 혼합된 의견이 있습니다.
Flight와 비교한 장점
- 슬림은 여러 개발자들로 구성된 커뮤니티를 가지고 있어 편리한 모듈을 만들어 휠을 다시 발명하지 않도록 도와줍니다.
- 슬림은 PHP 커뮤니티에서 일반적인 인터페이스 및 표준을 따르며 상호 운용성을 높입니다.
- 슬림은 프레임워크를 배우는 데 사용할 수 있는 꽤 훌륭한 문서와 튜토리얼을 갖고 있습니다 (그러나 Laravel 또는 Symfony만큼은 아닙니다).
- 슬림은 프레임워크를 배우는 데 사용할 수 있는 YouTube 튜토리얼 및 온라인 기사와 같은 다양한 자원을 갖고 있습니다.
- 슬림은 PSR-7 호환으로 핵심 라우팅 기능을 처리하기 위해 원하는 구성 요소를 사용할 수 있습니다.
Flight와 비교한 단점
- 놀랍게도 슬림은 미니 프레임워크로 생각했을 때 생각보다 빠르지 않습니다. 더 많은 정보는 TechEmpower benchmarks에서 확인할 수 있습니다.
- Flight는 가볍고 빠르며 사용하기 쉬운 웹 애플리케이션을 구축하려는 개발자를 대상으로 합니다.
- Flight에는 의존성이 없지만 슬림에는 설치해야 할 몇 가지 의존성이 있습니다.
- Flight는 간결함과 사용 편의성을 목표로 합니다.
- Flight의 핵심 기능 중 하나는 가능한 한 하위 호환성을 유지하려는 노력입니다. 슬림 v3에서 v4로 변경되면서 호환성이 깨졌습니다.
- Flight는 처음으로 프레임워크의 세계로 발을 내디뎌야 하는 개발자들을 대상으로 합니다.
- Flight는 기업급 애플리케이션도 처리할 수 있지만 슬림만큼 많은 예제와 튜토리얼이 없습니다. 또한 개발자가 조직화하고 잘 구조화된 상태를 유지하기 위해 더 많은 노력이 필요할 것입니다.
- Flight는 개발자에게 애플리케이션을 더 많은 제어권을 주지만, 슬림은 백그라운드에서 약간의 마법을 부리기도 합니다.
- Flight에는 데이터베이스와 상호 작용할 수 있는 간단한 PdoWrapper가 있습니다. 슬림은 써드 파티 라이브러리를 사용해야 합니다.
- Flight에는 애플리케이션을 보호할 수 있는 권한 플러그인이 있습니다. 슬림은 써드 파티 라이브러리를 사용해야 합니다.
- Flight에는 데이터베이스와 상호 작용할 수 있는 액티브 레코드라는 ORM이 있습니다. 슬림은 써드 파티 라이브러리를 사용해야 합니다.
- Flight에는 명령줄 인터페이스 애플리케이션이 있는데, runway라고 합니다. 슬림에는 해당 기능이 없습니다.
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
*/
// 네임스페이스가 필요하지 않음
// 모든 자동로드 클래스는 파스칼 케이스(각 단어의 첫 글자가 대문자이고 공백이 없는)로 권장합니다
// 3.7.2 버전부터 Loader::setV2ClassLoading(false);를 실행하여 클래스 이름에 Pascal_Snake_Case를 사용할 수 있습니다.
class MyController {
public function index() {
// 무언가 수행
}
}
네임스페이스
네임스페이스가 있는 경우 이 구현이 매우 쉬워집니다. 애플리케이션의 루트 디렉토리(문서 루트 또는 public/
폴더가 아님)를 지정하기 위해 Flight::path()
메서드를 사용해야 합니다.
/**
* public/index.php
*/
// 오토로더에 경로 추가
Flight::path(__DIR__.'/../');
이제 컨트롤러가 이와 같이 보일 수 있습니다. 아래 예제를 확인하되 중요한 정보를 위해 주석을 주의깊게 읽어주십시오.
/**
* app/controllers/MyController.php
*/
// 네임스페이스가 필요합니다
// 네임스페이스는 디렉토리 구조와 같아야 합니다
// 네임스페이스는 디렉토리 구조와 동일한 케이스를 따라야 합니다
// 네임스페이스와 디렉토리에는 밑줄을 사용할 수 없습니다 (Loader::setV2ClassLoading(false)가 설정된 경우를 제외하고)
namespace app\controllers;
// 모든 자동로드 클래스는 파스칼 케이스로 권장됩니다
// 3.7.2 버전부터 Loader::setV2ClassLoading(false);를 실행하여 클래스 이름에 Pascal_Snake_Case를 사용할 수 있습니다.
class MyController {
public function index() {
// 무언가 수행
}
}
그리고 utils 디렉토리에 있는 클래스를 자동으로로드하려면 기본적으로 다음을 수행하면 됩니다:
/**
* app/UTILS/ArrayHelperUtil.php
*/
// 네임스페이스는 디렉토리 구조 및 케이스와 일치해야 합니다 (UTILS 디렉토리가 위의 파일 트리처럼 대문자로 되어 있음에 유의하세요)
namespace app\UTILS;
class ArrayHelperUtil {
public function changeArrayCase(array $array) {
// 무언가 수행
}
}
클래스 이름에 밑줄 사용
3.7.2 버전부터 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 Not Found 또는 예기치 않은 경로 동작
404 Not Found 오류를 보고 있다면(하지만 정말 있고 오타가 아니라고 맹세한다면) 실제로는 route 끝점에서 값을 반환하는 것보다 그냥 출력하는 것이 문제가 될 수 있습니다. 그 이유는 의도적이지만 개발자들에겐 뜻밖의 문제가 될 수 있습니다.
Flight::route('/hello', function(){
// 이것이 404 Not Found 오류를 일으킬 수 있음
return 'Hello World';
});
// 아마 원하던 것
Flight::route('/hello', function(){
echo 'Hello World';
});
이 이유는 라우터에 내장된 특별한 메커니즘 때문입니다. 이 메커니즘은 반환 값을 다음 route로 "이동"으로 처리합니다. 이 동작은 Routing 섹션에서 문서화된 대로 확인할 수 있습니다.
클래스를 찾을 수 없음 (자동로딩이 작동하지 않음)
이 문제가 발생하지 않는 이유는 몇 가지가 있을 수 있습니다. 아래에는 일부 예가 나와 있지만 반드시 autoloading 섹션도 확인해야 합니다.
잘못된 파일 이름
가장 일반적인 이유는 클래스 이름이 파일 이름과 일치하지 않는 경우입니다.
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를 하위 디렉토리에서 사용해야 한다면,
RewriteEngine On
다음에 줄을 추가하세요.RewriteBase /subdir/
참고: db 또는 환경 파일과 같은 모든 서버 파일을 보호해야 한다면, 아래 내용을
.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단계: 프로젝트 설정
새 프로젝트 디렉토리를 생성하고 Composer를 통해 Flight를 설치합니다.
-
디렉토리 생성:
mkdir flight-blog cd flight-blog
-
Flight 설치:
composer require flightphp/core
-
공용 디렉토리 생성: Flight는 단일 진입점(
index.php
)을 사용합니다. 이를 위해public/
폴더를 생성합니다:mkdir public
-
기본
index.php
: 간단한 "hello world" 경로로public/index.php
를 생성합니다:<?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
-
Flight에서 Latte 구성:
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단계: 오류 처리 향상
더 나은 404 경험을 위해 notFound
메서드를 오버라이드합니다.
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와 같은 데이터베이스로 교체합니다. - 유효성 검사: 중복 슬러그 또는 빈 입력에 대한 검사를 추가합니다.
- 미들웨어: 게시물 작성을 위해 인증을 구현합니다.
결론
Flight PHP로 간단한 블로그를 만들었습니다! 이 가이드는 경로 설정, Latte로 템플릿화, 양식 제출 처리 등 핵심 기능을 보여줍니다. Flight의 문서를 살펴보고 블로그를 더 발전시켜 보세요!
License
MIT 라이센스 (MIT)
=====================
Copyright © 2024
@mikecao, @n0nag0n
어떠한 제약 없이 이 소프트웨어 및 관련 문서를 소유하고 있는 모든 사람에게 무료로 사용 권한이 제공됩니다.
("Software"
), 여기에는 사용, 복사, 수정, 병합, 발행, 배포, 하위 라이선스, 판매권 등이 포함됩니다.
소프트웨어의 사본을 획득한 사람은 상기 조건에 따라 소프트웨어를 다룰 수 있으며,
소프트웨어가 제공되는 사람들에게 소프트웨어를 사용할 권리가 부여됩니다.
다음 조건에 따라 조건부로 허용됩니다.
상기 저작권 고지 및 이 권한 통지는 소프트웨어의 모든 복사본 또는 상당한 부분에 포함되어야합니다.
소프트웨어는 "있는 그대로" 제공되며 어떠한 종류의 보증도 없이, 명시적이든 묵시적이든, 상품성, 특정 목적에의 적합성 및 비침해성을 포함하되 이에 한하지 않는 보증이 포함됩니다. 등의 사건에서 발생하는 모든 청구, 손해 또는 기타 책임에 대해 제작자 또는 저작권 소유자가 책임지지 않습니다. 계약, 불법 행위 또는 기타, 소프트웨어 또는 사용 또는 기타 거래에 대한 이러한 저작물로부터의 파생작품.
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 'hello world!';
});
Flight::route('/json', function() {
Flight::json(['hello' => 'world']);
});
Flight::start();
그게 전부입니다! 기본 Flight 애플리케이션이 생성되었습니다. 이제 php -S localhost:8000
로 이 파일을 실행하고 브라우저에서 http://localhost:8000
에 방문하여 출력을 확인할 수 있습니다.
빠른가요?
네! Flight는 빠릅니다. 현재 제공되는 가장 빠른 PHP 프레임워크 중 하나입니다. 모든 벤치마크를 TechEmpower에서 확인할 수 있습니다.
아래 벤치마크는 일부 다른 인기 있는 PHP 프레임워크와 비교한 것입니다.
프레임워크 | 일반 요청/초 | JSON 요청/초 |
---|---|---|
Flight | 190,421 | 182,491 |
Yii | 145,749 | 131,434 |
Fat-Free | 139,238 | 133,952 |
Slim | 89,588 | 87,348 |
Phalcon | 95,911 | 87,675 |
Symfony | 65,053 | 63,237 |
Lumen | 40,572 | 39,700 |
Laravel | 26,657 | 26,901 |
CodeIgniter | 20,628 | 19,901 |
스켈레톤/보일러플레이트 애플리케이션
Flight 프레임워크로 시작하는 데 도움이 되는 예제 애플리케이션이 있습니다. flightphp/skeleton에서 시작하는 방법에 대한 지침을 확인하세요! 또한 예제 페이지를 방문하여 Flight로 할 수 있는 일에 대한 영감을 얻을 수 있습니다.
커뮤니티
우리는 Matrix Chat에 있습니다.
그리고 Discord에도 있습니다.
기여하기
Flight에 기여하는 방법은 두 가지가 있습니다:
- 코어 리포지토리를 방문하여 코어 프레임워크에 기여할 수 있습니다.
- 문서에 기여할 수 있습니다. 이 문서 웹사이트는 Github에 호스팅되고 있습니다. 오류를 발견하거나 더 나은 내용을 보완하고 싶다면 언제든지 수정하고 풀 리퀘스트를 제출하세요! 우리는 사안을 잘 관리하려고 노력하지만 업데이트 및 언어 번역은 언제든지 환영합니다.
요구 사항
Flight는 PHP 7.4 이상이 필요합니다.
참고: PHP 7.4는 현재 작성 시점(2024)의 많은 LTS 리눅스 배포판에서 기본 버전이기 때문에 지원되고 있습니다. PHP >8로 강제로 전환하는 것은 그런 사용자들에게 많은 불편을 초래할 것입니다. 이 프레임워크는 또한 PHP >8도 지원합니다.
라이센스
Flight는 MIT 라이센스 하에 릴리스됩니다.
Awesome-plugins/php_cookie
쿠키
overclokk/cookie은 앱 내에서 쿠키를 관리하는 간단한 라이브러리입니다.
설치
컴포저를 사용하여 설치가 간단합니다.
composer require overclokk/cookie
사용
사용법은 Flight 클래스에 새로운 메소드를 등록하는 것만큼 간단합니다.
use Overclokk\Cookie\Cookie;
/*
* 부트스트랩 또는 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로 간단히 설치할 수 있습니다.
composer require defuse/php-encryption
설정
그런 다음 암호화 키를 생성해야 합니다.
vendor/bin/generate-defuse-key
이 과정에서 안전하게 보관해야 할 키가 표시됩니다. 이 키를 파일 하단에 있는 배열 안에 app/config/config.php
파일에 저장할 수 있습니다. 완벽한 위치는 아니지만 적어도 어딘가에 있습니다.
사용법
이제 라이브러리와 암호화 키가 준비되었으므로 데이터를 암호화하고 복호화할 수 있습니다.
use Defuse\Crypto\Crypto;
use Defuse\Crypto\Key;
/*
* 부트스트랩 또는 public/index.php 파일에 설정
*/
// 암호화 메서드
Flight::map('encrypt', function($raw_data) {
$encryption_key = /* $config['encryption_key'] 또는 키가 위치한 파일에 대한 file_get_contents */;
return Crypto::encrypt($raw_data, Key::loadFromAsciiSafeString($encryption_key));
});
// 복호화 메서드
Flight::map('decrypt', function($encrypted_data) {
$encryption_key = /* $config['encryption_key'] 또는 키가 위치한 파일에 대한 file_get_contents */;
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 인파일 캐싱 클래스
장점
- 가볍고, 독립적이며 간단함
- 모든 코드가 하나의 파일에 존재 - 쓸데없는 드라이버 없음.
- 안전함 - 생성된 모든 캐시 파일은 die가 포함된 php 헤더를 가지고 있어, 누군가 경로를 알고 서버가 제대로 구성되지 않았더라도 직접 접근이 불가능함
- 잘 문서화되고 테스트됨
- 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
플라이트PHP/권한
이것은 여러 역할이 있는 앱에서 사용할 수있는 권한 모듈입니다. 각 역할마다 약간 다른 기능이 있는 경우 사용할 수 있습니다. 이 모듈을 사용하면 각 역할에 대한 권한을 정의한 다음 현재 사용자가 특정 페이지에 액세스하거나 특정 작업을 수행할 수 있는 권한이 있는지 확인할 수 있습니다.
여기를 클릭하여 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 {
// 다른 작업 수행
}
}
}
의존성 주입
권한을 정의하는 클로저에 의존성을 주입할 수 있습니다. 확인하려는 일부 토글, ID 또는 기타 데이터 지점이 있는 경우 유용합니다. 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;
}
}
멋진 부분은 클래스에 대해 모든 메서드를 권한에 매핑하는 바로 가기가 있으며 (캐시 가능함!!!), 이렇게하면 $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을 전달하여 클래스 외부에서 가져올 수 있습니다.
$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';
// beanstalkd를 사용하려면 ['mysql']을 ['beanstalkd']로 변경하십시오.
Flight::register('queue', n0nag0n\Job_Queue::class, ['mysql'], function($Job_Queue) {
// Flight::db();에서 이미 PDO 연결이 있는 경우
$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
: supervisord 시작 시 자동으로 시작autorestart
: 프로세스가 종료되면 자동으로 다시 시작startretries
: 실패 시 시작을 재시도할 횟수stderr_logfile
/stdout_logfile
: 로그 파일 위치user
: 프로세스를 실행할 시스템 사용자numprocs
: 실행할 작업자 인스턴스 수process_name
: 여러 작업자 프로세스의 이름 형식
Supervisorctl로 작업자 관리하기
구성을 생성하거나 수정한 후:
# supervisor 구성 다시 로드
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 in-file 캐싱 클래스
디버깅
로컬 환경에서 개발할 때 디버깅은 중요합니다. 몇 가지 플러그인을 이용하여 디버깅 경험을 높일 수 있습니다.
- 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 세션 관리자 (비차단, 플래시, 세그먼트, 세션 암호화). PHP open_ssl을 사용하여 세션 데이터의 선택적 암호화/복호화를 수행합니다.
템플릿
템플릿은 UI가 있는 모든 웹 애플리케이션의 핵심입니다. Flight와 함께 사용할 수 있는 여러 템플릿 엔진이 있습니다.
- flightphp/core View - 핵심의 매우 기본적인 템플릿 엔진입니다. 프로젝트에 여러 페이지가 있는 경우 권장되지 않습니다.
- latte/latte - Latte는 매우 쉽게 사용할 수 있으며 PHP 구문에 더 가까운 템플릿 엔진입니다. Twig나 Smarty보다 확장하고 자체 필터와 함수를 추가하기도 매우 쉽습니다.
기여
공유하고 싶은 플러그인이 있나요? 이것을 목록에 추가하려면 풀 리퀘스트를 제출하세요!
Awesome-plugins/ghost_session
Ghostff/Session
PHP 세션 관리자 (비차단, 플래시, 세그먼트, 세션 암호화). 선택적 세션 데이터 암호화/복호화를 위해 PHP open_ssl을 사용합니다. 파일, 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();
// 세션 구성 파일에 대한 사용자 지정 경로를 설정하고 세션 ID에 무작위 문자열을 제공합니다.
$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
INSERT, UPDATE 또는 while 루프에서 SELECT를 사용할 경우에 사용합니다
$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"
Migration CLI에 대한 더 많은 정보를 얻으려면 byjg/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 |
작동 방식은?
데이터베이스 마이그레이션은 데이터베이스 버전 관리를 위해 PURE SQL을 사용합니다.
작동하게 하려면 다음이 필요합니다:
- SQL 스크립트 생성
- 명령 줄 또는 API를 사용하여 관리
SQL 스크립트
스크립트는 세 가지 세트로 나뉩니다:
- BASE 스크립트는 새 데이터베이스를 생성하기 위한 모든 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이라는 파일을 생성할 것입니다. 두 개발자는 문제 없이 업그레이드하고 다운그레이드를 수행할 것이며, 로컬 버전은 43이 될 것입니다.
하지만 개발자 1이 변경 사항을 병합하고 최종 버전 43.sql을 생성했습니다(git mv 43-dev.sql 43.sql
). 개발자 2가 로컬 브랜치를 업데이트하면 그는 43.sql(개발자 1의 파일)과 43-dev.sql를 갖게 됩니다.
그가 업그레이드하거나 다운그레이드를 시도하면 마이그레이션 스크립트는 다운되고 두 개의 버전 43이 있다고 경고합니다. 그 경우 개발자 2는 자신의 파일을 44-dev.sql로 업데이트하고 병합할 때까지 작업을 계속해야 합니다.
PHP API 사용 및 프로젝트에 통합하기
기본 사용법은 다음과 같습니다:
- ConnectionManagement 객체와의 연결을 생성합니다. 더 많은 정보는 "byjg/anydataset" 구성 요소를 참조하세요.
- 이 연결과 SQL 스크립트가 위치한 폴더로 Migration 객체를 생성합니다.
- 마이그레이션 스크립트를 "reset", "up" 또는 "down"을 위해 적절한 명령을 사용합니다.
예제를 보세요:
<?php
// 연결 URI 생성
// 추가 정보: https://github.com/byjg/anydataset#connection-based-on-uri
$connectionUri = new \ByJG\Util\Uri('mysql://migrateuser:migratepwd@localhost/migratedatabase');
// 데이터베이스 또는 데이터베이스를 등록할 수 있습니다:
\ByJG\DbMigration\Migration::registerDatabase(\ByJG\DbMigration\Database\MySqlDatabase::class);
// Migration 인스턴스 생성
$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);
Migration 객체는 데이터베이스 버전을 관리합니다.
프로젝트에서 버전 관리 생성
<?php
// 데이터베이스 또는 데이터베이스를 등록할 수 있습니다:
\ByJG\DbMigration\Migration::registerDatabase(\ByJG\DbMigration\Database\MySqlDatabase::class);
// Migration 인스턴스 생성
$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에서 사용할 수 없습니다. MySQL은 트랜잭션 내에서 DDL 명령을 지원하지 않기 때문입니다.
이 메서드를 MySQL에서 사용하면 Migration은 그를 무시합니다.
자세한 내용: https://dev.mysql.com/doc/refman/8.0/en/cannot-roll-back.html
Postgres에 대한 SQL 마이그레이션 작성 팁
트리거 및 SQL 함수 생성 시
-- DO
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- empname과 salary가 주어졌는지 확인
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname cannot be null'; -- 이 주석이 비어 있든 지 여부는 중요하지 않음
END IF; --
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% cannot have null salary', NEW.empname; --
END IF; --
-- 누가 우리를 위해 일하며 언제 대가를 지불해야 하는가?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% cannot have a negative salary', NEW.empname; --
END IF; --
-- 누가 언제 급여를 변경했는지 기억
NEW.last_date := current_timestamp; --
NEW.last_user := current_user; --
RETURN NEW; --
END; --
$emp_stamp$ LANGUAGE plpgsql;
-- DON'T
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- empname과 salary가 주어졌는지 확인
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname cannot be null';
END IF;
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% cannot have null salary', NEW.empname;
END IF;
-- 누가 우리를 위해 일하며 언제 대가를 지불해야 하는가?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% cannot have a negative salary', 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
문을 여러 부분으로 나누고 마이그레이션이 실패하게 됩니다.
콜론 문자(:
) 피하기
-- DO
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
);
-- DON'T
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
는 이를 잘못된 컨텍스트에서 잘못된 이름 있는 매개변수로 읽고 실행하려고 할 때 실패합니다.
이 inconsistency를 수정하는 유일한 방법은 콜론을 완전히 피하는 것입니다 (이 경우 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 # 비밀번호가 null이면 '.' 사용
export PSQL_TEST_HOST=localhost # 기본값은 localhost
export PSQL_PASSWORD=newpassword # 비밀번호가 null이면 '.' 사용
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
기본 사용법
다음은 Flight 애플리케이션에서 flightphp/session
플러그인을 사용하는 간단한 예시입니다:
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', // 암호화 활성화 (AES-256-CBC에 대해 32바이트 권장)
'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 |
테스트 모드를 위한 사용자 정의 세션 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'); // 검색 시 복호화됨
});
세션 재생성
보안을 위해 세션 ID를 재생성하세요 (예: 로그인 후):
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()
: 현재 세션 ID를 반환합니다.regenerate(bool $deleteOld = false)
: 세션 ID를 재생성하며, 필요 시 이전 데이터를 삭제합니다.
get()
및 id()
를 제외한 모든 메서드는 체인을 위한 Session
인스턴스를 반환합니다.
이 플러그인을 사용해야 하는 이유?
- 경량: 외부 종속성이 없으며, 파일만 있습니다.
- 비차단: 기본적으로
read_and_close
로 세션 잠금을 회피합니다. - 안전한: 민감한 데이터를 위한 AES-256-CBC 암호화를 지원합니다.
- 유연성: 자동 커밋, 테스트 모드 및 수동 제어 옵션을 제공합니다.
- Flight 네이티브: Flight 프레임워크를 위해 특별히 구축되어 있습니다.
기술 세부사항
- 저장 형식: 세션 파일은
sess_
로 접두사가 붙고 구성된save_path
에 저장됩니다. 암호화된 데이터는E
접두사를 사용하고, 일반 텍스트는P
를 사용합니다. - 암호화:
encryption_key
가 제공될 경우, 세션 쓰기 시 무작위 IV를 사용하는 AES-256-CBC를 사용합니다. - 가비지 수집: 만료된 세션을 정리하기 위해 PHP의
SessionHandlerInterface::gc()
를 구현합니다.
기여하기
기여를 환영합니다! 저장소를 포크하고, 변경 사항을 적용한 후 풀 요청을 제출하세요. 버그를 신고하거나 기능을 제안하려면 Github 이슈 트래커를 통해 연락하세요.
라이선스
이 플러그인은 MIT 라이선스에 따라 라이선스가 부여됩니다. 세부 정보는 Github 저장소를 참조하세요.
Awesome-plugins/runway
런웨이
런웨이는 Flight 어플리케이션을 관리하는 데 도움이 되는 CLI 어플리케이션입니다. 컨트롤러를 생성하고 모든 라우트를 표시할 수 있습니다. 이 라이브러리는 훌륭한 adhocore/php-cli 라이브러리를 기반으로 합니다.
여기를 클릭하여 코드를 확인하십시오.
설치
컴포저로 설치합니다.
composer require flightphp/runway
기본 구성
런웨이를 처음 실행하면 설정 프로세스를 진행하고 프로젝트 루트에 .runway.json
구성 파일을 생성합니다. 이 파일에는 런웨이가 올바르게 작동하기 위한 일부 필수 구성이 포함되어 있습니다.
사용법
런웨이에는 Flight 어플리케이션을 관리하는 데 사용할 수 있는 여러 명령이 있습니다. 런웨이를 사용하는 두 가지 쉬운 방법이 있습니다.
- 스켈레톤 프로젝트를 사용하는 경우 프로젝트의 루트에서
php runway [command]
를 실행할 수 있습니다. - 컴포저를 통해 설치된 패키지로 런웨이를 사용하는 경우 프로젝트의 루트에서
vendor/bin/runway [command]
를 실행할 수 있습니다.
어떤 명령이든 --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;
/**
* users 테이블을 위한 ActiveRecord 클래스.
* @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 .runway-config.json에서의 JSON 구성
*/
public function __construct(array $config)
{
parent::__construct('make:example', '문서에 예제 생성', $config);
$this->argument('<funny-gif>', '웃긴 GIF의 이름');
}
/**
* 함수 실행
*
* @return void
*/
public function execute(string $controller)
{
$io = $this->app()->io();
$io->info('예제 생성 중...');
// 여기에 코드 추가
$io->ok('예제가 생성되었습니다!');
}
}
Flight 어플리케이션에 사용자 정의 명령어를 구축하는 방법에 대해 자세히 알아보려면 adhocore/php-cli 문서를 참조하십시오!
Awesome-plugins/tracy_extensions
Tracy 체크 패널 확장
이것은 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 framework에 이를 연결할 경우
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에 어떤 세션 데이터 배열이든 전달하여 자동으로 출력할 수 있습니다. TracyExtensionLoader
생성자의 두 번째 매개변수에서 session_data
키로 전달합니다.
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 팀은 flightphp/tracy-extensions 플러그인을 통해 Flight 프로젝트를 위해 몇 가지 패널을 특별히 만들었습니다.
설치
컴포저로 설치합니다. 실제로 이것은 Tracy가 프로덕션 에러 핸들링 구성 요소와 함께 제공되므로 개발 버전 없이 설치하는 것이 좋습니다.
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); // Debugger 표시줄이 표시되는 경우 Flight에서 내용 길이를 설정할 수 없습니다
// 이것은 Tracy Extension for 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
);
이제 이 테이블을 나타내는 새로운 클래스를 설정할 수 있습니다:
/**
* 액티브 레코드 클래스는 일반적으로 단수형입니다.
*
* 테이블의 속성을 여기에 주석으로 추가하는 것이 강력히 권장됩니다.
*
* @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('some cool password');
$user->insert();
// 또는 $user->save();
echo $user->id; // 1
$user->name = 'Joseph Mamma';
$user->password = password_hash('some cool password again!!!');
$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
사용법
이 라이브러리는 독립 실행형 라이브러리로 사용할 수도 있고 Flight PHP Framework와 함께 사용할 수도 있습니다. 완전히 당신의 선택입니다.
독립형
생성자에 PDO 연결을 전달해야 합니다.
$pdo_connection = new PDO('sqlite:test.db'); // 이는 예시일 뿐이며, 실제 데이터베이스 연결을 사용할 것입니다.
$User = new User($pdo_connection);
항상 생성자에 데이터베이스 연결을 설정하고 싶지 않으신가요? 다른 아이디어는 데이터베이스 연결 관리를 참조하세요!
Flight에서 메소드로 등록하기
Flight PHP Framework를 사용하고 있다면, ActiveRecord 클래스를 서비스로 등록할 수 있지만, 꼭 해야 하는 건 아닙니다.
Flight::register('user', 'User', [ $pdo_connection ]);
// 그러면 컨트롤러, 함수 등에서 이렇게 사용할 수 있습니다.
Flight::user()->find(1);
runway
메소드
runway는 이 라이브러리를 위한 사용자 정의 명령이 있는 Flight의 CLI 도구입니다.
# 사용법
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;
/**
* 사용자 테이블을 위한 액티브 레코드 클래스입니다.
* @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(); // 또는 필요에 따라 고유 ID를 생성하는 방법
}
}
삽입 전에 기본 키를 설정하지 않으면 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'; // 이제 이메일은 변경되었으므로 "더럽다"라고 간주됩니다.
$user->update();
// 이제 데이터베이스에 업데이트되고 지속되었기 때문에 더러운 데이터는 없습니다.
$user->password = password_hash('newpassword'); // 이제 이것은 더럽습니다.
$user->dirty(); // 아무것도 전달하지 않으면 모든 더러운 항목이 삭제됩니다.
$user->update(); // 캡처된 더러운 항목이 없으므로 아무것도 업데이트되지 않습니다.
$user->dirty([ 'name' => 'something', 'password' => password_hash('a different password') ]);
$user->update(); // 이름과 비밀번호가 모두 업데이트됩니다.
copyFrom(array $data): ActiveRecord
(v0.4.0)
이는 dirty()
메소드의 별칭입니다. 더 명확하게 무엇을 하고 있는지 알 수 있습니다.
$user->copyFrom([ 'name' => 'something', 'password' => password_hash('a different password') ]);
$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();
OR 조건
조건을 OR 문으로 감싸는 것이 가능합니다. 이는 startWrap()
및 endWrap()
메소드를 사용하거나 필드 및 값 뒤에 조건의 세 번째 매개변수를 채우는 방식으로 이루어집니다.
// 방법 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 = [
// 키의 이름을 원하는 대로 지정할 수 있습니다. 액티브 레코드의 이름이 적합할 것입니다. 예: user, contact, client
'user' => [
// 필수
// self::HAS_MANY, self::HAS_ONE, self::BELONGS_TO
self::HAS_ONE, // 이것이 관계의 유형입니다.
// 필수
'Some_Class', // 이 관계를 참조할 "다른" 액티브 레코드 클래스
// 필수
// 관계 유형에 따라
// self::HAS_ONE = 조인을 참조하는 외래 키
// self::HAS_MANY = 조인을 참조하는 외래 키
// self::BELONGS_TO = 조인을 참조하는 로컬 키
'local_or_foreign_key',
// 참고: 이것은 "다른" 모델의 기본 키만 조인됩니다.
// 선택 사항
[ '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)
레코드가 삽입된 후 데이터를 변경해야 할 경우가 있을 수 있습니다.
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 beforeInsert(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 afterInsert(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();
참고: 단위 테스트를 계획하고 있다면, 이렇게 하면 단위 테스트에 일부 도전과제가 발생할 수 있지만, 전체적으로
setDatabaseConnection()
또는$config['connection']
로 연결을 주입할 수 있기 때문에 큰 문제는 아닙니다.
장시간 CLI 스크립트를 실행하면서 연결을 새로 고쳐야 하는 경우, $your_record->setDatabaseConnection($pdo_connection)
로 연결을 다시 설정할 수 있습니다.
기여
부탁드립니다. :D
설정
기여할 때는 composer test-coverage
를 실행하여 100% 테스트 커버리지를 유지하세요 (이것은 진정한 단위 테스트 커버리지가 아니라, 더 통합 테스트에 가깝습니다).
또한 composer beautify
및 composer phpcs
를 실행하여 어떤 린트 오류를 수정하세요.
라이센스
MIT
Awesome-plugins/latte
라떼
라떼는 매우 쉽게 사용할 수 있으며 Twig 또는 Smarty보다 PHP 구문에 가깝게 느껴지는 완전한 기능의 템플릿 엔진입니다. 또한 매우 쉽게 확장하고 자체 필터 및 함수를 추가할 수 있습니다.
설치
컴포저로 설치하세요.
composer require 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->setTempDirectory(__DIR__ . '/../cache/');
// 라떼에게 보기의 루트 디렉터리가 어디에 있는지 알려줍니다.
// $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 . ' - '}My App</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<nav>
<!-- 여기에 당신의 nav 요소 -->
</nav>
</header>
<div id="content">
<!-- 여기에 마법이 있습니다 -->
{block content}{/block}
</div>
<div id="footer">
© 저작권
</div>
</body>
</html>
이제 해당 콘텐츠 블록 내부에 렌더링될 파일이 있습니다.
<!-- app/views/home.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' => '홈 페이지'
]);
}
}
라떼 문서에서 라떼를 최대한 활용하는 방법에 대해 자세히 알아보세요!
Awesome-plugins/awesome_plugins
멋진 플러그인
Flight는 믿을 수 없을 정도로 확장 가능하다. 귀하의 Flight 애플리케이션에 기능을 추가하는 데 사용할 수 있는 여러 플러그인이 있다. 일부는 Flight 팀에서 공식적으로 지원하며, 다른 일부는 시작하는 데 도움이 되는 마이크로/라이트 라이브러리이다.
API 문서화
API 문서는 모든 API에 매우 중요하다. 이는 개발자가 귀하의 API와 상호작용하는 방법과 반환되는 내용을 이해하는 데 도움이 된다. 귀하의 Flight 프로젝트에 대한 API 문서를 생성하는 데 도움이 되는 몇 가지 도구가 있다.
- FlightPHP OpenAPI Generator - FlightPHP와 함께 OpenAPI Spec을 사용하는 방법에 대해 Daniel Schreiber가 작성한 블로그 게시물로, API 우선 접근 방식을 사용하여 API를 구축하는 데 도움이 된다.
- SwaggerUI - Swagger UI는 Flight 프로젝트에 대한 API 문서를 생성하는 데 도움이 되는 훌륭한 도구이다. 사용이 매우 간편하며 귀하의 요구에 맞게 사용자 정의할 수 있다. 이는 Swagger 문서를 생성하는 데 도움이 되는 PHP 라이브러리이다.
인증/권한 부여
인증 및 권한 부여는 누가 무엇에 접근할 수 있는지 제어가 필요한 모든 애플리케이션에 매우 중요하다.
- 공식 flightphp/permissions - 공식 Flight 권한 라이브러리. 이 라이브러리는 귀하의 애플리케이션에 사용자 및 애플리케이션 수준의 권한을 추가하는 간단한 방법이다.
캐싱
캐싱은 애플리케이션의 속도를 높이는 훌륭한 방법이다. Flight와 함께 사용할 수 있는 여러 캐싱 라이브러리가 있다.
- 공식 flightphp/cache - 가볍고 간단하며 독립적인 PHP 인파일 캐싱 클래스
CLI
CLI 애플리케이션은 귀하의 애플리케이션과 상호작용하는 훌륭한 방법이다. 이들을 사용하여 컨트롤러를 생성하고, 모든 라우트를 표시하고, 더 많은 작업을 수행할 수 있다.
- 공식 flightphp/runway - Runway는 귀하의 Flight 애플리케이션을 관리하는 데 도움이 되는 CLI 애플리케이션이다.
쿠키
쿠키는 클라이언트 측에서 작은 데이터 조각을 저장하는 훌륭한 방법이다. 이는 사용자 기본 설정, 애플리케이션 설정 등을 저장하는 데 사용될 수 있다.
- overclokk/cookie - PHP Cookie는 쿠키 관리를 위한 간단하고 효과적인 방법을 제공하는 PHP 라이브러리이다.
디버깅
디버깅은 로컬 환경에서 개발할 때 매우 중요하다. 귀하의 디버깅 경험을 향상시킬 수 있는 몇 가지 플러그인이 있다.
- tracy/tracy - 이는 Flight와 함께 사용할 수 있는 전체 기능의 오류 처리기이다. 애플리케이션을 디버깅하는 데 도움이 되는 여러 패널이 있다. 또한 매우 쉽게 확장하고 사용자 정의 패널을 추가할 수 있다.
- flightphp/tracy-extensions - Tracy 오류 처리기와 함께 사용되는 이 플러그인은 Flight 프로젝트에 특히 유용한 디버깅 패널을 추가한다.
데이터베이스
데이터베이스는 대부분의 애플리케이션의 핵심이다. 이는 데이터를 저장하고 검색하는 방법이다. 일부 데이터베이스 라이브러리는 쿼리를 작성하기 위한 래퍼일 뿐이고, 일부는 완전한 ORM이다.
- 공식 flightphp/core PdoWrapper - 코어의 일부인 공식 Flight PDO 래퍼. 이는 쿼리를 작성하고 실행하는 과정을 단순화하는 데 도움이 되는 간단한 래퍼이다. ORM이 아니다.
- 공식 flightphp/active-record - 공식 Flight ActiveRecord ORM/매퍼. 데이터베이스에서 데이터를 쉽게 검색하고 저장하는 데 유용한 작은 라이브러리이다.
- byjg/php-migration - 프로젝트에 대한 모든 데이터베이스 변경 사항을 추적하는 플러그인이다.
암호화
암호화는 민감한 데이터를 저장하는 모든 애플리케이션에 매우 중요하다. 데이터의 암호화 및 복호화는 그리 어렵지 않지만, 암호화 키를 적절히 저장하는 것은 어려울 수 있다 어려운 문제이다 어려운 문제이다. 가장 중요한 것은 절대 암호화 키를 공개 디렉터리에 저장하거나 코드 저장소에 커밋하지 않는 것이다.
- defuse/php-encryption - 이는 데이터를 암호화하고 복호화하는 데 사용할 수 있는 라이브러리이다. 암호화 및 복호화를 시작하는 것은 비교적 간단하다.
작업 큐
작업 큐는 비동기적으로 작업을 처리하는 데 정말 유용하다. 이는 이메일을 보내거나, 이미지를 처리하거나, 실시간으로 수행할 필요가 없는 모든 작업을 할 수 있다.
- n0nag0n/simple-job-queue - Simple Job Queue는 비동기적으로 작업을 처리하는 데 사용할 수 있는 라이브러리이다. 이는 beanstalkd, MySQL/MariaDB, SQLite 및 PostgreSQL과 함께 사용할 수 있다.
세션
세션은 API에는 그리 유용하지 않지만, 웹 애플리케이션을 구축할 때 세션은 상태 및 로그인 정보를 유지하는 데 매우 중요할 수 있다.
- 공식 flightphp/session - 공식 Flight 세션 라이브러리. 이는 세션 데이터를 저장하고 검색하는 데 사용할 수 있는 간단한 세션 라이브러리이다. PHP의 내장 세션 처리 기능을 사용한다.
- Ghostff/Session - PHP 세션 관리자(비차단, 플래시, 세그먼트, 세션 암호화). 선택적인 세션 데이터의 암호화/복호화에 PHP open_ssl을 사용한다.
템플릿화
템플릿화는 UI가 있는 모든 웹 애플리케이션의 핵심이다. Flight와 함께 사용할 수 있는 여러 템플릿 엔진이 있다.
- 사용 중단됨 flightphp/core View - 이는 코어의 일부인 매우 기본적인 템플릿 엔진이다. 프로젝트에 페이지가 두 개 이상 있다면 사용하지 않는 것이 좋다.
- latte/latte - Latte는 사용하기 매우 쉬운 다양한 기능의 템플릿 엔진이며, Twig나 Smarty보다 PHP 문법에 더 가깝게 느껴진다. 또한 매우 쉽게 확장하고 사용자 정의 필터 및 함수를 추가할 수 있다.
기여하기
나누고 싶은 플러그인이 있습니까? 목록에 추가하려면 풀 리퀘스트를 제출하세요!
Media
미디어
인터넷에서 Flight에 대한 다양한 유형의 미디어를 추적해 보았습니다. Flight에 대해 더 배우기 위해 사용할 수 있는 다양한 리소스를 아래에서 확인하세요.
기사 및 글
- 정의, 생성 및 구현: OpenAPI Generator 및 FlightPHP와 함께하는 API-First 접근법 저자: 다니엘 슈라이버 (2025)
- 2024년을 위한 최고의 PHP 마이크로 프레임워크 저자: n0nag0n (2024)
- Flight 프레임워크로 RESTful API 만들기 저자: n0nag0n (2024)
- Flight로 간단한 블로그 구축하기 2부 저자: n0nag0n (2024)
- Flight로 간단한 블로그 구축하기 1부 저자: n0nag0n (2024)
- 🚀 Flight 프레임워크로 PHP에서 간단한 CRUD API 구축하기 저자: 소헤일 칼레다바디 (2024)
- Flight 마이크로 프레임워크로 PHP 웹 애플리케이션 구축하기 저자: 아서 C. 코드엑스 (2023)
- 2024년 웹 개발을 위한 최고의 PHP 프레임워크 저자: 라비키란 A S (2023)
- 2023년을 위한 포괄적인 가이드: 최고의 12 PHP 프레임워크 저자: 마케팅 kbk (2023)
- 당신이 (아마도) 들어본 적 없는 5개의 PHP 프레임워크 저자: n0nag0n (2022)
- 2023년에 웹 개발자가 고려해야 할 12개의 최고의 PHP 프레임워크 저자: Anna Monus (2022)
- 클라우드 서버에서 최고의 PHP 마이크로 프레임워크 저자: 샤제브 아흐메드 (2021)
- 웹 개발을 위한 최고의 15개 PHP 프레임워크 저자: AHT 테크 (2020)
- FlightPHP로 간단한 PHP 라우팅 하기 저자: 루카스 콘세이상 (2019)
- 새로운 PHP 프레임워크(Flight)를 시도해 보기 저자: 레온 (2017)
- Backbonejs와 함께 사용하기 위해 FlightPHP 설정하기 저자: 티모시 토치 (2015)
비디오 및 튜토리얼
- PHP & FlightPHP를 사용하여 IoT 장치를 위한 REST API 생성하기 - ESP32 API 저자: IoT 크래프트 허브 (2024)
- PHP Flight 프레임워크 간단 소개 비디오 저자: n0nag0n (2024)
- Flightphp에서 HTTP 코드 헤더 설정하기 (3가지 솔루션!!) 저자: 로엘 반 데 파르 (2024)
- PHP Flight 프레임워크 튜토리얼. 매우 간단한 API 프로젝트! 저자: n0nag0n (2022)
- PHP와 MySQL 및 Bootstrap을 사용한 CRUD 웹 애플리케이션 저자: Devlopteca - 오스카 우흐 (2021)
- DevOps & 시스템 관리자: Flight PHP 마이크로 프레임워크를 위한 Lighttpd 재작성 규칙 저자: 로엘 반 데 파르 (2021)
- 튜토리얼 REST API Flight PHP #PART2 테이블 정보 삽입 #코드 (타갈로그) 저자: 정보 싱캇 공식 (2020)
- 튜토리얼 REST API Flight PHP #PART1 정보 #코드 (타갈로그) 저자: 정보 싱캇 공식 (2020)
- PHP에서 JSON REST API 생성하는 방법 - 2부 저자: 코드와이프 (2018)
- PHP에서 JSON REST API 생성하는 방법 - 1부 저자: 코드와이프 (2018)
- 테스트 마이크로 프레임워크 PHP - Flight PHP, Lumen, Slim 3 및 Laravel 저자: 코드마켓 (2016)
- 튜토리얼 1 Flight PHP - 설치 저자: 앱삭 (2014)
- 튜토리얼 2 Flight PHP - 라우팅 1부 저자: 앱삭 (2014)
Examples
빠른 시작이 필요하신가요?
새로운 Flight 프로젝트를 시작하는 데는 두 가지 옵션이 있습니다:
- 전체 스켈레톤 보일러플레이트: 컨트롤러와 뷰가 포함된 더 완전한 예제입니다.
- 단일 파일 스켈레톤 보일러플레이트: 앱을 단일 간단 파일로 실행하는 데 필요한 모든 것을 포함하는 단일 파일입니다.
커뮤니티가 기여한 예제:
- flightravel: PHP 도구와 GH Actions가 포함된 Laravel 디렉터리의 FlightPHP
- fleact - ReactJS 통합이 포함된 FlightPHP 스타터 키트입니다.
- flastro - Astro 통합이 포함된 FlightPHP 스타터 키트입니다.
- velt - Velt는 FlightPHP 백엔드와 함께 빠르고 쉬운 Svelte 스타터 템플릿입니다.
영감이 필요하신가요?
이것들은 Flight 팀에서 공식적으로 후원하는 것은 아니지만, Flight로 구축된 자신의 프로젝트 구조에 대한 아이디어를 줄 수 있습니다!
- Decay - 좀비에 관한 HTMX 및 SleekDB가 포함된 Flight v3! (데모)
- Flight 예제 블로그 - 미들웨어, 컨트롤러, 액티브 레코드 및 Latte가 포함된 Flight v3입니다.
- Flight CRUD RESTful API - Flight 프레임워크를 사용한 간단한 CRUD API 프로젝트로, CRUD 작업 및 데이터베이스 연결을 통해 PHP 응용 프로그램을 빠르게 설정할 수 있는 기본 구조를 제공합니다. 이 프로젝트는 RESTful API 개발을 위해 Flight를 사용하는 방법을 보여 주며, 초보자에게는 이상적인 학습 도구이자 경험이 더 많은 개발자에게 유용한 스타터 키트입니다.
- Flight 학교 관리 시스템 - Flight v3
- 댓글이 포함된 Paste Bin - Flight v3
- 기본 스켈레톤 앱
- 예제 위키
- IT-혁신자 PHP 프레임워크 응용 프로그램
- LittleEducationalCMS (스페인어)
- 이탈리아 황색 페이지 API
- 일반 콘텐츠 관리 시스템 (문서가 거의 없음)
- Flight와 medoo를 기반으로 한 작은 php 프레임워크.
- 예제 MVC 응용 프로그램
자신의 예제를 공유하고 싶으신가요?
공유하고 싶은 프로젝트가 있다면, 이 목록에 추가할 수 있도록 끌어오기 요청을 제출해 주세요!
Install/install
설치
파일 다운로드
시스템에 PHP가 설치되어 있는지 확인하십시오. 아니라면 시스템에 설치하는 방법에 대한 설명을 보려면 여기를 클릭하십시오.
만약 Composer를 사용 중이라면 다음 명령어를 실행할 수 있습니다:
composer require flightphp/core
또는 파일을 직접 다운로드하여 웹 디렉토리에 압축을 풉니다.
웹 서버 구성
내장 PHP 개발 서버
가장 간단한 방법으로 설치하고 실행할 수 있습니다. 내장 서버를 사용하여 응용 프로그램을 실행하고 sqlite3이 시스템에 설치되어 있다면 데이터베이스로 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를 설치하는 방법을 검색하십시오.
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 Apache를 설치하는 방법을 검색하십시오.
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
가 설치되어 있다면 이 지침을 건너뛰고 파일 다운로드 섹션으로 이동하십시오
그럼! macOS, Windows 10/11, Ubuntu 및 Rocky Linux에 PHP를 설치하는 지침이 있습니다. PHP의 다른 버전을 설치하는 방법에 대한 자세한 내용도 포함하겠습니다.
macOS
Homebrew를 사용하여 PHP 설치
-
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 버전 전환:
- 현재 버전 unlink하고 원하는 버전을 link하십시오:
brew unlink php brew link --overwrite --force php@8.1
- 설치된 버전 확인:
php -v
- 현재 버전 unlink하고 원하는 버전을 link하십시오:
Windows 10/11
수동으로 PHP 설치하기
-
PHP 다운로드:
- PHP for Windows 방문하고 최신 또는 특정 버전 (예: 7.4, 8.0)을 스레드되지 않은 zip 파일로 다운로드하십시오.
-
PHP 압축해제:
- 다운로드한 zip 파일을
C:\php
에 압축해제하십시오.
- 다운로드한 zip 파일을
-
시스템 PATH에 PHP 추가:
- 시스템 속성 > 환경 변수로 이동하십시오.
- 시스템 변수에서 Path를 찾아 편집을 클릭하십시오.
C:\php
경로 (또는 PHP를 압축해제한 위치)를 추가하십시오.- 모든 창을 닫으려면 확인을 클릭하십시오.
-
PHP 구성:
php.ini-development
을php.ini
로 복사하십시오.- 필요에 따라 PHP를 구성하려면
php.ini
를 편집하십시오 (예:extension_dir
설정, 확장 기능 활성화).
-
PHP 설치 확인:
- 명령 프롬프트 열고 실행:
php -v
- 명령 프롬프트 열고 실행:
여러 버전의 PHP 설치
-
각 버전에 대해 위의 단계를 반복하되 각 버전을 별도의 디렉토리에 둡니다 (예:
C:\php7
,C:\php8
). -
원하는 버전의 디렉토리로 시스템 PATH 변수를 조정하여 버전 간에 전환하십시오.
Ubuntu (20.04, 22.04 등)
apt를 사용하여 PHP 설치
-
패키지 목록 업데이트:
- 터미널 열고 다음을 실행하십시오:
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
yum/dnf를 사용하여 PHP 설치
-
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
모듈 명령 사용:sudo dnf module reset php sudo dnf module enable php:remi-8.0 sudo dnf install php
-
설치된 버전 확인:
- 실행:
php -v
- 실행:
일반 참고 사항
- 개발 환경에서는 프로젝트 요구 사항에 맞게 PHP 설정을 구성하는 것이 중요합니다.
- PHP 버전을 전환할 때 해당 버전에 필요한 모든 관련 PHP 확장 기능이 설치되어 있는지 확인하십시오.
- PHP 버전을 전환하거나 환경을 업데이트 한 후 변경사항을 적용하려면 웹 서버 (Apache, Nginx 등)를 다시 시작하십시오.
Guides
가이드
Flight PHP는 단순하면서도 강력하게 설계되었으며, 우리의 가이드는 실제 애플리케이션을 단계별로 구축하는 데 도움을 줍니다. 이러한 실용적인 튜토리얼은 Flight를 효과적으로 사용하는 방법을 보여주는 완전한 프로젝트를 안내합니다.
공식 가이드
블로그 만들기
Flight PHP로 기능적인 블로그 애플리케이션을 만드는 방법을 배우세요. 이 가이드는 다음을 안내합니다:
- 프로젝트 구조 설정
- Latte를 사용한 템플릿 작업
- 게시물 라우트 구현
- 데이터 저장 및 검색
- 폼 제출 처리
- 기본 오류 처리
이 튜토리얼은 실제 애플리케이션에서 모든 구성 요소가 어떻게 조화롭게 작동하는지 보고자 하는 초보자에게 완벽합니다.
비공식 가이드
이 가이드는 Flight 팀에 의해 공식적으로 유지 관리되지 않지만, 커뮤니티에서 만든 유용한 자료입니다. 다양한 주제와 사용 사례를 다루며, Flight PHP 사용에 대한 추가 통찰력을 제공합니다.
Flight 프레임워크로 RESTful API 만들기
이 가인은 Flight PHP 프레임워크를 사용하여 RESTful API를 만드는 과정을 안내합니다. API 설정의 기초, 라우트 정의 및 JSON 응답 반환을 다룹니다.
간단한 블로그 만들기
이 가이드는 Flight PHP 프레임워크를 사용하여 기본 블로그를 만드는 과정을 안내합니다. 실제로 2 부분으로 나뉘어 있으며, 하나는 기본적인 내용을 다루고 다른 하나는 생산 준비가 완료된 블로그를 위한 더 고급 주제와 세부 사항을 다룹니다.
- Flight로 간단한 블로그 만들기 - 1부 - 간단한 블로그 시작하기.
- Flight로 간단한 블로그 만들기 - 2부 - 생산을 위한 블로그 다듬기.
PHP로 포켓몬 API 만들기: 초보자 가이드
이 재미있는 가이드는 Flight PHP를 사용하여 간단한 포켓몬 API를 만드는 과정을 안내합니다. API 설정의 기초, 라우트 정의 및 JSON 응답 반환을 다룹니다.
기여하기
가이드에 대한 아이디어가 있으신가요? 실수를 찾으셨나요? 기여를 환영합니다! 우리의 가이드는 FlightPHP 문서 저장소에서 유지 관리됩니다.
Flight로 흥미로운 것을 만들었고 그것을 가이드로 공유하고 싶다면, 풀 리퀘스트를 제출해주세요. 지식을 공유하는 것은 Flight 커뮤니티의 성장을 돕습니다.
API 문서 찾고 계신가요?
Flight의 핵심 기능 및 메서드에 대한 특정 정보를 찾고 계시다면, 우리의 문서 Learn 섹션을 확인하세요.