Yaf is a PHP framework with high performance. It is written in C and built as a PHP extension.
Yaf is not "yet another PHP framework" — it's a C framework exposed through PHP. Every class, every router match, every dispatch cycle runs in compiled C code rather than interpreted PHP. The result: constant overhead per request measured in microseconds, not milliseconds.
This matters most in two scenarios:
- High-traffic applications where framework bootstrap overhead becomes the dominant cost. Yaf eliminates the class-file loading cascade of Composer-based frameworks — there are no PHP files to
requirefor the framework itself.new Yaf_Application()is instant; the router and dispatcher are already loaded in the extension's shared memory. - Long-lived services (Swoole, RoadRunner, ReactPHP) where you want your framework to not be the bottleneck. Yaf has no global state pollution issues across requests — each request gets a clean dispatch cycle with no static caches to leak.
Yaf pairs naturally with the rest of the "Yet Another" ecosystem:
- Use Yaconf for static configuration — it eliminates the INI parse overhead that
Yaf_Config_Inipays on every request. - Use Yac for runtime caching — database results, computed data, HTML fragments. All three share the same "local first, zero dependency, C-native" design philosophy.
- PHP 7.0+ (master branch)
- PHP 5.2+ (php5 branch)
Yaf is a PECL extension, which means you can simply install it by:
$ pecl install yaf$ /path/to/phpize
$ ./configure --with-php-config=/path/to/php-config
$ make && make install| INI Setting | Default | Description |
|---|---|---|
yaf.environ |
"product" |
Default environment name. This maps to the INI section loaded from application.ini |
yaf.library |
"" |
Global library directory, searched after the application's local library |
yaf.forward_limit |
5 |
Maximum number of forward() calls allowed in a single request. Prevents infinite loops |
yaf.name_suffix |
1 |
When 1, classes use suffix naming (IndexController). When 0, uses prefix naming (Controller_Index) |
yaf.name_separator |
"" |
In multi-module setups, replaces the underscore between module and class name. E.g. with "/": Admin/IndexController instead of Admin_IndexController |
yaf.use_namespace |
0 |
When 1, enables namespaced class names (Yaf\Application, Yaf\Controller_Abstract, etc.) |
yaf.action_prefer |
0 |
When 1, actions are resolved as standalone Yaf_Action_Abstract classes in an actions/ subdirectory instead of controller methods |
yaf.lowcase_path |
0 |
When 1, all paths are lowercased before class loading (e.g. controllers/Index.php → controllers/index.php) |
yaf.use_spl_autoload |
0 |
When 1, Yaf registers itself on the SPL autoload stack instead of replacing __autoload. Useful when coexisting with other autoloaders |
Yaf manual can be found at: http://www.php.net/manual/en/book.yaf.php
A documented prototype script can be found at: https://github.com/elad-yosifon/php-yaf-doc
A classic single-module application directory layout is:
- .htaccess # Rewrite rules
+ public
| - index.php # Application entry
| + css
| + js
| + img
+ conf
| - application.ini # Configuration
- application/
- Bootstrap.php # Bootstrap
+ controllers
- Index.php # Default controller
+ views
|+ index
- index.phtml # View template for default controller
+ library # Libraries
+ models # Models
+ plugins # Plugins
For multi-module applications, the layout is:
+ public/
+ conf/
+ application/
+ modules/
+ Index/ # Default module
+ controllers/
+ views/
+ Admin/ # Another module
+ controllers/
+ views/
+ library/
+ models/
+ plugins/
- Bootstrap.php
Set DocumentRoot to application/public, so only the public folder is accessible from the web.
index.php in the public directory is the only way into the application. You should rewrite all requests to it (using .htaccess in Apache + mod_php, or the equivalent in your web server).
<?php
define("APPLICATION_PATH", dirname(dirname(__FILE__)));
$app = new Yaf_Application(APPLICATION_PATH . "/conf/application.ini");
$app->bootstrap() // call bootstrap methods defined in Bootstrap.php
->run();A minimal Bootstrap.php looks like this:
<?php
class Bootstrap extends Yaf_Bootstrap_Abstract
{
public function _initConfig(Yaf_Dispatcher $dispatcher)
{
// put your init logic here
}
}Bootstrap auto-calling: Any method in Bootstrap whose name starts with _init is automatically called in definition order by Yaf_Application::bootstrap(). Each method receives the Yaf_Dispatcher instance as its first argument.
<?php
class Bootstrap extends Yaf_Bootstrap_Abstract
{
public function _initConfig(Yaf_Dispatcher $dispatcher)
{
// called first
}
public function _initPlugin(Yaf_Dispatcher $dispatcher)
{
// called second
}
public function _initRoute(Yaf_Dispatcher $dispatcher)
{
// called third
}
// This is NOT auto-called — doesn't start with _init
public function helperMethod()
{
}
}#.htaccess
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* index.phpserver {
listen 80;
server_name domain.com;
root /path/to/document/root;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ /index.php?$args;
}
}$HTTP["host"] =~ "(www.)?domain.com$" {
url.rewrite = (
"^/(.+)/?$" => "/index.php/$1",
)
}
application.ini is the application config file:
[product]
; Constants defined in index.php are supported
application.directory = APPLICATION_PATH "/application/"Alternatively, you can use a PHP array instead:
<?php
$config = [
"application" => [
"directory" => APPLICATION_PATH . "/application/",
],
];
$app = new Yaf_Application($config);In Yaf, the default controller is named IndexController:
<?php
class IndexController extends Yaf_Controller_Abstract
{
// default action name
public function indexAction()
{
$this->getView()->content = "Hello World";
}
}The view script for the default controller and default action is application/views/index/index.phtml. Yaf provides a simple view engine called Yaf_View_Simple, which supports view templates written in PHP:
<html>
<head>
<title>Hello World</title>
</head>
<body>
<?php echo $content; ?>
</body>
</html>Point your browser to your configured domain (e.g. http://www.example.com) and you should see "Hello World".
You can generate the example above using Yaf Code Generator:
$ ./yaf_cg -d output_directory [-a application_name] [--namespace]The code generator is located at: https://github.com/laruence/yaf/tree/master/tools/cg
Yaf supports 6 built-in route types, all implementing Yaf_Route_Interface:
| Route | Description |
|---|---|
Yaf_Route_Static |
Default route — /module/controller/action |
Yaf_Route_Simple |
Maps query-string parameters to module/controller/action |
Yaf_Route_Supervar |
Uses a single GET/POST variable for routing path (e.g. ?r=/module/controller/action) |
Yaf_Route_Rewrite |
Pattern-matching rewrite with named capture groups |
Yaf_Route_Regex |
Full regex matching with capture-to-variable mapping |
Yaf_Route_Map |
Maps the first URI segment to either controller or action |
Routes can be registered in Bootstrap:
<?php
class Bootstrap extends Yaf_Bootstrap_Abstract
{
public function _initRoute(Yaf_Dispatcher $dispatcher)
{
$router = $dispatcher->getRouter();
// Pattern: /user/123 → controller=user, action=index, id=123
$router->addRoute("user", new Yaf_Route_Rewrite(
"/user/:id",
["controller" => "user", "action" => "index"]
));
// Regex: /item/123.html → controller=item, action=view, id=123
$router->addRoute("item", new Yaf_Route_Regex(
"#^/item/(\d+)\.html$#",
["controller" => "item", "action" => "view"],
[1 => "id"]
));
}
}Routes can also be loaded from INI config:
$router->addConfig(new Yaf_Config_Ini("routes.ini"));Yaf provides two configuration parsers:
Parses standard INI files. Supports section inheritance:
[common]
db.host = "localhost"
db.port = 3306
[product : common]
db.user = "app_user"
db.pass = "secret"The [product : common] syntax inherits all keys from [common] then applies its own overrides.
Parses PHP arrays or INI files into a mutable configuration object. Values can be modified at runtime:
<?php
$config = new Yaf_Config_Simple([
"db" => ["host" => "localhost"],
]);
$config->db->host = "10.0.0.1"; // editableIn contrast, Yaf_Config_Ini is always read-only.
Performance tip: If you have a large amount of static configuration, consider using Yaconf — a persistent configuration container that keeps configs in shared memory across the entire PHP lifecycle, providing significantly faster access than parsing INI files on every request.
Yaf supports a plugin hook system via Yaf_Plugin_Abstract. Override any of 7 hooks:
<?php
class UserPlugin extends Yaf_Plugin_Abstract
{
public function routerStartup(Yaf_Request_Abstract $request, Yaf_Response_Abstract $response) {}
public function routerShutdown(Yaf_Request_Abstract $request, Yaf_Response_Abstract $response) {}
public function dispatchLoopStartup(Yaf_Request_Abstract $request, Yaf_Response_Abstract $response) {}
public function preDispatch(Yaf_Request_Abstract $request, Yaf_Response_Abstract $response) {}
public function postDispatch(Yaf_Request_Abstract $request, Yaf_Response_Abstract $response) {}
public function dispatchLoopShutdown(Yaf_Request_Abstract $request, Yaf_Response_Abstract $response) {}
public function preResponse(Yaf_Request_Abstract $request, Yaf_Response_Abstract $response) {}
}Register in Bootstrap:
$dispatcher->registerPlugin(new UserPlugin());Yaf defines a structured exception hierarchy:
Exception
└── Yaf_Exception
├── Yaf_Exception_StartupError — Application startup failure
├── Yaf_Exception_RouterError — Route matching failure
├── Yaf_Exception_DispatchError — Dispatch failure
├── Yaf_Exception_LoadFailed — Class/Method/View load failure
└── Yaf_Exception_TypeError — Type mismatch
You can control whether Yaf throws or silently swallows exceptions during dispatch:
$dispatcher->throwException(true); // throw to user
$dispatcher->catchException(true); // catch and store in request| Class | Purpose |
|---|---|
Yaf_Application |
Application bootstrap and lifecycle |
Yaf_Dispatcher |
Request dispatch pipeline |
Yaf_Controller_Abstract |
Base controller — use forward(), redirect(), render(), display() |
Yaf_Action_Abstract |
Standalone action class (requires yaf.action_prefer = 1) |
Yaf_Bootstrap_Abstract |
Auto-called _init* methods |
Yaf_Loader |
PSR-0 style class autoloading |
Yaf_Router |
Route registration and matching |
Yaf_Registry |
Static key-value store (get/set/has/del) |
Yaf_Session |
Namespaced session wrapper with ArrayAccess |
Yaf_Config_Ini |
Read-only INI config parser |
Yaf_Config_Simple |
Mutable config from INI or PHP array |
Yaf_Request_Http |
HTTP request abstraction |
Yaf_Request_Simple |
Synthetic request (CLI / testing) |
Yaf_Response_Http |
HTTP response with header management |
Yaf_Response_Cli |
CLI response |
Yaf_View_Simple |
PHP-template view engine (assign, render, display, eval, assignRef, clear) |
Yaf_Plugin_Abstract |
7-hook plugin base class |
Yaf_Registry is a static key-value store — a global data bus accessible from anywhere in the application. All methods are static.
Yaf_Registry::set("user_config", $config);
$config = Yaf_Registry::get("user_config");
Yaf_Registry::has("user_config"); // true
Yaf_Registry::del("user_config"); // removes the keyCommon use: storing objects initialized in Bootstrap (e.g. a database connection, a logger instance) so controllers can access them without re-initializing.
Yaf_Session is a namespaced wrapper around PHP's native $_SESSION. It implements ArrayAccess, Iterator, and Countable, and supports property-style access.
$session = Yaf_Session::getInstance();
$session->start();
// All equivalent:
$session->set("user", $data);
$session["user"] = $data;
$session->user = $data;
$session->get("user");
$session["user"];
$session->user;
$session->has("user");
isset($session->user);
$session->del("user");
unset($session["user"]);
unset($session->user);
$session->clear(); // removes all keys
count($session); // number of keysMore info can be found at the Yaf Manual: http://www.php.net/manual/en/book.yaf.php