Watch Star Fork

JIANGWL / YanPHPPHPMIT

YanPHP——一个为API开发而设计的高性能轻量级框架
克隆/下载
JIANGWEILONGGGG 最后提交于 2017-11-14 12:23 update readme
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README.md 21.32 KB

YanPHP V0.2

Release Note

Guide

Documentation

这是一个为API开发而设计的高性能轻量级框架。 框架为你集成了一些常用的类库,让你开发更加便捷。 另外引入了Composer,可以让你更加轻松地管理依赖以及处理命名空间问题。 让语法更加贴近原生PHP,并且在此基础上让你实现自己的定制化功能。 因为封装程度不高,所以框架的性能十分之优秀。

目录结构

├── Application                  --你的代码目录
│   ├── Cgi                        --分层应用根目录(这里是Cgi代码)
│   │   ├── Cache                    --缓存
│   │   ├── Compo                    --自定义组件
│   │   ├── Config                   --配置
│   │   ├── Controller               --控制器,用于编写业务逻辑
│   │   ├── Logs                     --日志存放
│   │   ├── Model                    --模型层
│   │   ├── Param                    --入参定义,以及参数校验
│   │   └── Util                     --工具类库
│   └── Server                     --分层应用根目录(这里是Server代码)
│   │   ├── Cache                    --缓存
│   │   ├── Compo                    --自定义组件
│   │   ├── Config                   --配置
│   │   ├── Controller               --控制器,用于编写业务逻辑
│   │   ├── Logs                     --日志存放
│   │   ├── Model                    --模型层
│   │   ├── Param                    --入参定义,以及参数校验
│   │   └── Util                     --工具类库
├── System                       --框架目录
│   └── Yan
│       ├── Common
│       └── Core
│           ├── Compo
│           └── Exception

安装

环境要求

Require:

PHP >= 7.0

Composer

Composer安装:https://getcomposer.org/download/

安装步骤

先把YanPHP从github拷贝到本地

git clone https://github.com/kong36088/YanPHP.git

然后进行框架依赖组件的安装,这里需要用到composer。请确保您的机器中已经安装了composer

composer install

完成!

hello world

首先安装YanPHP的依赖项:

composer install

来编写我们的第一个hello world

首先我们需要先新增一个控制器 新建一个控制器文件 Application/Cgi/Controller/HelloController.php

<?php
namespace App\Cgi\Controller;

use Yan\Core\Compo\ResultInterface;
use Yan\Core\Controller;

class HelloController extends Controller
{

    public function index(): ResultInterface
    {
        return $this->succ('hello world');
    }
}

紧接着到Param目录下创建我们的控制器入参规则, Application/Cgi/Param/HelloController.ini内容如下:

[index]

用命令行重新加载composer中注册的命名空间:

composer dump-autoload

最后,用浏览器访问我们刚才编写的hello world:http://localhost/interface.php/hello/index

返回结果为以下内容

{
    "code": 0,
    "message": "hello world",
    "data": []
}

若有新增类库文件,记得一定要运行一次 composer dump-autoload 以刷新我们的autoloader

路由

默认路由规则

默认路由寻路路径是:/interface.php/controller/method controller代表您的控制器,method是指向的方法

举例:http://localhost/interface.php/user/getUser 这个路径映射到UserControllergetUser方法

自定义路由规则

当然,您也可以自定义自己的路由规则。 路由规则存放在 Application/YourLevel/Config/router.php

$config['default_method'] = 'index';   //默认方法

$config['route'] = [
    '/' => [    //被映射的路径
        'request_method' => ['GET','POST'],   //支持的http动作,支持GET和POST
        'controller' => 'App\\Cgi\\Controller\\UserController',  //所映射到的控制器,需要包含命名空间,映射到Application/Cgi/Controller/UserController
        'method' => 'index'       //所映射到的方法,映射到UserController的index方法
    ],
    '/user' => [
        'request_method' => ['GET'],    //支持的http动作,支持GET
        'controller' => 'App\\Cgi\\Controller\\UserController',    //所映射到的控制器,需要包含命名空间,映射到Application/Cgi/Controller/UserController
        'method' => 'getUser'     //映射到UserController的index方法getUser方法
    ],
];

配置

配置文件统一存放在 Application/YourLevel/Config 目录下 Application下的各个文件夹对应着您应用的各个分层,每一层都采用自己独立的Config配置

系统配置相关

$config['namespace'] = 'App\\Cgi';

这里用于配置你的应用层采用的命名空间,在新添加应用层后请勿忘记修改这里的配置哦。

