fatfree
文件大小: unknow
源码售价: 5 个金币 积分规则     积分充值
资源说明:A powerful yet easy-to-use PHP micro-framework designed to help you build dynamic and robust Web applications - fast!
[![Fat-Free Framework](ui/images/logo.png)](http://fatfree.sf.net/)

**A powerful yet easy-to-use PHP micro-framework designed to help you build dynamic and robust Web applications - fast!**

[![Flattr this project](https://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=phpfatfree&url=https://github.com/bcosca/fatfree)

Condensed in a single ~65KB file, F3 (as we fondly call it) gives you solid foundation, a mature code base, and a no-nonsense approach to writing Web applications. Under the hood is an easy-to-use Web development tool kit, a high-performance URL routing and cache engine, built-in code highlighting, and support for multilingual applications. It's lightweight, easy-to-use, and fast. Most of all, it doesn't get in your way.

Whether you're a novice or an expert PHP programmer, F3 will get you up and running in no time. No unnecessary and painstaking installation procedures. No complex configuration required. No convoluted directory structures. There's no better time to start developing Web applications the easy way than right now!

F3 supports both SQL and NoSQL databases off-the-shelf: MySQL, SQLite, MSSQL/Sybase, PostgreSQL, DB2, and MongoDB. It also comes with powerful object-relational mappers for data abstraction and modeling that are just as lightweight as the framework. No configuration needed.

That's not all. F3 is packaged with other optional plug-ins that extend its capabilities:-

* Fast and clean template engine,
* Unit testing toolkit,
* Database-managed sessions with automatic CSRF protection,
* Markdown-to-HTML converter,
* Atom/RSS feed reader,
* Image processor,
* Geodata handler,
* Google static maps,
* On-the-fly Javascript/CSS compressor,
* OpenID (consumer),
* Custom logger,
* Basket/Shopping cart,
* Pingback server/consumer,
* Unicode-aware string functions,
* SMTP over SSL/TLS,
* Tools for communicating with other servers,
* And more in a tiny supercharged package!

Unlike other frameworks, F3 aims to be usable - not usual.

[![Flattr this project](https://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=phpfatfree&url=https://github.com/bcosca/fatfree)

The philosophy behind the framework and its approach to software architecture is towards minimalism in structural components, avoiding application complexity and striking a balance between code elegance, application performance and programmer productivity.

[![Paypal](ui/images/paypal.png)](https://www.paypal.me/fatfree)

## Table of Contents

* [Getting Started](#getting-started)
* [Routing Engine](#routing-engine)
* [Framework Variables](#framework-variables)
* [Views and Templates](#views-and-templates)
* [Databases](#databases)
* [Plug-Ins](#plug-ins)
* [Optimization](#optimization)
* [Unit Testing](#unit-testing)
* [Quick Reference](#quick-reference)
* [Support and Licensing](#support-and-licensing)

[![Twitter](ui/images/twitter.png)](https://twitter.com/phpfatfree)

### Get the latest release!

F3 has a stable enterprise-class architecture. Unbeatable performance, user-friendly features and a lightweight footprint. What more can you ask for?
To get this package, simply download this package or visit the [fatfree-core](https://github.com/bcosca/fatfree-core) repository to find the latest edge-version.

For all composer users out there:

*  start a new project using `composer create-project bcosca/fatfree`
*  add fatfree to your existing project with `composer require bcosca/fatfree-core`

It is highly recommended that experienced users develop new applications with the latest version to take advantage of an updated code base and ongoing improvements.

## Please visit FatFreeFramework.com

**The most up-to-date user-guide and detailed API documentation with lots of code examples and a graphic guide can be found at [fatfreeframework.com/](http://fatfreeframework.com/).**

Of course this handy online reference is powered by F3! It showcases the framework's capability and performance. Check it out now. If you'd like to read it at github directly, you can find the websites content at [github.com/F3Community/F3com-data](https://github.com/F3Community/F3com-data)

## Getting Started

> *A designer knows he has achieved perfection not when there is nothing left to add, but when there is nothing left to take away. -- Antoine de Saint-Exupéry*

Fat-Free Framework makes it easy to build entire Web sites in a jiffy. With the same power and brevity as modern Javascript toolkits and libraries, F3 helps you write better-looking and more reliable PHP programs. One glance at your PHP source code and anyone will find it easy to understand, how much you can accomplish in so few lines of code, and how powerful the results are.

F3 is one of the best documented frameworks around. Learning it costs next to nothing. No strict set of difficult-to-navigate directory structures and obtrusive programming steps. No truck load of configuration options just to display `'Hello, World'` in your browser. Fat-Free gives you a lot of freedom - and style - to get more work done with ease and in less time.

F3's declarative approach to programming makes it easy for novices and experts alike to understand PHP code. If you're familiar with the programming language Ruby, you'll notice the resemblance between Fat-Free and Sinatra micro-framework because they both employ a simple Domain-Specific Language for ReSTful Web services. But unlike Sinatra and its PHP incarnations (Fitzgerald, Limonade, Glue - to name a few), Fat-Free goes beyond just handling routes and requests. Views can be in any form, such as plain text, HTML, XML or an e-mail message. The framework comes with a fast and easy-to-use template engine. F3 also works seamlessly with other template engines, including Twig, Smarty, and PHP itself. Models communicate with F3's data mappers and the SQL helper for more complex interactions with various database engines. Other plug-ins extend the base functionality even more. It's a total Web development framework - with a lot of muscle!

### Enough Said - See For Yourself

Unzip the contents of the distribution package anywhere in your hard drive. By default, the framework file and optional plug-ins are located in the `lib/` path. Organize your directory structures any way you want. You may move the default folders to a path that's not Web-accessible for better security. Delete the plug-ins that you don't need. You can always restore them later and F3 will detect their presence automatically.

**Important:** If your application uses APC, Memcached, WinCache, XCache, or a filesystem cache, clear all cache entries first before overwriting an older version of the framework with a new one.

Make sure you're running the right version of PHP. F3 does not support versions earlier than PHP 5.6. You'll be getting syntax errors (false positives) all over the place because new language constructs and closures/anonymous functions are not supported by outdated PHP versions. To find out, open your console (`bash` shell on GNU/Linux, or `cmd.exe` on Windows):-

```
/path/to/php -v
```

PHP will let you know which particular version you're running and you should get something that looks similar to this:-

```
PHP 7.4.12 (cli) (built: Nov 30 2020 13:28:43) ( 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
```

Upgrade if necessary and come back here if you've made the jump to PHP 7.4 or a later release. Fatfree needs at least PHP 5.6 to function. If you need a hosting service provider, try one of these services:

* [DreamHost](http://www.dreamhost.com/r.cgi?665472)
* [Hostek](http://hostek.com/aff.php?aff=364&plat=L)
* [SiteGround](http://www.siteground.com/index.htm?referrerid=155694)

### Hello, World: The Less-Than-A-Minute Fat-Free Recipe

Time to start writing our first application:-

``` php
$f3 = require('path/to/base.php');
$f3->route('GET /',
    function() {
        echo 'Hello, world!';
    }
);
$f3->run();
```

Prepend `base.php` on the first line with the appropriate path. Save the above code fragment as `index.php` in your Web root folder. We've written our first Web page.

Using composer? Then just run `composer require bcosca/fatfree` and use the following:

``` php
require 'vendor/autoload.php';
$f3 = \Base::instance();
$f3->route('GET /',
    function() {
        echo 'Hello, world!';
    }
);
$f3->run();
```

The first command tells the PHP interpreter that you want the framework's functions and features available to your application. The `$f3->route()` method informs Fat-Free that a Web page is available at the relative URL indicated by the slash (`/`). Anyone visiting your site located at `http://www.example.com/` will see the `'Hello, world!'` message because the URL `/` is equivalent to the root page. To create a route that branches out from the root page, like `http://www.example.com/inside/`, you can define another route with a simple `GET /inside` string.

The route described above tells the framework to render the page only when it receives a URL request using the HTTP `GET` method. More complex Web sites containing forms use other HTTP methods like `POST`, and you can also implement that as part of a `$f3->route()` specification.

If the framework sees an incoming request for your Web page located at the root URL `/`, it will automatically route the request to the callback function, which contains the code necessary to process the request and render the appropriate HTML stuff. In this example, we just send the string `'Hello, world!'` to the user's Web browser.

So we've established our first route. But that won't do much, except to let F3 know that there's a process that will handle it and there's some text to display on the user's Web browser. If you have a lot more pages on your site, you need to set up different routes for each group. For now, let's keep it simple. To instruct the framework to start waiting for requests, we issue the `$f3->run()` command.

**Can't Get the Example Running?** If you're having trouble getting this simple program to run on your server, you may have to tweak your Web server settings a bit. Take a look at the sample Apache configuration in the following section (along with the Nginx and Lighttpd equivalents).

**Still having trouble?** Make sure the `$f3 = require('path/to/base.php');` assignment comes before any output in your script. `base.php` modifies the HTTP headers, so any character that is output to the browser before this assignment will cause errors.

## Routing Engine

### Overview

Our first example wasn't too hard to swallow, was it? If you like a little more flavor in your Fat-Free soup, insert another route before the `$f3->run()` command:-

``` php
$f3->route('GET /about',
    function() {
        echo 'Donations go to a local charity... us!';
    }
);
```

You don't want to clutter the global namespace with function names? Fat-Free recognizes different ways of mapping route handlers to OOP classes and methods:-

``` php
class WebPage {
    function display() {
        echo 'I cannot object to an object';
    }
}

$f3->route('GET /about','WebPage->display');
```

HTTP requests can also be routed to static class methods:-

``` php
$f3->route('GET /login','Auth::login');
```

Passed arguments are always provided as the second parameter:

``` php
$f3->route('GET /hello/@name','User::greet');

class User {
	public static function greet($f3, $args) { //$args is type of Array
		echo "Hello " . $args['name'];
	}
}
```
If the provided name argument would be **foo** (/hello/foo), the following output would be shown:

```
Hello foo
```

### Routes and Tokens

As a demonstration of Fat-Free's powerful domain-specific language (DSL), you can specify a single route to handle different possibilities:-

``` php
$f3->route('GET /brew/@count',
    function($f3) {
        echo $f3->get('PARAMS.count').' bottles of beer on the wall.';
    }
);
```

This example shows how we can specify a token `@count` to represent part of a URL. The framework will serve any request URL that matches the `/brew/` prefix, like `/brew/99`, `/brew/98`, etc. This will display `'99 bottles of beer on the wall'` and `'98 bottles of beer on the wall'`, respectively. Fat-Free will also accept a page request for `/brew/unbreakable`. (Expect this to display `'unbreakable bottles of beer on the wall'`.) When such a dynamic route is specified, Fat-Free automagically populates the global `PARAMS` array variable with the value of the captured strings in the URL. The `$f3->get()` call inside the callback function retrieves the value of a framework variable. You can certainly apply this method in your code as part of the presentation or business logic. But we'll discuss that in greater detail later.

Notice that Fat-Free understands array dot-notation. You can use `PARAMS['count']` regular notation instead in code, which is prone to typo errors and unbalanced braces. In views and templates, the framework permits `@PARAMS.count` notation which is somewhat similar to Javascript. (We'll cover views and templates later.)

Here's another way to access tokens in a request pattern:-

``` php
$f3->route('GET /brew/@count',
    function($f3,$params) {
        echo $params['count'].' bottles of beer on the wall.';
    }
);
```

You can use the asterisk (`*`) to accept any URL after the `/brew` route - if you don't really care about the rest of the path:-

``` php
$f3->route('GET /brew/*',
    function() {
        echo 'Enough beer! We always end up here.';
    }
);
```

An important point to consider: You will get Fat-Free (and yourself) confused if you have both `GET /brew/@count` and `GET /brew/*` together in the same application. Use one or the other. Another thing: Fat-Free sees `GET /brew` as separate and distinct from the route `GET /brew/@count`. Each can have different route handlers.


### Dynamic Web Sites

Wait a second - in all the previous examples, we never really created any directory in our hard drive to store these routes. The short answer: we don't have to. All F3 routes are virtual. They don't mirror our hard disk folder structure. If you have programs or static files (images, CSS, etc.) that do not use the framework - as long as the paths to these files do not conflict with any route defined in your application - your Web server software will deliver them to the user's browser, provided the server is configured properly.

### Named Routes

When you define a route, you can assign it a name. Use the route name in your code and templates instead of a typed url. Then if you need to change your urls to please the marketing overlords, you only need to make the change where the route was defined. The route names must follow php variable naming rules (no dots, dashes nor hyphens).

Let's name a route:-

``` php
$f3->route('GET @beer_list: /beer', 'Beer->list');
```

The name is inserted after the route VERB (`GET` in this example) preceded by an `@` symbol, and separated from the URL portion by a colon `:` symbol. You can insert a space after the colon if that makes it easier to read your code (as shown here).

To access the named route in a template, get the value of the named route as the key of the `ALIASES` hive array:-

``` html
View beer list
```

To redirect the visitor to a new URL, call the named route inside the `reroute()` method like:-

``` php
// a named route is a string value
$f3->reroute('@beer_list'); // note the single quotes
```

If you use tokens in your route, F3 will replace those tokens with their current value. If you want to change the token's value before calling reroute, pass it as the 2nd argument.:-

``` php
$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)');
```

Remember to `urlencode()` your arguments if you have characters that do not comply with RFC 1738 guidelines for well-formed URLs.

### PHP's Built-In Web Server

PHP's latest stable version has its own built-in Web server. Start it up using the following configuration:-

```
php -S localhost:80 -t /var/www/
```

The above command will start routing all requests to the Web root `/var/www`. If an incoming HTTP request for a file or folder is received, PHP will look for it inside the Web root and send it over to the browser if found. Otherwise, PHP will load the default `index.php` (containing your F3-enabled code).

### Sample Apache Configuration

If you're using Apache, make sure you activate the URL rewriting module (mod_rewrite) in your apache.conf (or httpd.conf) file. You should also create a .htaccess file containing the following:-

``` apache
# 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]
```

The script tells Apache that whenever an HTTP request arrives and if no physical file (`!-f`) or path (`!-d`) or symbolic link (`!-l`) can be found, it should transfer control to `index.php`, which contains our main/front controller, and which in turn, invokes the framework.

The `.htaccess file` containing the Apache directives stated above should always be in the same folder as `index.php`.

You also need to set up Apache so it knows the physical location of `index.php` in your hard drive. A typical configuration is:-

``` apache
DocumentRoot "/var/www/html"

    Options -Indexes +FollowSymLinks +Includes
    AllowOverride All
    Order allow,deny
    Allow from All

```

If you're developing several applications simultaneously, a virtual host configuration is easier to manage:-

``` apache
NameVirtualHost *

    ServerName site1.com
    DocumentRoot "/var/www/site1"
    
        Options -Indexes +FollowSymLinks +Includes
        AllowOverride All
        Order allow,deny
        Allow from All
    


    ServerName site2.com
    DocumentRoot "/var/www/site2"
    
        Options -Indexes +FollowSymLinks +Includes
        AllowOverride All
        Order allow,deny
        Allow from All
    

```

Each `ServerName` (`site1.com` and `site2.com` in our example) must be listed in your `/etc/hosts` file. On Windows, you should edit `C:/WINDOWS/system32/drivers/etc/hosts`. A reboot might be necessary to effect the changes. You can then point your Web browser to the address `http://site1.com` or `http://site2.com`. Virtual hosts make your applications a lot easier to deploy.

### Sample Nginx Configuration

For Nginx servers, here's the recommended configuration (replace ip_address:port with your environment's FastCGI PHP settings):-

``` nginx
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;
    }
}
```

### Sample Lighttpd Configuration

Lighttpd servers are configured in a similar manner:-

```
$HTTP["host"] =~ "www\.example\.com$" {
    url.rewrite-once = ( "^/(.*?)(\?.+)?$"=>"/index.php/$1?$2" )
    server.error-handler-404 = "/index.php"
}
```

### Sample IIS Configuration

Install the [URL rewrite module](http://www.iis.net/downloads/microsoft/url-rewrite) and the appropriate .NET framework corresponding to your Windows version. Then create a file named `web.config` in your application root with the following contents:

```


  
    
      
        
          
          
            
            
          
          
        
      
    
  

```

### Rerouting

So let's get back to coding. You can declare a page obsolete and redirect your visitors to another site/page:-

``` php
$f3->route('GET|HEAD /obsoletepage',
    function($f3) {
        $f3->reroute('/newpage');
    }
);
```

If someone tries to access the URL `http://www.example.com/obsoletepage` using either HTTP GET or HEAD request, the framework redirects the user to the URL: `http://www.example.com/newpage` as shown in the above example. You can also redirect the user to another site, like `$f3->reroute('http://www.anotherexample.org/');`.

Rerouting can be particularly useful when you need to do some maintenance work on your site. You can have a route handler that informs your visitors that your site is offline for a short period.

HTTP redirects are indispensable but they can also be expensive. As much as possible, refrain from using `$f3->reroute()` to send a user to another page on the same Web site if you can direct the flow of your application by invoking the function or method that handles the target route. However, this approach will not change the URL on the address bar of the user's Web browser. If this is not the behavior you want and you really need to send a user to another page, in instances like successful submission of a form or after a user has been authenticated, Fat-Free sends an `HTTP 302 Found` header. For all other attempts to reroute to another page or site, the framework sends an `HTTP 301 Moved Permanently` header.

### Triggering a 404

At runtime, Fat-Free automatically generates an HTTP 404 error whenever it sees that an incoming HTTP request does not match any of the routes defined in your application. However, there are instances when you need to trigger it yourself.

Take for instance a route defined as `GET /dogs/@breed`. Your application logic may involve searching a database and attempting to retrieve the record corresponding to the value of `@breed` in the incoming HTTP request. Since Fat-Free will accept any value after the `/dogs/` prefix because of the presence of the `@breed` token, displaying an `HTTP 404 Not Found` message programmatically becomes necessary when the program doesn't find any match in our database. To do that, use the following command:-

``` php
$f3->error(404);
```

### Representational State Transfer (ReST)

Fat-Free's architecture is based on the concept that HTTP URIs represent abstract Web resources (not limited to HTML) and each resource can move from one application state to another. For this reason, F3 does not have any restrictions on the way you structure your application. If you prefer to use the [Model-View-Controller](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) pattern, F3 can help you compartmentalize your application components to stick to this paradigm. On the other hand, the framework also supports the [Resource-Method-Representation](http://www.peej.co.uk/articles/rmr-architecture.html) pattern, and implementing it is more straightforward.

Here's an example of a ReST interface:-

``` php
class Item {
    function get() {}
    function post() {}
    function put() {}
    function delete() {}
}

$f3=require('lib/base.php');
$f3->map('/cart/@item','Item');
$f3->run();
```

Fat-Free's `$f3->map()` method provides a ReST interface by mapping HTTP methods in routes to the equivalent methods of an object or a PHP class. If your application receives an incoming HTTP request like `GET /cart/123`, Fat-Free will automatically transfer control to the object's or class' `get()` method. On the other hand, a `POST /cart/123` request will be routed to the `Item` class' `post()` method.

**Note:** Browsers do not implement the HTTP `PUT` and `DELETE` methods in regular HTML forms. These and other ReST methods (`HEAD`, and `CONNECT`) are accessible only via AJAX calls to the server.

If the framework receives an HTTP request for a route that maps to a method that is not implemented by a class (perhaps you've made an error in the route mapping, or the method is not written yet), it generates an `HTTP 405 Method Not Allowed` error.

If a client requests HTTP `OPTIONS` for a URL resource, F3 responds with the appropriate HTTP headers that indicate which methods are allowed for the resource (HEAD, GET, PUT, etc). The framework will not map the `OPTIONS` request to a class.

### The F3 Autoloader

Fat-Free has a way of loading classes only at the time you need them, so they don't gobble up more memory than a particular segment of your application needs. And you don't have to write a long list of `include` or `require` statements just to load PHP classes saved in different files and different locations. The framework can do this automatically for you. Just save your files (one class per file) in a folder and tell the framework to automatically load the appropriate file once you invoke a method in the class:-

``` php
$f3->set('AUTOLOAD','autoload/');
```

You can assign a different location for your autoloaded classes by changing the value of the `AUTOLOAD` global variable. You can also have multiple autoload paths. If you have your classes organized and in different folders, you can instruct the framework to autoload the appropriate class when a static method is called or when an object is instantiated. Modify the `AUTOLOAD` variable this way:-

``` php
$f3->set('AUTOLOAD','admin/autoload/; user/autoload/; default/');
```

**Important:** Except for the .php extension, the class name and file name must be identical, for the framework to autoload your class properly. The basename of this file must be identical to your class invocation, e.g. F3 will look for either `Foo/BarBaz.php` or `foo/barbaz.php` when it detects a `new Foo\BarBaz` statement in your application.

### Working with Namespaces

`AUTOLOAD` allows class hierarchies to reside in similarly-named subfolders, so if you want the framework to autoload a namespaced class that's invoked in the following manner:-

``` php
$f3->set('AUTOLOAD','autoload/');
$obj=new Gadgets\iPad;
```

You can create a folder hierarchy that follows the same structure. Assuming `/var/www/html/` is your Web root, then F3 will look for the class in `/var/www/html/autoload/gadgets/ipad.php`. The file `ipad.php` should have the following minimum code:-

``` php
namespace Gadgets;
class iPad {}
```

Remember: All directory names in Fat-Free must end with a slash. You can assign a search path for the autoloader as follows:-

``` php
$f3->set('AUTOLOAD','main/;aux/');
```

### Routing to a Namespaced Class

F3, being a namespace-aware framework, allows you to use a method in namespaced class as a route handler, and there are several ways of doing it. To call a static method:-

``` php
$f3->set('AUTOLOAD','classes/');
$f3->route('GET|POST /','Main\Home::show');
```

The above code will invoke the static `show()` method of the class `Home` within the `Main` namespace. The `Home` class must be saved in the folder `classes/main/home.php` for it to be loaded automatically.

If you prefer to work with objects:-

``` php
$f3->route('GET|POST /','Main\Home->show');
```

will instantiate the `Home` class at runtime and call the `show()` method thereafter.

### Event Handlers

F3 has a couple of routing event listeners that might help you improve the flow and structure of controller classes. Say you have a route defined as follows:-

``` php
$f3->route('GET /','Main->home');
```

If the application receives an HTTP request matching the above route, F3 instantiates `Main`, but before executing the `home()` method, the framework looks for a method in this class named `beforeRoute()`. In case it's found, F3 runs the code contained in the `beforeRoute()` event handler before transferring control to the `home()` method. Once this is accomplished, the framework looks for an `afterRoute()` event handler. Like `beforeRoute()`, the method gets executed if it's defined.

### Dynamic Route Handlers

Here's another F3 goodie:-

``` php
$f3->route('GET /products/@action','Products->@action');
```

If your application receives a request for, say, `/products/itemize`, F3 will extract the `'itemize'` string from the URL and pass it on to the `@action` token in the route handler. F3 will then look for a class named `Products` and execute the `itemize()` method.

Dynamic route handlers may have various forms:-

``` php
// static method
$f3->route('GET /public/@genre','Main::@genre');
// object mode
$f3->route('GET /public/@controller/@action','@controller->@action');
```

F3 triggers an `HTTP 404 Not Found` error at runtime if it cannot transfer control to the class or method associated with the current route, i.e. an undefined class or method.

### AJAX and Synchronous Requests

Routing patterns may contain modifiers that direct the framework to base its routing decision on the type of HTTP request:-

``` php
$f3->route('GET /example [ajax]','Page->getFragment');
$f3->route('GET /example [sync]','Page->getFull');
```

The first statement will route the HTTP request to the `Page->getFragment()` callback only if an `X-Requested-With: XMLHttpRequest` header (AJAX object) is received by the server. If an ordinary (synchronous) request is detected, F3 will simply drop down to the next matching pattern, and in this case it executes the `Page->getFull()` callback.

If no modifiers are defined in a routing pattern, then both AJAX and synchronous request types are routed to the specified handler.

Route pattern modifiers are also recognized by `$f3->map()`.

## Framework Variables

### Basic Use

Variables defined in Fat-Free are global, i.e. they can be accessed by any MVC component. Framework globals are not identical to PHP globals. An F3 variable named `content` is not identical to PHP's `$content`. F3 is a domain-specific language in its own right and maintains its own separate symbol table for system and application variables. The framework, like every well-designed object-oriented program, does not pollute the PHP global namespace with constants, variables, functions or classes that might conflict with any application. Unlike other frameworks, F3 does not use PHP's `define()` statement. All framework constants are confined to classes.

To assign a value to a Fat-Free variable:

``` php
$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
```

**Note:** Fat-Free variables accept all PHP data types, including objects and anonymous functions.

To set several variables at once:

``` php
$f3->mset(
    [
        'foo'=>'bar',
        'baz'=>123
    ]
);
```

To retrieve the value of a framework variable named `var`:-

``` php
echo $f3->get('var'); // or
echo $f3->var;
```

To remove a Fat-Free variable from memory if you no longer need it (discard it so it doesn't interfere with your other functions/methods), use the method:-

``` php
$f3->clear('var'); // or
unset($f3->var);
```

To find out if a variable has been previously defined:-

``` php
$f3->exists('var') //
isset($f3->var)
```

### Globals

F3 maintains its own symbol table for framework and application variables, which are independent of PHP's. Some variables are mapped to PHP globals. Fat-Free's `SESSION` is equivalent to `$_SESSION`, and `REQUEST` maps to `$_REQUEST`. Use of framework variables is recommended, instead of PHP's, to help you with data transfer across different functions, classes and methods. They also have other advantages:-

* You can use framework variables directly in your templates.
* You don't have to instruct PHP to reference a variable outside the current scope using a global keyword inside each function or method. All F3 variables are global to your application.
* Setting the Fat-Free equivalent of a PHP global like `SESSION` also changes PHP's underlying `$_SESSION`. Altering the latter also alters the framework counterpart.

Fat-Free does not maintain just a dumb storage for variables and their values. It can also automate session management and other things. Assigning or retrieving a value through F3's `SESSION` variable auto-starts the session. If you use `$_SESSION` (or session-related functions) directly, instead of the framework variable `SESSION`, your application becomes responsible for managing sessions.

As a rule, framework variables do not persist between HTTP requests. Only `SESSION` and `COOKIE` (and their elements) which are mapped to PHP's `$_SESSION` and `$_COOKIE` global variables are exempt from the stateless nature of HTTP.

There are several predefined global variables used internally by Fat-Free, and you can certainly utilize them in your application. Be sure you know what you're doing. Altering some Fat-Free global variables may result in unexpected framework behavior.

The framework has several variables to help you keep your files and directory structures organized. We've seen how we can automate class loading by using the `AUTOLOAD`. There's a `UI` global variable, which contains the path pointing to the location of your HTML views/templates. `DEBUG` is another variable you'll be using quite often during application development and it's used for setting the verbosity of error traces.

Refer to the [Quick Reference](#quick-reference) if you need a comprehensive list of built-in framework variables.

### Naming Rules

A framework variable may contain any number of letters, digits and underscores. It must start with an alpha character and should have no spaces. Variable names are case-sensitive.

F3 uses all-caps for internal predefined global variables. Nothing stops you from using variable names consisting of all-caps in your own program, but as a general rule, stick to lowercase (or camelCase) when you set up your own variables so you can avoid any possible conflict with current and future framework releases.

You should not use PHP reserved words like `if`, `for`, `class`, `default`, etc. as framework variable names. These may cause unpredictable results.

### Working with String and Array Variables

F3 also provides a number of tools to help you with framework variables.

``` php
$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 also provides some primitive methods for working with array variables:-

``` php
$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
```

### Do-It-Yourself Directory Structures

Unlike other frameworks that have rigid folder structures, F3 gives you a lot of flexibility. You can have a folder structure that looks like this (parenthesized words in all-caps represent the F3 framework variables that need tweaking):-

```
/ (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)
```

Feel free to organize your files and directories any way you want. Just set the appropriate F3 global variables. If you want a really secure site, Fat-Free even allows you to store all your files in a non-Web-accessible directory. The only requirement is that you leave `index.php`, `.htaccess` and your public files, like CSS, JavaScript, images, etc. in a path visible to your browser.

### About the F3 Error Handler

Fat-Free generates its own HTML error pages, with stack traces to help you with debugging. Here's an example:-

> ---
> ### Internal Server Error
> strpos() expects at least 2 parameters, 0 given
>
>     • var/html/dev/main.php:96 strpos()
>     • var/html/dev/index.php:16 Base->run()
> ---

If you feel it's a bit too plain or wish to do other things when the error occurs, you may create your own custom error handler:-

``` php
$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 maintains a global variable containing the details of the latest error that occurred in your application. The `ERROR` variable is an array structured as follows:-

```
ERROR.code - displays the error code (404, 500, etc.)
ERROR.status - header and page title
ERROR.text - error context
ERROR.trace - stack trace
```

While developing your application, it's best to set the debug level to maximum so you can trace all errors to their root cause:-

``` php
$f3->set('DEBUG',3);
```

Just insert the command in your application's bootstrap sequence.

Once your application is ready for release, simply remove the statement from your application, or replace it with:-

``` php
$f3->set('DEBUG',0);
```

This will suppress the stack trace output in any system-generated HTML error page (because it's not meant to be seen by your site visitors).

`DEBUG` can have values ranging from 0 (stack trace suppressed) to 3 (most verbose).

**Don't forget!** Stack traces may contain paths, file names, database commands, user names and passwords. You might expose your Web site to unnecessary security risks if you fail to set the `DEBUG` global variable to 0 in a production environment.

### Configuration Files

If your application needs to be user-configurable, F3 provides a handy method for reading configuration files to set up your application. This way, you and your users can tweak the application without altering any PHP code.

Instead of creating a PHP script that contains the following sample code:-

``` 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]);
```

You can construct a configuration file that does the same thing:-

``` ini
[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
```

Instead of lengthy `$f3->set()` statements in your code, you can instruct the framework to load a configuration file as code substitute. Let's save the above text as setup.cfg. We can then call it with a simple:-

``` php
$f3->config('setup.cfg');
```

String values need not be quoted, unless you want leading or trailing spaces included. If a comma should be treated as part of a string, enclose the string using double-quotes - otherwise, the value will be treated as an array (the comma is used as an array element separator). Strings can span multiple lines:-

``` ini
[globals]
str="this is a \
very long \
string"
```

F3 also gives you the ability to define HTTP routes in configuration files:-

``` ini
[routes]
GET /=home
GET /404=App->page404
GET /page/@num=Page->@controller
```

Route maps can be defined in configuration files too:-

``` ini
[maps]
/blog=Blog\Login
/blog/@controller=Blog\@controller
```

The `[globals]`, `[routes]`, and `[maps]` section headers are required. You can combine both sections in a single configuration file - although having `[routes]` and `[maps]` in a separate file is recommended. This way you can allow end-users to modify some application-specific flags, and at the same time restrict them from meddling with your routing logic.

## Views and Templates

### Separation of Concerns

A user interface like an HTML page should be independent of the underlying PHP code related to routing and business logic. This is fundamental to the MVC paradigm. A basic revision like converting `

` to `

` should not demand a change in your application code. In the same manner, transforming a simple route like `GET /about` to `GET /about-us` should not have any effect on your user interface and business logic, (the view and model in MVC, or representation and method in RMR). Mixing programming constructs and user interface components in a single file, like spaghetti coding, makes future application maintenance a nightmare. ### PHP as a Template Engine F3 supports PHP as a template engine. Take a look at this HTML fragment saved as `template.htm`:-. ``` html

Hello, !

``` If short tags are enabled on your server, this should work too:- ``` html

Hello,

``` To display this template, you can have PHP code that looks like this (stored in a file separate from the template):- ``` 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(); ``` The only issue with using PHP as a template engine, due to the embedded PHP code in these files, is the conscious effort needed to stick to the guidelines on separation of concerns and resist the temptation of mixing business logic with your user interface. ### A Quick Look at the F3 Template Language As an alternative to PHP, you can use F3's own template engine. The above HTML fragment can be rewritten as:- ``` html

Hello, {{ @name }}!

``` and the code needed to view this template:- ``` php $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(); ``` Like routing tokens used for catching variables in URLs (still remember the `GET /brew/@count` example in the previous section?), F3 template tokens begin with the `@` symbol followed by a series of letters and digits enclosed in curly braces. The first character must be alpha. Template tokens have a one-to-one correspondence with framework variables. The framework automatically replaces a token with the value stored in a variable of the same name. In our example, F3 replaces the `@name` token in our template with the value we assigned to the name variable. At runtime, the output of the above code will be:- ``` html

Hello, world

``` Worried about performance of F3 templates? At runtime, the framework parses and compiles/converts an F3 template to PHP code the first time it's displayed via `$template->render()`. The framework then uses this compiled code in all subsequent calls. Hence, performance should be the same as PHP templates, if not better due to code optimization done by the template compiler when more complex templates are involved. Whether you use PHP's template engine or F3's own, template rendering can be significantly faster if you have APC, WinCache or XCache available on your server. As mentioned earlier, framework variables can hold any PHP data type. However, usage of non-scalar data types in F3 templates may produce strange results if you're not careful. Expressions in curly braces will always be evaluated and converted to string. You should limit your user interface variables to simple scalars:- `string`, `integer`, `boolean` or `float` data types. But what about arrays? Fat-Free recognizes arrays and you can employ them in your templates. You can have something like:- ``` html

{{ @buddy[0] }}, {{ @buddy[1] }}, and {{ @buddy[2] }}

``` And populate the `@buddy` array in your PHP code before serving the template:- ``` php $f3->set('buddy',['Tom','Dick','Harry']); ``` However, if you simply insert `{{ @buddy }}` in your template, PHP will replace it with `'Array'` because it converts the token to a string. PHP, on the other hand, will generate an `Array to string conversion` notice at runtime. F3 allows you to embed expressions in templates. These expressions may take on various forms, like arithmetic calculations, boolean expressions, PHP constants, etc. Here are a few examples:- ``` html {{ 2*(@page-1) }} {{ (int)765.29+1.2e3 }} {{ var_dump(@xyz) }}

That is {{ preg_match('/Yes/i',@response)?'correct':'wrong' }}!

{{ @obj->property }} ``` An additional note about array expressions: Take note that `@foo.@bar` is a string concatenation `$foo.$bar`), whereas `@foo.bar` translates to `$foo['bar']`. If `$foo[$bar]` is what you intended, use the `@foo[@bar]` regular notation. Framework variables may also contain anonymous functions: ``` php $f3->set('func', function($a,$b) { return $a.', '.$b; } ); ``` The F3 template engine will interpret the token as expected, if you specify the following expression: ``` html {{ @func('hello','world') }} ``` ### Templates Within Templates Simple variable substitution is one thing all template engines have. Fat-Free has more up its sleeves:- ``` html ``` The directive will embed the contents of the header.htm template at the exact position where the directive is stated. You can also have dynamic content in the form of:- ``` html ``` A practical use for such template directive is when you have several pages with a common HTML layout but with different content. Instructing the framework to insert a sub-template into your main template is as simple as writing the following PHP code:- ``` 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'); ``` A sub-template may in turn contain any number of directives. F3 allows unlimited nested templates. You can specify filenames with something other than .htm or .html file extensions, but it's easier to preview them in your Web browser during the development and debugging phase. The template engine is not limited to rendering HTML files. In fact you can use the template engine to render other kinds of files. The `` directive also has an optional `if` attribute so you can specify a condition that needs to be satisfied before the sub-template is inserted:- ``` html ``` ### Exclusion of Segments During the course of writing/debugging F3-powered programs and designing templates, there may be instances when disabling the display of a block of HTML may be handy. You can use the `` directive for this purpose:- ``` html

A chunk of HTML we don't want displayed at the moment

``` That's like the `` HTML comment tag, but the `` directive makes the HTML block totally invisible once the template is rendered. Here's another way of excluding template content or adding comments:- ``` html {*

A chunk of HTML we don't want displayed at the moment

*} ``` ### Conditional Segments Another useful template feature is the `` directive. It allows you to embed an HTML fragment depending on the evaluation of a certain condition. Here are a few examples:- ``` html Inserted if condition is false
Appears when condition is true
Appears when condition is false
``` You can have as many nested `` directives as you need. An F3 expression inside an if attribute that equates to `NULL`, an empty string, a boolean `FALSE`, an empty array or zero, automatically invokes ``. If your template has no `` block, then the `` opening and closing tags are optional:- ``` html

HTML chunk to be included if condition is true

``` ### Repeating Segments Fat-Free can also handle repetitive HTML blocks:- ``` html

{{ trim(@fruit) }}

``` The `group` attribute `@fruits` inside the `` directive must be an array and should be set in your PHP code accordingly:- ``` php $f3->set('fruits',['apple','orange ',' banana']); ``` Nothing is gained by assigning a value to `@fruit` in your application code. Fat-Free ignores any preset value it may have because it uses the variable to represent the current item during iteration over the group. The output of the above HTML template fragment and the corresponding PHP code becomes:- ``` html

apple

orange

banana

``` The framework allows unlimited nesting of `` blocks:- ``` html

{{ @ikey }}

{{ @ispan }}

``` Apply the following F3 command:- ``` php $f3->set('div', [ 'coffee'=>['arabica','barako','liberica','kopiluwak'], 'tea'=>['darjeeling','pekoe','samovar'] ] ); ``` As a result, you get the following HTML fragment:- ``` html

coffee

arabica barako liberica kopiluwak

tea

darjeeling pekoe samovar

``` Amazing, isn't it? And the only thing you had to do in PHP was to define the contents of a single F3 variable `div` to replace the `@div` token. Fat-Free makes both programming and Web template design really easy. The `` template directive's `value` attribute returns the value of the current element in the iteration. If you need to get the array key of the current element, use the `key` attribute instead. The `key` attribute is optional. `` also has an optional counter attribute that can be used as follows:- ``` html

{{ trim(@fruit) }}

``` Internally, F3's template engine records the number of loop iterations and saves that value in the variable/token `@ctr`, which is used in our example to determine the odd/even classification. ### Embedding Javascript and CSS If you have to insert F3 tokens inside a ` ``` Embedding template directives inside your ` ``` ### Document Encoding By default, Fat-Free uses the UTF-8 character set unless changed. You can override this behavior by issuing something like:- ``` php $f3->set('ENCODING','ISO-8859-1'); ``` Once you inform the framework of the desired character set, F3 will use it in all HTML and XML templates until altered again. ### All Kinds of Templates As mentioned earlier in this section, the framework isn't limited to HTML templates. You can process XML templates just as well. The mechanics are pretty much similar. You still have the same `{{ @variable }}` and `{{ expression }}` tokens, ``, ``, ``, and `` directives at your disposal. Just tell F3 that you're passing an XML file instead of HTML:- ``` php echo Template::instance()->render('template.xml','application/xml'); ``` The second argument represents the MIME type of the document being rendered. The View component of MVC covers everything that doesn't fall under the Model and Controller, which means your presentation can and should include all kinds of user interfaces, like RSS, e-mail, RDF, FOAF, text files, etc. The example below shows you how to separate your e-mail presentation from your application's business logic:- ``` html MIME-Version: 1.0 Content-type: text/html; charset={{ @ENCODING }} From: {{ @from }} To: {{ @to }} Subject: {{ @subject }}

Welcome, and thanks for joining {{ @site }}!

``` Save the above e-mail template as welcome.txt. The associated F3 code would be:- ``` php $f3->set('from',''); $f3->set('to',''); $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') ); ``` Tip: Replace the SMTP mail() function with imap_mail() if your script communicates with an IMAP server. Now isn't that something? Of course, if you have a bundle of e-mail recipients, you'd be using a database to populate the firstName, lastName, and email tokens. Here's an alternative solution using the F3's SMTP plug-in:- ``` php $mail=new SMTP('smtp.gmail.com',465,'SSL','account@gmail.com','secret'); $mail->set('from',''); $mail->set('to','"Slasher" '); $mail->set('subject','Welcome'); $mail->send(Template::instance()->render('email.txt')); ``` ### Multilingual Support F3 supports multiple languages right out of the box. First, create a dictionary file with the following structure (one file per language):- ``` php 'I love F3', 'today'=>'Today is {0,date}', 'pi'=>'{0,number}', 'money'=>'Amount remaining: {0,number,currency}' ]; ``` Save it as `dict/en.php`. Let's create another dictionary, this time for German. Save the file as `dict/de.php`:- ``` php 'Ich liebe F3', 'today'=>'Heute ist {0,date}', 'money'=>'Restbetrag: {0,number,currency}' ]; ``` Dictionaries are nothing more than key-value pairs. F3 automatically instantiates framework variables based on the keys in the language files. As such, it's easy to embed these variables as tokens in your templates. Using the F3 template engine:- ``` html

{{ @love }}

{{ @today,time() | format }}.
{{ @money,365.25 | format }}
{{ @pi }}

``` And the longer version that utilizes PHP as a template engine:- ``` php

get('love'); ?>

get('today',time()); ?>.
get('money',365.25); ?> get('pi'); ?>

``` Next, we instruct F3 to look for dictionaries in the `dict/` folder:- ``` php $f3->set('LOCALES','dict/'); ``` But how does the framework determine which language to use? F3 will detect it automatically by looking at the HTTP request headers first, specifically the `Accept-Language` header sent by the browser. To override this behavior, you can trigger F3 to use a language specified by the user or application:- ``` php $f3->set('LANGUAGE','de'); ``` **Note:** In the above example, the key pi exists only in the English dictionary. The framework will always use English (`en`) as a fallback to populate keys that are not present in the specified (or detected) language. You may also create dictionary files for language variants like `en-US`, `es-AR`, etc. In this case, F3 will use the language variant first (like `es-AR`). If there are keys that do not exist in the variant, the framework will look up the key in the root language (`es`), then use the `en` language file as the final fallback. Dictionary key-value pairs become F3 variables once referenced. Make sure the keys do not conflict with any framework variable instantiated via `$f3->set()`, `$f3->mset()`, or `$f3->config()`. Did you notice the peculiar `'Today is {0,date}'` pattern in our previous example? F3's multilingual capability hinges on string/message formatting rules of the ICU project. The framework uses its own subset of the ICU string formatting implementation. There is no need for PHP's `intl` extension to be activated on the server. One more thing: F3 can also load .ini-style formatted files as dictionaries:- ``` ini love="I love F3" today="Today is {0,date}" pi="{0,number}" money="Amount remaining: {0,number,currency}" ``` Save it as `dict/en.ini` so the framework can load it automatically. ### Data Sanitation By default, both view handler and template engine escapes all rendered variables, i.e. converted to HTML entities to protect you from possible XSS and code injection attacks. On the other hand, if you wish to pass valid HTML fragments from your application code to your template:- ``` php $f3->set('ESCAPE',FALSE); ``` This may have undesirable effects. You might not want all variables to pass through unescaped. Fat-Free allows you to unescape variables individually. For F3 templates:- ``` html {{ @html_content | raw }} ``` In the case of PHP templates:- ``` php raw($html_content); ?> ``` As an addition to auto-escaping of F3 variables, the framework also gives you a free hand at sanitizing user input from HTML forms:- ``` php $f3->scrub($_GET,'p; br; span; div; a'); ``` This command will strip all tags (except those specified in the second argument) and unsafe characters from the specified variable. If the variable contains an array, each element in the array is sanitized recursively. If an asterisk (*) is passed as the second argument, `$f3->scrub()` permits all HTML tags to pass through untouched and simply remove unsafe control characters. ## Databases ### Connecting to a Database Engine Fat-Free is designed to make the job of interfacing with SQL databases a breeze. If you're not the type to immerse yourself in details about SQL, but lean more towards object-oriented data handling, you can go directly to the next section of this tutorial. However, if you need to do some complex data-handling and database performance optimization tasks, SQL is the way to go. Establishing communication with a SQL engine like MySQL, SQLite, SQL Server, Sybase, and Oracle is done using the familiar `$f3->set()` command. Connecting to a SQLite database would be:- ``` php $db=new DB\SQL('sqlite:/absolute/path/to/your/database.sqlite'); ``` Another example, this time with MySQL:- ``` php $db=new DB\SQL( 'mysql:host=localhost;port=3306;dbname=mysqldb', 'admin', 'p455w0rD' ); ``` ### Querying the Database OK. That was easy, wasn't it? That's pretty much how you would do the same thing in ordinary PHP. You just need to know the DSN format of the database you're connecting to. See the PDO section of the PHP manual. Let's continue our PHP code:- ``` php $f3->set('result',$db->exec('SELECT brandName FROM wherever')); echo Template::instance()->render('abc.htm'); ``` Huh, what's going on here? Shouldn't we be setting up things like PDOs, statements, cursors, etc.? The simple answer is: you don't have to. F3 simplifies everything by taking care of all the hard work in the backend. This time we create an HTML template like `abc.htm` that has at a minimum the following:- ``` html {{ @item.brandName }} ``` In most instances, the SQL command set should be enough to generate a Web-ready result so you can use the `result` array variable in your template directly. Be that as it may, Fat-Free will not stop you from getting into its SQL handler internals. In fact, F3's `DB\SQL` class derives directly from PHP's `PDO` class, so you still have access to the underlying PDO components and primitives involved in each process, if you need some fine-grain control. ### Transactions Here's another example. Instead of a single statement provided as an argument to the `$db->exec()` command, you can also pass an array of SQL statements:- ``` php $db->exec( [ 'DELETE FROM diet WHERE food="cola"', 'INSERT INTO diet (food) VALUES ("carrot")', 'SELECT * FROM diet' ] ); ``` F3 is smart enough to know that if you're passing an array of SQL instructions, this indicates a SQL batch transaction. You don't have to worry about SQL rollbacks and commits because the framework will automatically revert to the initial state of the database if any error occurs during the transaction. If successful, F3 commits all changes made to the database. You can also start and end a transaction programmatically:- ``` php $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(); ``` A rollback will occur if any of the statements encounter an error. To get a list of all database instructions issued:- ``` php echo $db->log(); ``` ### Parameterized Queries Passing string arguments to SQL statements is fraught with danger. Consider this:- ``` php $db->exec( 'SELECT * FROM users '. 'WHERE username="'.$f3->get('POST.userID'.'"') ); ``` If the `POST` variable `userID` does not go through any data sanitation process, a malicious user can pass the following string and damage your database irreversibly:- ``` sql admin"; DELETE FROM users; SELECT "1 ``` Luckily, parameterized queries help you mitigate these risks:- ``` php $db->exec( 'SELECT * FROM users WHERE userID=?', $f3->get('POST.userID') ); ``` If F3 detects that the value of the query parameter/token is a string, the underlying data access layer escapes the string and adds quotes as necessary. Our example in the previous section will be a lot safer from SQL injection if written this way:- ``` php $db->exec( [ 'DELETE FROM diet WHERE food=:name', 'INSERT INTO diet (food) VALUES (?)', 'SELECT * FROM diet' ], [ array(':name'=>'cola'), array(1=>'carrot'), NULL ] ); ``` ### CRUD (But With a Lot of Style) F3 is packed with easy-to-use object-relational mappers (ORMs) that sit between your application and your data - making it a lot easier and faster for you to write programs that handle common data operations - like creating, retrieving, updating, and deleting (CRUD) information from SQL and NoSQL databases. Data mappers do most of the work by mapping PHP object interactions to the corresponding backend queries. Suppose you have an existing MySQL database containing a table of users of your application. (SQLite, PostgreSQL, SQL Server, Sybase will do just as well.) It would have been created using the following SQL command:- ``` sql CREATE TABLE users ( userID VARCHAR(30), password VARCHAR(30), visits INT, PRIMARY KEY(userID) ); ``` **Note:** MongoDB is a NoSQL database engine and inherently schema-less. F3 has its own fast and lightweight NoSQL implementation called Jig, which uses PHP-serialized or JSON-encoded flat files. These abstraction layers require no rigid data structures. Fields may vary from one record to another. They can also be defined or dropped on the fly. Now back to SQL. First, we establish communication with our database. ``` php $db=new DB\SQL( 'mysql:host=localhost;port=3306;dbname=mysqldb', 'admin', 'wh4t3v3r' ); ``` To retrieve a record from our table:- ``` php $user=new DB\SQL\Mapper($db,'users'); $user->load(['userID=?','tarzan']); ``` The first line instantiates a data mapper object that interacts with the `users` table in our database. Behind the scene, F3 retrieves the structure of the `users` table and determines which field(s) are defined as primary key(s). At this point, the mapper object contains no data yet (dry state) so `$user` is nothing more than a structured object - but it contains the methods it needs to perform the basic CRUD operations and some extras. To retrieve a record from our users table with a `userID` field containing the string value `tarzan`, we use the `load() method`. This process is called "auto-hydrating" t


本源码包内暂不包含可直接显示的源代码文件,请下载源码包。