zf Build Status
a micro php framework/router for both web and cli
- use closure as request handler
paramhandler(inspired by expressjs)- commandline routing
- events
- jsonp support
- lazy
- scalable
- requires php 5.4
- ideal for building restful apis or commandline apps
to install using composer, add the fellowing to your composer.json
{ "require": { "zweifisch/zf": "*" } }
if you're not using composer, download soruce code here
require 'vendor/autoload.php'; # require 'zf/zf.php'; if you are not using composer $app = new \zf\App(); $app->get('/hello/:name', function(){ $this->send(['hello' => $this->params->name]); })->run();
components are just classes attached to the $app instance, any class can used, \zf\Mongo and \zf\Redis are available out of the box:
$app->register('mongo', '\zf\Mongo', $app->config->mongo); $app->register('redis', '\zf\Redis', $app->config->redis); // \zf\Mongo won't be initilazed unless $app->mongo is accessed $app->mongo->users->findOne();
$app->delete('/users/:user_ids', function() { $this->send(['deleted'=>count($this->params->user_ids)]); }); $app->param('user_ids', function($user_ids) { $ids = explode('-', $user_ids); if(count($ids) <= 10){ return $ids; } $this->send(400); });
the param handler won't be called, unless $this->params->user_ids is accessed
$app->on('user:hit', function($data){ # write to log }); $app->on('user:hit', function($data){ $this->redis->users->zincrby('hotusers',1,$data['_id']); }); $app->get('/user/:id', function($data){ $user = $this->mongo->user->findOne(['_id'=>$this->params->id]); $this->emit('user:hit', $user); $this->send($user); });
$app->helper('item', function($array, $key, $default=null){ return isset($array[$key]) ? $array[$key] : $default; }); $app->get('/user/:id', function(){ $users = [2 => 'zf']; $this->send($this->helper->item($users, $this->params->id, 'not found')); });
registrated helpers can also be accessed using $this->myhelper();
$app->handler('index', function(){ $this->render('index'); }); $app->handler('/index.php', function(){ $this->pass('index'); });
$this->body is parsed from raw request body according to content type (json|formdata)
and wraped as a FancyObject, $app->set('nofancy'); will keep it as an array.
request body:
{
"action": "login",
"user": {
"name" : "admin",
"password": "secret"
}
}access them:
$action = $this->body->action->in('login','register')->asStr(); $name = $this->body->user->name->minlen(3)->maxlen(20)->asStr(); $password = $this->body->user->password->minlen(8)->asStr();
access $_GET['page'] and $_GET['size']
$page = $this->query->page->asInt(1); $size = $this->query->size->between(10,20)->asInt();
available types are asStr, asInt, asNum, asArray, asFile
$app->post('/upload',function(){ $file = $this->body->image->asFile(); $file->extension; new \MongoBinData($file->content); // content won't be read unless accessed });
it's possible to add new types:
$app->map('User', function($value){ return new User($value); }); $this->body->asUser()->save();
when validation fails, null will be returned and validation:failed will be emmitted.
$password = $this->body->user->password->minlen(8)->asStr(); // all keys are required, unless a default value is supplied: $gender = $this->body->user->gender->in(0,1)->asInt(0); $app->on('validation:failed', function($message){ $this->send(400, $message); });
available validators between, min, max, in, minlen, maxlen
add a new validator:
$app->validator('startWith', function($str) { return function($value) use ($str) { return 0 == strncmp($value, $value, strlen($str)); }; }); // use it $this->body->some->key->startWith(':')->asStr();
$this->send($statusCode); $this->send($statusCode, $body); $this->send($statusCode, $body, ['type'=>$contentType]); $this->send($body); $this->send($body, ['type'=>$contentType, 'charset'=>$charset]);
json response
$this->set('pretty'); # enable json pretty print $this->send($object);
$this->lastModified($timestamp);
$this->cacheControl('public', 'must-revalidate', ['max-age'=> 60]);
$app->jsonp($result);
if $_GET['callback'] is set, javascript will be returned, otherwise it's equivelent to $this->send($result)
there is no nested, complicated server side view rendering mechanism in zf.
but it's still possible to rendering simple views in plain old php. please
consider client side view rendering using requirejs with knockoutjs, angularjs
or similar libs/framworks.
in request handler
$this->render('index',['pageview'=>1000]);
and in views/index.php
<!DOCTYPE HTML> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> Pageview today: <?= $this->pageview ?> </body> </html>
to specify a different location other than views, use $app->set('views','path/to/templates');
configs.php will be loaded if exists
set
$app->set('key','value'); $app->set('fancy'); # equivelant to $app->set('fancy', true); $app->set('nofancy'); # equivelant to $app->set('fancy', false);
retrieve
$app->config->key;
$app->get('/user/:id', function(){ # ... })->post('/user', function(){ # ... })->run();
dump object in header
$app->set('debug'); $app->debug($msg, $object);
$app->cmd('hello <name>', function(){ echo 'say hello to ', $this->params->name; }); $app->set('pretty'); $app->cmd('ls user --skip <from> --limit <max> <pattern>', function(){ this->send($this->params); });
all options are required, unless default values are provided
$app->cmd('ls user --skip <from> --limit <max> <pattern>', function(){ # ... })->defaults(['max' => 20]);
if no command matched(404), a help message will be printed, and program will exit with err(code 1);
use $this->getstdin();
see examples/cli.php for more details
$app->sigint(function(){ echo 'ctrl-c pressed'; exit(0); });
all can be put in it's own file
$app->post('/user', 'create-user');
return a closure in handlers/create-user.php
return function() { # ... };
request handlers should be located in handlers by default, this can be changed using $app->set('handlers','path/to/handlers');
similarly, event handlers in events, param handlers in params ...
when calling a helper which is not registrated, zf will look for it under helpers
$app->on('error', function($data){ # helpers/mail.php will be loaded $this->helper->mail(['to'=>$this->config->admin,'body'=>$data->message]); });
helpers can also be registrated in this way:
$app->helper(['helper','helper2','helper3']); // so they can be accessed as $app->helper3(); # ommit the 'helper'
$app->registerwon't initilize class$app->attr = closureclosure won't be invoked unless$app->attris accessed- param handler won't be called unless
$app->params->paramis accessed, to make the handler get called as soon as possible, supply a extra parameter like this$app->param('param','handler',true); - request body won't be parsed unless
$app->bodyis accessed
- phpredis
- mongo
sudo pecl install mongo
there is an exmaple demostrating how to add/list/delte users, to run it using php's builtin server: (needs the php mongo extension metioned above)
cd examples && php -S localhost:5000
a cli example is also included.
run tests
composer.phar install --dev vendor/bin/phpunit -c tests