$config['session_path'] = BASE_PATH.'/Cache/session';
$config['session_name'] = 'YAN_SESSION';

session_path 用于配置session存放的僦 session_name 用于配置session名称,即用于标识用户session id的key值

日志配置相关

几种日志等级。比如日志等级配置为INFO,则INFO及INFO以上的(NOTICE、WARING、ERROR)等等级的日志将会被记录。

/**
'DEBUG'
'INFO'
'NOTICE'
'WARNING'
'ERROR'
'CRITICAL'
'ALERT'
'EMERGENCY'
*/

$config['log_level'] = 'DEBUG';

日志存放路径

/**
 * The log path
 */
$config['log_path'] = BASE_PATH . '/logs/cgi.log';
/**
 *  最大存放的日志文件数量
 */
$config['log_max_file'] = 0;
/**
 * 配置日志记录的格式
 * "[%datetime%] %channel%.%level_name%: %message% %context%\n";
 */
$config['log_format'] = "[%datetime%]-%extra.process_id% %channel%.%level_name%: %message% %context%\n";

database

database.php

config options description
db_host DB host
db_user 用户名
db_password 密码
db_port 3306/(others) 端口号
db_database
db_charset utf8/(others)
db_driver mysql/postgres/sqlite/sqlsrv 目前支持四种数据库类型

可以配置多个数据库连接,默认使用default进行连接

例如下面的例子:

$config['db'] = [
    'default' => [
        /** host */
        'db_host' => 'mysql',
        /** 数据库用户名 */
        'db_user' => 'root',
        /** 数据库密码 */
        'db_password' => 'root',
        /** 端口 */
        'db_port' => 3306,
        /** 数据库 */
        'db_database' => 'yan',
        /** 表名前缀 */
        'db_prefix' => '',
        /**
         * mysql/postgres/sqlite/sqlsrv
         */
        'db_driver' => 'mysql',
        'db_charset' => 'utf8',
        'db_collation' => 'utf8_unicode_ci'
    ],
    'mysql1'=>[
        'db_host' => '',
        'db_user' => '',
        'db_password' => '',
        'db_port' => 3306,
        'db_database' => '',
        'db_prefix' => '',
        'db_driver' => 'mysql',
        'db_charset' => '',
        'db_collation' => ''
    ]
];

这里我们可以对连接进行管理,其中上面的default以及mysql1是我们的连接名称,我们可以根据名称进行数据库连接的切换。 具体可以看Database/DB多连接管理

YAssert

YanPHP内嵌的断言支持。感谢beberlei/assert提供类库支持

详细的使用方法在这里:YassertDocument

入参和Input

用法介绍

所有入参都需要定义在应用目录路径下的Param目录,并且可以对其进行相关的参数校验操作。

下面我们会举例对该功能进行讲解。

例如我们需要请求UserControllerindex方法,那么我们需要创建一个入参配置文件 Param/UserController.ini

文件内容如下:

[index]
user_id="starts_with[1]|required|numeric|between[1,123]"
page="numeric"
domain="string|numeric"
arr="array"

[getUser]

“=”号左边的是需要的入参,右边的是需要验证的规则。规则都是Validator内置好的,基于Respect/Validation开发 并且只有被定义在入参配置文件中的参数才会被Input类所识别,其余参数一律丢弃。

若参数不符合规则要求,则会直接返回错误信息。

如若你需要为参数配置多个验证规则,可以用 | 进行规则分割,例子:domain="string|length[1,20]"。 在这个例子中,我们要求domain必须是字符串类型,并且长度在1-20个字符之间。

相关入参规则

规则 参数 使用说明 例子
required 参数必填
optional 参数可空
integer 整型
numeric 所有字符都是数字(不区分变量类型)
float 浮点型
string 字符型
array 数组型
ip 验证是否为一个有效的ip
json 验证是否为合法json格式
email 验证是否为合法邮箱
domain 验证是否为合法域名
regex 正则匹配 regex(/[0-9]+/)
starts_with 是否以规定的字符开头 starts_with(ab)
ends_with 是否以规定的字符结束 ends_with(ab)
between 数值在定义的范围之间 between(1,100)
min 定义最小不小于 min(1)
max 定义最大不大于 max(100)
length 定义字符串长度在定义范围内 length(1,100)
equal 入参的值必须等于定义的值 equal(123)
contain 入参是否包含给出的值 contain([ab])

获取输入参数

Input::get('user_id'); //获取参数user_id

Input::set('user_id',1); //设置参数

Database

DB方面YanPHP采用了illuminate/Database。 编码设计风格与其保持总体一致。

