美国著名棒球运动员约格.贝拉说过:“通过仔细地观察,你可以了解很多事情。”

1.29.1 绝对的开放,还是绝对的固化?

框架应该都会考虑这样一个问题:到底是应该给应用项目提供统一固定的入口和初始化流程呢,还是应该给他们完全自由的空间?

但我发现,很多PHP的框架都提供了一个绝对的固化流程。也就是你能作出改动的地方很少,虽然这样应用不需要过多地去考虑。

然而我觉得,这样做,弊大于利。特别在现在项目需求背景各有各的不同时。所以,我为PhalApi框架选择了开放式的初始化做法。考虑到若开放的度太大,项目可能会迷茫,所以又结合了统一的初始化。

下面分别说明这两点:开放式入口和封闭式的初始化。

1.29.2 开放式的入口

作为一个接口系统,是需要为不同的终端、不同的开放人群,甚至不同的版本提供不同的服务的。

如:

  • 按不同的终端:iOS设备、Android设备、PC版、网页版
  • 按不同的开放人群:手机客户端、内部管理后台、内部其他系统、公共开放平台
  • 按不同的版本:版本从V1、V2到VN
  • 按不同的项目:同一个应用下可能会存在多个项目,当然更推荐每个项目单独一个系统
    基于此,为不同的维度提供不同的入口就很有现实实用场景了。

更为重要的是,这些不同的入口都应该尽可能简单,并能统一共享公共的组件资源、数据库、日记系统这些。一个可能的入口示例如下:

  1. <?php
  2. /**
  3. * Demo 统一入口
  4. */
  5. require_once dirname(__FILE__) . '/../init.php';
  6. //装载你的接口
  7. DI()->loader->addDirs('Demo');
  8. /** ---------------- 响应接口请求 ---------------- **/
  9. $api = new PhalApi();
  10. $rs = $api->response();
  11. $rs->output();

简单解读一下上面的代码,首先要加载统一初始化文件,其实装载挂靠的接口项目(对应你的项目目录的名称),最后创建一个PhalApi接口实例进行响应、输出结果。

下面来看下统一初始化文件需要做的事情。

1.29.3 封闭式的初始化

不言而喻,尽管我们有按不同维度划分入口的需要,但统一初始化的过程更是必不可少的。

不同的入口提供了各维度定制的机会,统一的初始化则为应用提供了统一定制的机会。

(1)常规入口代码

常规的入口,需要以下初始化操作:

  1. <?php
  2. /**
  3. * 统一初始化
  4. */
  5. /** ---------------- 根目录定义,自动加载 ---------------- **/
  6. date_default_timezone_set('Asia/Shanghai');
  7. defined('API_ROOT') || define('API_ROOT', dirname(__FILE__) . '/..');
  8. require_once API_ROOT . '/PhalApi/PhalApi.php';
  9. $loader = new PhalApi_Loader(API_ROOT);
  10. /** ---------------- 注册&初始化服务组件 ---------------- **/
  11. //自动加载
  12. DI()->loader = $loader;
  13. //配置
  14. DI()->config = new PhalApi_Config_File(API_ROOT . '/Config');
  15. //日记纪录
  16. DI()->logger = new PhalApi_Logger_File(API_ROOT . '/Runtime',
  17. PhalApi_Logger::LOG_LEVEL_DEBUG | PhalApi_Logger::LOG_LEVEL_INFO | PhalApi_Logger::LOG_LEVEL_ERROR);
  18. //数据操作 - 基于NotORM,$_GET['__sql__']可自行改名
  19. DI()->notorm = function() {
  20. $debug = !empty($_GET['__sql__']) ? true : false;
  21. return new PhalApi_DB_NotORM(DI()->config->get('dbs'), $debug);
  22. };
  23. //调试模式,$_GET['__debug__']可自行改名
  24. DI()->debug = !empty($_GET['__debug__']) ? true : DI()->config->get('sys.debug');
  25. //翻译语言包设定
  26. SL('zh_cn');

(2)常规代码解读

上面是框架执行所需的基础服务注册和配置,一般直接可用,但也可以根据需要作些细微的调整。如日记的级别设定、调试的参数修改(改成一个只有自己知道的参数名字,别让外界知道!)等。

出于让大家对初始化过程有一个更理性的认识,这里补充一下各代码的作用。

最开始是利用了PHP原生态的时区设置和宏定义:

  1. date_default_timezone_set('Asia/Shanghai');
  2. defined('API_ROOT') || define('API_ROOT', dirname(__FILE__) . '/..');

