a micro php framework/router for both web and cli
- use closure as request handler
param
handler(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->register
won't initilize class$app->attr = closure
closure won't be invoked unless$app->attr
is accessed- param handler won't be called unless
$app->params->param
is 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->body
is 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