anpv1/iceage-php

闪电般的微型PHP框架

安装: 30

依赖项: 0

建议者: 0

安全性: 0

星标: 0

关注者: 1

分支: 0

开放问题: 0

类型:微型PHP框架

1.0.5 2020-04-06 07:45 UTC

This package is auto-updated.

Last update: 2024-09-06 17:42:33 UTC


README

IceAge 是一个闪电般快速且极小的 PHP 纳米框架,总代码量约为 350 行。IceAge 提供了一个非常简单、但强大且灵活的接口,用于构建基于 Web 的应用程序。

在我的联想 U4170 上,Intel Core i5 5200U,4GB RAM,使用 apache bench,一个全页面使用 IceAge 和 dotenv 加载环境变量,Twig 作为模板引擎以及一个 PDO MySQL 查询可以达到 1639.09 请求/秒。使用 IceAge 的“Hello, world!”页面可以达到 10167.46 请求/秒。

Hello, world!

<?php
// index.php
// composer autoload, install any dependencies you need
require_once('../vendor/autoload.php');
$app = new \IceAge\Application();

// routes definition
$app->get('/', function(){return 'Hello, world!';});
$result = $app->run();
$app->response($result);

路由

// Routing with regex definition of parameters and multiple methods
// id in URL must be digit to match
$app->route(
    '/hello/:id|[0-9]+|', 
    '\\App\\Controller\\Hello::get', 
    'GET|POST'
);
// optional parameters
// this will match /blog/2017, /blog/2017/07, /blog/2017/07/01
$app->get('/blog(/:year|[\d]{4}|(/:month|[\d]{2}|(/:day|[\d]{2}|)?)?)?', function($route_params){
    return $route_params;
});

// grouping routes
$app->group('/admin', function(){
    $this->get('/photo', function(){
        return 'PhotoAdmin';
    }); // match /admin/photo

    $this->post('/gallery', function(){
        return 'GalleryAdmin';
    }); // match /admin/gallery

})->middleware(function($required_login){
    if($required_login){
        return 'RequiredLogin';
    }
}, array('required_login' => 1));

使用服务

IceAge 支持 2 个主要特性:路由和服务管理。您可以使用 register 方法将服务注册到应用程序中

<?php
// index.php
// composer autoload, install any dependencies you need
require_once('../vendor/autoload.php');
use \Twig_Environment as Twig;
$app = new \IceAge\Application();

// register $db service
$app->register('db', function(){
    return new \PDO(
        $_ENV['DB_DSN'], 
        $_ENV['DB_USER'], 
        $_ENV['DB_PASSWORD'],
        array(
            \PDO::ATTR_PERSISTENT => true
        )
    );
});

// register Twig_Environment template
$app->register('Twig_Environment', function(){
    $loader = new \Twig_Loader_Filesystem(realpath('app/templates/views'));
    return new Twig($loader, array(
        'cache' => realpath('app/templates/cache'),
        'auto_reload' => true
    ));
});

// routes definition
// in the route handler you can use the $db service which is a PDO instance
// and load any parameter name which is a Twig_Environment instance
$app->get('/', function($db, Twig $template){
    return $template->render('template.html', array('message' => 'Hello, world!'));
});

$result = $app->run();
$app->response($result);

如上例所示,服务可以通过其名称($db)或其类名(Twig_Environment)来加载

启动应用程序

IceAge 应用程序对象支持一个 bootstrap 方法,可以用来注册所有服务和路由处理程序。例如

<?php
// public/index.php
chdir(getcwd().'/../');
// composer autoload, install any dependencies you need
require_once('vendor/autoload.php');

$app = new \IceAge\Application();
$app->bootstrap(array(
    '\\App\\Bootstrap\\Env::load',
    '\\App\\Bootstrap\\Services::register',
    '\\App\\Bootstrap\\Routes::register'
));

try {
    $result = $app->run();
}
catch(Exception $e){
    $response = $app->run_handler('\\App\\Controller\\Error::error', array('error' => $e));
    $result = $app->response($response);
}

$app->response($result);
<?php
// app/Bootstrap/Env.php
namespace App\Bootstrap;
use Rfussien\Dotenv\Loader;