使用介绍

composer require "illuminate/events" required when you need to use observers with Eloquent.

Once the Capsule instance has been registered. You may use it like so:

Using The Query Builder

$users = Capsule::table('users')->where('votes', '>', 100)->get();

Other core methods may be accessed directly from the Capsule in the same manner as from the DB facade:

$results = Capsule::select('select * from users where id = ?', array(1));

Using The Schema Builder

Capsule::schema()->create('users', function ($table) {
    $table->increments('id');
    $table->string('email')->unique();
    $table->timestamps();
});

Using The Eloquent ORM

class User extends Illuminate\Database\Eloquent\Model {}

$users = User::where('votes', '>', 1)->get();

或者使用YanPHP提供的风格

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;

class User extends Model
{
    protected $table = 'user';

    protected $primaryKey = 'uid';

    protected $keyType = 'int';

    public function getById($id): Collection
    {
        return $this->where([$this->primaryKey => $id])->get();
    }

    public function getByCond($cond): Collection
    {
        return $this->where($cond)->get();
    }

    public function updateByCond($cond, $update): bool
    {
        return $this->where($cond)->update($update);
    }

    public function deleteById($id)
    {
        return $this->where($id)->delete();
    }
}


$UserModel = new User();
$UserModel->getById(1); // 获取user表中uid为1的用户数据信息

For further documentation on using the various database facilities this library provides, consult the Laravel database documentation.

DB多连接管理

在配置文件database.php配置我们的连接后,可以实现多个db连接实例。

下面我们将介绍如何进行连接的切换。

Model/User.php

<?php
namespace App\Cgi\Model;

use Illuminate\Support\Collection;
use Yan\Core\Model;

class User extends Model
{
    protected $table = 'user';
    protected $connection = 'mysql1';  //这里可以配置User Model默认使用"mysql1"连接

    public function getById($id): Collection
    {
        //这里可以使当前实例的连接切换为"default"
        $this->setConnection('default');
        
        return $this->where([$this->primaryKey => $id])->get();
    }
}

我们可以使用Model当中的$connection配置默认的连接。

另外一种方法是使用自带的$this->setConnection($name)方法进行连接的设置

Session

用法示例

use Yan\Core\Session;

Session::set('a','b');  //设置session值

$sessionVaue = Session::get('a');  //获取session中的值

Session::destroy(); //销毁所有session

Session类中有以下方法

/**
 * @method static mixed get($key, $alt = null)
 * @method static mixed set($key, $val)
 * @method static null clear()
 * @method static mixed getFlash($key, $alt = null)
 * @method static null setFlash($key, $val)
 * @method static null clearFlash()
 * @method static mixed getFlashNext($key, $alt = null)
 * @method static null setFlashNow($key, $val)
 * @method static null clearFlashNow()
 * @method static null keepFlash()
 * @method boo null destroy()
 */

定制化

定制Result格式

可以到你的应用目录下的Compo/Result.php定制化你的Result格式

下面是Result类的示例:

namespace App\Cgi\Compo;

use Yan\Core\Compo\ResultInterface;

class Result implements ResultInterface
{
    protected $code;
    protected $message;
    protected $data;

    public function __construct(int $code, string $message, array $data = [])
    {
        $this->code = $code;
        $this->message = $message;
        $this->data = $data;
    }

    function getCode(): int
    {
        return $this->code;
    }

    function getMessage(): string
    {
        return $this->message;
    }

    /**
     * Specify data which should be serialized to JSON
     * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
     * @return mixed data which can be serialized by <b>json_encode</b>,
     * which is a value of any type other than a resource.
     * @since 5.4.0
     */
    function jsonSerialize()
    {
        return ['code' => $this->code, 'message' => $this->message, 'data' => $this->data];
    }
}

jsonSerialize()返回一个数组,作为结果输出

定制ReturnCode

YanPHP为你定义了一个全局的返回码,返回码的修改可以到System/Yan/Core/ReturnCode.php修改

YanPHP命名规范准则

YanPHP的所有的类文件文件名都 必须 与类名保持一致

框架的命名方式应该遵循驼峰命名法的命名规范。相关介绍可以看这里

控制器

控制器类名需要以驼峰命名法进行命名,并且以Controller作为后缀结束。

例如:UserControllerTotalStatisticsControllerInfoListController

Model

采用驼峰法,名字可以根据你自己的喜好进行命名。我们会推荐你根据数据库表名或相关的业务用途对model进行命名。

例如:UserProductPrice