接着,便开始引入PhalApi框架的类自动加载器:

  1. require_once API_ROOT . '/PhalApi/PhalApi.php';
  2. $loader = new PhalApi_Loader(API_ROOT);

这样,我们就可以从原生态的PHP开发,切入到了PhalApi接口开发模式。但在捲起袖口准备大干一场前,我们还需要注册一些必备的服务:

  1. //自动加载
  2. DI()->loader = $loader;
  3. //配置
  4. DI()->config = new PhalApi_Config_File(API_ROOT . '/Config');
  5. //日记纪录
  6. DI()->logger = new PhalApi_Logger_File(API_ROOT . '/Runtime',
  7. PhalApi_Logger::LOG_LEVEL_DEBUG | PhalApi_Logger::LOG_LEVEL_INFO | PhalApi_Logger::LOG_LEVEL_ERROR);
  8. //数据操作 - 基于NotORM,$_GET['__sql__']可自行改名
  9. DI()->notorm = function() {
  10. $debug = !empty($_GET['__sql__']) ? true : false;
  11. return new PhalApi_DB_NotORM(DI()->config->get('dbs'), $debug);
  12. };

上面的自动加载、配置、日记和数据库操作通常而言,对于一个项目都是必须的。但配置文件的路径可自行指定,日记的存储类型也可以自由组合(多个类型采用或运算),还可以选择你合适的数据库配置。注意到,PhalApi_DB_NotORM初始化时,除了配置文件外,还有一个debug参数,此参数的作用是用于控制是否打印显示执行的SQL语句及对应消耗的时间。

至此,我们已经为项目完成了绝大部分的基础服务注册,且上面的初始化顺序建议保留不变。因为,前后有依赖关系。

但为了让我们的项目更有活力、更具生气、更国际化,我们还可以多加这么两行代码:

  1. //调试模式,$_GET['__debug__']可自行改名
  2. DI()->debug = !empty($_GET['__debug__']) ? true : DI()->config->get('sys.debug');
  3. //翻译语言包设定
  4. SL('zh_cn');

这里,也有一个debug参数,之所以和数据库的分开,是因为如果混在一起会导致返回结果解析失败(如不再是JSON格式)。此debug的来源,默认来自环境的系统配置文件(如区分生产环境和测试环境);也可以来自某个请求的手动设置,这样,开发同学便可以快速进行在线调试了。而这个参数,则是框架代码、项目代码以及扩展类库所共用的调试开关,至于各个场景使用的效果,视各环节而定。

SL()是一个快速函数,作用是设定翻译语言包。如果觉得中文下的UTF-8查看不直观,可以自行加个参数修改,如:

  1. SL(isset($_GET['__lan__']) ? $_GET['__lan__'] : 'zh_cn');

(3)更多定制注册代码

上面的操作,涵盖了大部分项目的需要。除此之外,还有一些额外的服务,可根据自身的情况,定制处理:

  1. /** ---------------- 以下服务组件就根据需要定制注册 ---------------- **/
  2. //缓存 - MC
  3. /**
  4. DI()->cache = function() {
  5. $mc = new PhalApi_Cache_Memcached(DI()->config->get('sys.mc'));
  6. return $mc;
  7. };
  8. */
  9. //签名验证服务
  10. //DI()->filter = 'Common_SignFilter';
  11. //支持JsonP的返回
  12. if (!empty($_GET['callback'])) {
  13. DI()->response = new PhalApi_Response_JsonP($_GET['callback']);
  14. }

(4)更多定制解读

此部分的注册,非项目必须部分。可根据需要,自行定制。

如上面出现的第一个,即缓存服务,使用的是Memcached:

  1. DI()->cache = function() {
  2. $mc = new PhalApi_Cache_Memcached(DI()->config->get('sys.mc'));
  3. return $mc;
  4. };

接下来的,是重要的接口签名验证服务。之所以没有提供这个服务的实现,是出于更高安全性考虑而建议项目各自制定签名规则并实现。然后这样简单注册即可被框架自动调用:

  1. //签名验证服务
  2. //DI()->filter = 'Common_SignFilter';

在有些需要支持JsonP返回的场景,可以使用PhalApi_Response_JsonP返回格式开放callback操作:

  1. //支持JsonP的返回
  2. if (!empty($_GET['callback'])) {
  3. DI()->response = new PhalApi_Response_JsonP($_GET['callback']);
  4. }

1.29.4 用一张图来表示

多级缓存静态结构图-多入口

原文: https://www.phalapi.net/wikis/1-29.html