class Env {
    public static function load(){
        $dotenv = new Loader('app/');
        $dotenv->load();
    }
}
<?php
// app/Bootstrap/Routes.php
namespace App\Bootstrap;

class Routes {
    public static function register($app){
        // routes definition
        $app->get('/', '\\App\\Controller\\Index::get');
        $app->get('/login', '\\App\\Controller\\Login::index');
        $app->route(
            '/hello/:name/:id', 
            '\\App\\Controller\\Hello::get', 
            'GET|POST'
        );
        $app->get('/group/get/:id', '\\App\\Controller\\Group::get');
        $app->post('/user/signin', '\\App\\Controller\\Login::signin');
    }
}
<?php
// app/Bootstrap/Services.php
namespace App\Bootstrap;

class Services {
    private static $dbh;
    private static $twig;
    private static $acl;

    public static function register($app){
        $app->register('db', '\\App\\Bootstrap\\Services::db_service');
        $app->register('twig', '\\App\\Bootstrap\\Services::twig_service');
    }

    public static function db_service(){
        if(!self::$dbh){
            self::$dbh = new \PDO(
                $_ENV['DB_DSN'], 
                $_ENV['DB_USER'], 
                $_ENV['DB_PASSWORD'],
                array(
                    \PDO::ATTR_PERSISTENT => true
                )
            );
        }
        return self::$dbh;
    }

    public static function twig_service(){
        if(!self::$twig){
            $loader = new \Twig_Loader_Filesystem(realpath('app/templates/views'));
            self::$twig = new \Twig_Environment($loader, array(
                'cache' => realpath('app/templates/cache'),
                'auto_reload' => true
            ));
        }

        return self::$twig;
    }
}
<?php
// app/Controller/Hello.php
namespace App\Controller;

use App\DataTable\TestTbl;

class Hello
{
    // $route_params is a special service to get the parameters defined on route
    // In this case the route is /hello/:name/:id
    // So if the request URL is /hello/Bob/1 then $route_params['name'] = "Bob"
    // $route_params['id'] = 1
    public static function get($twig, $db, $route_params)
    {
        $test_tbl = new TestTbl($db);
        $row = $test_tbl->get($route_params['id']);
        return $twig->render('hello/index.html', array('row' => $row));
    }
}
<?php
// app/Controller/Error.php
namespace App\Controller;

class Error {
    // $error is passed from the below line in index.php
    // $response = $app->run_handler('\\App\\Controller\\Error::error', array('error' => $e));
    public static function error($twig, $error){
        return $twig->render('error/index.html', array('error' => $error, 'debug' => $_ENV['DEBUG_MODE']));
    }
}

请求生命周期

  • bootstrap 函数被调用,传入 IceAge 应用程序对象的实例
  • 应用程序对象将尝试找到匹配的路由
  • 如果没有匹配的路由,将抛出一个 IceAge\Exception 异常,代码为 IceAge\Exception::NO_ROUTE 并退出
  • 如果有一个匹配的路由,IceAge 应用程序将运行存在的路由中间件。
  • 如果路由中间件处理程序运行并返回响应,IceAge 应用程序将跳过路由处理程序并进入输出处理步骤。
  • 如果路由中间件没有返回值,IceAge 应用程序将加载路由处理程序使用的所有服务,然后使用加载的服务调用路由处理程序,并获取响应。
  • 根据路由处理程序的响应
    • 如果是字符串,IceAge 会立即将字符串作为输出发送到浏览器。
    • 如果是 \Psr\Http\Message\ResponseInterface 的实例,IceAge 使用 \Zend\Diactoros 生成输出并发送到浏览器。
    • 在其他情况下,IceAge 使用 json_encode 函数生成输出,并通过 Content-type: application/json 头部发送。

要注册路由中间件,您可以使用路由对象的 middleware 方法

<?php
$app->get('/', '\\App\\Controller\\Index::get')
    // middleware handler can use any registered services and params passed on
    ->middleware(function($db, $permission){
        // $permission = "admin"
        // implement middleware here
    },
    array('permission' => 'admin'))
    ->middleware(function($login_required){
        // $login_required = True
        // implement middleware here
    },
    array('login_required' => True));