一个功能强大且易于使用的 PHP 微框架,旨在帮助您快速构建动态且健壮的 Web 应用程序!
F3(我们亲切地称呼它)压缩在一个约 65KB 的文件中,为您提供了坚实的基础、成熟的代码库以及编写 Web 应用程序的严肃方法。其底层是一个易于使用的 Web 开发工具包、高性能 URL 路由和缓存引擎、内置代码突出显示以及对多语言应用程序的支持。它重量轻、易于使用且速度快。最重要的是,它不会妨碍您。
无论您是 PHP 程序员的新手还是专家,F3 都会让您立即上手并运行。没有不必要和繁琐的安装过程。无需复杂的配置。没有复杂的目录结构。现在就是开始以简单的方式开发 Web 应用程序的最佳时机!
F3 支持现成的 SQL 和 NoSQL 数据库:MySQL、SQLite、MSSQL/Sybase、PostgreSQL、DB2 和 MongoDB。它还配备了强大的对象关系映射器,用于数据抽象和建模,与框架一样轻量级。无需配置。
这还不是全部。 F3 与其他扩展其功能的可选插件打包在一起:-
与其他框架不同,F3 的目标是可用——这并不常见。
该框架及其软件架构方法背后的理念是实现结构组件的极简主义,避免应用程序复杂性并在代码优雅、应用程序性能和程序员生产力之间取得平衡。
F3拥有稳定的企业级架构。无与伦比的性能、用户友好的功能和轻巧的占地面积。您还能要求什么?要获取此软件包,只需下载此软件包或访问 fatfree-core 存储库以查找最新的边缘版本。
对于所有作曲家用户:
composer create-project bcosca/fatfree
启动一个新项目composer require bcosca/fatfree-core
将 fatfree 添加到现有项目中强烈建议有经验的用户使用最新版本开发新应用程序,以利用更新的代码库和持续的改进。
最新的用户指南和详细的 API 文档以及大量代码示例和图形指南可以在 fatfreeframework.com/ 上找到。
当然,这个方便的在线参考是由 F3 提供支持的!它展示了该框架的功能和性能。现在就检查一下。如果您想直接在 github 上阅读,可以在 github.com/F3Community/F3com-data 找到网站内容
设计师知道他已经达到了完美,不是当没有什么可以添加的时候,而是当没有什么可以删除的时候。 ——安东尼·德·圣埃克苏佩里
Fat-Free Framework 使您可以轻松地快速构建整个网站。 F3 具有与现代 Javascript 工具包和库相同的功能和简洁性,可以帮助您编写更美观、更可靠的 PHP 程序。只要看一眼你的 PHP 源代码,任何人都会发现它很容易理解,你可以用这么少的几行代码完成多少工作,以及结果有多么强大。
F3 是文档最好的框架之一。学习它几乎不需要任何成本。没有严格的难以导航的目录结构和烦人的编程步骤。无需大量配置选项即可在浏览器中显示'Hello, World'
。无脂给您很大的自由和风格,让您在更短的时间内轻松完成更多的工作。
F3 的声明式编程方法使新手和专家都可以轻松理解 PHP 代码。如果您熟悉 Ruby 编程语言,您会注意到 Fat-Free 和 Sinatra 微框架之间的相似之处,因为它们都采用简单的领域特定语言来实现 ReSTful Web 服务。但与 Sinatra 及其 PHP 版本(Fitzgerald、Limonade、Glue 等等)不同,Fat-Free 不仅仅只处理路由和请求。视图可以是任何形式,例如纯文本、HTML、XML 或电子邮件消息。该框架配备了快速且易于使用的模板引擎。 F3 还可以与其他模板引擎无缝协作,包括 Twig、Smarty 和 PHP 本身。模型与 F3 的数据映射器和 SQL 帮助程序进行通信,以便与各种数据库引擎进行更复杂的交互。其他插件进一步扩展了基本功能。它是一个完整的 Web 开发框架 - 功能强大!
将分发包的内容解压缩到硬盘驱动器中的任意位置。默认情况下,框架文件和可选插件位于lib/
路径中。以您想要的方式组织您的目录结构。为了提高安全性,您可以将默认文件夹移动到无法通过 Web 访问的路径。删除不需要的插件。您稍后可以随时恢复它们,F3 将自动检测它们的存在。
重要提示:如果您的应用程序使用 APC、Memcached、WinCache、XCache 或文件系统缓存,请先清除所有缓存条目,然后再用新版本的框架覆盖旧版本的框架。
确保您运行的是正确版本的 PHP。 F3 不支持 PHP 7.2 之前的版本。您会到处遇到语法错误(误报),因为过时的 PHP 版本不支持新的语言结构和闭包/匿名函数。要找到答案,请打开控制台(GNU/Linux 上的bash
shell,或 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
前缀。将以上代码片段保存为 Web 根文件夹中的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()
方法通知 Fat-Free 在斜杠 ( /
) 指示的相对 URL 处有可用的网页。任何访问您位于http://www.example.com/
网站的人都会看到'Hello, world!'
消息,因为 URL /
相当于根页面。要创建从根页面分支的路由(例如http://www.example.com/inside/
,您可以使用简单的GET /inside
字符串定义另一个路由。
上述路由告诉框架仅在使用 HTTP GET
方法接收到 URL 请求时才渲染页面。包含表单的更复杂的网站使用其他 HTTP 方法,例如POST
,您也可以将其实现为$f3->route()
规范的一部分。
如果框架发现对位于根 URL /
网页的传入请求,它会自动将请求路由到回调函数,该函数包含处理请求和呈现适当的 HTML 内容所需的代码。在此示例中,我们仅发送字符串'Hello, world!'
到用户的 Web 浏览器。
所以我们已经建立了我们的第一条路线。但这并没有多大作用,只是让 F3 知道有一个进程可以处理它并且有一些文本要显示在用户的 Web 浏览器上。如果您的网站上有很多页面,则需要为每个组设置不同的路由。现在,让我们保持简单。为了指示框架开始等待请求,我们发出$f3->run()
命令。
无法运行示例?如果您在服务器上运行这个简单的程序时遇到困难,您可能需要稍微调整一下 Web 服务器设置。查看下一节中的示例 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. ' ;
}
);
此示例展示了如何指定标记@count
来表示 URL 的一部分。该框架将提供与/brew/
前缀匹配的任何请求 URL,例如/brew/99
、 /brew/98
等。这将显示'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 会自动使用 URL 中捕获的字符串值填充全局PARAMS
数组变量。回调函数内的$f3->get()
调用检索框架变量的值。您当然可以在代码中应用此方法作为表示或业务逻辑的一部分。但我们稍后会更详细地讨论这个问题。
请注意,Fat-Free 理解数组点表示法。您可以在代码中使用PARAMS['count']
常规表示法,这很容易出现拼写错误和大括号不平衡。在视图和模板中,框架允许@PARAMS.count
表示法,这有点类似于 Javascript。 (稍后我们将介绍视图和模板。)
这是在请求模式中访问令牌的另一种方法:-
$ 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 等)——只要这些文件的路径不与应用程序中定义的任何路由冲突——您的 Web 服务器软件会将它们传送到用户的浏览器,前提是服务器配置正确。
定义路线时,您可以为其指定一个名称。在代码和模板中使用路由名称,而不是键入的 URL。那么如果你需要改变你的url来取悦营销霸主,你只需要在定义路由的地方进行改变。路由名称必须遵循 php 变量命名规则(没有点、破折号或连字符)。
让我们命名一条路线:-
$ f3 -> route ( ' GET @beer_list: /beer ' , ' Beer->list ' );
该名称插入在路由 VERB(本示例中为GET
)之后,前面带有@
符号,并用冒号:
符号与 URL 部分分隔。如果可以更轻松地阅读代码,您可以在冒号后插入空格(如此处所示)。
要访问模板中的命名路由,请获取命名路由的值作为ALIASES
hive 数组的键:-
< 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 会将这些令牌替换为其当前值。如果您想在调用重新路由之前更改令牌的值,请将其作为第二个参数传递。:-
$ 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) ' );
如果您的字符不符合 RFC 1738 格式正确的 URL 准则,请记住对您的参数urlencode()
。
PHP 的最新稳定版本有自己的内置Web 服务器。使用以下配置启动它:-
php -S localhost:80 -t /var/www/
上面的命令将开始将所有请求路由到 Web 根目录/var/www
。如果收到对文件或文件夹的传入 HTTP 请求,PHP 将在 Web 根目录中查找它,如果找到则将其发送到浏览器。否则,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]
该脚本告诉 Apache,每当 HTTP 请求到达时,如果找不到物理文件 ( !-f
) 或路径 ( !-d
) 或符号链接 ( !-l
),它应该将控制权转移到index.php
,其中包含我们的主/前端控制器,然后调用框架。
包含上述 Apache 指令的.htaccess file
应始终与index.php
位于同一文件夹中。
您还需要设置 Apache,以便它知道index.php
在硬盘中的物理位置。典型的配置是:-
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
。可能需要重新启动才能使更改生效。然后,您可以将 Web 浏览器指向地址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"
}
安装与您的 Windows 版本相对应的 URL 重写模块和适当的 .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: http://www.example.com/newpage
如以下所示上面的例子。您还可以将用户重定向到另一个站点,例如$f3->reroute('http://www.anotherexample.org/');
。
当您需要在站点上进行一些维护工作时,重新路由尤其有用。您可以有一个路由处理程序,通知您的访问者您的网站在短时间内处于离线状态。
HTTP 重定向是必不可少的,但它们也可能很昂贵。如果您可以通过调用处理目标路由的函数或方法来引导应用程序的流程,请尽可能避免使用$f3->reroute()
将用户发送到同一网站上的另一个页面。但是,此方法不会更改用户 Web 浏览器地址栏上的 URL。如果这不是您想要的行为,并且您确实需要将用户发送到另一个页面,例如成功提交表单或用户经过身份验证后,Fat-Free 会发送HTTP 302 Found
标头。对于重新路由到另一个页面或站点的所有其他尝试,框架会发送HTTP 301 Moved Permanently
标头。
在运行时,只要 Fat-Free 发现传入的 HTTP 请求与应用程序中定义的任何路由不匹配,就会自动生成 HTTP 404 错误。但是,在某些情况下您需要自己触发它。
以定义为GET /dogs/@breed
路由为例。您的应用程序逻辑可能涉及搜索数据库并尝试检索与传入 HTTP 请求中的@breed
值相对应的记录。由于存在@breed
令牌,Fat-Free 将接受/dogs/
前缀之后的任何值,因此当程序在我们的数据库中找不到任何匹配项时,有必要以编程方式显示HTTP 404 Not Found
消息。为此,请使用以下命令:-
$ f3 -> error ( 404 );
Fat-Free 的体系结构基于以下概念:HTTP URI 表示抽象 Web 资源(不限于 HTML),并且每个资源都可以从一种应用程序状态转移到另一种应用程序状态。因此,F3 对您构建应用程序的方式没有任何限制。如果您更喜欢使用模型-视图-控制器模式,F3 可以帮助您划分应用程序组件以遵循此范例。另一方面,该框架还支持资源-方法-表示模式,并且实现起来更加简单。
这是 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 接口。如果您的应用程序收到传入的 HTTP 请求(例如GET /cart/123
,Fat-Free 会自动将控制权转移到对象或类的get()
方法。另一方面, POST /cart/123
请求将被路由到Item
类的post()
方法。
注意:浏览器不会以常规 HTML 形式实现 HTTP PUT
和DELETE
方法。这些和其他 ReST 方法( HEAD
和CONNECT
)只能通过对服务器的 AJAX 调用来访问。
如果框架接收到映射到类未实现的方法的路由的 HTTP 请求(可能您在路由映射中犯了错误,或者尚未编写该方法),它会生成HTTP 405 Method Not Allowed
错误。
如果客户端请求 URL 资源的 HTTP OPTIONS
,F3 将使用适当的 HTTP 标头进行响应,该标头指示资源允许使用哪些方法(HEAD、GET、PUT 等)。框架不会将OPTIONS
请求映射到类。
Fat-Free 有一种仅在您需要时加载类的方法,因此它们不会占用比应用程序特定部分所需的内存更多的内存。而且您不必编写一长串include
或require
语句来加载保存在不同文件和不同位置的 PHP 类。该框架可以自动为您完成此操作。只需将文件(每个文件一个类)保存在文件夹中,并告诉框架在调用类中的方法后自动加载适当的文件:-
$ 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/
是您的 Web 根目录,那么 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 无法将控制权转移到与当前路由关联的类或方法(即未定义的类或方法),则 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()
语句。所有框架常量都仅限于类。
为无脂变量赋值:
$ 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 ;
如果您不再需要它,要从内存中删除无脂肪变量(丢弃它,这样它就不会干扰您的其他函数/方法),请使用以下方法:-
$ 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 的底层$_SESSION
。改变后者也会改变对应的框架。 Fat-Free 不仅仅维护变量及其值的愚蠢存储。它还可以自动化会话管理和其他事情。通过 F3 的SESSION
变量分配或检索值会自动启动会话。如果您直接使用$_SESSION
(或与会话相关的函数),而不是框架变量SESSION
,您的应用程序将负责管理会话。
通常,框架变量在 HTTP 请求之间不会持续存在。只有映射到 PHP 的$_SESSION
和$_COOKIE
全局变量的SESSION
和COOKIE
(及其元素)不受 HTTP 无状态特性的影响。
Fat-Free 在内部使用了几个预定义的全局变量,您当然可以在您的应用程序中使用它们。确保你知道自己在做什么。更改一些 Fat-Free 全局变量可能会导致意外的框架行为。
该框架有几个变量可以帮助您保持文件和目录结构井井有条。我们已经了解了如何使用AUTOLOAD
自动加载类。有一个UI
全局变量,其中包含指向 HTML 视图/模板位置的路径。 DEBUG
是您在应用程序开发过程中经常使用的另一个变量,它用于设置错误跟踪的详细程度。
如果您需要内置框架变量的完整列表,请参阅快速参考。
框架变量可以包含任意数量的字母、数字和下划线。它必须以字母字符开头并且不能有空格。变量名称区分大小写。
F3 对内部预定义的全局变量使用全部大写。没有什么可以阻止您在自己的程序中使用由全大写组成的变量名称,但作为一般规则,在设置自己的变量时坚持使用小写(或驼峰式),这样就可以避免与当前和未来的框架版本发生任何可能的冲突。
您不应使用 PHP 保留字(例如if
、 for
、 class
、 default
等)作为框架变量名称。这些可能会导致不可预测的结果。
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 甚至允许您将所有文件存储在无法通过 Web 访问的目录中。唯一的要求是将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模板令牌以@
符号开始,然后是一系列的字母和数字,并包含在卷发括号中。第一个字符必须是alpha。模板令牌与框架变量具有一对一的对应关系。框架自动将令牌替换为存储在同名变量中的值。
在我们的示例中,F3用分配给名称变量的值代替了模板中的@name
令牌。在运行时,上述代码的输出将为: -
< p > Hello, world </ p >
担心F3模板的性能?在运行时,框架解析和编译/将F3模板转换为PHP代码,该模板是通过$template->render()
首次显示的PHP代码。然后,该框架在所有后续调用中使用此编译的代码。因此,当涉及更复杂的模板时,由于模板编译器完成的代码优化,性能应与PHP模板相同,即使不是更好。
无论您是使用PHP的模板引擎还是F3自己的模板引擎,如果您在服务器上有APC,Wincache或XCache,模板渲染都可以更快。
如前所述,框架变量可以容纳任何PHP数据类型。但是,如果您不小心,则在F3模板中使用非量表数据类型可能会产生奇怪的结果。卷曲括号中的表达式将始终评估并转换为字符串。您应该将用户界面变量限制为简单标量: - string
, integer
, boolean
或float
数据类型。
但是数组呢?无脂肪识别阵列,您可以将它们雇用在模板中。您可以拥有类似的东西: -
< p > {{ @buddy[0] }}, {{ @buddy[1] }}, and {{ @buddy[2] }} </ p >
并在使用模板之前将@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 " />
该指令将嵌入标题的内容。您也可以以: -
< 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文件。实际上,您可以使用模板引擎渲染其他类型的文件。
if
属性,则<include>
指令还具有可选的,因此您可以在插入子网络之前指定需要满足的条件: -
< 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>
指令。
IF属性中的F3表达式等于NULL
,一个空字符串,布尔FALSE
,一个空数组或零,自动调用<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 >
group
属性@fruits
<repeat>
指令必须是一个数组,应相应地在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中唯一要做的就是定义单个F3变量div
的内容以替换@div
令牌。无脂肪使编程和Web模板设计非常容易。
<repeat>
模板指令的value
属性返回迭代中当前元素的值。如果需要获取当前元素的数组键,请改用key
属性。 key
属性是可选的。
<repeat>
还具有可选的计数器属性,可以使用如下: -
< repeat group =" {{ @fruits }} " value =" {{ @fruit }} " counter =" {{ @ctr }} " >
< p class =" {{ @ctr%2?'odd':'even' }} " > {{ trim(@fruit) }} </ p >
</ repeat >
在内部,F3的模板引擎记录了循环迭代的数量,并将该值保存在变量/令牌@ctr
中,该@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>, <check>
, <include>
和<exclude>
指令。只需告诉F3,您正在传递XML文件而不是HTML: -
echo Template:: instance ()-> render ( ' template.xml ' , ' application/xml ' );
第二个参数代表正在渲染的文档的MIME类型。
MVC的视图组件涵盖了不属于模型和控制器的所有内容,这意味着您的演示文稿可以并且应该包括各种用户界面,例如RSS,电子邮件,RDF,FOAF,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 >
将上面的电子邮件模板保存为welcome.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()。
现在不是吗?当然,如果您有一堆电子邮件收件人,则将使用数据库来填充名称,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/
文件夹中查找字典: -
$ f3 -> set ( ' LOCALES ' , ' dict/ ' );
但是该框架如何确定要使用的语言? F3首先查看HTTP请求标头,特别是浏览器发送的Accept-Language
标头,从而自动检测它。
为了覆盖此行为,您可以触发F3使用用户或应用程序指定的语言: -
$ f3 -> set ( ' LANGUAGE ' , ' de ' );
注意:在上面的示例中,键PI仅存在英语词典中。该框架将始终使用英语( en
)作为后备,以填充指定(或检测到的)语言中未存在的密钥。
您还可以为en-US
, es-AR
等语言变体创建字典文件。在这种情况下,F3将首先使用语言变体(例如es-AR
)。如果变体中不存在键,则框架将在es
语言中查找键,然后将en
语言文件用作最终后备。字典键值对成为F3变量。确保密钥与通过$f3->set()
, $f3->mset()
或$f3->config()
实例化的任何框架变量不冲突。
您是否注意到我们以前的示例中的'Today is {0,date}'
模式? F3的多语言功能取决于ICU项目的字符串/消息格式规则。该框架使用其自己的ICU字符串格式实现的子集。无需在服务器上激活PHP的intl
扩展名。
还有一件事:F3也可以加载.INI风格的格式化文件作为字典: -
love = " I love F3 "
today = " Today is {0,date} "
pi = " {0,number} "
money = " Amount remaining: {0,number,currency} "
将其保存为dict/en.ini
以便框架可以自动加载它。
默认情况下,查看处理程序和模板引擎都可以逃脱所有渲染变量,即转换为HTML实体,以保护您免受可能的XS和代码注入攻击。另一方面,如果您希望将有效的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标签通过未触及的,并简单地删除不安全的控制字符。
无脂肪旨在使与SQL数据库接口的工作变得轻而易举。如果您不是沉浸于SQL的详细信息的类型,而是更倾向于面向对象的数据处理,则可以直接转到本教程的下一部分。但是,如果您需要执行一些复杂的数据处理和数据库性能优化任务,则SQL是必经之路。
使用熟悉的$f3->set()
命令与MySQL,SQLITE,SQL Server,Sybase和Oracle建立通信。连接到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通过照顾后端的所有辛勤工作来简化一切。
这次,我们创建了一个HTML模板,例如abc.htm
,该模板至少具有以下内容: -
< repeat group =" {{ @result }} " value =" {{ @item }} " >
< span > {{ @item.brandName }} </ span >
</ repeat >
在大多数情况下,SQL命令集应该足以生成适合Web的结果,以便您可以直接在模板中使用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
变量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具有自己的快速且轻巧的NOSQL实现,称为JIG,它使用PHP序列化或JSON编码的平面文件。这些抽象层不需要严格的数据结构。字段可能从一个记录到另一个记录。它们也可以随时定义或掉落。
现在回到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
表的结构,并确定哪些字段定义为主键。在这一点上,映射器对象尚无数据(干状态),因此$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或您使用的任何数据库引擎中执行此操作。进行数据库引擎的更改后,运行应用程序时,无脂肪将与数据映射器对象自动同步。
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如何知道何时应插入或更新记录?当数据映射器对象被记录检索自动填充时,该框架可以跟踪记录的主要键(或_id
,在MongoDB和JIG的情况下) - 因此,它知道应该更新或删除哪些记录 - 甚至当主密钥的值更改时。一个编程的数据映射器(未从数据库中检索的值,而是由应用程序填充的值)将在其主键中没有任何先前值的内存。 MongoDB和JIG也是如此,但使用对象_id
作为参考。因此,当我们实例化上面的$user
对象并使用程序中的值填充其属性 - 根本没有从用户表中检索记录时,F3知道它应该插入此记录。
save()
后,映射器对象不会为空。如果您想在数据库中添加新记录,则必须首先脱水映射器: -
$ user -> reset ();
$ user -> userID = ' cheetah ' ;
$ user -> password = password_hash ( ' unknown ' , PASSWORD_BCRYPT , [ ' cost ' => 12 ]);
$ user -> save ();
第二次调用save()
不调用reset()
只需更新映射器当前指向的记录即可。
尽管在数据库中所有表中都有主键的问题是有争议的,但F3并不能阻止您创建与包含没有主键的表通信的数据映射对象。唯一的缺点是:您无法删除或更新映射的记录,因为F3绝对没有办法确定您指的是哪个记录加上位置参考不可靠的事实。行ID在不同的SQL引擎上不可移植,并且可能不会由PHP数据库驱动程序返回。
要从我们的表中删除映射的记录,请在自动含水数据映射器上调用erase()
方法。例如:-
$ user = new DB SQL Mapper ( $ db , ' users ' );
$ user -> load ([ ' userID=? AND password=? ' , ' cheetah ' , ' ch1mp ' ]);
$ user -> erase ();
夹具的查询语法会稍微相似: -
$ user = new DB Jig Mapper ( $ db , ' users ' );
$ user -> load ([ ' @userID=? AND @password=? ' , ' cheetah ' , ' chimp ' ]);
$ user -> erase ();
同等数字将是: -
$ 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()
方法将映射器对象与框架数组变量的元素进行水合,其数组键必须具有与映射器对象属性相同的名称,又与记录的字段名称相对应。因此,当提交Web表单(假设将HTML名称属性设置为userID
)时,该输入字段的内容将传输到$_POST['userID']
,由F3在其POST.userID
actible中重复,并保存到映射的字段$user->userID
在数据库中。如果它们都具有相同的命名元素,则该过程变得非常简单。数组键的一致性,IE模板令牌名称,框架变量名称和字段名称是键:)
另一方面,如果我们想检索记录并将字段值复制到框架变量以供以后使用,例如模板渲染: -
$ 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注入的安全。
默认情况下,数据映射器的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->next()
作为$user->skip()
和$user->prev()
的替代品,如果您认为它对$user->skip(-1)
具有更大的意义。
使用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-Emm允许您处理从现有字段得出的数据。
假设我们的下表定义为: -
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交叉引用在products
表中使用supplierID
商表中的supplerID
在suppliers
表中。
要销毁虚拟字段,请使用unset($item->totalPrice);
。如果定义了totalPrice
虚拟字段,则isset($item->totalPrice)
表达式返回true,或者否则为false。
请记住,必须在数据检索之前定义虚拟字段。 ORM不会执行实际的计算,也不执行其他表的结果。这是数据库引擎可以完成所有艰苦的工作。
如果您不需要按记录录制导航,则可以一次拍摄一批记录: -
$ frequentUsers = $ user -> find ([ ' visits>? ' , 3 ],[ ' order ' => ' userID ' ]);
夹具mapper的查询语法有点相似: -
$ frequentUsers = $ user -> find ([ ' @visits>? ' , 3 ],[ ' order ' => ' userID ' ]);
使用MongoDB映射器的等效代码: -
$ frequentUsers = $ user -> find ([ ' visits ' =>[ ' $gt ' => 3 ]],[ ' userID ' => 1 ]);
find()
方法搜索users
表以查看符合条件的记录,将userID
的结果分类,并将结果返回作为映射对象的数组。 find('visits>3')
不同于load('visits>3')
。后者是指当前$user
对象。 find()
对skip()
没有任何影响。
重要的是:宣布空条件,空或零长度字符串为find()
或load()
的第一个参数()将检索所有记录。确保您知道自己在做什么 - 您可能会超过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语句(或间接通过映射器对象)会导致性能瓶颈,则可以简单地做到这一点: -
echo $ db -> log ();
F3跟踪发布给基础SQL数据库驱动程序的所有命令,以及每个语句完成的时间 - 您需要调整应用程序性能所需的正确信息。
在大多数情况下,您可以根据到目前为止讨论的数据映射方法给出的舒适度生活。如果您需要进行一些重型工作的框架,则可以通过使用自定义方法声明自己的课程来扩展SQL映射器 - 但是您无法避免在一些铁杆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只是不必要的开销。但是关键是 - 数据映射器为您提供了使用对象来表示数据库实体的附加功能。作为开发人员,您可以更快地编写代码并提高效率。结果程序将更加干净,即使不是更短。但是,您必须权衡速度妥协的好处 - 特别是在处理大型和复杂的数据存储时。请记住,所有ORMS - 无论它们有多薄)永远只是另一个抽象层。他们仍然必须将工作传递给基础的SQL引擎。
根据设计,F3的ORM不提供直接连接对象相互连接的方法,即SQL加入 - 因为这打开了一罐蠕虫。它使您的应用程序比应该更复杂,并且由于对象的继承和多态性和多态性(阻抗不匹配),对物体的趋势要陷入僵局,甚至不同步,它们与数据库实体映射到它们。使用虚拟字段,在SQL映射器中有一些间接的方法 - 但是您必须以编程方式和自身的风险进行此操作。
如果您想在应用程序中应用“纯” OOP概念来表示您的所有数据(因为“一切都是对象”),请记住,数据几乎总是比应用程序更长。您的程序可能已经过时了,直到数据失去其价值。不要使用相互论来的对象和类与数据的架构和物理结构过多的相互偏离的对象和类中添加另一层复杂性。
在应用程序中将多个对象编织在一起以操纵数据库中的基础表之前,请考虑一下:创建视图来表示关系并触发数据库引擎中的对象行为更有效。关系数据库引擎旨在处理视图,连接表和触发器。它们不是愚蠢的数据存储。在视图中加入的表将以单个桌子出现,无脂肪可以自动映射视图以及常规桌子。与数据库引擎的机器代码,关系代数和优化逻辑相比,复制加入为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将永远是您的后备。
插件无非是自动加载类,这些类使用框架内置的框架来扩展F3的功能和功能。如果您想做出贡献,请在Google组托管的无脂讨论区域上留言,或在FreeNode #fatfree
IRC频道中告诉我们。其他人可能参与了一个类似的项目。如果我们统一努力,框架社区将非常感谢。
在某些情况下,您想使您的表格更加安全,以防止垃圾邮件机器人和恶意自动脚本。 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.默认值为60
秒。 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
);
这就是全部! 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. | 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.您的捐款可能可以免税,也可能不能免税; 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]>
嘿伙计!帮我几个吧!