1 快速开发
1.1 安装
composer一键安装
composer create-project phalapi/phalapi
手动下载安装
下载 phalapi 项目master-2x分支
composer update
Nginx配置
......
location / {
index index.php;
}
# 开启URI路由匹配
# location / {
# try_files $uri $uri/ /?$args;
# }
# if (!-e $request_filename) {
# rewrite ^/(.*)$ /index.php last;
# }
......
nginx -t
nginx -s reload
1.2 运行Hello world
1.3 如何请求接口服务
1.4 接口响应与在线调试
跨域
在./config/di.php
后面的位置添加
// 允许跨域
$response = \PhalApi\DI()->response;
$response->addHeaders('Access-Control-Allow-Origin', '*'); // *代表允许任何网址请求
// $response->addHeaders('Access-Control-Allow-Origin', 'www.phalapi.net'); // 推荐指定网站
$response->addHeaders('Access-Control-Allow-Methods', 'POST,GET,OPTIONS,DELETE'); // 允许请求的类型
$response->addHeaders('Access-Control-Allow-Credentials', 'true'); // 设置是否允许发送 cookies
$response->addHeaders('Access-Control-Allow-Headers', 'Content-Type,Content-Length,Accept-Encoding,X-Requested-with, Origin'); // 设置允许自定义请求头的字段
在线调试
- 单次请求开启调试:默认添加请求参数
&__debug__=1
- 全部请求开启调试:把配置文件
./config/sys.php
文件中的配置改成'debug' => true,
自定义埋点
// 添加纪录埋点,并指定节点标识
PhalApi\DI()->tracer->mark('DO_SOMETHING');
自定义调试信息
$x = 'this is x';
$y = array('this is y');
\PhalApi\DI()->response->setDebug('XXX_x', $x); // XXX扩展
\PhalApi\DI()->response->setDebug('XXX_y', $y);
1.5 Api接口层
异常抛出
use App\Common\AppException;
......
throw new AppException('提示消息', 1000);
......
手动指定ret状态码
// 手动设置ret为1000
// ret=200时表示正常返回,ret=4xx表示额端非法请求,ret=500表示服务器内部错误,手动设置时应设置成其他整数范围,避免语义冲突
\PhalApi\DI()->response->setRet(1000);
// 手动设置提示消息
\PhalApi\DI()->response->setMsg('手动设置提示消息');
钩子函数
- PhalApi\Api::getRules(),获取参数设置的规则,可由开发人员根据需要重载
- PhalApi\Api::userCheck(),用户身份验证,可由开发人员根据需要重载,此通用操作一般可以使用委托或者放置在应用接口基类
1.6 DataApi通用数据接口
DataApi有哪些接口?
PhalApi\Api\DataApi
目前有5个数据接口(后面会进一步扩展):
- 创建新数据,
{命名空间}.{接口类名}.CreateData
- 批量删除,
{命名空间}.{接口类名}.DeleteDataIDs
- 获取一条数据,
{命名空间}.{接口类名}.GetData
- 获取表格列表数据,
{命名空间}.{接口类名}.TableList
- 更新数据,
{命名空间}.{接口类名}.UpdateData
如何屏蔽不需要的接口?
/**
* @ignore
*/
public function createData() {
throw new \PhalApi\Exception\BadRequestException('此接口已关闭');
}
如何修改接口文档?
/**
* 发布一篇新的博客文章
* @desc 进行博客文章的发布,发布后内容进入待审状态
* @return int id 新博客文章的ID
*/
public function createData() {
return parent::createData();
}
1.7 Domain领域业务层与ADM模式
ADM调用关系
需要注意不能越层调用也不能逆向调用,即不能Api调用Model。而应该是上层调用下层,或者同层级调用,也就是说,我们应该:
- Api层调用Domain层
- Domain层调用Domain层
- Domain层调用Model层
- Model层调用Model层
1.8 Model数据模型层与数据库操作
一个简单的Model例子
1.9 DataModel数据模型
简单:4个CURD基本操作
$model = new App\Model\User();
// 查询
$row = $model->get(1);
$row = $model->get(1, 'id, name'); //取指定的字段
$row = $model->get(1, array('id', 'name')); //可以数组取指定要获取的字段
// 更新
$data = array('name' => 'test', 'update_time' => time());
$model->update(1, $data); //基于主键的快速更新
// 插入
$data = array('name' => 'phalapi');
$id = $model->insert($data);
//$id = $model->insert($data, 5); //如果是分表,可以通过第二个参数指定分表的参考ID
// 删除
$model->delete(1);
总数
接口:PhalApi\Model\DataModel::count($where = NULL, $countBy = '*')
最小值
接口:PhalApi\Model\DataModel::min($where, $minBy)
最大值
接口:PhalApi\Model\DataModel::max($where, $maxBy)
求和
接口:PhalApi\Model\DataModel::sum($where, $sumBy)
获取字段值
接口:PhalApi\Model\DataModel::getValueBy($field, $value, $selectFiled, $default = FALSE)
获取字段值(多个)
接口:PhalApi\Model\DataModel::getValueMoreBy($field, $value, $selectFiled, $limit = 0, $isDistinct = FALSE)
获取一条纪录
接口:PhalApi\Model\DataModel::getDataBy($field, $value, $select = '*', $default = FALSE)
获取多条纪录
接口:PhalApi\Model\DataModel::getDataMoreBy($field, $value, $limit = 0, $select = '*')
执行SQL查询语句
接口:PhalApi\Model\DataModel::queryAll($sql, $parmas = array())
执行SQL变更语句
接口:PhalApi\Model\DataModel::executeSql($sql, $params = array())
第三种获取NotORM的方式
- 全局获取方式,通过
\PhalApi\DI()->notorm->表名
方式获取,可以用于任何地方。- 局部获取方式,通过在继承
PhalApi\Model\NotORMModel
的子类中使用$this->getORM()
获取当前Model
对应的NotORM
,仅限用于Model
子类内部。
- 局部获取方式,通过在继承
- 使用
PhalApi\Model\DataModel::notorm()
静态方法获取。
DataModel与NotORMModel的区别
DataModel是比NotORMModel更新推出的数据基类,比NotORMModel功能更强大,并且开发使用更友好。推荐从PhalApi 2.12.0 及以上版本改用DataModel。
使用DataModel前后的继承关系对比如下:
1.10 单元测试
PHPUnit
PHPUnit官网:https://phpunit.de,如需进行单元测试,请先安装PHPUnit。
拓展: phpstorm使用单元测试
composer require phpunit/phpunit
Run->......
(后续我会单独做单元测试的笔记!!!)
1.11 自动加载和PSR-4
如何增加一个顶级的命名空间?
注册顶级命名空间需要在composer.json
的autoload
里注册, 如:
{
"autoload": {
"psr-4": {
"App\\": "src/app",
"Foo\\": "src/foo"
}
}
}
更新
composer update
或(只更新命名空间的映射关系)
composer dumpautoload
添加全局函数
放置到./src/app/functions.php
文件内, 如:
<?php
namespace App;
function hello() {
return 'Hey, man~';
}
使用:
echo \App\hello();
1.12 接口文档
在线接口文档
http://dev.phalapi.net/docs.php
注释
接口服务名称通常为接口类成员函数的第一行注释
接口说明对应接口类成员函数的@desc
注释,支持HTML格式
@method POST
指定了当前接口,只允许POST请求
接口参数在当前接口类getRules()
方法中的返回
如:
class Site extends Api {
/**
* 默认接口服务
* @desc 默认接口服务,当未指定接口服务时执行此接口服务
* @return string title 标题
* @return string content 内容
* @return string version 版本,格式:X.X.X
* @return int time 当前时间戳
*/
public function index() {
}
}
隐藏接口
类注释添加@ignore
生成离线文档
php ./public/docs.php
1.13 初始化
框架初始化
public/init.php
2 数据库
2.1 数据库连接
MySQL (PDO)
MS SQL Server (PDO)
PostgreSQL (PDO)
数据库基本配置
./config/dbs.php
主要有两部分配置:servers
和tables
servers,针对数据库的配置,可以配置多个数据库
tables,针对表的配置,支持配置分表(不需要分表可不配置分表)
如何排查数据库连接错误?
?__debug__=1
如何断开数据库连接?
\PhalApi\DI()->notorm->disconnect();
在./public/index.php文件最后进行手动断开
2.2 数据库于NotORM
当我们需要操作数据库时,主要分为三个步骤:连接数据库、实现数据库表操作、调用。
如何指定表名?
<?php
namespace App\Model;
use PhalApi\Model\NotORMModel as NotORM;
class User extends NotORM {
protected function getTableName($id) {
return 'my_user'; // 手动设置表名为 my_user
}
}
4个CURD基本操作
$model = new App\Model\User();
// 查询
$row = $model->get(1);
$row = $model->get(1, 'id, name'); //取指定的字段
$row = $model->get(1, array('id', 'name')); //可以数组取指定要获取的字段
// 更新
$data = array('name' => 'test', 'update_time' => time());
$model->update(1, $data); //基于主键的快速更新
// 插入
$data = array('name' => 'phalapi');
$id = $model->insert($data);
//$id = $model->insert($data, 5); //如果是分表,可以通过第二个参数指定分表的参考ID
// 删除
$model->delete(1);
附录:PhalApi对NotORM的优化
如果了解NotORM的使用,自然而然对PhalApi中的数据库操作也就一目了然了。但为了更符合接口类项目的开发,PhalApi对NotORM的底层进行优化和调整。以下改动点包括但不限于:
- 将原来返回的结果全部从对象类型改成数组类型,便于数据流通
- 添加查询多条纪录的接口:
NotORM_Result::fetchAll()
和NotORM_Result::fetchRows()
- 添加支持原生SQL语句查询的接口:
NotORM_Result::queryAll()
和NotORM_Result::queryRows()
- limit 操作的调整,取消原来OFFSET关键字的使用
- 当数据库操作失败时,抛出PDOException异常
- 将结果集中以主键作为下标改为以顺序索引作为下标
- 禁止全表删除,防止误删
- 调整调试模式
2.3 数据库使用和查询
事务
// Step 1: 开启事务
\PhalApi\DI()->notorm->beginTransaction('db_master');
// Step 2: 数据库操作
\PhalApi\DI()->notorm->user->insert(array('name' => 'test1'));
\PhalApi\DI()->notorm->user->insert(array('name' => 'test2'));
// Step 3: 提交事务/回滚
\PhalApi\DI()->notorm->commit('db_master');
//\PhalApi\DI()->notorm->rollback('db_master');
在Model子类内,可以:
- 开启事务:
$this->getORM()->transaction('BEGIN')
; - 提交事务:
$this->getORM()->transaction('COMMIT')
; - 回滚事务:
$this->getORM()->transaction('ROLLBACK')
;
可以使用更底层更通用的接口,即:\NotORM_Result::query($query, $parameters)
。
如,删除一张表
// DROP TABLE tbl_user
return $this->getORM()->query('DROP TABLE tbl_user', array());
2.4 数据库分库分表策略
2.5 连接多个数据库
2.6 打印和保存SQL语句
- sys.debug:是否开启接口调试模式,开启后在客户端可以直接看到更多调试信息
- sys.notorm_debug,是否开启NotORM调试模式,开启后仅针对NotORM服务开启调试模式
- sys.enable_sql_log,是否纪录SQL到日志,需要同时开启notorm_debug方可写入日志
3 高级专题
3.1 接口参数
参数规则格式
参数规则是针对各个接口服务而配置的多维规则数组,由接口类的getRules()
方法返回。其中,
- 一维下标是接口类的方法名,对应接口服务的
Action
; - 二维下标是类属性名称,对应在服务端获取通过验证和转换化的最终客户端参数;
- 三维下标
name
是接口参数名称,对应外部客户端请求时需要提供的参数名称。
三级参数规则配置
参数规则主要有三种,分别是:系统参数规则、应用参数规则、接口参数规则。
多个参数规则优先级
简而言之,多个参数规则的优先级从高到下,分别是(正如你想到的那样):
- 1、指定接口参数规则
- 2、通用接口参数规则
- 3、应用参数规则
- 4、系统参数规则
对于重叠的接口参数,若指定接口不需要某个接口参数,可以通过将参数规则配置置为NULL或FALSE,从而取消此参数。例如取消sign全局参数;
'sign' => NULL,
参数规则配置详细说明
主要的类型有:字符串、整数、浮点数、布尔值、时间戳/日期、数组、枚举类型、文件上传和回调函数
3.2 配置
配置文件简单读取
// 配置
$di->config = new FileConfig(API_ROOT . '/config');
// app.php里面的全部配置
\PhalApi\DI()->config->get('app');//返回:array( ... ... )
\PhalApi\DI()->config->get('app.not_found', 404); //返回:404
当前环境配置文件
// 运行模式,可以是:dev, test, prod// 运行模式,可以是:dev, test, prod
defined('API_MODE') || define('API_MODE', 'prod');
API_MODE有三个值,分别是:
- dev表示开发模式,此时如果./config/sys_dev.php、./config/app_dev.php、./config/dbs_dev.php配置文件若存在,则会优先加载
*_dev.php
系列配置文件。 - test表示测试模式,此时如果./config/sys_test.php、./config/app_test.php、./config/dbs_test.php配置文件若存在,则会优先加载
*_test.php
系列配置文件。 - prod表示生产模式,则加载./config/sys.php、./config/app.php、./config/dbs.php配置文件。
3.3 日志
7种级别:
const EMERGENCY = 'energency';
const ALERT = 'alert';
const CRITICAL = 'critical';
const ERROR = 'error';
const WARNING = 'warning';
const NOTICE = 'notice';
const INFO = 'info';
const DEBUG = 'debug';
简化版日记接口
只有三种
error: 系统异常类日记
info: 业务纪录类日记
debug: 开发调试类日记
error
// 只有描述
\PhalApi\DI()->logger->error('fail to insert DB');
// 描述 + 简单的信息
\PhalApi\DI()->logger->error('fail to insert DB', 'try to register user dogstar');
// 描述 + 当时的上下文数据
$data = array('name' => 'dogstar', 'password' => '123456');
\PhalApi\DI()->logger->error('fail to insert DB', $data);
info
// 假设:10 + 2 = 12
\PhalApi\DI()->logger->info('add user exp', array('name' => 'dogstar', 'before' => 10, 'addExp' => 2, 'after' => 12, 'reason' => 'help one more phper'));
debug
// 只有描述
\PhalApi\DI()->logger->debug('just for test');
// 描述 + 简单的信息
\PhalApi\DI()->logger->debug('just for test', '一些其他的描述 ...');
// 描述 + 当时的上下文数据
\PhalApi\DI()->logger->debug('just for test', array('name' => 'dogstar', 'password' => '******'));
更灵活的记录:
\PhalApi\DI()->logger->log('demo', 'add user exp', array('name' => 'dogstar', 'after' => 12));
\PhalApi\DI()->logger->log('test', 'add user exp', array('name' => 'dogstar', 'after' => 12));
扩展:定制你的日志
<?php
namespace App\Common\Logger;
use PhalApi\Logger;
class DBLogger extends Logger {
public function log($type, $msg, $data) {
// TODO 数据库的日记写入 ...
}
}
3.4 缓存
本地缓存
这里所指的简单缓存,主要是存储在单台服务器上的缓存,例如使用系统文件的文件缓存,PHP语言提供的APCU缓存。因为实现简单,且部署方便。但其缺点也是明显的,如文件I/O读写导致性能低,不能支持分布式。所以在没有集群服务器下是适用的。
文件缓存
例如,当需要使用文件缓存时,先在DI容器中注册对文件缓存到\PhalApi\DI()->cache
。
$di->cache = new PhalApi\Cache\FileCache(array('path' => API_ROOT . '/runtime', 'prefix' => 'demo'));
初始化文件缓存时,需要传入配置数组,其中path为缓存数据的目录,可选的前缀prefix,用于区别不同的项目。
// 使用方法
// 设置
PhalApi\DI()->cache->set('thisYear', 2015, 600);
// 获取,输出:2015
echo PhalApi\DI()->cache->get('thisYear');
// 删除
PhalApi\DI()->cache->delete('thisYear');
APCU缓存
安装好APCU扩展和设置相关配置并重启PHP后,便可开始使用APCU缓存。APCU缓存的初始化比较简单,只需要简单创建实例即可,不需要任何配置。
$di->cache = new PhalApi\Cache\APCUCache();
Memcache/Memcached缓存
若需要使用Memcache/Memcached缓存,则需要安装相应的PHP扩展。PHP 7中已经逐渐不支持Memcache,因此建议尽量使用Memcached扩展。
如使用Memcached:
$di->cache = new PhalApi\Cache\MemcachedCache(array('host' => '127.0.0.1', 'port' => 11211, 'prefix' => 'demo_'));
初始化Memcached时,需要传递一个配置数组,其中host为缓存服务器,port为缓存端口,prefix为可选的前缀,用于区别不同的项目。配置前缀,可以防止同一台MC服务器同一端口下key名冲突。对于缓存的配置,更好的建议是使用配置文件来统一管理配置。例如调整成:
$di->cache = new PhalApi\Cache\MemcachedCache(DI()->config->get('sys.mc'));
相应的配置,则在./config/sys.php中的mc选项中统一维护。
Redis缓存
当需要使用Redis缓存时,需要先安装对应的Redis扩展。
简单的Redis缓存的初始化如下:
$config = array('host' => '127.0.0.1', 'port' => 6379);
$di->cache = new PhalApi\Cache\RedisCache($config);
3.5 过滤器(签名)
默认可用的MD5签名
白名单配置
实现过滤器接口
微信验签
注册过滤器服务
随后,我们只需要再简单地注册一下过滤器服务即可,在./config/di.php
文件最后追加:
// 签名验证服务
$di->filter = new App\Common\SignFilter();
3.6 cookie
如同其他的服务一样,我们在使用前需要对COOKIE进行注册。COOKIE服务注册在\PhalApi\DI()->cookie
中,可以使用PhalApi\Cookie实例进行初始化,如:
$config = array('domain' => '.phalapi.net');
\PhalApi\DI()->cookie = new PhalApi\Cookie($config);
// 设置COOKIE
// Set-Cookie:"name=phalapi; expires=Sun, 07-May-2017 03:26:45 GMT; domain=.phalapi.net"
\PhalApi\DI()->cookie->set('name', 'phalapi', $_SERVER['REQUEST_TIME'] + 600);
// 获取COOKIE,输出:phalapi
echo \PhalApi\DI()->cookie->get('name');
// 删除COOKIE
\PhalApi\DI()->cookie->delete('name');
扩展:定制专属的COOKIE
当项目中需要定制专属的COOKIE服务时,可以继承PhalApi\Cookie基类,并按需要重写对应的接口。主要的接口有三个:
- 设置COOKIE:
PhalApi\Cookie::set($name, $value, $expire = NULL)
- 获取COOKIE:
PhalApi\Cookie::get($name = NULL)
- 删除COOKIE:
PhalApi\Cookie::delete($name)
3.7 加密
PHP的mcrypt加密扩展
3.8 国际化
语言设定
在初始化文件./public/init.php
中,通过快速函数\PhalApi\SL($language)
可以设定当前所使用的语言。例如设置语言为简体中文,可以:
// 翻译语言包设定
\PhalApi\SL('zh_cn');
翻译包
翻译包的文件路径为:./language/语言/common.php
,例如简体中文zh_cn对应的翻译包文件为:./Language/zh_cn/common.php
。此翻译包文件返回的是一个数组,其中键为待翻译的内容,值为翻译后的内容.
3.9 CURL请求
当需要进行curl请求时,可使用PhalApi封装的CURL请求类PhalApi\CUrl,从而实现快捷方便的请求。
3.10 工具和杂项
$ip = \PhalApi\Tool::getClientIp();
// 指定使用字符集,如6位数字验证码
$len = 6;
$str = \PhalApi\Tool::createRandStr($len, '0123456789');
$arr = array('name' => 'PhalApi');
$xml = \PhalApi\Tool::arrayToXml($arr);
$xml = '<xml><name>PhalApi</name></xml>';
$arr = \PhalApi\Tool::xmlToArray($xml);
......
3.11 DI服务汇总
基本注册
$di = \PhalApi\DI();
// 配置
$di->config = new FileConfig(API_ROOT . '/config');
// 调试模式,$_GET['__debug__']可自行改名
$di->debug = !empty($_GET['__debug__']) ? true : $di->config->get('sys.debug');
// 日记纪录
$di->logger = new FileLogger(API_ROOT . '/runtime', Logger::LOG_LEVEL_DEBUG | Logger::LOG_LEVEL_INFO | Logger::LOG_LEVEL_ERROR);
// 数据操作 - 基于NotORM
$di->notorm = new NotORMDatabase($di->config->get('dbs'), $di->debug);
定制注册
// 签名验证服务
// $di->filter = new \PhalApi\Filter\SimpleMD5Filter();
// 缓存 - Memcache/Memcached
// $di->cache = function () {
// return new \PhalApi\Cache\MemcacheCache(DI()->config->get('sys.mc'));
// };
// 支持JsonP的返回
// if (!empty($_GET['callback'])) {
// $di->response = new \PhalApi\Response\JsonpResponse($_GET['callback']);
// }
DI服务资源一览表
正确的用法应该是:
// 先获取,再判断
$XXX = $di->XXX;
var_dump(isset($XXX));
var_dump(!empty($XXX));
3.12 扩展类库
扩展类库列表
3.13 SDK包的使用
SDK包列表
3.14 脚本命令
phalapi-buildtest命令
phalapi-buildsqls命令
phalapi-cli命令
3.15 MQ队列
Gearman整合
RabbitMQ整合
NSQ整合
3.16 错误处理
PhalApi的错误处理
在./config/di.php文件注册:
$di->error = new \PhalApi\Error\ApiError();
在这背后,会:
- 1、通过set_error_handler()注册用户错误处理函数
- 2、通过register_shutdown_function()处理PHP致命错误
主要处理方式,是将相关的警告、错误、和提醒信息纪录到文件日志。
4 运营平台
6 视频教程
PhalApi 2.x 接口开发 - 2020视频教程开讲啦!
后记: 20200315