入参配置文件(Param/*.ini

文件名 必须 与你的控制器名称保持一致。每个控制器单独对应一个入参配置文件。

例如:UserController.iniTotalStatisticsController.iniInfoListController.ini

安全

重新生成SessionID

Any time a user has a change in privilege (that is, gaining or losing access rights within a system) be sure to regenerate the session ID:

<?php
\Yan\Core\Session::regenerateId();
?>

N.b.: The regenerateId() method also regenerates the CSRF token value.

Cross-Site Request Forgery

CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。 尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。

http://en.wikipedia.org/wiki/Cross-site_request_forgery

CSRF防御措施

为了防范Csrf攻击,server端的处理逻辑应该是这样的:

  1. 为每一个已经登陆了的用户的每个表单请求放置一个验证token;

  2. 确保所有通过 POST/PUT/DELETE (i.e., "unsafe") 的请求都包含上一步骤提到的token值。

N.b.: 如果我们的应用是通过GET请求方法去修改应用资源 (这 是一个错误的做法), 我们同样应该对GET方法的请求 进行CSRF token的验证,以确保用户资源的安全。

例如,我们想要生成一个CSRF token,可以参考以下做法

<?php
\Yan\Core\Session::getCsrfToken()->getValue();

当server端接收到一个请求时,我们应该对CSRF token进行以下的处理:

<?php
use Yan\Core\Session;
use \Yan\Core\Input;

/**
 * @var Vendor\Package\User $user A user-authentication object.
 */
$unsafe = $_SERVER['REQUEST_METHOD'] == 'POST'
       || $_SERVER['REQUEST_METHOD'] == 'PUT'
       || $_SERVER['REQUEST_METHOD'] == 'DELETE';

if ($unsafe && $user->auth->isValid()) {
    $csrf_value = Input::get('__csrf_value');
    $csrf_token = Session::getCsrfToken();
    if (! $csrf_token->isValid($csrf_value)) {
        echo "This looks like a cross-site request forgery.";
    } else {
        echo "This looks like a valid request.";
    }
} else {
    echo "CSRF attacks only affect unsafe requests by authenticated users.";
}
?>

Nginx

我们需要把每一层应用层作为你的根目录

# 这里是你的Cgi层
server 
{
    listen       80;
    server_name  cgi.example.com;
    index index.shtml index.html index.htm interface.php;
    root  root  /path/to/root/YanPHP/Application/Cgi/;
     location / {
        try_files $uri $uri/ =404;
        if (!-e $request_filename)
        {
            rewrite (.*) /interface.php;
        }
    }
    location ~ .*\.(php|php5)?$
    {
        fastcgi_pass  127.0.0.1:9000;
        fastcgi_index index.php;
        include fastcgi.conf;
    }
    access_log  /var/log/YanPHP/access.log;
    error_log   /var/log/YanPHP/error.log;
}

# 这里是你的Server层
server 
{
    listen       80;
    server_name  server.example.com;
    index index.shtml index.html index.htm interface.php;
    root  root  /path/to/root/YanPHP/Application/Server/;
     location / {
        try_files $uri $uri/ =404;
        if (!-e $request_filename)
        {
            rewrite (.*) /interface.php;
        }
    }
    location ~ .*\.(php|php5)?$
    {
        fastcgi_pass  127.0.0.1:9000;
        fastcgi_index index.php;
        include fastcgi.conf;
    }
    access_log  /var/log/YanPHP/access.log;
    error_log   /var/log/YanPHP/error.log;
}

Apache

如果你需要进行url重写,那么你需要开启 rewrite module。 YanPHP已经为你编写好了 .htaccess 文件,并且存放在每一个应用层的目录下。

<VirtualHost *:80>
    DocumentRoot "/path/to/root/YanPHP/Application/Cgi/;"
    ServerName cgi.example.com
    AddType application/x-httpd-php .php
    <Directory />
        Options Indexes FollowSymLinks
        AllowOverride None
        Require all granted
        DirectoryIndex interface.php
    </Directory>
</VirtualHost>

<VirtualHost *:80>
    DocumentRoot "/path/to/root/YanPHP/Application/Server/;"
    ServerName server.example.com
    AddType application/x-httpd-php .php
    <Directory />
        Options Indexes FollowSymLinks
        AllowOverride None
        Require all granted
        DirectoryIndex interface.php
    </Directory>
</VirtualHost>

Tests

YanPHP编写了相关的测试用例,可以通过运行一下命令进行单元测试,同时也欢迎提交pull request进行测试用例的补充。

phpunit --configuration phpunit.xml

License

MIT

项目点评 ( 1 )

你可以在登录后,对此项目发表评论