강력하면서도 사용하기 쉬운 PHP 마이크로 프레임워크는 역동적이고 견고한 웹 애플리케이션을 빠르게 구축할 수 있도록 도와줍니다!
단일 ~65KB 파일로 압축된 F3(우리가 즐겨 부르는 이름)은 웹 애플리케이션 작성에 대한 견고한 기반, 성숙한 코드 기반 및 말도 안되는 접근 방식을 제공합니다. 내부에는 사용하기 쉬운 웹 개발 도구 키트, 고성능 URL 라우팅 및 캐시 엔진, 내장 코드 강조 표시, 다국어 애플리케이션 지원이 포함되어 있습니다. 가볍고, 사용하기 쉽고 빠릅니다. 무엇보다도 방해가 되지 않습니다.
초보자이든 전문 PHP 프로그래머이든 F3를 사용하면 즉시 시작할 수 있습니다. 불필요하고 힘든 설치 절차가 없습니다. 복잡한 구성이 필요하지 않습니다. 복잡한 디렉토리 구조가 없습니다. 지금보다 쉬운 방법으로 웹 애플리케이션 개발을 시작하기에 더 좋은 시기는 없습니다!
F3은 SQL 및 NoSQL 데이터베이스(MySQL, SQLite, MSSQL/Sybase, PostgreSQL, DB2 및 MongoDB)를 모두 지원합니다. 또한 프레임워크만큼 가벼운 데이터 추상화 및 모델링을 위한 강력한 객체 관계형 매퍼도 함께 제공됩니다. 구성이 필요하지 않습니다.
그게 다가 아닙니다. F3은 기능을 확장하는 다른 선택적 플러그인과 함께 패키지되어 있습니다.
다른 프레임워크와 달리 F3은 일반적이지 않지만 사용 가능한 것을 목표로 합니다.
프레임워크의 철학과 소프트웨어 아키텍처에 대한 접근 방식은 구조적 구성 요소의 미니멀리즘을 지향하여 애플리케이션 복잡성을 피하고 코드 우아함, 애플리케이션 성능 및 프로그래머 생산성 간의 균형을 맞추는 것입니다.
F3는 안정적인 엔터프라이즈급 아키텍처를 갖추고 있습니다. 탁월한 성능, 사용자 친화적인 기능 및 가벼운 설치 공간. 무엇을 더 요구할 수 있나요? 이 패키지를 얻으려면 이 패키지를 다운로드하거나 fatfree-core 저장소를 방문하여 최신 edge-version을 찾으십시오.
모든 작곡가 사용자의 경우:
composer create-project bcosca/fatfree
사용하여 새 프로젝트를 시작합니다.composer require bcosca/fatfree-core
숙련된 사용자는 업데이트된 코드 기반과 지속적인 개선을 활용하기 위해 최신 버전으로 새로운 애플리케이션을 개발하는 것이 좋습니다.
많은 코드 예제와 그래픽 가이드가 포함된 최신 사용자 가이드와 자세한 API 문서는 fatfreeframework.com/에서 찾을 수 있습니다.
물론 이 편리한 온라인 참조 자료는 F3에서 제공됩니다! 이는 프레임워크의 기능과 성능을 보여줍니다. 지금 확인해 보세요. github에서 직접 읽고 싶다면 github.com/F3Community/F3com-data에서 웹사이트 콘텐츠를 찾아보세요.
디자이너는 더할 것이 없을 때가 아니라, 더 이상 뺄 것이 없을 때 자신이 완벽함을 달성했다는 것을 압니다. -- 앙투안 드 생텍쥐페리
Fat-Free Framework를 사용하면 전체 웹 사이트를 순식간에 쉽게 구축할 수 있습니다. 최신 Javascript 툴킷 및 라이브러리와 동일한 강력함과 간결성을 갖춘 F3를 사용하면 보기에 좋고 신뢰할 수 있는 PHP 프로그램을 작성할 수 있습니다. PHP 소스 코드를 한 번 보면 누구나 쉽게 이해할 수 있고, 몇 줄의 코드로 얼마나 많은 작업을 수행할 수 있는지, 그리고 그 결과가 얼마나 강력한지 알 수 있습니다.
F3은 가장 잘 문서화된 프레임워크 중 하나입니다. 배우는 데에는 비용이 전혀 들지 않습니다. 탐색하기 어려운 디렉토리 구조와 눈에 띄는 프로그래밍 단계에 대한 엄격한 세트가 없습니다. 브라우저에 'Hello, World'
를 표시하기 위해 구성 옵션을 잔뜩 로드할 필요가 없습니다. Fat-Free는 더 많은 작업을 더 짧은 시간에 쉽게 완료할 수 있도록 많은 자유와 스타일을 제공합니다.
프로그래밍에 대한 F3의 선언적 접근 방식을 통해 초보자와 전문가 모두 PHP 코드를 쉽게 이해할 수 있습니다. 프로그래밍 언어 Ruby에 익숙하다면 Fat-Free와 Sinatra 마이크로 프레임워크가 ReSTful 웹 서비스에 대해 간단한 도메인 특정 언어를 사용하기 때문에 유사점을 발견할 수 있습니다. 그러나 Sinatra 및 PHP 버전(Fitzgerald, Limonade, Glue 등)과 달리 Fat-Free는 단순히 경로와 요청을 처리하는 것 이상의 기능을 제공합니다. 보기는 일반 텍스트, HTML, XML 또는 전자 메일 메시지 등 모든 형식이 될 수 있습니다. 프레임워크에는 빠르고 사용하기 쉬운 템플릿 엔진이 함께 제공됩니다. F3은 또한 Twig, Smarty 및 PHP 자체를 포함한 다른 템플릿 엔진과 원활하게 작동합니다. 모델은 다양한 데이터베이스 엔진과의 보다 복잡한 상호 작용을 위해 F3의 데이터 매퍼 및 SQL 도우미와 통신합니다. 다른 플러그인은 기본 기능을 더욱 확장합니다. 이는 많은 기능을 갖춘 전체 웹 개발 프레임워크입니다!
하드 드라이브 아무 곳에나 배포 패키지의 압축을 푼다. 기본적으로 프레임워크 파일과 선택적 플러그인은 lib/
경로에 있습니다. 원하는 방식으로 디렉토리 구조를 구성하십시오. 보안 강화를 위해 기본 폴더를 웹에서 액세스할 수 없는 경로로 이동할 수 있습니다. 필요하지 않은 플러그인을 삭제하세요. 나중에 언제든지 복원할 수 있으며 F3는 해당 존재를 자동으로 감지합니다.
중요: 애플리케이션이 APC, Memcached, WinCache, XCache 또는 파일 시스템 캐시를 사용하는 경우 이전 버전의 프레임워크를 새 버전으로 덮어쓰기 전에 먼저 모든 캐시 항목을 지우십시오.
올바른 버전의 PHP를 실행하고 있는지 확인하세요. F3은 PHP 7.2 이전 버전을 지원하지 않습니다. 오래된 PHP 버전에서는 새로운 언어 구성과 클로저/익명 함수가 지원되지 않기 때문에 구문 오류(오탐)가 곳곳에서 발생합니다. 확인하려면 콘솔을 엽니다(GNU/Linux의 경우 bash
쉘, Windows의 경우 cmd.exe
).
/path/to/php -v
PHP는 현재 실행 중인 특정 버전을 알려주며 다음과 유사한 결과를 얻게 됩니다.
PHP 7.4.21 (cli) (built: Jul 27 2021 15:56:07) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
with Xdebug v2.9.8, Copyright (c) 2002-2020, by Derick Rethans
필요한 경우 업그레이드하고 PHP 7.4 이상 릴리스로 전환한 경우 여기로 돌아오세요. Fatfree가 작동하려면 최소한 PHP 7.2가 필요합니다. 호스팅 서비스 제공업체가 필요한 경우 다음 서비스 중 하나를 사용해 보세요.
첫 번째 애플리케이션 작성을 시작할 시간입니다.
$ f3 = require ( ' path/to/base.php ' );
$ f3 -> route ( ' GET / ' ,
function () {
echo ' Hello, world! ' ;
}
);
$ f3 -> run ();
첫 번째 줄 앞에 적절한 경로를 추가하여 base.php
추가하세요. 위의 코드 조각을 웹 루트 폴더에 index.php
로 저장하세요. 우리는 첫 번째 웹 페이지를 작성했습니다.
작곡가를 사용하시나요? 그런 다음 composer require bcosca/fatfree
실행하고 다음을 사용하십시오.
require ' vendor/autoload.php ' ;
$ f3 = Base:: instance ();
$ f3 -> route ( ' GET / ' ,
function () {
echo ' Hello, world! ' ;
}
);
$ f3 -> run ();
첫 번째 명령은 프레임워크의 기능과 기능을 애플리케이션에서 사용할 수 있기를 원한다는 것을 PHP 인터프리터에게 알려줍니다. $f3->route()
메소드는 슬래시( /
)로 표시된 상대 URL에서 웹 페이지를 사용할 수 있음을 Fat-Free에 알립니다. http://www.example.com/
에 있는 귀하의 사이트를 방문하는 사람은 누구나 'Hello, world!'
메시지를 보게 될 것입니다. URL /
루트 페이지와 동일하기 때문에 메시지가 표시됩니다. http://www.example.com/inside/
와 같은 루트 페이지에서 분기되는 경로를 생성하려면 간단한 GET /inside
문자열을 사용하여 다른 경로를 정의할 수 있습니다.
위에 설명된 경로는 프레임워크가 HTTP GET
메서드를 사용하여 URL 요청을 수신할 때만 페이지를 렌더링하도록 지시합니다. 양식을 포함하는 보다 복잡한 웹 사이트는 POST
와 같은 다른 HTTP 메서드를 사용하며 이를 $f3->route()
사양의 일부로 구현할 수도 있습니다.
프레임워크가 루트 URL /
에 있는 웹 페이지에 대해 들어오는 요청을 확인하면 자동으로 요청을 처리하고 적절한 HTML 항목을 렌더링하는 데 필요한 코드가 포함된 콜백 함수로 요청을 라우팅합니다. 이 예에서는 'Hello, world!'
문자열만 보냅니다. 사용자의 웹 브라우저에.
그래서 우리는 첫 번째 경로를 설정했습니다. 그러나 F3에게 이를 처리할 프로세스가 있고 사용자의 웹 브라우저에 표시할 일부 텍스트가 있음을 알리는 것 외에는 별 효과가 없습니다. 사이트에 더 많은 페이지가 있는 경우 각 그룹마다 다른 경로를 설정해야 합니다. 지금은 간단하게 설명하겠습니다. 프레임워크가 요청 대기를 시작하도록 지시하려면 $f3->run()
명령을 실행합니다.
예제를 실행할 수 없나요? 이 간단한 프로그램을 서버에서 실행하는 데 문제가 있는 경우 웹 서버 설정을 약간 조정해야 할 수도 있습니다. 다음 섹션에서 샘플 Apache 구성을 살펴보세요(Nginx 및 Lighttpd와 함께).
아직도 문제가 있나요? $f3 = require('path/to/base.php');
할당은 스크립트의 출력보다 먼저 옵니다. base.php
HTTP 헤더를 수정하므로 이 할당 전에 브라우저에 출력되는 모든 문자는 오류를 발생시킵니다.
첫 번째 예는 삼키기가 그리 어렵지 않았죠? 무지방 수프에 좀 더 풍미를 더하고 싶다면 $f3->run()
명령 앞에 다른 경로를 삽입하세요:-
$ f3 -> route ( ' GET /about ' ,
function () {
echo ' Donations go to a local charity... us! ' ;
}
);
전역 네임스페이스를 함수 이름으로 복잡하게 만들고 싶지 않으신가요? Fat-Free는 경로 핸들러를 OOP 클래스 및 메소드에 매핑하는 다양한 방법을 인식합니다.
class WebPage {
function display () {
echo ' I cannot object to an object ' ;
}
}
$ f3 -> route ( ' GET /about ' , ' WebPage->display ' );
HTTP 요청은 정적 클래스 메소드로 라우팅될 수도 있습니다.
$ f3 -> route ( ' GET /login ' , ' Auth::login ' );
전달된 인수는 항상 두 번째 매개변수로 제공됩니다.
$ f3 -> route ( ' GET /hello/@name ' , ' User::greet ' );
class User {
public static function greet ( $ f3 , $ args ) { // $ args is type of Array
echo " Hello " . $ args [ ' name ' ];
}
}
제공된 이름 인수가 foo (/hello/foo)인 경우 다음 출력이 표시됩니다.
Hello foo
Fat-Free의 강력한 도메인별 언어(DSL)를 시연하여 다양한 가능성을 처리하는 단일 경로를 지정할 수 있습니다.
$ f3 -> route ( ' GET /brew/@count ' ,
function ( $ f3 ) {
echo $ f3 -> get ( ' PARAMS.count ' ). ' bottles of beer on the wall. ' ;
}
);
이 예에서는 URL의 일부를 나타내기 위해 @count
토큰을 지정하는 방법을 보여줍니다. 프레임워크는 /brew/
/brew/99
, /brew/98
등과 같이 /brew/ 접두사와 일치하는 모든 요청 URL을 제공합니다. 그러면 '99 bottles of beer on the wall'
및 '98 bottles of beer on the wall'
표시됩니다. , 각각. Fat-Free는 /brew/unbreakable
에 대한 페이지 요청도 허용합니다. (이를 통해 'unbreakable bottles of beer on the wall'
표시될 것으로 예상합니다.) 이러한 동적 경로가 지정되면 Fat-Free는 전역 PARAMS
배열 변수를 URL에 캡처된 문자열 값으로 자동으로 채웁니다. 콜백 함수 내의 $f3->get()
호출은 프레임워크 변수의 값을 검색합니다. 프레젠테이션이나 비즈니스 로직의 일부로 코드에 이 방법을 적용할 수 있습니다. 하지만 이에 대해서는 나중에 더 자세히 논의하겠습니다.
Fat-Free는 배열 점 표기법을 이해합니다. 코드에서 대신 PARAMS['count']
일반 표기법을 사용할 수 있습니다. 이는 오타 오류 및 불균형 중괄호가 발생하기 쉽습니다. 뷰와 템플릿에서 프레임워크는 Javascript와 다소 유사한 @PARAMS.count
표기법을 허용합니다. (뷰와 템플릿은 나중에 다루겠습니다.)
요청 패턴에서 토큰에 액세스하는 또 다른 방법은 다음과 같습니다.
$ f3 -> route ( ' GET /brew/@count ' ,
function ( $ f3 , $ params ) {
echo $ params [ ' count ' ]. ' bottles of beer on the wall. ' ;
}
);
별표( *
)를 사용하여 /brew
경로 뒤에 있는 모든 URL을 허용할 수 있습니다. 나머지 경로에 대해 별로 신경 쓰지 않는 경우:-
$ f3 -> route ( ' GET /brew/* ' ,
function () {
echo ' Enough beer! We always end up here. ' ;
}
);
고려해야 할 중요한 점: 동일한 애플리케이션에서 GET /brew/@count
및 GET /brew/*
모두 사용하면 Fat-Free(및 자신)가 혼란스러워질 것입니다. 둘 중 하나를 사용하십시오. 또 다른 점: Fat-Free는 GET /brew
GET /brew/@count
경로와 별개로 간주합니다. 각각은 서로 다른 경로 처리기를 가질 수 있습니다.
잠시만 기다리십시오. 이전의 모든 예에서는 이러한 경로를 저장하기 위해 하드 드라이브에 디렉터리를 실제로 만들지 않았습니다. 짧은 대답은: 그럴 필요가 없다는 것입니다. 모든 F3 경로는 가상입니다. 이는 하드 디스크 폴더 구조를 반영하지 않습니다. 프레임워크를 사용하지 않는 프로그램이나 정적 파일(이미지, CSS 등)이 있는 경우 이러한 파일의 경로가 애플리케이션에 정의된 경로와 충돌하지 않는 한 웹 서버 소프트웨어는 이를 서버가 올바르게 구성된 경우 사용자의 브라우저.
경로를 정의할 때 경로에 이름을 할당할 수 있습니다. 입력된 URL 대신 코드와 템플릿에 경로 이름을 사용하세요. 그런 다음 마케팅 담당자를 기쁘게 하기 위해 URL을 변경해야 하는 경우 경로가 정의된 부분만 변경하면 됩니다. 경로 이름은 PHP 변수 명명 규칙(점, 대시, 하이픈 금지)을 따라야 합니다.
경로 이름을 지정해 보겠습니다.
$ f3 -> route ( ' GET @beer_list: /beer ' , ' Beer->list ' );
이름은 경로 VERB(이 예에서는 GET
) 뒤에 @
기호가 앞에 삽입되고 콜론 :
기호로 URL 부분과 구분됩니다. 여기에 표시된 것처럼 코드를 더 쉽게 읽을 수 있도록 콜론 뒤에 공백을 삽입할 수 있습니다.
템플릿의 명명된 경로에 액세스하려면 ALIASES
하이브 배열의 키로 명명된 경로의 값을 가져옵니다.
< a href =" {{ @ALIASES.beer_list }} " > View beer list </ a >
방문자를 새 URL로 리디렉션하려면 다음과 같이 reroute()
메서드 내에서 명명된 경로를 호출합니다.
// a named route is a string value
$ f3 -> reroute ( ' @beer_list ' ); // note the single quotes
경로에서 토큰을 사용하는 경우 F3는 해당 토큰을 현재 값으로 바꿉니다. reroute를 호출하기 전에 토큰 값을 변경하려면 이를 두 번째 인수로 전달하십시오.:-
$ f3 -> route ( ' GET @beer_list: /beer/@country ' , ' Beer->bycountry ' );
$ f3 -> route ( ' GET @beer_list: /beer/@country/@village ' , ' Beer->byvillage ' );
// a set of key - value pairs is passed as argument to named route
$ f3 -> reroute ( ' @beer_list(@country=Germany) ' );
// if more than one token needed
$ f3 -> reroute ( ' @beer_list(@country=Germany,@village=Rhine) ' );
올바른 형식의 URL에 대한 RFC 1738 지침을 준수하지 않는 문자가 있는 경우 인수를 urlencode()
해야 합니다.
PHP의 최신 안정 버전에는 자체 웹 서버가 내장되어 있습니다. 다음 구성을 사용하여 시작하십시오.
php -S localhost:80 -t /var/www/
위 명령은 모든 요청을 웹 루트 /var/www
로 라우팅하기 시작합니다. 파일이나 폴더에 대한 수신 HTTP 요청이 수신되면 PHP는 웹 루트 내에서 이를 찾아 브라우저로 보냅니다. 그렇지 않으면 PHP는 기본 index.php
(F3 지원 코드 포함)를 로드합니다.
Apache를 사용하는 경우 apache.conf(또는 httpd.conf) 파일에서 URL 재작성 모듈(mod_rewrite)을 활성화했는지 확인하세요. 또한 다음을 포함하는 .htaccess 파일을 생성해야 합니다.
# Enable rewrite engine and route requests to framework
RewriteEngine On
# Some servers require you to specify the `RewriteBase` directive
# In such cases, it should be the path (relative to the document root)
# containing this .htaccess file
#
# RewriteBase /
RewriteRule ^(tmp)/|.ini$ - [R=404]
RewriteCond %{REQUEST_FILENAME} !-l
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .* index.php [L,QSA]
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
스크립트는 HTTP 요청이 도착할 때마다 물리적 파일( !-f
), 경로( !-d
) 또는 기호 링크( !-l
)를 찾을 수 없으면 제어권을 index.php
로 전송해야 한다고 Apache에 지시합니다. 메인/프론트 컨트롤러가 프레임워크를 호출합니다.
위에 언급된 Apache 지시문을 포함하는 .htaccess file
항상 index.php
와 동일한 폴더에 있어야 합니다.
또한 하드 드라이브에 있는 index.php
의 물리적 위치를 알 수 있도록 Apache를 설정해야 합니다. 일반적인 구성은 다음과 같습니다.
DocumentRoot " /var/www/html "
< Directory "/var/www/html">
Options -Indexes +FollowSymLinks +Includes
AllowOverride All
Order allow,deny
Allow from All
</ Directory >
여러 애플리케이션을 동시에 개발하는 경우 가상 호스트 구성을 관리하기가 더 쉽습니다.
NameVirtualHost *
< VirtualHost *>
ServerName site1.com
DocumentRoot " /var/www/site1 "
< Directory "/var/www/site1">
Options -Indexes +FollowSymLinks +Includes
AllowOverride All
Order allow,deny
Allow from All
</ Directory >
</ VirtualHost >
< VirtualHost *>
ServerName site2.com
DocumentRoot " /var/www/site2 "
< Directory "/var/www/site2">
Options -Indexes +FollowSymLinks +Includes
AllowOverride All
Order allow,deny
Allow from All
</ Directory >
</ VirtualHost >
각 ServerName
(예제에서는 site1.com
및 site2.com
)이 /etc/hosts
파일에 나열되어야 합니다. Windows에서는 C:/WINDOWS/system32/drivers/etc/hosts
편집해야 합니다. 변경 사항을 적용하려면 재부팅이 필요할 수 있습니다. 그런 다음 웹 브라우저에서 http://site1.com
또는 http://site2.com
주소를 지정할 수 있습니다. 가상 호스트를 사용하면 애플리케이션을 훨씬 쉽게 배포할 수 있습니다.
Nginx 서버의 경우 권장 구성은 다음과 같습니다(ip_address:port를 해당 환경의 FastCGI PHP 설정으로 교체).
server {
root /var/www/html;
location / {
index index.php index.html index.htm;
try_files $uri $uri / /index.php? $query_string ;
}
location ~ .php$ {
fastcgi_pass ip_address:port;
include fastcgi_params;
}
}
Lighttpd 서버는 비슷한 방식으로 구성됩니다.
$HTTP["host"] =~ "www.example.com$" {
url.rewrite-once = ( "^/(.*?)(?.+)?$"=>"/index.php/$1?$2" )
server.error-handler-404 = "/index.php"
}
URL 재작성 모듈과 Windows 버전에 해당하는 적절한 .NET 프레임워크를 설치합니다. 그런 다음 애플리케이션 루트에 다음 내용으로 web.config
라는 파일을 만듭니다.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Application" stopProcessing="true">
<match url=".*" ignoreCase="false" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
</conditions>
<action type="Rewrite" url="index.php" appendQueryString="true" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
그럼 다시 코딩으로 돌아가 보겠습니다. 페이지를 더 이상 사용되지 않는다고 선언하고 방문자를 다른 사이트/페이지로 리디렉션할 수 있습니다.
$ f3 -> route ( ' GET|HEAD /obsoletepage ' ,
function ( $ f3 ) {
$ f3 -> reroute ( ' /newpage ' );
}
);
누군가 HTTP GET 또는 HEAD 요청을 사용하여 URL http://www.example.com/obsoletepage
에 액세스하려고 하면 프레임워크는 다음과 같이 사용자를 URL: http://www.example.com/newpage
로 리디렉션합니다. 위의 예. $f3->reroute('http://www.anotherexample.org/');
와 같은 다른 사이트로 사용자를 리디렉션할 수도 있습니다. .
경로 재지정은 사이트에서 일부 유지 관리 작업을 수행해야 할 때 특히 유용할 수 있습니다. 귀하의 사이트가 짧은 기간 동안 오프라인 상태임을 방문자에게 알리는 경로 핸들러를 가질 수 있습니다.
HTTP 리디렉션은 필수이지만 비용이 많이 들 수도 있습니다. 대상 경로를 처리하는 함수나 메서드를 호출하여 응용 프로그램의 흐름을 지시할 수 있는 경우 $f3->reroute()
사용하여 사용자를 동일한 웹 사이트의 다른 페이지로 보내는 것을 최대한 자제하세요. 그러나 이 접근 방식은 사용자 웹 브라우저의 주소 표시줄에 있는 URL을 변경하지 않습니다. 이것이 원하는 동작이 아니고 사용자를 다른 페이지로 보내야 하는 경우(예: 양식 제출 성공 또는 사용자 인증 후) Fat-Free는 HTTP 302 Found
헤더를 보냅니다. 다른 페이지나 사이트로 다시 라우팅하려는 모든 시도에 대해 프레임워크는 HTTP 301 Moved Permanently
헤더를 보냅니다.
런타임 시 Fat-Free는 들어오는 HTTP 요청이 애플리케이션에 정의된 경로와 일치하지 않는 것을 확인할 때마다 자동으로 HTTP 404 오류를 생성합니다. 그러나 직접 실행해야 하는 경우도 있습니다.
GET /dogs/@breed
로 정의된 경로를 예로 들어 보겠습니다. 애플리케이션 논리에는 데이터베이스 검색과 수신 HTTP 요청의 @breed
값에 해당하는 레코드 검색 시도가 포함될 수 있습니다. Fat-Free는 @breed
토큰이 있기 때문에 /dogs/
접두사 뒤에 있는 모든 값을 허용하므로 프로그램이 데이터베이스에서 일치하는 항목을 찾지 못할 때 프로그래밍 방식으로 HTTP 404 Not Found
메시지를 표시해야 합니다. 그렇게 하려면 다음 명령을 사용하십시오.
$ f3 -> error ( 404 );
Fat-Free의 아키텍처는 HTTP URI가 추상적인 웹 리소스(HTML에 국한되지 않음)를 나타내고 각 리소스가 한 애플리케이션 상태에서 다른 애플리케이션 상태로 이동할 수 있다는 개념을 기반으로 합니다. 이러한 이유로 F3에는 애플리케이션 구성 방식에 대한 제한이 없습니다. 모델-뷰-컨트롤러 패턴을 사용하려는 경우 F3을 사용하면 애플리케이션 구성 요소를 분류하여 이 패러다임을 고수할 수 있습니다. 반면에 프레임워크는 Resource-Method-Representation 패턴도 지원하며 이를 구현하는 것이 더 간단합니다.
다음은 ReST 인터페이스의 예입니다.
class Item {
function get () {}
function post () {}
function put () {}
function delete () {}
}
$ f3 = require ( ' lib/base.php ' );
$ f3 -> map ( ' /cart/@item ' , ' Item ' );
$ f3 -> run ();
Fat-Free의 $f3->map()
메소드는 경로의 HTTP 메소드를 객체 또는 PHP 클래스의 동등한 메소드에 매핑하여 ReST 인터페이스를 제공합니다. 응용 프로그램이 GET /cart/123
과 같은 수신 HTTP 요청을 받으면 Fat-Free는 자동으로 개체 또는 클래스의 get()
메서드에 제어권을 넘깁니다. 반면에 POST /cart/123
요청은 Item
클래스의 post()
메소드로 라우팅됩니다.
참고: 브라우저는 일반 HTML 형식으로 HTTP PUT
및 DELETE
메소드를 구현하지 않습니다. 이러한 메소드와 기타 ReST 메소드( HEAD
및 CONNECT
)는 서버에 대한 AJAX 호출을 통해서만 액세스할 수 있습니다.
프레임워크가 클래스에 의해 구현되지 않은 메서드에 매핑되는 경로에 대한 HTTP 요청을 수신하는 경우(경로 매핑에서 오류가 발생했거나 메서드가 아직 작성되지 않았을 수 있음) HTTP 405 Method Not Allowed
생성합니다. HTTP 405 Method Not Allowed
오류입니다.
클라이언트가 URL 리소스에 대해 HTTP OPTIONS
요청하면 F3은 리소스(HEAD, GET, PUT 등)에 허용되는 메서드를 나타내는 적절한 HTTP 헤더로 응답합니다. 프레임워크는 OPTIONS
요청을 클래스에 매핑하지 않습니다.
Fat-Free에는 필요할 때만 클래스를 로드하는 방법이 있으므로 애플리케이션의 특정 세그먼트에 필요한 것보다 더 많은 메모리를 차지하지 않습니다. 그리고 다른 파일과 다른 위치에 저장된 PHP 클래스를 로드하기 위해 긴 include
또는 require
문 목록을 작성할 필요가 없습니다. 프레임워크는 이 작업을 자동으로 수행할 수 있습니다. 폴더에 파일(파일당 하나의 클래스)을 저장하고 클래스에서 메서드를 호출하면 프레임워크에 적절한 파일을 자동으로 로드하도록 지시하기만 하면 됩니다.
$ f3 -> set ( ' AUTOLOAD ' , ' autoload/ ' );
AUTOLOAD
전역 변수의 값을 변경하여 자동 로드된 클래스에 대해 다른 위치를 할당할 수 있습니다. 자동 로드 경로가 여러 개 있을 수도 있습니다. 클래스가 서로 다른 폴더에 구성되어 있는 경우 정적 메서드가 호출되거나 개체가 인스턴스화될 때 프레임워크에 적절한 클래스를 자동 로드하도록 지시할 수 있습니다. AUTOLOAD
변수를 다음과 같이 수정하세요.
$ f3 -> set ( ' AUTOLOAD ' , ' admin/autoload/; user/autoload/; default/ ' );
중요: 프레임워크가 클래스를 올바르게 자동 로드하려면 .php 확장자를 제외하고 클래스 이름과 파일 이름이 동일해야 합니다. 이 파일의 기본 이름은 클래스 호출과 동일해야 합니다. 예를 들어 F3는 애플리케이션에서 new FooBarBaz
문을 감지하면 Foo/BarBaz.php
또는 foo/barbaz.php
를 찾습니다.
AUTOLOAD
하면 클래스 계층 구조가 유사한 이름의 하위 폴더에 상주할 수 있으므로 프레임워크가 다음 방식으로 호출되는 네임스페이스 클래스를 자동 로드하도록 하려는 경우:-
$ f3 -> set ( ' AUTOLOAD ' , ' autoload/ ' );
$ obj = new Gadgets iPad ;
동일한 구조를 따르는 폴더 계층 구조를 생성할 수 있습니다. /var/www/html/
이 웹 루트라고 가정하면 F3은 /var/www/html/autoload/gadgets/ipad.php
에서 클래스를 찾습니다. ipad.php
파일에는 다음과 같은 최소 코드가 있어야 합니다.
namespace Gadgets ;
class iPad {}
기억하세요: Fat-Free의 모든 디렉토리 이름은 슬래시로 끝나야 합니다. 다음과 같이 오토로더에 대한 검색 경로를 할당할 수 있습니다.
$ f3 -> set ( ' AUTOLOAD ' , ' main/;aux/ ' );
네임스페이스 인식 프레임워크인 F3을 사용하면 네임스페이스 클래스의 메서드를 경로 핸들러로 사용할 수 있으며 이를 수행하는 방법에는 여러 가지가 있습니다. 정적 메소드를 호출하려면:-
$ f3 -> set ( ' AUTOLOAD ' , ' classes/ ' );
$ f3 -> route ( ' GET|POST / ' , ' MainHome::show ' );
위 코드는 Main
네임스페이스 내 Home
클래스의 정적 show()
메서드를 호출합니다. Home
클래스가 자동으로 로드되려면 classes/main/home.php
폴더에 저장되어야 합니다.
개체 작업을 선호하는 경우:-
$ f3 -> route ( ' GET|POST / ' , ' MainHome->show ' );
런타임에 Home
클래스를 인스턴스화하고 그 후에 show()
메서드를 호출합니다.
F3에는 컨트롤러 클래스의 흐름과 구조를 개선하는 데 도움이 될 수 있는 몇 가지 라우팅 이벤트 리스너가 있습니다. 다음과 같이 정의된 경로가 있다고 가정해 보겠습니다.
$ f3 -> route ( ' GET / ' , ' Main->home ' );
애플리케이션이 위 경로와 일치하는 HTTP 요청을 수신하면 F3은 Main
인스턴스화하지만 home()
메서드를 실행하기 전에 프레임워크는 beforeRoute()
라는 이 클래스에서 메서드를 찾습니다. 발견된 경우 F3은 제어를 home()
메서드로 전송하기 전에 beforeRoute()
이벤트 핸들러에 포함된 코드를 실행합니다. 이 작업이 완료되면 프레임워크는 afterRoute()
이벤트 핸들러를 찾습니다. beforeRoute()
와 마찬가지로 메서드가 정의된 경우 실행됩니다.
F3의 또 다른 장점은 다음과 같습니다.
$ f3 -> route ( ' GET /products/@action ' , ' Products->@action ' );
애플리케이션이 /products/itemize
에 대한 요청을 받으면 F3은 URL에서 'itemize'
문자열을 추출하여 이를 경로 핸들러의 @action
토큰에 전달합니다. 그런 다음 F3은 Products
라는 클래스를 찾고 itemize()
메서드를 실행합니다.
동적 경로 핸들러는 다양한 형태를 가질 수 있습니다:-
// static method
$ f3 -> route ( ' GET /public/@genre ' , ' Main::@genre ' );
// object mode
$ f3 -> route ( ' GET /public/@controller/@action ' , ' @controller->@action ' );
F3은 현재 경로와 연결된 클래스나 메서드, 즉 정의되지 않은 클래스나 메서드로 제어를 전송할 수 없는 경우 런타임 시 HTTP 404 Not Found
오류를 트리거합니다.
라우팅 패턴에는 프레임워크가 HTTP 요청 유형에 따라 라우팅 결정을 내리도록 지시하는 수정자가 포함될 수 있습니다.
$ f3 -> route ( ' GET /example [ajax] ' , ' Page->getFragment ' );
$ f3 -> route ( ' GET /example [sync] ' , ' Page->getFull ' );
첫 번째 명령문은 X-Requested-With: XMLHttpRequest
헤더(AJAX 개체)가 서버에서 수신되는 경우에만 HTTP 요청을 Page->getFragment()
콜백으로 라우팅합니다. 일반(동기) 요청이 감지되면 F3은 일치하는 다음 패턴으로 드롭다운하고 이 경우 Page->getFull()
콜백을 실행합니다.
라우팅 패턴에 수정자가 정의되지 않은 경우 AJAX 및 동기 요청 유형이 모두 지정된 핸들러로 라우팅됩니다.
경로 패턴 수정자는 $f3->map()
에서도 인식됩니다.
Fat-Free에 정의된 변수는 전역적입니다. 즉, 모든 MVC 구성 요소에서 액세스할 수 있습니다. 프레임워크 전역은 PHP 전역과 동일하지 않습니다. content
라는 F3 변수는 PHP의 $content
와 동일하지 않습니다. F3은 그 자체로 도메인 특정 언어이며 시스템 및 응용 프로그램 변수에 대해 별도의 기호 테이블을 유지 관리합니다. 잘 설계된 모든 객체 지향 프로그램과 마찬가지로 프레임워크는 응용 프로그램과 충돌할 수 있는 상수, 변수, 함수 또는 클래스로 PHP 전역 네임스페이스를 오염시키지 않습니다. 다른 프레임워크와 달리 F3은 PHP의 define()
문을 사용하지 않습니다. 모든 프레임워크 상수는 클래스로 제한됩니다.
Fat-Free 변수에 값을 할당하려면:
$ f3 -> set ( ' var ' ,value); // or
$ f3 -> var =value;
$ f3 -> set ( ' hello.world ' , ' good morning ' ); // translates to : 'hello' == array ( 'world' = > 'good morning' )
$ f3 ->{ ' hello.world ' }= ' good morning ' ; // same as prior statement
참고: Fat-Free 변수는 객체 및 익명 함수를 포함한 모든 PHP 데이터 유형을 허용합니다.
여러 변수를 한 번에 설정하려면:
$ f3 -> mset (
[
' foo ' => ' bar ' ,
' baz ' => 123
]
);
var
라는 프레임워크 변수의 값을 검색하려면 :-
echo $ f3 -> get ( ' var ' ); // or
echo $ f3 -> var ;
더 이상 필요하지 않은 경우 Fat-Free 변수를 메모리에서 제거하려면(다른 기능/방법을 방해하지 않도록 삭제) 다음 방법을 사용하십시오.
$ f3 -> clear ( ' var ' ); // or
unset( $ f3 -> var );
변수가 이전에 정의되었는지 확인하려면:-
$ f3 -> exists ( ' var ' ) //
isset ( $ f3 -> var )
F3은 PHP와 독립적인 프레임워크 및 애플리케이션 변수에 대한 자체 기호 테이블을 유지 관리합니다. 일부 변수는 PHP 전역에 매핑됩니다. Fat-Free의 SESSION
$_SESSION
과 동일하며 REQUEST
$_REQUEST
에 매핑됩니다. 다양한 함수, 클래스 및 메소드 간 데이터 전송을 돕기 위해 PHP 대신 프레임워크 변수를 사용하는 것이 좋습니다. 또한 다른 장점도 있습니다.
SESSION
과 같은 PHP 전역에 해당하는 Fat-Free를 설정하면 PHP의 기본 $_SESSION
도 변경됩니다. 후자를 변경하면 프레임워크 대응도 변경됩니다. Fat-Free는 변수와 해당 값에 대한 단순한 저장 공간을 유지하지 않습니다. 또한 세션 관리 및 기타 작업을 자동화할 수도 있습니다. F3의 SESSION
변수를 통해 값을 할당하거나 검색하면 세션이 자동으로 시작됩니다. 프레임워크 변수 SESSION
대신 $_SESSION
(또는 세션 관련 함수)을 직접 사용하는 경우 애플리케이션이 세션 관리를 담당하게 됩니다.
일반적으로 프레임워크 변수는 HTTP 요청 간에 유지되지 않습니다. PHP의 $_SESSION
및 $_COOKIE
전역 변수에 매핑된 SESSION
및 COOKIE
(및 해당 요소)만 HTTP의 상태 비저장 특성에서 제외됩니다.
Fat-Free 내부적으로 사용되는 사전 정의된 전역 변수가 여러 개 있으며 이를 응용 프로그램에서 확실히 활용할 수 있습니다. 당신이 무엇을하고 있는지 확인하십시오. 일부 Fat-Free 전역 변수를 변경하면 예기치 않은 프레임워크 동작이 발생할 수 있습니다.
프레임워크에는 파일과 디렉터리 구조를 체계적으로 유지하는 데 도움이 되는 여러 변수가 있습니다. AUTOLOAD
사용하여 클래스 로딩을 자동화하는 방법을 살펴보았습니다. HTML 보기/템플릿의 위치를 가리키는 경로를 포함하는 UI
전역 변수가 있습니다. DEBUG
애플리케이션 개발 중에 자주 사용하게 될 또 다른 변수이며 오류 추적의 자세한 정도를 설정하는 데 사용됩니다.
내장된 프레임워크 변수의 전체 목록이 필요한 경우 빠른 참조를 참조하세요.
프레임워크 변수에는 문자, 숫자, 밑줄이 포함될 수 있습니다. 영문자로 시작해야 하며 공백이 없어야 합니다. 변수 이름은 대소문자를 구분합니다.
F3은 미리 정의된 내부 전역 변수에 모두 대문자를 사용합니다. 자신의 프로그램에서 모두 대문자로 구성된 변수 이름을 사용하는 것을 막을 수는 없지만, 일반적으로 자신의 변수를 설정할 때 소문자(또는 camelCase)를 사용하여 현재 및 향후 프레임워크 릴리스와의 충돌 가능성을 피할 수 있습니다. .
if
, for
, class
, default
등과 같은 PHP 예약어를 프레임워크 변수 이름으로 사용하면 안 됩니다. 이로 인해 예측할 수 없는 결과가 발생할 수 있습니다.
F3은 또한 프레임워크 변수에 도움이 되는 다양한 도구를 제공합니다.
$ f3 -> set ( ' a ' , ' fire ' );
$ f3 -> concat ( ' a ' , ' cracker ' );
echo $ f3 -> get ( ' a ' ); // returns the string 'firecracker'
$ f3 -> copy ( ' a ' , ' b ' );
echo $ f3 -> get ( ' b ' ); // returns the same string : 'firecracker'
F3은 또한 배열 변수 작업을 위한 몇 가지 기본 메서드를 제공합니다.
$ f3 -> set ( ' colors ' ,[ ' red ' , ' blue ' , ' yellow ' ]);
$ f3 -> push ( ' colors ' , ' green ' ); // works like PHP ' s array_push ()
echo $ f3 -> pop ( ' colors ' ); // returns 'green'
$ f3 -> unshift ( ' colors ' , ' purple ' ); // similar to array_unshift ()
echo $ f3 -> shift ( ' colors ' ); // returns 'purple'
$ f3 -> set ( ' grays ' ,[ ' light ' , ' dark ' ]);
$ result = $ f3 -> merge ( ' colors ' , ' grays ' ); // merges the two arrays
엄격한 폴더 구조를 가진 다른 프레임워크와 달리 F3은 많은 유연성을 제공합니다. 다음과 같은 폴더 구조를 가질 수 있습니다(모두 대문자로 괄호 안에 있는 단어는 조정이 필요한 F3 프레임워크 변수를 나타냅니다).
/ (your Web root, where index.php is located)
app/ (application files)
dict/ (LOCALES, optional)
controllers/
logs/ (LOGS, optional)
models/
views/ (UI)
css/
js/
lib/ (you can store base.php here)
tmp/ (TEMP, used by the framework)
cache/ (CACHE)
원하는 방식으로 파일과 디렉터리를 자유롭게 구성하세요. 적절한 F3 전역 변수를 설정하기만 하면 됩니다. 정말 안전한 사이트를 원한다면 Fat-Free를 사용하면 웹에서 액세스할 수 없는 디렉토리에 모든 파일을 저장할 수도 있습니다. 유일한 요구 사항은 index.php
, .htaccess
및 CSS, JavaScript, 이미지 등과 같은 공개 파일을 브라우저에 표시되는 경로에 남겨두는 것입니다.
Fat-Free는 디버깅에 도움이 되는 스택 추적과 함께 자체 HTML 오류 페이지를 생성합니다. 예는 다음과 같습니다.-
내부 서버 오류
strpos()는 최소 2개의 매개변수를 예상하며, 0이 주어지면
• var/html/dev/main.php:96 strpos() • var/html/dev/index.php:16 Base->run()
너무 평범하다고 느끼거나 오류가 발생할 때 다른 작업을 수행하려는 경우 고유한 사용자 정의 오류 처리기를 만들 수 있습니다.
$ f3 -> set ( ' ONERROR ' ,
function ( $ f3 ) {
// custom error handler code goes here
// use this if you want to display errors in a
// format consistent with your site ' s theme
echo $ f3 -> get ( ' ERROR.status ' );
}
);
F3은 애플리케이션에서 발생한 최신 오류의 세부 정보가 포함된 전역 변수를 유지 관리합니다. ERROR
변수는 다음과 같이 구성된 배열입니다.
ERROR.code - displays the error code (404, 500, etc.)
ERROR.status - header and page title
ERROR.text - error context
ERROR.trace - stack trace
애플리케이션을 개발하는 동안 모든 오류의 근본 원인을 추적할 수 있도록 디버그 수준을 최대로 설정하는 것이 가장 좋습니다.
$ f3 -> set ( ' DEBUG ' , 3 );
애플리케이션의 부트스트랩 시퀀스에 명령을 삽입하기만 하면 됩니다.
애플리케이션을 출시할 준비가 되면 애플리케이션에서 해당 문을 제거하거나 다음으로 바꾸세요.
$ f3 -> set ( ' DEBUG ' , 0 );
이렇게 하면 시스템에서 생성된 HTML 오류 페이지에서 스택 추적 출력이 억제됩니다(사이트 방문자에게 표시되지 않기 때문입니다).
DEBUG
0(스택 추적 억제)에서 3(가장 자세한 정보) 범위의 값을 가질 수 있습니다.
잊지 마세요! 스택 추적에는 경로, 파일 이름, 데이터베이스 명령, 사용자 이름 및 비밀번호가 포함될 수 있습니다. 프로덕션 환경에서 DEBUG
전역 변수를 0으로 설정하지 않으면 웹 사이트가 불필요한 보안 위험에 노출될 수 있습니다.
애플리케이션을 사용자가 구성할 수 있어야 하는 경우 F3은 구성 파일을 읽어 애플리케이션을 설정하는 편리한 방법을 제공합니다. 이렇게 하면 귀하와 귀하의 사용자는 PHP 코드를 변경하지 않고도 애플리케이션을 조정할 수 있습니다.
다음 샘플 코드를 포함하는 PHP 스크립트를 생성하는 대신:-
$ f3 -> set ( ' num ' , 123 );
$ f3 -> set ( ' str ' , ' abc ' );
$ f3 -> set ( ' hash ' ,[ ' x ' => 1 , ' y ' => 2 , ' z ' => 3 ]);
$ f3 -> set ( ' items ' ,[ 7 , 8 , 9 ]);
$ f3 -> set ( ' mix ' ,[ ' this ' , 123.45 , FALSE ]);
동일한 작업을 수행하는 구성 파일을 생성할 수 있습니다.
[globals]
num =123
; this is a regular string
str =abc
; another way of assigning strings
str = " abc "
; this is an array
hash[x]=1
hash[y]=2
hash[z]=3
; dot-notation is recognized too
hash.x =1
hash.y =2
hash.z =3
; this is also an array
items =7,8,9
; array with mixed elements
mix = " this " ,123.45,FALSE
코드에서 긴 $f3->set()
문 대신 구성 파일을 코드 대체 항목으로 로드하도록 프레임워크에 지시할 수 있습니다. 위의 텍스트를 setup.cfg로 저장해 보겠습니다. 그런 다음 간단하게 호출할 수 있습니다.
$ f3 -> config ( ' setup.cfg ' );
선행 또는 후행 공백을 포함하려는 경우가 아니면 문자열 값을 따옴표로 묶을 필요가 없습니다. 쉼표를 문자열의 일부로 처리해야 하는 경우 큰따옴표를 사용하여 문자열을 묶습니다. 그렇지 않으면 값이 배열로 처리됩니다(쉼표는 배열 요소 구분 기호로 사용됨). 문자열은 여러 줄에 걸쳐 있을 수 있습니다.
[globals]
str = " this is a
very long
string "
F3은 또한 구성 파일에서 HTTP 경로를 정의하는 기능을 제공합니다.
[routes]
GET /=home
GET / 404 =App->page404
GET /page/@ num =Page->@controller
경로 맵은 구성 파일에서도 정의할 수 있습니다.
[maps]
/ blog =BlogLogin
/blog/@ controller =Blog@controller
[globals]
, [routes]
및 [maps]
섹션 헤더가 필요합니다. 단일 구성 파일에 두 섹션을 결합할 수 있습니다. 하지만 [routes]
및 [maps]
별도의 파일에 포함하는 것이 좋습니다. 이렇게 하면 최종 사용자가 일부 애플리케이션별 플래그를 수정하도록 허용하는 동시에 라우팅 논리에 간섭하지 못하도록 제한할 수 있습니다.
HTML 페이지와 같은 사용자 인터페이스는 라우팅 및 비즈니스 로직과 관련된 기본 PHP 코드와 독립적이어야 합니다. 이는 MVC 패러다임의 기본입니다. <h3>
<p>
로 변환하는 것과 같은 기본 개정에서는 애플리케이션 코드 변경을 요구해서는 안 됩니다. 같은 방식으로, GET /about
과 같은 간단한 경로를 GET /about-us
로 변환하는 것은 사용자 인터페이스와 비즈니스 로직(MVC의 뷰와 모델 또는 RMR의 표현과 메서드)에 어떤 영향도 주어서는 안 됩니다.
스파게티 코딩과 같은 단일 파일의 프로그래밍 구성 및 사용자 인터페이스 구성 요소를 혼합하면 향후 애플리케이션 유지 관리가 악몽이됩니다.
F3은 PHP를 템플릿 엔진으로 지원합니다. template.htm
:-로 저장된이 HTML 조각을 살펴보십시오.
< p > Hello, < ?php echo $name; ? > ! </ p >
서버에서 짧은 태그가 활성화되면 다음과 같이 작동합니다.
< p > Hello, < ?= $name ? > </ p >
이 템플릿을 표시하려면 다음과 같은 것처럼 보이는 PHP 코드를 가질 수 있습니다 (템플릿과 별개의 파일에 저장) :-
$ f3 = require ( ' lib/base.php ' );
$ f3 -> route ( ' GET / ' ,
function ( $ f3 ) {
$ f3 -> set ( ' name ' , ' world ' );
$ view = new View ;
echo $ view -> render ( ' template.htm ' );
// Previous two lines can be shortened to : -
// echo View :: instance () - > render ( 'template.htm' );
}
);
$ f3 -> run ();
이 파일의 내장 된 PHP 코드로 인해 PHP를 템플릿 엔진으로 사용하는 데있어 유일한 문제는 우려 사항 분리에 대한 지침을 고수하고 비즈니스 논리를 사용자 인터페이스와 혼합하는 유혹에 저항하는 데 필요한 의식적인 노력입니다.
PHP의 대안으로 F3의 자체 템플릿 엔진을 사용할 수 있습니다. 위의 HTML 조각은 다음과 같이 다시 작성할 수 있습니다.
< p > Hello, {{ @name }}! </ p >
이 템플릿을 보는 데 필요한 코드는 다음과 같습니다.
$ f3 = require ( ' lib/base.php ' );
$ f3 -> route ( ' GET / ' ,
function ( $ f3 ) {
$ f3 -> set ( ' name ' , ' world ' );
$ template = new Template ;
echo $ template -> render ( ' template.htm ' );
// Above lines can be written as : -
// echo Template :: instance () - > render ( 'template.htm' );
}
);
$ f3 -> run ();
URL에서 변수를 잡는 데 사용되는 라우팅 토큰 (이전 섹션에서 GET /brew/@count
예제를 기억하십시오)과 마찬가지로 F3 템플릿 토큰은 @
기호로 시작한 다음 곱슬 브레이스로 둘러싸인 일련의 문자 및 숫자가 뒤 따릅니다. 첫 번째 캐릭터는 알파이어야합니다. 템플릿 토큰은 프레임 워크 변수와 일대일 서신을 가지고 있습니다. 이 프레임 워크는 토큰을 동일한 이름의 변수에 저장된 값으로 자동 대체합니다.
이 예에서 F3은 템플릿의 @name
토큰을 이름 변수에 할당 한 값으로 대체합니다. 런타임에 위의 코드의 출력은 다음과 같습니다.
< p > Hello, world </ p >
F3 템플릿의 성능에 대해 걱정하십니까? 런타임에 프레임 워크는 $template->render()
통해 처음 표시 될 때 F3 템플릿을 PHP 코드로 컴파일/컴파일/변환합니다. 그런 다음 프레임 워크는이 컴파일 된 코드를 모든 후속 통화에서 사용합니다. 따라서 더 복잡한 템플릿이 관련 될 때 템플릿 컴파일러의 코드 최적화로 인해 성능은 PHP 템플릿과 동일해야합니다.
PHP의 템플릿 엔진을 사용하든 F3의 자체를 사용하든 서버에 APC, Wincache 또는 Xcache를 사용할 수 있으면 템플릿 렌더링이 훨씬 빠를 수 있습니다.
앞에서 언급했듯이 프레임 워크 변수는 모든 PHP 데이터 유형을 보유 할 수 있습니다. 그러나 F3 템플릿에서 비 스칼라 데이터 유형을 사용하면 조심하지 않으면 이상한 결과가 발생할 수 있습니다. 곱슬 버팀대의 표현은 항상 평가되고 문자열로 변환됩니다. 사용자 인터페이스 변수를 간단한 스칼라로 제한해야합니다.- string
, integer
, boolean
또는 float
데이터 유형.
그러나 배열은 어떻습니까? 지방이없는 배열을 인식하고 템플릿에서 사용할 수 있습니다. 당신은 다음과 같은 것을 가질 수 있습니다.-
< p > {{ @buddy[0] }}, {{ @buddy[1] }}, and {{ @buddy[2] }} </ p >
템플릿을 제공하기 전에 PHP 코드에 @buddy
배열을 채우십시오.
$ f3 -> set ( ' buddy ' ,[ ' Tom ' , ' Dick ' , ' Harry ' ]);
그러나 템플릿에 {{ @buddy }}
간단히 삽입하면 PHP는 토큰을 문자열로 변환하기 때문에 'Array'
으로 대체합니다. 반면에 PHP는 런타임에 Array to string conversion
통지를 생성합니다.
F3을 사용하면 템플릿에 표현식을 포함시킬 수 있습니다. 이러한 표현식은 산술 계산, 부울 표현, PHP 상수 등과 같은 다양한 형태를 취할 수 있습니다. 다음은 다음과 같습니다.
{{ 2*(@page-1) }}
{{ (int)765.29+1.2e3 }}
< option value =" F " {{ @active? 'selected=" selected "':'' }} > Female </ option >
{{ var_dump(@xyz) }}
< p > That is {{ preg_match('/Yes/i',@response)?'correct':'wrong' }}! </ p >
{{ @obj- > property }}
배열 표현식에 대한 추가 참고 사항 : @foo.@bar
는 문자열 연결 $foo.$bar
), @foo.bar
$foo['bar']
로 번역됩니다. $foo[$bar]
의도 한 것이라면 @foo[@bar]
일반 표기법을 사용하십시오.
프레임 워크 변수에는 익명 기능이 포함될 수도 있습니다.
$ f3 -> set ( ' func ' ,
function ( $ a , $ b ) {
return $ a . ' , ' . $ b ;
}
);
F3 템플릿 엔진은 다음 표현식을 지정하면 토큰을 예상대로 해석합니다.
{{ @func('hello','world') }}
간단한 가변 대체는 모든 템플릿 엔진이 가진 것 중 하나입니다. 지방이없는 소매가 더 있습니다.-
< include href =" header.htm " />
지침은 Header.htm 템플릿의 내용을 지시문이 명시된 정확한 위치에 포함시킵니다. 다음과 같은 형태의 동적 컨텐츠를 가질 수도 있습니다.
< include href =" {{ @content }} " />
이러한 템플릿 지시문에 대한 실질적인 용도는 공통 HTML 레이아웃이 있지만 콘텐츠가 다른 여러 페이지가있을 때입니다. 기본 템플릿에 하위 템플릿을 삽입하도록 프레임 워크를 지시하는 것은 다음 PHP 코드를 작성하는 것만 큼 간단합니다.
// switch content to your blog sub - template
$ f3 -> set ( ' content ' , ' blog.htm ' );
// in another route , switch content to the wiki sub - template
$ f3 -> set ( ' content ' , ' wiki.htm ' );
하위 템플릿에는 수많은 지시문이 포함될 수 있습니다. F3은 무제한 중첩 템플릿을 허용합니다.
.htm 또는 .html 파일 확장자 이외의 내용으로 파일 이름을 지정할 수 있지만 개발 및 디버깅 단계에서 웹 브라우저에서 미리보기가 더 쉽습니다. 템플릿 엔진은 HTML 파일 렌더링에만 국한되지 않습니다. 실제로 템플릿 엔진을 사용하여 다른 종류의 파일을 렌더링 할 수 있습니다.
<include>
지시문에는 옵션이있는 if
속성이 있으므로 하위 템플릿이 삽입되기 전에 만족 해야하는 조건을 지정할 수 있습니다.
< include if =" {{ count(@items) }} " href =" items.htm " />
F3 기반 프로그램을 작성/디버깅하고 템플릿을 설계하는 과정에서 HTML 블록의 디스플레이를 비활성화하는 것이 편리 할 수 있습니다. 이 목적을 위해 <exclude>
지시문을 사용할 수 있습니다.
< exclude >
< p > A chunk of HTML we don't want displayed at the moment </ p >
</ exclude >
그것은 <!-- comment -->
html 주석 태그와 같지만 <exclude>
지시문은 템플릿이 렌더링되면 HTML 블록을 완전히 보이지 않습니다.
다음은 템플릿 내용을 제외하거나 주석을 추가하는 또 다른 방법입니다.
{* < p > A chunk of HTML we don't want displayed at the moment </ p > *}
또 다른 유용한 템플릿 기능은 <check>
지시입니다. 특정 조건의 평가에 따라 HTML 조각을 포함시킬 수 있습니다. 몇 가지 예는 다음과 같습니다.-
< check if =" {{ @page=='Home' }} " >
< false > < span > Inserted if condition is false </ span > </ false >
</ check >
< check if =" {{ @gender=='M' }} " >
< true >
< div > Appears when condition is true </ div >
</ true >
< false >
< div > Appears when condition is false </ div >
</ false >
</ check >
필요한만큼 중첩 된 <check>
지침을 가질 수 있습니다.
NULL
, 빈 문자열, 부울 FALSE
, 빈 배열 또는 0과 동일하게 IF 속성 내부의 F3 표현식은 자동으로 <false>
호출합니다. 템플릿에 <false>
블록이없는 경우 <true>
열기 및 닫는 태그는 선택 사항입니다.
< check if =" {{ @loggedin }} " >
< p > HTML chunk to be included if condition is true </ p >
</ check >
지방이없는 경우도 반복적 인 HTML 블록을 처리 할 수 있습니다.
< repeat group =" {{ @fruits }} " value =" {{ @fruit }} " >
< p > {{ trim(@fruit) }} </ p >
</ repeat >
<repeat>
지침 내부의 @fruits
group
속성은 배열이어야하며 다음과 같이 PHP 코드에 설정해야합니다.
$ f3 -> set ( ' fruits ' ,[ ' apple ' , ' orange ' , ' banana ' ]);
애플리케이션 코드에서 @fruit
에 값을 할당하면 얻을 수 없습니다. 지방이없는 것은 변수를 사용하여 그룹을 반복하는 동안 현재 항목을 나타 내기 때문에 사전 설정 값을 무시합니다. 위의 HTML 템플릿 조각의 출력 및 해당 PHP 코드는 다음과 같습니다.
< p > apple </ p >
< p > orange </ p >
< p > banana </ p >
이 프레임 워크는 <repeat>
블록의 무제한 중첩을 허용합니다.
< repeat group =" {{ @div }} " key =" {{ @ikey }} " value =" {{ @idiv }} " >
< div >
< p > < span > < b > {{ @ikey }} </ b > </ span > </ p >
< p >
< repeat group =" {{ @idiv }} " value =" {{ @ispan }} " >
< span > {{ @ispan }} </ span >
</ repeat >
</ p >
</ div >
</ repeat >
다음 F3 명령을 적용하십시오.
$ f3 -> set ( ' div ' ,
[
' coffee ' =>[ ' arabica ' , ' barako ' , ' liberica ' , ' kopiluwak ' ],
' tea ' =>[ ' darjeeling ' , ' pekoe ' , ' samovar ' ]
]
);
결과적으로 다음 HTML 조각을 얻을 수 있습니다.
< div >
< p > < span > < b > coffee </ b > </ span > </ p >
< p >
< span > arabica </ span >
< span > barako </ span >
< span > liberica </ span >
< span > kopiluwak </ span >
< p >
</ div >
< div >
< p > < span > < b > tea </ b > </ span > </ p >
< p >
< span > darjeeling </ span >
< span > pekoe </ span >
< span > samovar </ span >
</ p >
</ div >
놀랍지 않나요? PHP에서해야 할 유일한 일은 @div
토큰을 대체하기 위해 단일 F3 변수 div
의 내용을 정의하는 것이 었습니다. 지방이 없으면 프로그래밍과 웹 템플릿 디자인이 모두 쉽습니다.
<repeat>
pealplate directive의 value
속성은 반복에서 현재 요소의 값을 반환합니다. 현재 요소의 배열 키를 가져와야하는 경우 대신 key
속성을 사용하십시오. key
속성은 선택 사항입니다.
<repeat>
또한 다음과 같이 사용할 수있는 선택적 카운터 속성이 있습니다.
< repeat group =" {{ @fruits }} " value =" {{ @fruit }} " counter =" {{ @ctr }} " >
< p class =" {{ @ctr%2?'odd':'even' }} " > {{ trim(@fruit) }} </ p >
</ repeat >
내부적으로 F3의 템플릿 엔진은 루프 반복 수를 기록하고 변수/토큰 @ctr
에 해당 값을 저장하며,이 예제에서 홀수/짝수 분류를 결정하는 데 사용됩니다.
템플릿의 <script>
또는 <style>
섹션 내부에 f3 토큰을 삽입 해야하는 경우 프레임 워크는 여전히 일반적인 방법을 대체합니다.
< script type =" text/javascript " >
function notify ( ) {
alert ( 'You are logged in as: {{ @userID }}' ) ;
}
</ script >
<script>
또는 <style>
태그 내부에 템플릿 지시를 포함시켜야합니다. 특별한 처리가 필요하지 않습니다.
< script type =" text/javascript " >
var discounts = [ ] ;
< repeat group = "{{ @rates }}" value = "{{ @rate }}" >
// whatever you want to repeat in Javascript, e.g.
discounts.push(" { { @ rate } } ");
</ repeat >
</ script >
기본적으로 지방이없는 것은 변경되지 않는 한 UTF-8 문자 세트를 사용합니다. 다음과 같은 것을 발행 하여이 동작을 무시할 수 있습니다.
$ f3 -> set ( ' ENCODING ' , ' ISO-8859-1 ' );
원하는 문자 세트의 프레임 워크를 알리면 F3은 다시 변경 될 때까지 모든 HTML 및 XML 템플릿에서 사용합니다.
이 섹션의 앞부분에서 언급했듯이 프레임 워크는 HTML 템플릿에만 국한되지 않습니다. XML 템플릿도 처리 할 수 있습니다. 역학은 거의 비슷합니다. 당신은 여전히 {{ @variable }}
및 {{ expression }}
토큰, <repeat>
, <check>
, <include>
및 <exclude>
지시 사항이 동일합니다. HTML 대신 XML 파일을 전달한다고 F3에게 말하십시오.
echo Template:: instance ()-> render ( ' template.xml ' , ' application/xml ' );
두 번째 인수는 렌더링되는 문서의 마임 유형을 나타냅니다.
MVC의보기 구성 요소는 모델 및 컨트롤러에 해당하지 않는 모든 것을 다루며, 이는 프레젠테이션이 RSS, 이메일, RDF, FOAF, 텍스트 파일 등과 같은 모든 종류의 사용자 인터페이스를 포함시켜야한다는 것을 의미합니다. 아래는 응용 프로그램의 비즈니스 로직에서 이메일 프리젠 테이션을 분리하는 방법을 보여줍니다.
MIME-Version: 1.0
Content-type: text/html; charset={{ @ENCODING }}
From: {{ @from }}
To: {{ @to }}
Subject: {{ @subject }}
< p > Welcome, and thanks for joining {{ @site }}! </ p >
위의 이메일 템플릿을 Welcom.txt로 저장하십시오. 관련 F3 코드는 다음과 같습니다.-
$ f3 -> set ( ' from ' , ' <[email protected]> ' );
$ f3 -> set ( ' to ' , ' <[email protected]> ' );
$ f3 -> set ( ' subject ' , ' Welcome ' );
ini_set ( ' sendmail_from ' , $ f3 -> get ( ' from ' ));
mail (
$ f3 -> get ( ' to ' ),
$ f3 -> get ( ' subject ' ),
Template:: instance ()-> render ( ' email.txt ' , ' text/html ' )
);
팁 : 스크립트가 IMAP 서버와 통신하는 경우 SMTP Mail () 함수를 imap_mail ()로 바꾸십시오.
이제 그게 뭔가 아니야? 물론 이메일 수신자 묶음이있는 경우 데이터베이스를 사용하여 FirstName, LastName 및 이메일 토큰을 채 웁니다.
다음은 F3의 SMTP 플러그인을 사용하는 대체 솔루션입니다.
$ mail = new SMTP ( ' smtp.gmail.com ' , 465 , ' SSL ' , ' [email protected] ' , ' secret ' );
$ mail -> set ( ' from ' , ' <[email protected]> ' );
$ mail -> set ( ' to ' , ' "Slasher" <[email protected]> ' );
$ mail -> set ( ' subject ' , ' Welcome ' );
$ mail -> send (Template:: instance ()-> render ( ' email.txt ' ));
F3은 상자에서 바로 여러 언어를 지원합니다.
먼저 다음 구조 (언어 당 하나의 파일)가있는 사전 파일을 만듭니다.
<?php
return [
' love ' => ' I love F3 ' ,
' today ' => ' Today is {0,date} ' ,
' pi ' => ' {0,number} ' ,
' money ' => ' Amount remaining: {0,number,currency} '
];
dict/en.php
로 저장하십시오. 이번에는 독일어를 위해 또 다른 사전을 만들어 봅시다. 파일을 dict/de.php
로 저장하십시오.
<?php
return [
' love ' => ' Ich liebe F3 ' ,
' today ' => ' Heute ist {0,date} ' ,
' money ' => ' Restbetrag: {0,number,currency} '
];
사전은 키 가치 쌍에 지나지 않습니다. F3은 언어 파일의 키를 기반으로 프레임 워크 변수를 자동으로 인스턴스화합니다. 따라서 이러한 변수를 템플릿의 토큰으로 쉽게 포함시킬 수 있습니다. F3 템플릿 엔진 사용 :-
< h1 > {{ @love }} </ h1 >
< p >
{{ @today,time() | format }}. < br />
{{ @money,365.25 | format }} < br />
{{ @pi }}
</ p >
PHP를 템플릿 엔진으로 사용하는 더 긴 버전 :-
<?php $ f3 =Base:: instance (); ?>
<h1> <?php echo $ f3 -> get ( ' love ' ); ?> </h1>
<p>
<?php echo $ f3 -> get ( ' today ' , time ()); ?> .<br />
<?php echo $ f3 -> get ( ' money ' , 365.25 ); ?>
<?php echo $ f3 -> get ( ' pi ' ); ?>
</p>
다음으로 F3에게 dict/
Folder에서 사전을 찾도록 지시합니다.
$ f3 -> set ( ' LOCALES ' , ' dict/ ' );
그러나 프레임 워크는 어떤 언어를 사용할 언어를 어떻게 결정합니까? F3은 먼저 HTTP 요청 헤더, 특히 브라우저에서 보낸 Accept-Language
헤더를 보면서 자동으로 감지합니다.
이 동작을 무시하려면 F3을 트리거하여 사용자 또는 응용 프로그램에서 지정된 언어를 사용할 수 있습니다.
$ f3 -> set ( ' LANGUAGE ' , ' de ' );
참고 : 위의 예에서 키 PI는 영어 사전에만 존재합니다. 이 프레임 워크는 항상 영어 (또는 감지) 언어로 존재하지 않는 키를 채우기 위해 영어 ( en
)를 폴백으로 사용합니다.
en-US
es-AR
es-AR
변형에 존재하지 않는 키가있는 경우 프레임 워크는 루트 언어 ( es
)의 키를 조회하고 en
언어 파일을 최종 폴백으로 사용합니다. 사전 키-값 쌍은 한 번 참조 된 F3 변수가됩니다. 키가 $f3->set()
, $f3->mset()
또는 $f3->config()
통해 인스턴스화 된 프레임 워크 변수와 충돌하지 않도록하십시오.
이전 예제에서 독특한 'Today is {0,date}'
패턴을 보았습니까? F3의 다국어 기능은 ICU 프로젝트의 문자열/메시지 서식 규칙에 달려 있습니다. 이 프레임 워크는 ICU 문자열 서식 구현의 자체 하위 집합을 사용합니다. 서버에서 PHP의 intl
확장이 활성화 될 필요가 없습니다.
한 가지 더 : F3은 또한 .ini 스타일 형식의 파일을 Dictionaries로로드 할 수 있습니다.
love = " I love F3 "
today = " Today is {0,date} "
pi = " {0,number} "
money = " Amount remaining: {0,number,currency} "
프레임 워크가 자동으로로드 할 수 있도록 dict/en.ini
로 저장하십시오.
기본적으로 View Handler 및 Template Engine은 모두 렌더링 된 모든 변수를 빠져 나와 HTML 엔티티로 변환하여 가능한 XSS 및 코드 주입 공격으로부터 귀하를 보호합니다. 반면에, 유효한 HTML 조각을 응용 프로그램 코드에서 템플릿으로 전달하려면 :-
$ f3 -> set ( ' ESCAPE ' , FALSE );
이것은 바람직하지 않은 효과가있을 수 있습니다. 모든 변수가 에스코퍼를 통과하는 것을 원하지 않을 수 있습니다. 지방이 없으면 변수를 개별적으로 방출 할 수 있습니다. F3 템플릿의 경우 :-
{{ @html_content | raw }}
PHP 템플릿의 경우 :-
<?php echo View:: instance ()-> raw ( $ html_content ); ?>
F3 변수의 자동 에스코핑에 추가하여 프레임 워크는 HTML 양식에서 사용자 입력을 소독 할 때 무료 손을 제공합니다.
$ f3 -> scrub ( $ _GET , ' p; br; span; div; a ' );
이 명령은 지정된 변수의 모든 태그 (두 번째 인수에 지정된 태그 제외)와 안전하지 않은 문자를 제거합니다. 변수에 배열이 포함 된 경우 배열의 각 요소는 재귀 적으로 소독됩니다. 두 번째 인수로 별표 (*)가 전달되면 $f3->scrub()
모든 HTML 태그가 손길이 닿지 않고 안전하지 않은 제어 문자를 제거 할 수 있도록 허용합니다.
FATFREE는 SQL 데이터베이스와 인터페이스 작업을 산들 바람으로 만들도록 설계되었습니다. SQL에 대한 자세한 내용을 담그는 유형이 아니지만 객체 지향 데이터 처리에 더 많이 기울이면이 튜토리얼의 다음 섹션으로 직접 이동할 수 있습니다. 그러나 복잡한 데이터 처리 및 데이터베이스 성능 최적화 작업을 수행 해야하는 경우 SQL이 진행됩니다.
MySQL, SQLite, SQL Server, Sybase 및 Oracle과 같은 SQL 엔진과 통신을 설정하는 것은 친숙한 $f3->set()
명령을 사용하여 수행됩니다. SQLite 데이터베이스에 연결하는 것은 다음과 같습니다.
$ db = new DB SQL ( ' sqlite:/absolute/path/to/your/database.sqlite ' );
또 다른 예, 이번에는 mysql과 함께 :-
$ db = new DB SQL (
' mysql:host=localhost;port=3306;dbname=mysqldb ' ,
' admin ' ,
' p455w0rD '
);
좋아요. 쉬웠어요? 그것은 평범한 PHP에서 똑같은 일을하는 방법입니다. 연결하는 데이터베이스의 DSN 형식을 알아야합니다. PHP 매뉴얼의 PDO 섹션을 참조하십시오.
PHP 코드를 계속하자 :-
$ f3 -> set ( ' result ' , $ db -> exec ( ' SELECT brandName FROM wherever ' ));
echo Template:: instance ()-> render ( ' abc.htm ' );
허, 여기서 무슨 일이야? PDO, 진술, 커서 등과 같은 것을 설정하지 않아야합니까? 간단한 대답은 다음과 같습니다. 필요하지 않습니다. F3은 백엔드에서 모든 노력을 돌려 모든 것을 단순화합니다.
이번에는 abc.htm
과 같은 HTML 템플릿을 최소로 만듭니다.
< repeat group =" {{ @result }} " value =" {{ @item }} " >
< span > {{ @item.brandName }} </ span >
</ repeat >
대부분의 경우 SQL 명령 세트는 웹 지원 결과를 생성하기에 충분해야 템플릿의 result
배열 변수를 직접 사용할 수 있어야합니다. 지방이 없으면 SQL 핸들러 내부에 들어가는 것을 막을 수는 없습니다. 실제로 F3의 DBSQL
클래스는 PHP의 PDO
클래스에서 직접 파생되므로 미세한 곡물 제어가 필요한 경우 각 프로세스와 관련된 기본 PDO 구성 요소 및 프리미티브에 여전히 액세스 할 수 있습니다.
또 다른 예가 있습니다. $db->exec()
명령에 대한 인수로 제공된 단일 문장 대신 SQL 문을 전달할 수도 있습니다.
$ db -> exec (
[
' DELETE FROM diet WHERE food="cola" ' ,
' INSERT INTO diet (food) VALUES ("carrot") ' ,
' SELECT * FROM diet '
]
);
F3은 SQL 명령어 배열을 전달하는 경우 SQL 배치 트랜잭션을 나타냅니다. 트랜잭션 중에 오류가 발생하면 프레임 워크가 데이터베이스의 초기 상태로 자동 복귀하기 때문에 SQL 롤백 및 커밋에 대해 걱정할 필요가 없습니다. 성공하면 F3은 데이터베이스에 대한 모든 변경 사항을 제공합니다.
프로그래밍 방식으로 거래를 시작하고 종료 할 수도 있습니다.
$ db -> begin ();
$ db -> exec ( ' DELETE FROM diet WHERE food="cola" ' );
$ db -> exec ( ' INSERT INTO diet (food) VALUES ("carrot") ' );
$ db -> exec ( ' SELECT * FROM diet ' );
$ db -> commit ();
진술 중 하나가 오류가 발생하면 롤백이 발생합니다.
발행 된 모든 데이터베이스 지침 목록을 얻으려면 다음과 같습니다.
echo $ db -> log ();
문자열 인수를 SQL 문에 전달하는 것은 위험에 처해 있습니다. 이것을 고려하십시오 :-
$ db -> exec (
' SELECT * FROM users ' .
' WHERE username=" ' . $ f3 -> get ( ' POST.userID ' . ' " ' )
);
POST
Variable userID
데이터 위생 프로세스를 거치지 않으면 악의적 인 사용자가 다음 문자열을 전달하고 데이터베이스를 돌이킬 수 없게 손상시킬 수 있습니다.
admin " ; DELETE FROM users; SELECT " 1
운 좋게도 매개 변수화 된 쿼리는 이러한 위험을 완화하는 데 도움이됩니다.
$ db -> exec (
' SELECT * FROM users WHERE userID=? ' ,
$ f3 -> get ( ' POST.userID ' )
);
F3이 쿼리 매개 변수/토큰의 값이 문자열임을 감지하면 기본 데이터 액세스 계층이 문자열을 피하고 필요에 따라 따옴표를 추가합니다.
이전 섹션의 예제는 다음과 같은 방식으로 작성된 경우 SQL 주입에서 훨씬 더 안전합니다.
$ db -> exec (
[
' DELETE FROM diet WHERE food=:name ' ,
' INSERT INTO diet (food) VALUES (?) ' ,
' SELECT * FROM diet '
],
[
array ( ' :name ' => ' cola ' ),
array ( 1 => ' carrot ' ),
NULL
]
);
F3은 응용 프로그램과 데이터 사이에있는 사용하기 쉬운 객체 관계 매핑 (ORM)으로 가득 차있어 제작, 검색, 업데이트, 업데이트, 업데이트 등 일반적인 데이터 작업을 처리하는 프로그램을 작성하는 것이 훨씬 쉽고 빠릅니다. SQL 및 NOSQL 데이터베이스에서 정보 삭제 (CRUD) 정보. 데이터 매퍼는 PHP 객체 상호 작용을 해당 백엔드 쿼리에 매핑하여 대부분의 작업을 수행합니다.
응용 프로그램 사용자 테이블이 포함 된 기존 MySQL 데이터베이스가 있다고 가정합니다. (sqlite, postgresql, sql server, sybase도 마찬가지로 할 것입니다.) 다음 SQL 명령을 사용하여 생성되었을 것입니다.
CREATE TABLE users (
userID VARCHAR ( 30 ),
password VARCHAR ( 30 ),
visits INT ,
PRIMARY KEY (userID)
);
참고 : MongoDB는 NOSQL 데이터베이스 엔진이며 본질적으로 스키마가 없습니다. F3에는 PHP-Serialized 또는 JSON에 인코딩 된 플랫 파일을 사용하는 Jig라는 고유하고 경량 NOSQL 구현이 있습니다. 이러한 추상화 계층은 엄격한 데이터 구조가 필요하지 않습니다. 필드는 레코드마다 다를 수 있습니다. 그들은 또한 정의되거나 즉석에서 떨어질 수 있습니다.
이제 SQL로 돌아갑니다. 먼저 데이터베이스와의 커뮤니케이션을 설정합니다.
$ db = new DB SQL (
' mysql:host=localhost;port=3306;dbname=mysqldb ' ,
' admin ' ,
' wh4t3v3r '
);
테이블에서 레코드를 검색하려면 :-
$ user = new DB SQL Mapper ( $ db , ' users ' );
$ user -> load ([ ' userID=? ' , ' tarzan ' ]);
첫 번째 줄은 데이터베이스의 users
테이블과 상호 작용하는 데이터 맵퍼 객체를 인스턴스화합니다. 현장 뒤에서 F3은 users
테이블의 구조를 검색하고 어떤 필드가 기본 키로 정의되는지 결정합니다. 이 시점에서 Mapper 객체에는 아직 데이터가 포함되어 있지 않으므로 (건조 상태) $user
구조화 된 객체에 지나지 않지만 기본 CRUD 작업 및 일부 추가 기능을 수행하는 데 필요한 방법이 포함되어 있습니다. 문자열 값 tarzan
포함 된 userID
필드로 사용자 테이블에서 레코드를 검색하려면 load() method
사용합니다. 이 프로세스를 데이터 맵퍼 객체 인 "자동 확장"이라고합니다.
쉽지 않았습니까? F3은 SQL 테이블에 이미 데이터베이스 엔진 자체에 존재하는 구조적 정의가 있음을 이해합니다. 다른 프레임 워크와 달리 F3는 추가 클래스 선언이 필요하지 않습니다 (복잡한 객체에 맞게 데이터 매퍼를 확장하지 않는 한), 중복 PHP 배열/객체 속성-필드 매핑 (노력의 복제), 코드 생성기 (코드가 필요) 재생 데이터베이스 구조가 변경되는 경우), 멍청한 XML/YAML 파일 없음 모델을 구성 할 수 없으며 단일 레코드를 검색하기 위해 불필요한 명령이 없습니다. F3을 사용하면 MySQL에서 varchar
필드의 간단한 크기 조정에 응용 프로그램 코드의 변경이 필요하지 않습니다. MVC 및 "관심 분리"와 일치하여 데이터베이스 관리자는 템플릿 디자이너가 HTML/XML 템플릿을 통해 데이터 (및 구조)를 많이 제어 할 수 있습니다.
NOSQL 데이터베이스 작업을 선호하는 경우 쿼리 구문의 유사성이 피상적입니다. MongoDB 데이터 맵퍼의 경우 동등한 코드는 다음과 같습니다.
$ db = new DB Mongo ( ' mongodb://localhost:27017 ' , ' testdb ' );
$ user = new DB Mongo Mapper ( $ db , ' users ' );
$ user -> load ([ ' userID ' => ' tarzan ' ]);
지그를 사용하면 구문은 F3의 템플릿 엔진과 유사합니다.
$ db = new DB Jig ( ' db/data/ ' , DB Jig:: FORMAT_JSON );
$ user = new DB Jig Mapper ( $ db , ' users ' );
$ user -> load ([ ' @userID=? ' , ' tarzan ' ]);
이 프레임 워크는 객체 인스턴스화 중에 테이블의 필드 visits
데이터 맵퍼 속성에 자동으로 매핑합니다. 즉, $user=new DBSQLMapper($db,'users');
. 객체가 생성되면 $user->password
및 $user->userID
각각 테이블의 password
및 userID
필드에 매핑됩니다.
맵핑 된 필드를 추가하거나 삭제하거나 ORM을 사용하여 테이블의 구조를 변경할 수 없습니다. MySQL 또는 사용중인 데이터베이스 엔진 에서이 작업을 수행해야합니다. 데이터베이스 엔진을 변경 한 후 FATFREE는 애플리케이션을 실행할 때 데이터 맵퍼 객체와 새 테이블 구조를 자동으로 동기화합니다.
F3은 데이터베이스 스키마에서 데이터 맵퍼 구조를 직접 도출합니다. 관련된 추측은 관련이 없습니다. MySQL, SQLite, MSSQL, Sybase 및 PostgreSQL 데이터베이스 엔진의 차이점을 이해합니다.
SQL 식별자는 예약 된 단어를 사용해서는 안되며, 영숫자 AZ
, 0-9
및 밑줄 기호 ( _
)로 제한되어야합니다. 공백 (또는 특수 문자)을 포함하고 데이터 정의의 인용문으로 둘러싸인 열 이름은 ORM과 호환되지 않습니다. PHP 객체 특성으로 제대로 표현할 수 없습니다.
사용자의 방문 횟수를 증가시키고 사용자 테이블에서 해당 레코드를 업데이트하려고한다고 가정 해 보겠습니다. 다음 코드를 추가 할 수 있습니다.
$ user -> visits ++;
$ user -> save ();
레코드를 삽입하려면이 과정을 따릅니다.
$ user = new DB SQL Mapper ( $ db , ' users ' );
// or $ user = new DB Mongo Mapper ($ db , 'users' );
// or $ user = new DB Jig Mapper ($ db , 'users' );
$ user -> userID = ' jane ' ;
$ user -> password = password_hash ( ' secret ' , PASSWORD_BCRYPT , [ ' cost ' => 12 ]);
$ user -> visits = 0 ;
$ user -> save ();
우리는 여전히 동일한 save()
메소드를 사용합니다. 그러나 F3은 언제 레코드를 삽입하거나 업데이트 해야하는지 어떻게 알 수 있습니까? 데이터 맵퍼 객체가 레코드 검색으로 자동 이수화 될 때 프레임 워크는 레코드의 기본 키 (또는 MongoDB 및 Jig의 경우 _id
)를 추적합니다. 따라서 어떤 레코드를 업데이트하거나 삭제 해야하는지 알고 있습니다. 기본 키의 값이 변경되면 프로그래밍 방식으로 이식 된 데이터 맵퍼 (데이터베이스에서 검색되지 않았지만 응용 프로그램으로 채워진 값은 기본 키에 이전 값에 대한 메모리가 없습니다. MongoDB 및 Jig에도 동일하게 적용되지만 객체 _id
참조로 사용합니다. 따라서 위의 $user
객체를 인스턴스화하고 프로그램의 값으로 속성을 채우면 사용자 테이블에서 레코드를 검색하지 않고이 레코드를 삽입해야한다는 것을 알고 있습니다.
매퍼 객체는 save()
후에 비어 있지 않습니다. 데이터베이스에 새 레코드를 추가하려면 먼저 맵퍼를 탈수해야합니다.
$ user -> reset ();
$ user -> userID = ' cheetah ' ;
$ user -> password = password_hash ( ' unknown ' , PASSWORD_BCRYPT , [ ' cost ' => 12 ]);
$ user -> save ();
reset()
호출하지 않고 두 번째로 save()
호출하면 Mapper가 현재 지적한 레코드를 업데이트합니다.
데이터베이스의 모든 테이블에 기본 키가있는 문제는 논증 적이지만 F3은 기본 키가 포함 된 테이블과 통신하는 데이터 맵퍼 객체를 만드는 것을 막지 않습니다. 유일한 단점은 다음과 같습니다. F3가 귀하가 언급하는 레코드와 위치 참조가 신뢰할 수 없다는 사실을 결정할 방법이 전혀 없기 때문에 매핑 된 레코드를 삭제하거나 업데이트 할 수 없습니다. 행 ID는 다른 SQL 엔진에서 휴대용이 없으며 PHP 데이터베이스 드라이버에서 반환 할 수 없습니다.
테이블에서 매핑 된 레코드를 제거하려면 자동 수확량 데이터 맵퍼에서 erase()
메소드를 호출하십시오. 예를 들어:-
$ user = new DB SQL Mapper ( $ db , ' users ' );
$ user -> load ([ ' userID=? AND password=? ' , ' cheetah ' , ' ch1mp ' ]);
$ user -> erase ();
Jig의 쿼리 구문은 약간 비슷합니다.
$ user = new DB Jig Mapper ( $ db , ' users ' );
$ user -> load ([ ' @userID=? AND @password=? ' , ' cheetah ' , ' chimp ' ]);
$ user -> erase ();
그리고 mongodb 동등한 것은 다음과 같습니다.
$ user = new DB Mongo Mapper ( $ db , ' users ' );
$ user -> load ([ ' userID ' => ' cheetah ' , ' password ' => ' chimp ' ]);
$ user -> erase ();
데이터 맵퍼가 수화되었는지 여부를 찾기 위해 :-
if ( $ user -> dry ())
echo ' No record matching criteria ' ;
우리는 CRUD 핸들러를 다루었습니다. 유용 할 수있는 몇 가지 추가 방법이 있습니다.-
$ f3 -> set ( ' user ' , new DB SQL Mapper ( $ db , ' users ' ));
$ f3 -> get ( ' user ' )-> copyFrom ( ' POST ' );
$ f3 -> get ( ' user ' )-> save ();
맵퍼 객체의 컨테이너로 치명적인 변수를 사용할 수도 있습니다. copyFrom()
메소드는 프레임 워크 배열 변수의 요소로 맵퍼 객체를 수화하며, 배열 키는 맵퍼 객체 속성과 동일한 이름을 가져야하며, 이는 레코드의 필드 이름에 해당합니다. 따라서 웹 양식이 제출되면 (HTML 이름 속성이 userID
로 설정되어 있다고 가정) 해당 입력 필드의 내용은 $_POST['userID']
로 전송되며, POST.userID
의 F3에 의해 복제되어 다음에 저장됩니다. 데이터베이스의 매핑 된 필드 $user->userID
. 프로세스는 모두 동일하게 이름이 지정된 요소를 가지고 있으면 매우 간단 해집니다. 배열 키, 즉 템플릿 토큰 이름, 프레임 워크 변수 이름 및 필드 이름의 일관성 :)
반면, 레코드를 검색하고 필드 값을 나중에 사용하기 위해 프레임 워크 변수로 복사하려면 템플릿 렌더링 :-
$ f3 -> set ( ' user ' , new DB SQL Mapper ( $ db , ' users ' ));
$ f3 -> get ( ' user ' )-> load ([ ' userID=? ' , ' jane ' ]);
$ f3 -> get ( ' user ' )-> copyTo ( ' POST ' );
그런 다음 동일한 입력 필드의 값 속성에 {{ @post.userid}}를 할당 할 수 있습니다. 요약하면, HTML 입력 필드는 다음과 같습니다.
< input type =" text " name =" userID " value =" {{ @POST.userID }} " />
save()
, update()
, copyFrom()
데이터 맵퍼 메소드 및 load()
및 erase()
의 매개 변수 변형은 SQL 주입으로부터 안전합니다.
기본적으로 Data Mapper의 load()
메소드는 지정된 기준과 일치하는 첫 번째 레코드 만 검색합니다. 첫 번째 레코드로드와 동일한 조건을 충족하는 하나 이상의 경우 skip()
메소드를 탐색 할 수 있습니다.
$ user = new DB SQL Mapper ( $ db , ' users ' );
$ user -> load ( ' visits>3 ' );
// Rewritten as a parameterized query
$ user -> load ([ ' visits>? ' , 3 ]);
// For MongoDB users : -
// $ user = new DB Mongo Mapper ($ db , 'users' );
// $ user - > load ([ 'visits' = > [ '$gt' = > 3 ]]);
// If you prefer Jig : -
// $ user = new DB Jig Mapper ($ db , 'users' );
// $ user - > load ( '@visits>?' , 3 );
// Display the userID of the first record that matches the criteria
echo $ user -> userID ;
// Go to the next record that matches the same criteria
$ user -> skip (); // Same as $ user - > skip ( 1 );
// Back to the first record
$ user -> skip (- 1 );
// Move three records forward
$ user -> skip ( 3 );
$ user-> $user->skip()
$user->next()
사용할 수 있으며 $user->skip(-1)
에게 더 많은 의미를 부여한다고 생각되면 $user->prev()
사용할 수 있습니다.
dry()
메소드를 사용하여 결과 세트의 한계를 넘어서 기동했는지 확인하십시오. 첫 번째 레코드에서 skip(-1)
시도하면 dry()
True를 반환합니다. 검색 기준을 충족하는 마지막 레코드에서 skip(1)
True가 반환됩니다.
load()
메소드는 두 번째 인수를 받아들입니다.
$ user -> load (
[ ' visits>? ' , 3 ],
[
' order ' => ' userID DESC '
'offset'=> 5 ,
' limit ' => 3
]
);
MySQL을 사용하는 경우 쿼리가 다음과 같이 번역됩니다.
SELECT * FROM users
WHERE visits > 3
ORDER BY userID DESC
LIMIT 3 OFFSET 5 ;
이것은 작은 덩어리로 데이터를 제시하는 한 가지 방법입니다. 결과적으로 결과를 이끌어내는 또 다른 방법은 다음과 같습니다.-
$ page = $ user -> paginate ( 2 , 5 ,[ ' visits>? ' , 3 ]);
위의 시나리오에서 F3은 'visits>3'
기준과 일치하는 레코드를 검색합니다. 그런 다음 결과를 페이지 오프셋 2 (0 기반)에서 시작하는 5 개의 레코드 (페이지 당)로 제한합니다. 프레임 워크는 다음 요소로 구성된 배열을 반환합니다.
[subset] array of mapper objects that match the criteria
[count] number of subsets available
[pos] actual subset position
paginate()
의 첫 번째 인수가 음수이거나 발견 된 서브 세트 수를 초과하면 반환 된 실제 하위 집합 위치는 Null이됩니다.
필드의 계산 된 값 또는 다른 테이블에서 교차 참조 값을 검색 해야하는 경우가 있습니다. 가상 필드를 입력하십시오. SQL Mini-Orm을 사용하면 기존 필드에서 파생 된 데이터에 대해 작업 할 수 있습니다.
다음 테이블이 다음과 같이 정의되어 있다고 가정합니다.
CREATE TABLE products
productID VARCHAR ( 30 ),
description VARCHAR ( 255 ),
supplierID VARCHAR ( 30 ),
unitprice DECIMAL ( 10 , 2 ),
quantity INT ,
PRIMARY KEY (productID)
);
totalprice
필드는 존재하지 않으므로 데이터베이스 엔진에서 두 필드의 산술 제품을 요청하도록 프레임 워크에 알릴 수 있습니다.
$ item = new DB SQL Mapper ( $ db , ' products ' );
$ item -> totalprice = ' unitprice*quantity ' ;
$ item -> load ([ ' productID=:pid ' , ' :pid ' => ' apple ' ]);
echo $ item -> totalprice ;
위의 코드 스 니펫은 totalprice
라는 가상 필드를 정의하며, 이는 unitprice
에 quantity
곱하여 계산됩니다. SQL Mapper는 해당 규칙/공식을 저장하므로 데이터베이스에서 레코드를 검색 할 때 가상 필드를 일반 매핑 필드처럼 사용할 수 있습니다.
더 복잡한 가상 필드를 가질 수 있습니다.
$ item -> mostNumber = ' MAX(quantity) ' ;
$ item -> load ();
echo $ item -> mostNumber ;
이번에는 프레임 워크가 가장 높은 수량으로 제품을 검색합니다 ( load()
메소드는 기준을 정의하지 않으므로 테이블의 모든 레코드가 처리됩니다). 물론, 가상 필드 mostNumber
지정된 기준과 일치하는 특정 레코드 그룹으로 표현식을 제한하려면 여전히 올바른 수치를 제공합니다.
다른 테이블에서 값을 도출 할 수도 있습니다.
$ item -> supplierName =
' SELECT name FROM suppliers ' .
' WHERE products.supplierID=suppliers.supplierID ' ;
$ item -> load ();
echo $ item -> supplierName ;
제품 테이블에서 레코드를로드 할 때마다 ORM은 suppliers
테이블의 supplierID
와 함께 products
테이블의 supplerID
를 교차 참조합니다.
가상 필드를 파괴하려면 unset($item->totalPrice);
. isset($item->totalPrice)
표현식은 totalPrice
가상 필드가 정의 된 경우 true를 반환하거나 그렇지 않으면 false를 반환합니다.
데이터 검색 전에 가상 필드를 정의해야합니다. ORM은 실제 계산이나 다른 테이블에서 결과의 도출을 수행하지 않습니다. 모든 노력을하는 것은 데이터베이스 엔진입니다.
레코드별로 내비게이션이 필요하지 않은 경우 한 번의 전체 레코드 배치를 검색 할 수 있습니다.
$ frequentUsers = $ user -> find ([ ' visits>? ' , 3 ],[ ' order ' => ' userID ' ]);
Jig Mapper의 쿼리 구문은 약간 유사합니다.
$ frequentUsers = $ user -> find ([ ' @visits>? ' , 3 ],[ ' order ' => ' userID ' ]);
MongoDB Mapper를 사용한 동등한 코드 :-
$ frequentUsers = $ user -> find ([ ' visits ' =>[ ' $gt ' => 3 ]],[ ' userID ' => 1 ]);
find()
메소드는 기준과 일치하는 레코드에 대해 users
테이블을 검색하고 userID
별로 결과를 정렬하고 결과를 맵퍼 객체 배열로 리턴합니다. find('visits>3')
load('visits>3')
과 다릅니다. 후자는 현재 $user
객체를 나타냅니다. find()
skip()
에 영향을 미치지 않습니다.
중요 : find()
또는 load()
의 첫 번째 인수로 빈 상태, null 또는 제로 길이 문자열을 선언하면 모든 레코드를 검색합니다. 당신이하고있는 일을 알고 있는지 확인하십시오. 큰 테이블이나 컬렉션에서 PHP의 memory_limit을 초과 할 수 있습니다.
find()
메소드에는 다음 구문이 있습니다.
find (
$ criteria ,
[
' group ' => ' foo ' ,
' order ' => ' foo,bar ' ,
' limit ' => 5 ,
' offset ' => 0
]
);
찾기 () 객체 배열을 반환합니다. 각 객체는 지정된 기준과 일치하는 레코드에 대한 매퍼입니다. :-
$ place = new DB SQL Mapper ( $ db , ' places ' );
$ list = $ place -> find ( ' state="New York" ' );
foreach ( $ list as $ obj )
echo $ obj -> city . ' , ' . $ obj -> country ;
맵퍼 객체를 연관 배열로 변환 해야하는 경우 cast()
메소드를 사용하십시오.
$ array = $ place -> cast ();
echo $ array [ ' city ' ]. ' , ' . $ array [ ' country ' ];
특정 조건과 일치하는 테이블에서 레코드 수를 검색하려면 count()
메소드를 사용하십시오.
if (! $ user -> count ([ ' visits>? ' , 10 ]))
echo ' We need a better ad campaign! ' ;
find()
와 유사하지만 반환 된 필드에 대한보다 세밀한 제어를 제공하는 select()
메소드도 있습니다. SQL과 같은 구문이 있습니다.
select (
' foo, bar, MIN(baz) AS lowest ' ,
' foo > ? ' ,
[
' group ' => ' foo, bar ' ,
' order ' => ' baz ASC ' ,
' limit ' => 5 ,
' offset ' => 3
]
);
find()
메소드와 마찬가지로 select()
맵퍼 객체의 내용을 변경하지 않습니다. 매핑 된 테이블을 쿼리하기위한 편의 방법으로 만 사용됩니다. 두 방법의 반환 값은 맵퍼 객체의 배열입니다. 이 방법 중에서 레코드가 발견되었는지 여부를 결정하기 위해 dry()
사용하는 것은 부적절합니다. 레코드가없는 경우 find()
또는 select()
기준과 일치하는 경우 반환 값은 빈 배열입니다.
응용 프로그램에서 직접 발행 한 SQL 문 (또는 맵퍼 객체를 통해 간접적으로)이 성능 병목 현상을 일으키는 SQL 문을 찾으려면 다음과 같이 할 수 있습니다.
echo $ db -> log ();
F3은 기본 SQL 데이터베이스 드라이버에 발행 된 모든 명령을 추적하고 각 명령문이 완료하는 데 걸리는 시간 (응용 프로그램 성능을 조정하는 데 필요한 올바른 정보 만 추적합니다.
대부분의 경우 지금까지 논의한 데이터 매퍼 방법으로 제공되는 편안함으로 살 수 있습니다. 강력한 작업을 수행하기 위해 프레임 워크가 필요한 경우 사용자 정의 방법으로 자신의 클래스를 선언하여 SQL Mapper를 확장 할 수 있습니다. 그러나 일부 하드 코어 SQL에서 손을 기름칠하는 것을 피할 수는 없습니다.
class Vendor extends DB SQL Mapper {
// Instantiate mapper
function __construct ( DB SQL $ db ) {
// This is where the mapper and DB structure synchronization occurs
parent :: __construct ( $ db , ' vendors ' );
}
// Specialized query
function listByCity () {
return $ this -> select (
' vendorID,name,city ' ,[ ' order ' => ' city DESC ' ]);
/ *
We could have done the the same thing with plain vanilla SQL : -
return $ this - > db - > exec (
'SELECT vendorID,name,city FROM vendors ' .
'ORDER BY city DESC;'
);
* /
}
}
$ vendor = new Vendor ;
$ vendor -> listByCity ();
이러한 방식으로 데이터 매퍼를 확장하는 것은 애플리케이션의 DB 관련 모델을 쉽게 구성하는 방법입니다.
SQL에 편리하다면 아마도 ORM의 모든 것을 구식 SQL 쿼리로 처리 할 수 있습니다. 물론. 데이터베이스 트리거 및 저장 프로 시저를 사용하여 추가 이벤트 리스너없이 할 수 있습니다. 결합 된 테이블로 관계형 쿼리를 달성 할 수 있습니다. ORM은 불필요한 오버 헤드입니다. 그러나 요점은 데이터 매퍼는 객체를 사용하여 데이터베이스 엔티티를 나타내는 추가 기능을 제공합니다. 개발자는 코드를 더 빨리 작성하고 생산성을 높일 수 있습니다. 결과 프로그램은 짧지 않으면 더 깨끗합니다. 그러나 특히 크고 복잡한 데이터 저장소를 처리 할 때 속도의 타협에 대한 이점을 평가해야합니다. 모든 ORM은 아무리 얇더라도 항상 또 다른 추상화 레이어 일 것입니다. 그들은 여전히 작업을 기본 SQL 엔진으로 전달해야합니다.
F3의 Orms는 디자인에 의해 객체를 서로 직접 연결하는 방법을 제공하지 않습니다. 즉, SQL 조인은 벌레 캔을 열어주기 때문입니다. 응용 프로그램이 예상보다 더 복잡하게 만들고, 간절히 또는 게으른 페치 기술을 통해 객체 상속 및 다형성 (임피던스 불일치)으로 인해 교착 상태를 벗어나는 객체가 맵핑 된 데이터베이스 엔티티와 함께 객체가 맵핑되는 경향이 있습니다. . 가상 필드를 사용하여 SQL Mapper에는 간접적 인 방법이 있지만 프로그래밍 방식으로 자신의 위험에 처해 있어야합니다.
애플리케이션에 "순수한"OOP 개념을 적용하여 모든 데이터를 표현하기 위해 ( "모든 것이 객체이기 때문에), 데이터는 거의 항상 애플리케이션보다 더 길다는 점을 명심하십시오. 데이터의 가치가 손실되기 오래 전에 프로그램이 이미 구식이 될 수 있습니다. 데이터의 스키마와 물리적 구조에서 너무 많이 벗어나는 얽힌 객체 및 클래스를 사용하여 프로그램에 다른 복잡성 계층을 추가하지 마십시오.
응용 프로그램에서 여러 객체를 짜기 전에 데이터베이스의 기본 테이블을 조작하기 전에 다음과 같이 생각하십시오. 데이터베이스 엔진에서 객체 동작을 정의하기위한 관계를 나타내는 뷰를 작성하는 것이 더 효율적입니다. 관계형 데이터베이스 엔진은 뷰, 결합 된 테이블 및 트리거를 처리하도록 설계되었습니다. 그들은 멍청한 데이터 저장소가 아닙니다. 보기에 결합 된 테이블은 단일 테이블로 표시되며 Fatfree는 일반 테이블뿐만 아니라보기를 자동으로 자동 매핑 할 수 있습니다. 복제는 PHP의 관계형 객체로 결합하는 것이 데이터베이스 엔진의 기계 코드, 관계형 대수 및 최적화 로직에 비해 느려집니다. 게다가, 응용 프로그램에서 테이블을 반복적으로 결합하는 것은 데이터베이스 디자인을 감사해야한다는 확실한 신호이며, 뷰는 데이터 검색의 필수 부분으로 간주됩니다. 테이블이 다른 테이블의 데이터를 자주 상호 참조하는 경우 구조를 정규화하거나 대신보기를 만드는 것을 고려하십시오. 그런 다음 해당보기를 자동으로 매핑하기 위해 맵퍼 객체를 만듭니다. 더 빠르고 노력이 줄어 듭니다.
데이터베이스 엔진 내에서 생성 된이 SQL보기를 고려하십시오.
CREATE VIEW combined AS
SELECT
projects . project_id AS project,
users . name AS name
FROM projects
LEFT OUTER JOIN users ON
projects . project_id = users . project_id AND
projects . user_id = users . user_id ;
응용 프로그램 코드는 두 개의 결합 된 테이블에서 데이터를 검색하기 위해 두 개의 맵퍼 객체 (프로젝트 테이블과 사용자 용)를 유지할 필요가 없기 때문에 간단 해집니다.
$ combined = new DB SQL Mapper ( $ db , ' combined ' );
$ combined -> load ([ ' project=? ' , 123 ]);
echo $ combined -> name ;
팁 : 도구가 설계된대로 사용하십시오. 지방이없는에는 이미 사용하기 쉬운 SQL 도우미가 있습니다. 더 큰 망치가 필요한 경우 사용하십시오 :) 편의성과 성능 사이의 균형을 찾으십시오. 복잡한 및 레거시 데이터 구조 작업을 수행하는 경우 SQL은 항상 귀하의 폴백이됩니다.
Plug-ins are nothing more than autoloaded classes that use framework built-ins to extend F3's features and functionality. If you'd like to contribute, leave a note at the Fat-Free Discussion Area hosted by Google Groups or tell us about it in the FreeNode #fatfree
IRC channel. Someone else might be involved in a similar project. The framework community will appreciate it a lot if we unify our efforts.
There might be instances when you want to make your forms more secure against spam bots and malicious automated scripts. F3 provides a captcha()
method to generate images with random text that are designed to be recognizable only by humans.
$ img = new Image ();
$ img -> captcha ( ' fonts/CoolFont.ttf ' , 16 , 5 , ' SESSION.captcha_code ' );
$ img -> render ();
This example generates an random image based on your desired TrueType font. The fonts/
folder is a subfolder within application's UI
path. The second parameter indicates the font size, and the third argument defines the number of hexadecimal characters to generate.
The last argument represents an F3 variable name. This is where F3 will store the string equivalent of the CAPTCHA image. To make the string reload-safe, we specified a session variable:- SESSION.captcha_code
which maps to $_SESSION['captcha_code']
, which you can use later to verify whether the input element in the form submitted matches this string.
We've covered almost every feature available in the framework to run a stand-alone Web server. For most applications, these features will serve you quite well. But what do you do if your application needs data from another Web server on the network? F3 has the Web plugin to help you in this situation:-
$ web = new Web ;
$ request = $ web -> request ( ' http://www.google.com/ ' );
// another way to do it : -
$ request =Web:: instance ()-> request ( ' http://www.google.com/ ' );
This simple example sends an HTTP request to the page located at www.google.com and stores it in the $request
PHP variable. The request()
method returns an array containing the HTTP response such that $request['headers']
and $request['body']
represent the response headers and body, respectively. We could have saved the contents using the F3::set command, or echo'ed the output directly to our browser. Retrieving another HTML page on the net may not have any practical purpose. But it can be particularly useful in ReSTful applications, like querying a CouchDB server.
$ host = ' localhost:5984 ' ;
$ web -> request ( $ host . ' /_all_dbs ' ),
$ web -> request ( $ host . ' /testdb/ ' ,[ ' method ' => ' PUT ' ]);
You may have noticed that you can pass an array of additional options to the request()
method:-
$ web -> request (
' https://www.example.com:443? ' .
http_build_query (
[
' key1 ' => ' value1 ' ,
' key2 ' => ' value2 '
]
),
[
' headers ' =>[
' Accept: text/html,application/xhtml+xml,application/xml ' ,
' Accept-Language: en-us '
],
' follow_location ' => FALSE ,
' max_redirects ' => 30 ,
' ignore_errors ' => TRUE
]
);
If the framework variable CACHE
is enabled, and if the remote server instructs your application to cache the response to the HTTP request, F3 will comply with the request and retrieve the cached response each time the framework receives a similar request from your application, thus behaving like a browser.
Fat-Free will use whatever means are available on your Web server for the request()
method to run: PHP stream wrappers ( allow_url_fopen
), cURL module, or low-level sockets.
F3 has a utility for sending files to an HTTP client, ie fulfilling download requests. You can use it to hide the real path to your download files. This adds some layer of security because users won't be able to download files if they don't know the file names and their locations. Here's how it's done:-
$ f3 -> route ( ' GET /downloads/@filename ' ,
function ( $ f3 , $ args ) {
// send () method returns FALSE if file doesn ' t exist
if (!Web:: instance ()-> send ( ' /real/path/ ' . $ args [ ' filename ' ]))
// Generate an HTTP 404
$ f3 -> error ( 404 );
}
);
The request()
method can also be used in complex SOAP or XML-RPC applications, if you find the need for another Web server to process data on your computer's behalf - thus harnessing the power of distributing computing. W3Schools.com has an excellent tutorial on SOAP. On the other hand, TutorialsPoint.com gives a nice overview of XML-RPC.
Caching static Web pages - so the code in some route handlers can be skipped and templates don't have to be reprocessed - is one way of reducing your Web server's work load so it can focus on other tasks. You can activate the framework's cache engine by providing a third argument to the $f3->route()
method. Just specify the number of seconds before a cached Web page expires:-
$ f3 -> route ( ' GET /my_page ' , ' App->method ' , 60 );
작동 방식은 다음과 같습니다. In this example, when F3 detects that the URL /my_page
is accessed for the first time, it executes the route handler represented by the second argument and saves all browser output to the framework's built-in cache (server-side). A similar instruction is automatically sent to the user's Web browser (client-side), so that instead of sending an identical request to the server within the 60-second period, the browser can just retrieve the page locally. The framework uses the cache for an entirely different purpose - serving framework-cached data to other users asking for the same Web page within the 60-second time frame. It skips execution of the route handler and serves the previously-saved page directly from disk. When someone tries to access the same URL after the 60-second timer has lapsed, F3 will refresh the cache with a new copy.
Web pages with static data are the most likely candidates for caching. Fat-Free will not cache a Web page at a specified URL if the third argument in the $f3->route()
method is zero or unspecified. F3 conforms to the HTTP specifications: only GET and HEAD requests can be cached.
Here's an important point to consider when designing your application. Don't cache Web pages unless you understand the possible unwanted side-effects of the cache at the client-side. Make sure that you activate caching on Web pages that have nothing to do with the user's session state.
For example, you designed your site in such a way that all your Web pages have the menu options: "Home"
, "About Us"
, and "Login"
, displayed when a user is not logged into your application. You also want the menu options to change to: "Home"
, "About Us"
, and "Logout"
, once the user has logged in. If you instructed Fat-Free to cache the contents of "About Us"
page (which includes the menu options), it does so and also sends the same instruction to the HTTP client. Regardless of the user's session state, ie logged in or logged out, the user's browser will take a snapshot of the page at the session state it was in. Future requests by the user for the "About Us"
page before the cache timeout expires will display the same menu options available at that time the page was initially saved. Now, a user may have already logged in, but the menu options are still the same as if no such event occurred. That's not the kind of behavior we want from our application.
Some pointers:-
GET
routes only. It will not cache submitted forms!Don't activate the cache on Web pages that at first glance look static. In our example, the "About Us" content may be static, but the menu isn't."About Us"
page, make sure it's available only when a user is not logged in.CACHE
global variable so it points to that drive. This will make your application run like a Formula 1 race car. Note: Don't set the timeout value to a very long period until you're ready to roll out your application, ie the release or production state. Changes you make to any of your PHP scripts may not have the expected effect on the displayed output if the page exists in the framework cache and the expiration period has not lapsed. If you do alter a program that generates a page affected by the cache timer and you want these changes to take effect immediately, you should clear the cache by erasing the files in the cache/ directory (or whatever path the CACHE
global variable points to) . F3 will automatically refresh the cache if necessary. At the client-side, there's little you can do but instruct the user to clear the browser's cache or wait for the cache period to expire.
PHP needs to be set up correctly for the F3 cache engine to work properly. Your operating system timezone should be synchronized with the date.timezone setting in the php.ini
file.
Similar to routes, Fat-Free also allows you to cache database queries. Speed gains can be quite significant, specially when used on complex SQL statements that involve look-up of static data or database content that rarely changes. Activating the database query cache so the framework doesn't have to re-execute the SQL statements every time is as simple as adding a 3rd argument to the F3::sql command - the cache timeout. 예를 들어:-
$ db -> exec ( ' SELECT * from sizes; ' , NULL , 86400 );
If we expect the result of this database query to always be Small
, Medium
, and Large
within a 24-hour period, we specify 86400
seconds as the 2nd argument so Fat-Free doesn't have to execute the query more than once a day . Instead, the framework will store the result in the cache, retrieve it from the cache every time a request comes in during the specified 24-hour time frame, and re-execute the query when the timer lapses.
The SQL data mapper also uses the cache engine to optimize synchronization of table structures with the objects that represent them. The default is 60
seconds. If you make any changes to a table's structure in your database engine, you'll have to wait for the cache timer to expire before seeing the effect in your application. You can change this behavior by specifying a third argument to the data mapper constructor. Set it to a high value if you don't expect to make any further changes to your table structure.
$ user = new DB SQL Mapper ( $ db , ' users ' , 86400 );
By default, Fat-Free's cache engine is disabled. You can enable it and allow it to auto-detect APC, WinCache or XCache. If it cannot find an appropriate backend, F3 will use the filesystem, ie the tmp/cache/
folder:-
$ f3 -> set ( ' CACHE ' , TRUE );
Disabling the cache is as simple as:-
$ f3 -> set ( ' CACHE ' , FALSE );
If you wish to override the auto-detection feature, you can do so - as in the case of a Memcached back-end which F3 also supports:-
$ f3 -> set ( ' CACHE ' , ' memcache=localhost:11211 ' );
You can also use the cache engine to store your own variables. These variables will persist between HTTP requests and remain in cache until the engine receives instructions to delete them. To save a value in the cache:-
$ f3 -> set ( ' var ' , ' I want this value saved ' , 90 );
$f3->set()
method's third argument instructs the framework to save the variable in the cache for a 90-second duration. If your application issues a $f3->get('var')
within this period, F3 will automatically retrieve the value from cache. In like manner, $f3->clear('var')
will purge the value from both cache and RAM. If you want to determine if a variable exists in cache, `$f3->exists('var')); returns one of two possible values: FALSE if the framework variable passed does not exist in cache, or an integer representing the time the variable was saved (Un*x time in seconds, with microsecond precision).
Fat-Free also has a Javascript and CSS compressor available in the Web plug-in. It can combine all your CSS files into one stylesheet (or Javascript files into a single script) so the number of components on a Web page are decreased. Reducing the number of HTTP requests to your Web server results in faster page loading. First you need to prepare your HTML template so it can take advantage of this feature. Something like:-
< link rel =" stylesheet " type =" text/css "
href =" /minify/css?files=typo.css,grid.css " />
Do the same with your Javascript files:-
< script type =" text/javascript " src =" /minify/js?&files=underscore.js " >
</ script >
Of course we need to set up a route so your application can handle the necessary call to the Fat-Free CSS/Javascript compressor:-
$ f3 -> route ( ' GET /minify/@type ' ,
function ( $ f3 , $ args ) {
$ f3 -> set ( ' UI ' , $ args [ ' type ' ]. ' / ' );
echo Web:: instance ()-> minify ( $ _GET [ ' files ' ]);
},
3600
);
And that's all there is to it! minify()
reads each file ( typo.css
and grid.css
in our CSS example, underscore.js
in our Javascript example), strips off all unnecessary whitespaces and comments, combines all of the related items as a single Web page component, and attaches a far-future expiry date so the user's Web browser can cache the data. It's important that the PARAMS.type
variable base points to the correct path. Otherwise, the URL rewriting mechanism inside the compressor won't find the CSS/Javascript files.
In our examples, the framework sends a far-future expiry date to the client's Web browser so any request for the same CSS or Javascript block will come from the user's hard drive. On the server side, F3 will check each request and see if the CSS or Javascript blocks have already been cached. The route we specified has a cache refresh period of 3600
seconds. Additionally, if the Web browser sends an If-Modified-Since
request header and the framework sees the cache hasn't changed, F3 just sends an HTTP 304 Not Modified
response so no content is actually delivered. Without the If-Modified-Since
header, Fat-Free renders the output from the cached file if available. Otherwise, the relevant code is executed.
Tip: If you're not modifying your Javascript/CSS files frequently (as it would be if you're using a Javascript library like jQuery, MooTools, Dojo, etc.), consider adding a cache timer to the route leading to your Javascript/CSS minify handler (3rd argument of F3::route()) so Fat-Free doesn't have compress and combine these files each time such a request is received.
Want to make your site run even faster? Fat-Free works best with either Alternative PHP Cache (APC), XCache, or WinCache. These PHP extensions boost performance of your application by optimizing your PHP scripts (including the framework code).
A fast application that processes all HTTP requests and responds to them at the shortest time possible is not always a good idea - specially if your bandwidth is limited or traffic on your Web site is particularly heavy. Serving pages ASAP also makes your application vulnerable to Denial-of-Service (DOS) attacks. F3 has a bandwidth throttling feature that allows you to control how fast your Web pages are served. You can specify how much time it should take to process a request:-
$ f3 -> route ( ' /throttledpage ' , ' MyApp->handler ' , 0 , 128 );
In this example, the framework will serve the Web page at a rate of 128KiBps.
Bandwidth throttling at the application level can be particularly useful for login pages. Slow responses to dictionary attacks is a good way of mitigating this kind of security risk.
Robust applications are the result of comprehensive testing. Verifying that each part of your program conforms to the specifications and lives up to the expectations of the end-user means finding bugs and fixing them as early as possible in the application development cycle.
If you know little or nothing about unit testing methodologies, you're probably embedding pieces of code directly in your existing program to help you with debugging. That of course means you have to remove them once the program is running. Leftover code fragments, poor design and faulty implementation can creep up as bugs when you roll out your application later.
F3 makes it easy for you to debug programs - without getting in the way of your regular thought processes. The framework does not require you to build complex OOP classes, heavy test structures, and obtrusive procedures.
A unit (or test fixture) can be a function/method or a class. Let's have a simple example:-
function hello () {
return ' Hello, World ' ;
}
Save it in a file called hello.php
. Now how do we know it really runs as expected? Let's create our test procedure:-
$ f3 = require ( ' lib/base.php ' );
// Set up
$ test = new Test ;
include ( ' hello.php ' );
// This is where the tests begin
$ test -> expect (
is_callable ( ' hello ' ),
' hello() is a function '
);
// Another test
$ hello = hello ();
$ test -> expect (
! empty ( $ hello ),
' Something was returned '
);
// This test should succeed
$ test ->expect
is_string ( $ hello ),
' Return value is a string '
);
// This test is bound to fail
$ test -> expect (
strlen ( $ hello )== 13 ,
' String length is 13 '
);
// Display the results ; not MVC but let ' s keep it simple
foreach ( $ test -> results () as $ result ) {
echo $ result [ ' text ' ]. ' <br /> ' ;
if ( $ result [ ' status ' ])
echo ' Pass ' ;
else
echo ' Fail ( ' . $ result [ ' source ' ]. ' ) ' ;
echo ' <br /> ' ;
}
Save it in a file called test.php
. This way we can preserve the integrity of hello.php
.
Now here's the meat of our unit testing process.
F3's built-in Test
class keeps track of the result of each expect()
call. The output of $test->results()
is an array of arrays with the keys text
(mirroring argument 2 of expect()
), status
(boolean representing the result of a test), and source
(file name/line number of the specific test) to aid in debugging.
Fat-Free gives you the freedom to display test results in any way you want. You can have the output in plain text or even a nice-looking HTML template. So how do we run our unit test? If you saved test.php
in the document root folder, you can just open your browser and specify the address http://localhost/test.php
. 그게 전부입니다.
F3 gives you the ability to simulate HTTP requests from within your PHP program so you can test the behavior of a particular route. Here's a simple mock request:-
$ f3 -> mock ( ' GET /test?foo=bar ' );
To mock a POST request and submit a simulated HTML form:-
$ f3 -> mock ( ' POST /test ' ,[ ' foo ' => ' bar ' ]);
Once you get the hang of testing the smallest units of your application, you can then move on to the bigger components, modules, and subsystems - checking along the way if the parts are correctly communicating with each other. Testing manageable chunks of code leads to more reliable programs that work as you expect, and weaves the testing process into the fabric of your development cycle. The question to ask yourself is:- Have I tested all possible scenarios? More often than not, those situations that have not been taken into consideration are the likely causes of bugs. Unit testing helps a lot in minimizing these occurrences. Even a few tests on each fixture can greatly reduce headaches. On the other hand, writing applications without unit testing at all invites trouble.
string AGENT
Mozilla/5.0 (Linux; Android 4.2.2; Nexus 7) AppleWebKit/537.31
. bool AJAX
TRUE
if an XML HTTP request is detected, FALSE
otherwise. string AUTOLOAD
|
), comma ( ,
), or semi-colon ( ;
) as path separator. string BASE
index.php
main/front controller. string BODY
bool/string CACHE
'memcache=localhost'
(and the PHP memcache module is present), F3 auto-detects the presence of APC, WinCache and XCache and uses the first available PHP module if set to TRUE. If none of these PHP modules are available, a filesystem-based backend is used (default directory: tmp/cache
). The framework disables the cache engine if assigned a FALSE
value. bool CASELESS
FALSE
to make it case-sensitive. array COOKIE, GET, POST, REQUEST, SESSION, FILES, SERVER, ENV
integer DEBUG
string DNSBL
403 Forbidden
error if the user's IPv4 address is listed on the specified server(s). array DIACRITICS
string ENCODING
UTF-8
. array ERROR
ERROR.code
is the HTTP status code. ERROR.status
contains a brief description of the error. ERROR.text
provides more detail. For HTTP 500 errors, use ERROR.trace
to retrieve the stack trace. bool ESCAPE
string EXEMPT
string FALLBACK
bool HALT
array HEADERS
bool HIGHLIGHT
TRUE
(requires code.css
stylesheet). string HOST
$_SERVER['SERVER_NAME']
is not available, return value of gethostname()
is used. string IP
array JAR
string LANGUAGE
LOCALES
. If set to NULL
, language is auto-detected from the HTTP Accept-Language
request header. string LOCALES
string LOGS
mixed ONERROR
string PACKAGE
array PARAMS
route()
pattern. PARAMS.0
contains the captured URL relative to the Web root. string PATTERN
string PLUGINS
base.php
. int PORT
string PREFIX
bool QUIET
bool RAW
BODY
. Should be TRUE when processing large data coming from php://input
which will not fit in memory. Default value: FALSE
string REALM
string RESPONSE
QUIET
setting. string ROOT
array ROUTES
string SCHEME
http
or https
. string SERIALIZER
php
, unless PHP igbinary
extension is auto-detected. Assign json
if desired. string TEMP
tmp/
folder inside the Web root. Adjust accordingly to conform to your site's security policies. string TZ
date_default_timezone_set()
function. string UI
View
and Template
classes' render()
method. Default value is the Web root. Accepts a pipe ( |
), comma ( ,
), or semi-colon ( ;
) as separator for multiple paths. callback UNLOAD
string UPLOADS
string URI
string VERB
string VERSION
@token
@token
with value of equivalent F3 variable. {{ mixed expr }}
expr
may include template tokens, constants, operators (unary, arithmetic, ternary and relational), parentheses, data type converters, and functions. If not an attribute of a template directive, result is echoed. {{ string expr | raw }}
expr
. F3 auto-escapes strings by default. {{ string expr | esc }}
expr
. This is the default framework behavior. The | esc
suffix is only necessary if ESCAPE
global variable is set to FALSE
. {{ string expr, arg1, ..., argN | format }}
expr
and pass the comma-separated arguments, where arg1, ..., argn
is one of:- 'date'
, 'time'
, 'number, integer'
, 'number, currency'
, or 'number, percent'
. <include
[ if="{{ bool condition }}" ]
href="{{ string subtemplate }}"
/>
subtemplate
and insert at current position in template if optional condition is TRUE
. <exclude>text-block</exclude>
text-block
at runtime. Used for embedding comments in templates. <ignore>text-block</ignore>
text-block
as-is, without interpretation/modification by the template engine. <check if="{{ bool condition }}">
<true>true-block</true>
<false>false-block</false>
</check>
TRUE
, then true-block
is rendered. Otherwise, false-block
is used. <loop
from="{{ statement }}"
to="{{ bool expr }}"
[ step="{{ statement }}" ]>
text-block
</loop>
from
statement once. Check if the expression in the to
attribute is TRUE
, render text-block
and evaluate step
statement. Repeat iteration until to
expression is FALSE
. <repeat
group="{{ array @group|expr }}"
[ key="{{ scalar @key }}" ]
value="{{ mixed @value }}"
[ counter="{{ scalar @key }}" ]>
text-block
</repeat>
text-block
as many times as there are elements in the array variable @group
or the expression expr
. @key
and @value
function in the same manner as the key-value pair in the equivalent PHP foreach()
statement. Variable represented by key
in counter
attribute increments by 1
with every iteration. <switch expr="{{ scalar expr }}">
<case value="{{ scalar @value|expr }}" break="{{ bool TRUE|FALSE }}">
text-block
</case>
.
.
.
</switch>
{* text-block *}
<exclude>
.The most up-to-date documentation is located at http://fatfreeframework.com/. It contains examples of usage of the various framework components.
Technical support is available at the official discussion forum: https://groups.google.com/forum/#!forum/f3-framework
. If you need live support, you can talk to the development team and other members of the F3 community via Slack or IRC. We're on the FreeNode #fatfree
channel ( chat.freenode.net
). Visit http://webchat.freenode.net/
to join the conversation. You can also download the Firefox Chatzilla add-on or Pidgin if you don't have an IRC client so you can participate in the live chat. You can also find help at Stack Overflow
F3 uses Git for version control. To clone the latest code repository on GitHub:
git clone git://github.com/bcosca/fatfree-core.git
If all you want is a zipball of our test bench with all unit tests, grab it here .
To file a bug report, visit https://github.com/bcosca/fatfree-core/issues
.
Fat-Free Framework is free and released as open source software covered by the terms of the GNU Public License (GPL v3). You may not use the software, documentation, and samples except in compliance with the license. If the terms and conditions of this license are too restrictive for your use, alternative licensing is available for a very reasonable fee.
If you feel that this software is one great weapon to have in your programming arsenal, it saves you a lot of time and money, use it for commercial gain or in your business organization, please consider making a donation to the project. A significant amount of time, effort, and money has been spent on this project. Your donations help keep this project alive and the development team motivated. Donors and sponsors get priority support (24-hour response time on business days).
The Fat-Free Framework is community-driven software. It can't be what it is today without the help and support from the following people and organizations:
Special thanks to the selfless others who expressed their desire to remain anonymous, yet share their time, contribute code, send donations, promote the framework to a wider audience, as well as provide encouragement and regular financial assistance. Their generosity is F3's prime motivation.
By making a donation to this project you signify that you acknowledged, understood, accepted, and agreed to the terms and conditions contained in this notice. Your donation to the Fat-Free Framework project is voluntary and is not a fee for any services, goods, or advantages, and making a donation to the project does not entitle you to any services, goods, or advantages. We have the right to use the money you donate to the Fat-Free Framework project in any lawful way and for any lawful purpose we see fit and we are not obligated to disclose the way and purpose to any party unless required by applicable law. Although Fat-Free Framework is free software, to our best knowledge this project does not have any tax-exempt status. The Fat-Free Framework project is neither a registered non-profit corporation nor a registered charity in any country. Your donation may or may not be tax-deductible; please consult this with your tax advisor. We will not publish/disclose your name and e-mail address without your consent, unless required by applicable law. Your donation is non-refundable.
Copyright (c) 2009-2022 F3::Factory/Bong Cosca <[email protected]>
안녕 친구! 몇 분 동안 저를 도와주세요!