Phalcon搭建多模块框架三十二:完整的框架代码(改进版)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u014691098/article/details/80761057

在引入第三方包时,发现怎么引入都不合理,也不利于扩展。同时之前一直处于边学习边搭建,有的地方不是很合理,所以对代码进行了优化。
下面根据下图顺序贴出框架完整代码。
这里写图片描述

1、public/index.php,这是web入口文件

<?php
/**
 * @desc 入口文件
 * @author zhaoyang
 * @date 2018年5月3日 下午5:16:27
 */
use \Phalcon\Mvc\Application;

// 检查版本,搭建用到php7一些新特性
version_compare(PHP_VERSION, '7.0.0', '>') || exit('Require PHP > 7.0.0 !');
extension_loaded('phalcon') || exit('Please open the Phalcon extension !');

// 引入自定义常量文件
require '../config/define.php';

version_compare(PHALCON_VERSION, '3.0.0', '>') || exit('Require Phalcon > 3.0.0 !');

// 设置时区
date_default_timezone_set('Asia/Shanghai');

NOW_ENV != 'dev' && error_reporting(E_ALL & ~E_NOTICE);

try {

    // 引入注册服务
    $di = require BASE_PATH . 'config/services.php';

    // 处理请求
    $application = new Application($di);

    // 组装应用程序模块
    $modules = [ ];
    foreach (MODULE_ALLOW_LIST as $v) {
        $modules[$v] = [ 
            'className' => APP_NAMESPACE . '\\' . ucfirst($v) . '\Module',
            'path' => APP_PATH . $v . '/Module.php'
        ];
    }
    // 加入模块分组配置
    $application->registerModules($modules);

    // 输出请求内容
    echo $application->handle()->getContent();
} catch (\Throwable $e) {
    $previous = $e->getPrevious();
    $applicationConfig = $application->config->application;
    if ($applicationConfig->debug->state ?? false) {
        if (empty($applicationConfig->debug->path)) {
            echo 'Exception: <br/>', '所在文件:', $e->getFile(), '<br/>所在行:', $e->getLine(), '<br/>错误码:', $e->getCode(), '<br/>错误消息:', $e->getMessage();
            if (!is_null($previous)) {
                echo '<br/>前一个Exception: <br/>', '所在文件:', $previous->getFile(), '<br/>所在行:', $previous->getLine(), '<br/>错误码:', $previous->getCode(), '<br/>错误消息:', $previous->getMessage();
            }
            exit();
        }
        $errorType = 'debug';
    } else {
        $errorType = 'error';
    }
    $errorFile = $applicationConfig->$errorType->path;
    $errorMessage = 'Exception: [所在文件:' . $e->getFile() . '] [所在行:' . $e->getLine() . '] [错误码:' . $e->getCode() . '] [错误消息:' . $e->getMessage() . '] '/*  . PHP_EOL . '[异常追踪信息:' . $e->getTraceAsString() . ']' */;
    if (!is_null($previous)) {
        $errorMessage .= '  前一个Exception: [所在文件:' . $previous->getFile() . '] [所在行:' . $previous->getLine() . '] [错误码:' . $previous->getCode() . '] [错误消息:' . $previous->getMessage() . '] '/*  . PHP_EOL . '[异常追踪信息:' . $previous->getTraceAsString() . ']' */;
    }
    $application->di->get('logger', [$errorFile])->$errorType($errorMessage);
}

2、config/define.php,常量定义文件

<?php
// phalcon版本
define('PHALCON_VERSION', Phalcon\Version::get());

//重新命名文件分隔符,建议路径后面加上分隔符
define('DS', DIRECTORY_SEPARATOR);

// 应用程序名称(应用程序所在目录名)
define('APP_NAME', 'app');

// 顶级命名空间
define('APP_NAMESPACE', 'App');

// 项目根目录
define('BASE_PATH', dirname(__DIR__) . DS);

// 应用程序所在目录
define('APP_PATH', BASE_PATH . APP_NAME . DS);

// 模块列表
// @formatter:off
define('MODULE_ALLOW_LIST', ['home', 'admin']);
// @formatter:on

// 默认模块
define('DEFAULT_MODULE', 'home');

// 默认模块命名空间
define('DEFAULT_MODULE_NAMESPACE', APP_NAMESPACE . '\\Home');

// 默认使用的配置文件名
define('NOW_ENV', 'dev');

3、config/config_dev.php,全局开发版配置文件,正式版和测试版只需要拷贝这份,然后进行针对性配置即可。
增加了自动加载配置,默认查找文件的后缀名是php,也可以添加别的,例如class.php。
自动加载机制注册完成会加载files里配置的所有文件
查找类时第一步是查找classes匹配的类,
如果没找到就会在注册的namespaces中查找,优先匹配php后缀文件,如果extensions还设置了class.php后缀,则没有匹配到php后缀的文件就会循环匹配一遍class.php后缀的文件。
如果还未找到就会循环在注册的directories目录下进行匹配。
由于directories匹配性能较低,一般建议使用命名空间注册,尽量不要使用directories注册。

<?php
/**
 * @desc 全局配置文件
 * @author zhaoyang
 * @date 2018年5月3日 下午7:54:47
 */
return [ 
    // 应用配置
    'application' => [ 
        'debug' => [ 
            'state' => false,
            'path' => BASE_PATH . 'runtime/debug/{YmdH}.log'
        ],
        'error' => [ 
            'path' => BASE_PATH . 'runtime/error/{YmdH}.log'
        ],
        // 自动加载
        'loader' => [ 
            // 文件扩展名
            'extensions' => [ 
                'php'
            ],
            'classes' => [ 
                'Smarty' => BASE_PATH . 'library/vendors/smarty/Smarty.class.php'
            ],
            'namespaces' => [ 
                'Common' => BASE_PATH . 'common/',
                'Library\\Adapter' => BASE_PATH . 'library/adapter/',
                'Library\\Extensions' => BASE_PATH . 'library/extensions/',
                'Library\\Plugins' => BASE_PATH . 'library/plugins/',
                'Library\\Tools' => BASE_PATH . 'library/tools/',
                'Library\\Validators' => BASE_PATH . 'library/validators/'
            ],
            'files' => [ 
            ],
            'directories' => [ 
            ]
        ]
    ],
    // 服务配置
    'services' => [ 
        // mysql数据库配置
        'db' => [ 
            // 是否记录执行的mysql语句
            'logged' => true,
            // 记录执行时间超过0秒的mysql语句
            'max_execute_time' => 0,
            // 比较时间到小数点后几位
            'scale' => 5,
            'log_path' => BASE_PATH . 'runtime/mysql/{Y/m/d}/{YmdH}.log',
            // 使用动态更新
            'use_dynamic_update' => true,
            // ORM选项配置
            'orm_options' => [ 
                // 是否对字段是否为空的判断
                'not_null_validations' => false
            ],
            'mysql' => [ 
                'host' => 'localhost',
                'port' => 3306,
                'username' => 'root',
                'password' => '123456',
                'dbname' => 'phalcon',
                'charset' => 'utf8'
            ]
        ],
        // 调度器配置
        'dispatcher' => [ 
            // 处理 Not-Found错误配置
            'notfound' => [ 
                // 错误码及错误提示
                'status_code' => 404,
                'message' => 'Not Found',
                // 错误跳转的页面
                'namespace' => DEFAULT_MODULE_NAMESPACE . '\\Controllers',
                'controller' => 'error',
                'action' => 'error404'
            ]
        ],
        // volt引擎相关配置
        'view_engine_volt' => [ 
            // 编译模板目录
            'compiled_path' => BASE_PATH . 'runtime/' . DEFAULT_MODULE . '/compiled/volt' . DS,
            // 是否实时编译
            'compile_always' => false,
            // 附加到已编译的PHP文件的扩展名
            'compiled_extension' => '.php',
            // 使用这个替换目录分隔符
            'compiled_separator' => '%%',
            // 是否要检查在模板文件和它的编译路径之间是否存在差异
            'stat' => true,
            // 模板前缀
            'prefix' => '',
            // 支持HTML的全局自动转义
            'autoescape' => false
        ],
        // smarty引擎相关配置,直接配置smarty参数
        'view_engine_smarty' => [ 
            'compile_dir' => BASE_PATH . 'runtime/' . DEFAULT_MODULE . '/compiled/smarty' . DS,
            'caching' => false,
            'cache_lifetime' => 3600,
            'cache_dir' => BASE_PATH . 'runtime/' . DEFAULT_MODULE . '/cache/smarty' . DS
        ],
        // 模板相关配置
        'view' => [ 
            // 模板路径
            'view_path' => APP_PATH . DEFAULT_MODULE . '/views' . DS,
            // 模板引擎,根据模板后缀自动匹配视图引擎,不启用则设为false
            'engines' => [ 
                '.volt' => 'viewEngineVolt',
                '.phtml' => 'viewEnginePhp',
                '.html' => 'viewEngineSmarty'
            ],
            'disable_level' => [ 
                'level_action_view' => false,
                'level_before_template' => true,
                'level_layout' => true,
                'level_after_template' => true,
                'level_main_layout' => true
            ]
        ],
        // 过滤器设置
        'filter' => [ 
            // 过滤类型,支持string、trim、absint、int、email、float、int!、float!、alphanum、striptags、lower、upper、url、special_chars
            'default_filter' => 'string,trim'
        ],
        // 文件日志,formatter常用line,adapter常用file
        'logger' => [ 
            'line' => [ 
                'format' => '[%date%][%type%] %message%',
                'date_format' => 'Y-m-d H:i:s'
            ],
            'file' => [ 
                'alert' => BASE_PATH . 'runtime/' . DEFAULT_MODULE . '/logs/alert/{Y/m/d}/{YmdH}.log',
                'critical' => BASE_PATH . 'runtime/' . DEFAULT_MODULE . '/logs/critical/{Y/m/d}/{YmdH}.log',
                'debug' => BASE_PATH . 'runtime/' . DEFAULT_MODULE . '/logs/debug/{Y/m/d}/{YmdH}.log',
                'error' => BASE_PATH . 'runtime/' . DEFAULT_MODULE . '/logs/error/{Y/m/d}/{YmdH}.log',
                'emergency' => BASE_PATH . 'runtime/' . DEFAULT_MODULE . '/logs/emergency/{Y/m/d}/{YmdH}.log',
                'info' => BASE_PATH . 'runtime/' . DEFAULT_MODULE . '/logs/info/{Y/m/d}/{YmdH}.log',
                'notice' => BASE_PATH . 'runtime/' . DEFAULT_MODULE . '/logs/notice/{Y/m/d}/{YmdH}.log',
                'warning' => BASE_PATH . 'runtime/' . DEFAULT_MODULE . '/logs/warning/{Y/m/d}/{YmdH}.log'
            ]
        ],
        // session配置
        'session' => [ 
            // 是否自动开启 SESSION
            'auto_start' => true,
            'options' => [ 
                'adapter' => 'files',
                'unique_id' => DEFAULT_MODULE
            ]
            // @formatter:off
            /* // phalcon提供了四种适配器,分别是files、memcache、redis、libmemcached
            'options' => [
                'adapter'    => 'memcache',
                'unique_id' => DEFAULT_MODULE,
                'prefix' => DEFAULT_MODULE,
                'persistent' => true,
                'lifetime' => 3600
            ],
            'options' => [
                'adapter'    => 'redis',
                'unique_id' => DEFAULT_MODULE,
                'prefix' => DEFAULT_MODULE,
                'auth' => '',
                'persistent' => false,
                'lifetime' => 3600,
                'index' => 1
            ] */
            // @formatter:on
        ],
        // 加密配置
        'crypt' => [ 
            // 加密秘钥
            'key' => DEFAULT_MODULE,
            // 填充方式,默认是0(PADDING_DEFAULT),1(PADDING_ANSI_X_923)、2(PADDING_PKCS7)、3(PADDING_ISO_10126)、4(PADDING_ISO_IEC_7816_4)、5(PADDING_ZERO)、6(PADDING_SPACE)
            'padding' => '',
            // 加密方法,默认是"aes-256-cfb"
            'cipher' => ''
        ],
        // cookies配置
        'cookies' => [ 
            // 是否使用加密,使用加密必须要设置crypt 的key值
            'use_encryption' => true
        ],
        // 缓存配置
        'cache' => [ 
            'frontend' => [ 
                // 数据处理方式,支持data(序列化)、json、base64、none、output、igbinary、msgpack
                'data' => [ 
                    'lifetime' => 172800
                ],
                'output' => [ 
                    'lifetime' => 172800
                ]
            ],
            'backend' => [ 
                // 数据缓存方式,支持memcache、file、redis、mongo、apc、apcu、libmemcached、memory、xcache
                'file' => [ 
                    'cache_dir' => BASE_PATH . 'runtime/' . DEFAULT_MODULE . '/cache/',
                    // 对保存的键名进行md5加密
                    'safekey' => true,
                    'prefix' => ''
                ],
                'memcache' => [ 
                    'host' => 'localhost',
                    'port' => '11211',
                    'persistent' => false,
                    'prefix' => '',
                    // 默认情况下禁用对缓存键的跟踪
                    'stats_key' => ''
                ],
                'redis' => [ 
                    'host' => '127.0.0.1',
                    'port' => 6379,
                    'auth' => '',
                    'persistent' => false,
                    'prefix' => '',
                    'stats_key' => '',
                    'index' => 0
                ]
            ]
        ],
        // 模型元数据缓存配置
        'models_metadata' => [ 
            'options' => [ 
                // 适配器,默认使用memory(内存),还支持apc、apcu、files、libmemcached、memcache、redis、session、xcache
                'adapter' => 'files',
                'meta_data_dir' => BASE_PATH . 'runtime/' . DEFAULT_MODULE . '/models_metadata/'
            ]
            // @formatter:off
            /* 'options' => [
                'adapter' => 'files',
                'meta_data_dir' => BASE_PATH . 'runtime/' . DEFAULT_MODULE . '/models_metadata/'
            ],
            'options' => [
                'adapter'    => 'memcache',
                'unique_id' => DEFAULT_MODULE,
                'prefix' => DEFAULT_MODULE,
                'persistent' => true,
                'lifetime' => 3600
            ],
            'options' => [
                'adapter' => 'memory',
            ],
            'options' => [
                'adapter'    => 'redis',
                'unique_id' => DEFAULT_MODULE,
                'prefix' => DEFAULT_MODULE,
                'auth' => '',
                'persistent' => false,
                'lifetime' => 3600,
                'index' => 1
            ],
            'options' => [
                'adapter' => 'session',
                'prefix' => DEFAULT_MODULE,
            ] */
            // @formatter:on
        ],
        // 模型缓存配置
        'models_cache' => [ 
            'frontend' => [ 
                'adapter' => 'data',
                'lifetime' => 86400
            ],
            'backend' => [ 
                'adapter' => 'file',
                'cache_dir' => BASE_PATH . 'runtime/' . DEFAULT_MODULE . '/models_cache/'
            ]
        ],
        // 视图缓存配置
        'view_cache' => [ 
            'frontend' => [ 
                'adapter' => 'output',
                'lifetime' => 86400
            ],
            'backend' => [ 
                'adapter' => 'file',
                'cache_dir' => BASE_PATH . 'runtime/' . DEFAULT_MODULE . '/cache/view/',
                'prefix' => ''
            ]
        ],
        // url配置
        'url' => [ 
            'base_uri' => '/',
            'static_base_uri' => '/',
            'base_path' => ''
        ],
        'flash' => [ 
            // 消息class属性值
            'css_classes' => [ 
                'error' => 'alert alert-danger',
                'success' => 'alert alert-success',
                'notice' => 'alert alert-info',
                'warning' => 'alert alert-warning'
            ],
            // 是否在生成的html中设置自动转义模式
            'autoescape' => true,
            // 是否必须使用HTML隐式格式化输出
            'automatic_html' => true,
            // 是否立即输出,为true时,调用$this->flash->message()或其他设置消息(例如success)时,消息立即输出(echo)
            // 为false时,消息不会输出,会保存在flash对象中并返回消息$res = $this->flash->success('my message');
            'implicit_flush' => false
        ],
        'flash_session' => [ 
            // 消息class属性值
            'css_classes' => [ 
                'error' => 'alert alert-danger',
                'success' => 'alert alert-success',
                'notice' => 'alert alert-info',
                'warning' => 'alert alert-warning'
            ],
            // 是否在生成的html中设置自动转义模式
            'autoescape' => true,
            // 是否必须使用HTML隐式格式化输出
            'automatic_html' => true,
            // 是否立即输出,必须设为true(默认为true),否则调用->output()不输出
            'implicit_flush' => true
        ],
        // 安全配置
        'security' => [ 
            // 设置由openssl伪随机生成器生成的字节数
            'random_bytes' => 16,
            // 设置默认hash,0=7(CRYPT_BLOWFISH_Y),1(CRYPT_STD_DES),2(CRYPT_EXT_DES),3(CRYPT_MD5),4(CRYPT_BLOWFISH),5(CRYPT_BLOWFISH_A),6(CRYPT_BLOWFISH_X),8(CRYPT_SHA256),9(CRYPT_SHA512)
            'default_hash' => 7,
            'work_factor' => 8
        ]
    ]

];

4、config/routers.php,路由规则文件

<?php
/**
 * @desc 路由规则
 * @author zhaoyang
 * @date 2018年5月3日 下午7:59:20
 */
$defaultRouters = [
    '/' => [
        'namespace' => DEFAULT_MODULE_NAMESPACE . '\\Controllers',
        'module' => DEFAULT_MODULE,
        'controller' => 'index',
        'action' => 'index'
    ],
    '/:controller' => [
        'namespace' => DEFAULT_MODULE_NAMESPACE . '\\Controllers',
        'module' => DEFAULT_MODULE,
        'controller' => 1,
        'action' => 'index'
    ],
    '/:controller/:action' => [
        'namespace' => DEFAULT_MODULE_NAMESPACE . '\\Controllers',
        'module' => DEFAULT_MODULE,
        'controller' => 1,
        'action' => 2
    ],
    '/:controller/:action/:params' => [
        'namespace' => DEFAULT_MODULE_NAMESPACE . '\\Controllers',
        'module' => DEFAULT_MODULE,
        'controller' => 1,
        'action' => 2,
        'params' => 3
    ]
];

$routers = [ ];
foreach (MODULE_ALLOW_LIST as $v) {
    $vUcfirst = ucfirst($v);
    $routers['/' . $v] = [
        'namespace' => APP_NAMESPACE . '\\' . $vUcfirst . '\\Controllers',
        'module' => $v,
        'controller' => 'Index',
        'action' => 'index'
    ];
    $routers['/' . $v . '/:controller'] = [
        'namespace' => APP_NAMESPACE . '\\' . $vUcfirst . '\\Controllers',
        'module' => $v,
        'controller' => 1,
        'action' => 'index'
    ];
    $routers['/' . $v . '/:controller/:action'] = [
        'namespace' => APP_NAMESPACE . '\\' . $vUcfirst . '\\Controllers',
        'module' => $v,
        'controller' => 1,
        'action' => 2
    ];
    $routers['/' . $v . '/:controller/:action/:params'] = [
        'namespace' => APP_NAMESPACE . '\\' . $vUcfirst . '\\Controllers',
        'module' => $v,
        'controller' => 1,
        'action' => 2,
        'params' => 3
    ];
}
return array_merge($defaultRouters, $routers);

5、config/services.php,注册服务文件

<?php
/**
 * @desc 注册服务
 * @author zhaoyang
 * @date 2018年5月3日 下午8:01:34
 */

// 引入配置文件
$config = require BASE_PATH . 'config/config_' . NOW_ENV . '.php';

// 引入路由规则
$routerRules = require BASE_PATH . 'config/routers.php';

use Common\Common;
use Common\Validate;
use Library\Adapter\SmartyEngine as ViewEngineSmarty;
use Library\Extensions\VoltExtension;
use Library\Plugins\DbProfilerPlugin;
use Library\Plugins\DIspatcherPlugin;
use Phalcon\Cache\Frontend\Factory as CacheFrontendFactory;
use Phalcon\Cache\Backend\Factory as CacheBackendFactory;
use Phalcon\Config;
use Phalcon\Crypt;
use Phalcon\Db\Adapter\Pdo\Mysql;
use Phalcon\Db\Profiler;
use Phalcon\DI;
use Phalcon\Di\FactoryDefault;
use Phalcon\Events\Manager as EventsManager;
use Phalcon\Flash\Direct as FlashDirect;
use Phalcon\Flash\Session as FlashSession;
use Phalcon\Http\Response\Cookies;
use Phalcon\Logger\Adapter\File as LoggerAdapterFile;
use Phalcon\Logger\Formatter\Line as LoggerFormatterLine;
use Phalcon\Mvc\Dispatcher;
use Phalcon\Mvc\Router;
use Phalcon\Mvc\Url;
use Phalcon\Mvc\View;
use Phalcon\Mvc\View\Engine\Php as ViewEnginePhp;
use Phalcon\Mvc\View\Engine\Volt as ViewEngineVolt;
use Phalcon\Security;
use Phalcon\Session\Factory as SessionFactory;
use Phalcon\Text;

$di = new FactoryDefault();

/**
 * @desc 注册调度器服务
 * @author zhaoyang
 * @date 2018年5月3日 下午8:38:34
 */
$di->setShared('dispatcher', function () {
    $dispatcherConfig = $this->getConfig()->services->dispatcher;
    $dispatcher = new Dispatcher();
    $dispatcher->setDefaultNamespace($dispatcherConfig->module_default_namespaces);
    $eventsManager = new EventsManager();
    $eventsManager->attach('dispatch', new DIspatcherPlugin());
    $dispatcher->setEventsManager($eventsManager);
    return $dispatcher;
});

/**
 * @desc 注册配置服务
 * @author zhaoyang
 * @date 2018年5月3日 下午8:38:53
 */
$di->setShared('config', function () use ($config) {
    return new Config($config);
});

/**
 * @desc 注册路由服务
 * @author zhaoyang
 * @date 2018年5月3日 下午8:39:06
 */
$di->setShared('router', function () use ($routerRules) {
    $router = new Router();
    // 自动删除末尾斜线
    $router->removeExtraSlashes(true);
    foreach ($routerRules as $k => $v) {
        $router->add($k, $v);
    }
    return $router;
});

/**
 * @desc 注册视图引擎volt服务
 * @author zhaoyang
 * @date 2018年5月4日 下午5:28:52
 */
$di->setShared('viewEngineVolt', function (View $view, DI $di) {
    // 获取config服务有多种方法,这是其一
    $voltConfig = $di->get('config')->services->view_engine_volt->toArray();
    $voltConfig = Common::convertArrKeyUnderline($voltConfig);
    $viewEngineVolt = new ViewEngineVolt($view, $di);
    $voltConfig['compiledPath'] = isset($voltConfig['compiledPath']) ? Common::dirFormat($voltConfig['compiledPath']) : BASE_PATH . 'runtime/' . DEFAULT_MODULE . '/compiled/volt' . DS;
    $mkdirRes = Common::mkdir($voltConfig['compiledPath']);
    if (!$mkdirRes) {
        throw new \Exception('创建目录 ' . $voltConfig['compiledPath'] . ' 失败');
    }
    $viewEngineVolt->setOptions($voltConfig);
    // 获取编译器对象
    $compiler = $viewEngineVolt->getCompiler();
    // 添加扩展
    $compiler->addExtension(new VoltExtension());
    return $viewEngineVolt;
});

/**
 * @desc 注册视图引擎php服务
 * @author zhaoyang
 * @date 2018年5月4日 下午5:29:15
 */
$di->setShared('viewEnginePhp', function (View $view, DI $di) {
    $viewEnginePhp = new ViewEnginePhp($view, $di);
    return $viewEnginePhp;
});

/**
 * @desc 注册视图引擎smarty服务
 * @author: ZhaoYang
 * @date: 2018年6月21日 下午11:55:22
 */
$di->setShared('viewEngineSmarty', function (View $view, DI $di) {
    $smartyConfig = $this->getConfig()->services->view_engine_smarty->toArray();
    $viewEngineSmarty = new ViewEngineSmarty($view, $di);
    $viewEngineSmarty->setOptions($smartyConfig);
    return $viewEngineSmarty;
});


/**
 * @desc 注册视图服务
 * @author zhaoyang
 * @date 2018年5月3日 下午10:52:37
 */
$di->set('view', function () {
    // 获取config服务有多种方法,这是其二
    $viewConfig = $this->getConfig()->services->view;
    $view = new View();
    // 设置视图路径
    $view->setViewsDir($viewConfig->view_path);
    $engines = $viewConfig->engines->toArray();
    foreach($engines as $k => $v){
        if($v === false){
            unset($engines[$k]);
        }
    }
    // 注册视图引擎
    $view->registerEngines($engines);
    $disableLevelConfig = $viewConfig->disable_level;
    // 关闭渲染级别
    $disableLevel = [ ];
    foreach ($disableLevelConfig as $k => $v) {
        // 设置了就代表disableLevel,与设置的true无关
        if($v){
            switch ($k) {
                case 'level_action_view':
                    $disableLevel[View::LEVEL_ACTION_VIEW] = true;
                    break;
                case 'level_before_template':
                    $disableLevel[View::LEVEL_BEFORE_TEMPLATE] = true;
                    break;
                case 'level_layout':
                    $disableLevel[View::LEVEL_LAYOUT] = true;
                    break;
                case 'level_after_template':
                    $disableLevel[View::LEVEL_AFTER_TEMPLATE] = true;
                    break;
                case 'level_main_layout':
                    $disableLevel[View::LEVEL_MAIN_LAYOUT] = true;
                    break;
            }
        }
    }
    $view->disableLevel($disableLevel);
    return $view;
});

/** 
 * @desc 注册验证服务 
 * @author zhaoyang 
 * @date 2018年5月11日 下午7:26:30 
 */
$di->set('validate', function () {
    $validate = new Validate();
    return $validate;
});

/** 
 * @desc 注册性能分析组件 
 * @author zhaoyang 
 * @date 2018年5月20日 下午9:34:33 
 */
$di->setShared('profiler', function () {
    $profiler = new Profiler();
    return $profiler;
});

/** 
 * @desc 注册数据库(连接)服务 
 * @author zhaoyang 
 * @date 2018年5月14日 下午9:01:36 
 */
$di->setShared('db', function () {
    $dbConfig = $this->getConfig()->services->db->mysql->toArray();
    $mysql = new Mysql($dbConfig);
    if ($dbConfig['logged'] ?? false) {
        $eventsManager = new EventsManager();
        $eventsManager->attach('db', new DbProfilerPlugin());
        $mysql->setEventsManager($eventsManager);
    }
    return $mysql;
});

/** 
 * @desc 注册日志服务 
 * @author zhaoyang 
 * @date 2018年5月19日 下午6:20:36 
 */
$di->set('logger', function (string $file = null, array $line = null) {
    $config = $this->getConfig()->services->logger;
    $linConfig = clone $config->line;
    !is_null($line) && $linConfig = $linConfig->merge(new Config($line));
    $loggerFormatterLine = new LoggerFormatterLine($linConfig->format, $linConfig->date_format);
    $fileConfig = $config->file;
    if (empty($file)) {
        $file = $fileConfig->info;
    } else if (array_key_exists($file, $fileConfig->toArray())) {
        $file = $fileConfig->$file;
    }
    $file = Common::dirFormat($file);
    $dir = dirname($file);
    $mkdirRes = Common::mkdir($dir);
    if (!$mkdirRes) {
        throw new \Exception('创建目录 ' . $dir . ' 失败');
    }
    $loggerAdapterFile = new LoggerAdapterFile($file);
    $loggerAdapterFile->setFormatter($loggerFormatterLine);
    return $loggerAdapterFile;
});

/** 
 * @desc 注册session服务 
 * @author zhaoyang 
 * @date 2018年5月26日 下午4:48:03 
 */
$di->setShared('session', function () {
    $sessionConfig = $this->getConfig()->services->session;
    $backendConfig = $this->getConfig()->services->cache->backend;
    $optionsArr = $sessionConfig->options->toArray();
    if (!isset($optionsArr['adapter'])) {
        throw new \Exception('session必须设置adapter');
    }
    if (array_key_exists($optionsArr['adapter'], $backendConfig->toArray())) {
        $backendOption = clone $backendConfig->{$optionsArr['adapter']};
        $optionsArr = $backendOption->merge(new Config($optionsArr))->toArray();
    }
    $optionsArr = Common::convertArrKeyUnderline($optionsArr);
    if (version_compare(PHALCON_VERSION, '3.2.0', '>')) {
        $session = SessionFactory::load($optionsArr);
    } else {
        $adapterClassName = 'Phalcon\\Session\\Adapter\\' . Text::camelize($optionsArr['adapter']);
        $session = new $adapterClassName($optionsArr);
    }
    $sessionConfig->auto_start && $session->start();
    return $session;
});

/** 
 * @desc 注册加密服务 
 * @author zhaoyang 
 * @date 2018年5月28日 下午8:17:46 
 */
$di->set('crypt', function (string $key = null, int $padding = null, string $cipher = null) {
    $cryptConfig = $this->getConfig()->services->crypt;
    $crypt = new Crypt();
    if (!empty($cryptConfig->key) || !empty($padding)) {
        $crypt->setKey($key ?? $cryptConfig->key);
    }
    if (!empty($cryptConfig->padding) || !empty($key)) {
        $crypt->setPadding($padding ?? $cryptConfig->padding);
    }
    if (!empty($cryptConfig->cipher) || !empty($cipher)) {
        $crypt->setCipher($cipher ?? $cryptConfig->cipher);
    }
    return $crypt;
});

/** 
 * @desc 注册cookies服务 
 * @author zhaoyang 
 * @date 2018年5月29日 上午9:54:23 
 */
$di->set('cookies', function () {
    $cookiesConfig = $this->getConfig()->services->cookies;
    $cookies = new Cookies();
    isset($cookiesConfig->use_encryption) && $cookies->useEncryption((bool) $cookiesConfig->use_encryption);
    return $cookies;
});

/** 
 * @desc 注册缓存 
 * @author zhaoyang 
 * @date 2018年5月30日 下午10:30:29 
 */
$di->set('cache', function (array $options = []) {
    $cacheConfig = $this->getConfig()->services->cache;
    $frontendConfig = $cacheConfig->frontend;
    if (isset($options['frontend']['adapter'])) {
        $frontendOption = new Config($options['frontend']);
        if (array_key_exists($options['frontend']['adapter'], $frontendConfig->toArray())) {
            $frontendOptionClone = clone $frontendConfig->{$options['frontend']['adapter']};
            $frontendOptionClone->merge($frontendOption);
            $frontendOption = $frontendOptionClone;
        }
    } else {
        $frontendOption = clone $frontendConfig->data;
        $frontendOption->adapter = 'data';
    }
    $frontendOption = Common::convertArrKeyUnderline($frontendOption->toArray());
    if (version_compare(PHALCON_VERSION, '3.2.0', '>')) {
        $frontendCache = CacheFrontendFactory::load($frontendOption);
    } else {
        $frontendClassName = 'Phalcon\\Cache\\Frontend\\' . Text::camelize($frontendOption['adapter']);
        $frontendCache = new $frontendClassName($frontendOption);
    }
    $backendConfig = $cacheConfig->backend;
    if (isset($options['backend']['adapter'])) {
        $backendOption = new Config($options['backend']);
        if (array_key_exists($options['backend']['adapter'], $backendConfig->toArray())) {
            $backendOptionClone = clone $backendConfig->{$options['backend']['adapter']};
            $backendOptionClone->merge($backendOption);
            $backendOption = $backendOptionClone;
        }
    } else {
        $backendOption = clone $backendConfig->file;
        $backendOption->adapter = 'file';
    }
    if ($backendOption->adapter == 'file') {
        if (empty($dir = $backendOption->cache_dir)) {
            throw new \Exception('缓存目录不能为空');
        }
        $dir = Common::dirFormat($dir);
        $mkdirRes = Common::mkdir($dir);
        if (!$mkdirRes) {
            throw new \Exception('创建目录 ' . $dir . ' 失败');
        }
    }
    $backendOption = Common::convertArrKeyUnderline($backendOption->toArray());
    if (version_compare(PHALCON_VERSION, '3.2.0', '>')) {
        $backendOption['frontend'] = $frontendCache;
        $backendCache = CacheBackendFactory::load($backendOption);
    } else {
        $backendClassName = 'Phalcon\\Cache\\Backend\\' . Text::camelize($backendOption['adapter']);
        $backendCache = new $backendClassName($frontendCache, $backendOption);
    }
    return $backendCache;
});

/** 
 * @desc 注册 modelsMetadata服务
 * @author zhaoyang 
 * @date 2018年6月2日 下午10:39:43 
 */
$di->setShared('modelsMetadata', function () {
    $modelsMetadataConfig = $this->getConfig()->services->models_metadata;
    $backendConfig = $this->getConfig()->services->cache->backend;
    $optionsArr = $modelsMetadataConfig->options->toArray();
    if (!isset($optionsArr['adapter'])) {
        throw new \Exception('modelsMetadata必须设置adapter');
    }
    if (array_key_exists($optionsArr['adapter'], $backendConfig->toArray())) {
        $backendOption = clone $backendConfig->{$optionsArr['adapter']};
        $optionsArr = $backendOption->merge(new Config($optionsArr))->toArray();
    }
    if ($optionsArr['adapter'] == 'files') {
        if (empty($optionsArr['meta_data_dir'])) {
            throw new \Exception('缓存目录不能为空');
        }
        $dir = Common::dirFormat($optionsArr['meta_data_dir']);
        $mkdirRes = Common::mkdir($dir);
        if (!$mkdirRes) {
            throw new \Exception('创建目录 ' . $dir . ' 失败');
        }
    }
    $optionsArr = Common::convertArrKeyUnderline($optionsArr);
    $modelsMetadataClassName = 'Phalcon\\Mvc\\Model\\MetaData\\' . Text::camelize($optionsArr['adapter']);
    $modelsMetadata = new $modelsMetadataClassName($optionsArr);
    return $modelsMetadata;
});

/** 
 * @desc 注册modelsCache服务
 * @author zhaoyang 
 * @date 2018年6月3日 下午6:22:31 
 */
$di->set('modelsCache', function (array $options = []) {
    $modelsCacheConfig = clone $this->getConfig()->services->models_cache;
    !empty($options) && $modelsCacheConfig->merge(new Config($options));
    $options = $modelsCacheConfig->toArray();
    $modelsCache = $this->get('cache', [ 
        $options
    ]);
    return $modelsCache;
});

/** 
 * @desc 注册视图缓存 
 * @author zhaoyang 
 * @date 2018年6月4日 下午10:14:52 
 */
$di->set('viewCache', function (array $options = []) {
    $viewCacheConfig = clone $this->getConfig()->services->view_cache;
    !empty($options) && $viewCacheConfig->merge(new Config($options));
    $options = $viewCacheConfig->toArray();
    $viewCache = $this->get('cache', [ 
        $options
    ]);
    return $viewCache;
});

/** 
 * @desc 注册url服务 
 * @author zhaoyang 
 * @date 2018年6月6日 下午8:13:37 
 */
$di->setShared('url', function () {
    $urlConfig = $this->getConfig()->services->url;
    $url = new Url();
    $urlConfig->base_uri && $url->setBaseUri($urlConfig->base_uri);
    $urlConfig->static_base_uri && $url->setStaticBaseUri($urlConfig->static_base_uri);
    $urlConfig->base_path && $url->setBasePath($urlConfig->base_path);
    return $url;
});

/** 
 * @desc 注册flash服务 
 * @author zhaoyang 
 * @date 2018年6月9日 下午8:22:46 
 */
$di->set('flash', function () {
    $flashConfig = $this->getConfig()->services->flash;
    $flashDirect = new FlashDirect($flashConfig->css_classes->toArray());
    $flashDirect->setAutoescape($flashConfig->autoescape);
    $flashDirect->setAutomaticHtml($flashConfig->automatic_html);
    $flashDirect->setImplicitFlush($flashConfig->implicit_flush);
    return $flashDirect;
});

/** 
 * @desc 注册flashSession服务 
 * @author zhaoyang 
 * @date 2018年6月9日 下午8:23:45 
 */
$di->set('flashSession', function () {
    $flashSessionConfig = $this->getConfig()->services->flash_session;
    $flashSession = new FlashSession($flashSessionConfig->css_classes->toArray());
    $flashSession->setAutoescape($flashSessionConfig->autoescape);
    $flashSession->setAutomaticHtml($flashSessionConfig->automatic_html);
    $flashSession->setImplicitFlush($flashSessionConfig->implicit_flush);
    return $flashSession;
});

/** 
 * @desc 注册安全服务 
 * @author zhaoyang 
 * @date 2018年6月7日 下午9:19:07 
 */
$di->set('security', function () {
    $securityConfig = $this->getConfig()->services->security;
    $security = new Security();
    $securityConfig->random_bytes && $security->setRandomBytes($securityConfig->random_bytes);
    $securityConfig->default_hash && $security->setDefaultHash($securityConfig->default_hash);
    $securityConfig->work_factor && $security->setWorkFactor($securityConfig->work_factor);
    return $security;
});

return $di;

6、app/home/config/config_dev.php,这是home模块的配置文件,每个模块都有自己的配置文件,会和全局配置文件合并。所以如果模块有特定配置,可以对其进行单独设置,而无需改动全局配置文件。

<?php
// 模块名称
define('MODULE_NAME', 'home');
// 模块命名空间
define('MODULE_NAMESPACE', APP_NAMESPACE . '\\Home');

return [ 
    // 应用配置
    'application' => [ 
        'debug' => [ 
            'state' => true,
            'path' => '' // BASE_PATH . 'runtime/' . MODULE_NAME . '/debug/{YmdH}.log'
        ],
        'error' => [ 
            'path' => BASE_PATH . 'runtime/' . MODULE_NAME . '/error/{YmdH}.log'
        ],
        'loader' => [ 
            'namespaces' => [ 
                MODULE_NAMESPACE . '\\Controllers' => APP_PATH . MODULE_NAME . '/controllers' . DS,
                MODULE_NAMESPACE . '\\Models' => APP_PATH . MODULE_NAME . '/models' . DS
            ]
        ]
    ],
    // 服务配置
    'services' => [ 
        // 调度器配置
        'dispatcher' => [ 
            // 模块默认的命名空间
            'module_default_namespaces' => MODULE_NAMESPACE . '\\Controllers',
            // 处理 Not-Found错误配置
            'notfound' => [ 
                // 错误码及错误提示
                'status_code' => 404,
                'message' => 'Not Found',
                // 错误跳转的页面
                'namespace' => MODULE_NAMESPACE . '\\Controllers',
                'controller' => 'error',
                'action' => 'error404'
            ]
        ],
        // volt引擎相关配置
        'view_engine_volt' => [ 
            // 编译模板目录
            'compiled_path' => BASE_PATH . 'runtime/' . MODULE_NAME . '/compiled/volt' . DS,
            // 是否实时编译
            'compile_always' => false,
            // 附加到已编译的PHP文件的扩展名
            'compiled_extension' => '.php',
            // 使用这个替换目录分隔符
            'compiled_separator' => '%%',
            // 是否要检查在模板文件和它的编译路径之间是否存在差异
            'stat' => true,
            // 模板前缀
            'prefix' => '',
            // 支持HTML的全局自动转义
            'autoescape' => false
        ],
        // 模板相关配置
        'view' => [ 
            // 模板路径
            'view_path' => APP_PATH . MODULE_NAME . '/views' . DS,
            // 模板引擎,根据模板后缀自动匹配视图引擎,不启用则设为false
            'engines' => [ 
                '.volt' => 'viewEngineVolt',
                '.phtml' => 'viewEnginePhp',
                '.html' => 'viewEngineSmarty'
            ],
            'disable_level' => [ 
                'level_action_view' => false,
                'level_before_template' => true,
                'level_layout' => true,
                'level_after_template' => true,
                'level_main_layout' => true
            ]
        ],
        // 过滤器设置
        'filter' => [ 
            // 过滤类型,支持string、trim、absint、int、email、float、int!、float!、alphanum、striptags、lower、upper、url、special_chars
            'default_filter' => 'string,trim'
        ],
        // 文件日志,formatter常用line,adapter常用file
        'logger' => [ 
            'line' => [ 
                'format' => '[%date%][%type%] %message%',
                'dateFormat' => 'Y-m-d H:i:s'
            ],
            'file' => [ 
                'alert' => BASE_PATH . 'runtime/' . MODULE_NAME . '/logs/alert/{Y/m/d}/{YmdH}.log',
                'critical' => BASE_PATH . 'runtime/' . MODULE_NAME . '/logs/critical/{Y/m/d}/{YmdH}.log',
                'debug' => BASE_PATH . 'runtime/' . MODULE_NAME . '/logs/debug/{Y/m/d}/{YmdH}.log',
                'error' => BASE_PATH . 'runtime/' . MODULE_NAME . '/logs/error/{Y/m/d}/{YmdH}.log',
                'emergency' => BASE_PATH . 'runtime/' . MODULE_NAME . '/logs/emergency/{Y/m/d}/{YmdH}.log',
                'info' => BASE_PATH . 'runtime/' . MODULE_NAME . '/logs/info/{Y/m/d}/{YmdH}.log',
                'notice' => BASE_PATH . 'runtime/' . MODULE_NAME . '/logs/notice/{Y/m/d}/{YmdH}.log',
                'warning' => BASE_PATH . 'runtime/' . MODULE_NAME . '/logs/warning/{Y/m/d}/{YmdH}.log'
            ]
        ],
        'session' => [ 
            'auto_start' => true,
            'options' => [ 
                'adapter' => 'files',
                'unique_id' => MODULE_NAME
            ]
        ],
        // 加密配置
        'crypt' => [ 
            // 加密秘钥
            'key' => MODULE_NAME,
            // 填充方式,默认是0(PADDING_DEFAULT),1(PADDING_ANSI_X_923)、2(PADDING_PKCS7)、3(PADDING_ISO_10126)、4(PADDING_ISO_IEC_7816_4)、5(PADDING_ZERO)、6(PADDING_SPACE)
            'padding' => '',
            // 加密方法,默认是"aes-256-cfb"
            'cipher' => ''
        ],
        // cookies配置
        'cookies' => [ 
            // 是否使用加密,使用加密必须要设置crypt 的key值
            'use_encryption' => true
        ],
        // 缓存配置
        'cache' => [ 
            'backend' => [ 
                // 数据缓存方式,支持memcache、file、redis、mongo、apc、apcu、libmemcached、memory、xcache
                'file' => [ 
                    'cache_dir' => BASE_PATH . 'runtime/' . MODULE_NAME . '/cache/'
                ]
            ]
        ],
        // 模型元数据缓存配置
        'models_metadata' => [ 
            'options' => [ 
                // 适配器,默认使用memory(内存),还支持apc、apcu、files、libmemcached、memcache、redis、session、xcache
                'adapter' => 'files',
                'meta_data_dir' => BASE_PATH . 'runtime/' . MODULE_NAME . '/models_metadata/'
            ]
        ],
        // 模型缓存配置
        'models_cache' => [ 
            'frontend' => [ 
                'adapter' => 'data',
                'lifetime' => 86400
            ],
            'backend' => [ 
                'adapter' => 'file',
                'cache_dir' => BASE_PATH . 'runtime/' . MODULE_NAME . '/models_cache/'
            ]
        ],
        // 视图缓存配置
        'view_cache' => [ 
            'frontend' => [ 
                'adapter' => 'output',
                'lifetime' => 20
            ],
            'backend' => [ 
                'adapter' => 'file',
                'cache_dir' => BASE_PATH . 'runtime/' . MODULE_NAME . '/cache/view/',
                'safekey' => false,
                'prefix' => ''
            ]
        ],
        // url配置
        'url' => [ 
            'base_uri' => '/' . MODULE_NAME . '/',
            'static_base_uri' => '/' . MODULE_NAME . '/static/',
            'base_path' => ''
        ],
        'flash' => [ 
            // 消息class属性值
            'css_classes' => [ 
                'error' => 'alert alert-danger',
                'success' => 'alert alert-success',
                'notice' => 'alert alert-info',
                'warning' => 'alert alert-warning'
            ],
            // 是否在生成的html中设置自动转义模式
            'autoescape' => true,
            // 是否必须使用HTML隐式格式化输出
            'automatic_html' => true,
            // 是否立即输出,为true时,调用$this->flash->message()或其他设置消息(例如success)时,消息立即输出(echo)
            // 为false时,消息不会输出,会保存在flash对象中并返回消息$res = $this->flash->success('my message');
            'implicit_flush' => false
        ],
        'flash_session' => [ 
            // 消息class属性值
            'css_classes' => [ 
                'error' => 'alert alert-danger',
                'success' => 'alert alert-success',
                'notice' => 'alert alert-info',
                'warning' => 'alert alert-warning'
            ],
            // 是否在生成的html中设置自动转义模式
            'autoescape' => true,
            // 是否必须使用HTML隐式格式化输出
            'automatic_html' => true,
            // 是否立即输出,必须设为true,否则调用->output()不输出
            'implicit_flush' => true
        ],
        // 安全配置
        'security' => [ 
            // 设置由openssl伪随机生成器生成的字节数
            'random_bytes' => 16,
            // 设置默认hash,0=7(CRYPT_BLOWFISH_Y),1(CRYPT_STD_DES),2(CRYPT_EXT_DES),3(CRYPT_MD5),4(CRYPT_BLOWFISH),5(CRYPT_BLOWFISH_A),6(CRYPT_BLOWFISH_X),8(CRYPT_SHA256),9(CRYPT_SHA512)
            'default_hash' => 7,
            'work_factor' => 8
        ]
    ]
];

7、app/home/Module.php,模块初始化文件。自动加载也在这里完成。同时这里可以注册和重写模块特有的服务。

<?php
/**
 * @desc 模块配置
 * @author zhaoyang
 * @date 2018年5月3日 下午8:49:49
 */
namespace App\Home;

use Phalcon\DiInterface;
use Phalcon\Loader;
use Phalcon\Mvc\ModuleDefinitionInterface;
use Phalcon\Config\Adapter\Php as ConfigAdapterPhp;

class Module implements ModuleDefinitionInterface {

    // 模块配置文件目录
    private static $_configPath = __DIR__ . '/config/config_' . NOW_ENV . '.php';

    public function registerAutoloaders(DiInterface $di = NULL) {
        $this->registerConfigService($di);
        $config = $di->getConfig();
        $loaderConfig = $config->application->loader->toArray();
        $loader = new Loader();
        $loader->registerClasses($loaderConfig['classes'])
        ->registerNamespaces($loaderConfig['namespaces'])
        ->registerFiles($loaderConfig['files'])
        ->registerDirs($loaderConfig['directories'])
        ->register();
    }

    public function registerServices(DiInterface $di) {
        // 这里可以注册和重写服务
        // 注册配置文件服务,合并主配置和模块配置
        // $this->registerConfigService($di);
    }

    /**
     * @desc 注册配置服务
     * @author zhaoyang
     * @date 2018年5月3日 下午8:50:51
     */
    private function registerConfigService(DiInterface $di) {
        $config = $di->getConfig();
        $di->setShared('config', function () use ($config) {
            $moduleConfigPath = self::$_configPath;
            if (is_file($moduleConfigPath)) {
                $override = new ConfigAdapterPhp($moduleConfigPath);
                $config->merge($override);
            }
            return $config;
        });
    }
}

至此web框架已经完成。
如果需要增加模块,比如增加一个admin模块,只需要复制一份home模块,重命名为admin,同时将模块中的home替换成admin,Home替换成Admin即可。
主要换的地方有两个,一个是Module.php文件,另一个是config_*.php文件

下面对cli进行改造。对于一般项目,cli脚本通常不会很多,一般使用单模块足以。
8、cli/cli.php,cli应用入口文件

<?php
/**
 * @desc 命令行入口文件
 * @author: ZhaoYang
 * @date: 2018年6月17日 下午5:42:16
 */
use Phalcon\Cli\Console;
use Phalcon\Loader;

// 检查版本,搭建用到php7一些新特性
version_compare(PHP_VERSION, '7.0.0', '>') || exit('Require PHP > 7.0.0 !');
extension_loaded('phalcon') || exit('Please open the Phalcon extension !');

// 引入自定义常量文件
require '../config/define.php';

version_compare(PHALCON_VERSION, '3.0.0', '>') || exit('Require Phalcon > 3.0.0 !');

// 设置时区
date_default_timezone_set('Asia/Shanghai');

NOW_ENV != 'dev' && error_reporting(E_ALL & ~E_NOTICE);

try {

    // 引入注册服务
    $di = require BASE_PATH . 'cli/config/services.php';

    // 处理请求
    $console = new Console($di);
    $loaderConfig = $console->config->application->loader->toArray();

    $loader = new Loader();
    // 通常还可能用到各模块的模型
    foreach (MODULE_ALLOW_LIST as $v) {
        $loaderConfig['namespaces'][APP_NAMESPACE . '\\' . ucfirst($v) . '\\Models'] = APP_PATH . $v . '/models' . DS;
    }
    $loader->registerClasses($loaderConfig['classes'])
    ->registerNamespaces($loaderConfig['namespaces'])
    ->registerFiles($loaderConfig['files'])
    ->registerDirs($loaderConfig['directories'])
    ->register();

    // 设置选项
    $console->setArgument($argv);

    $arguments = [];
    foreach ($argv as $k => $arg) {
        if ($k === 1) {
            $arguments['task'] = $arg;
        } elseif ($k === 2) {
            $arguments['action'] = $arg;
        } elseif ($k >= 3) {
            $arguments['params'][] = $arg;
        }
    }

    // 处理请求
    $console->handle($arguments);
} catch (\Throwable $e) {
    $previous = $e->getPrevious();
    $applicationConfig = $console->config->application;
    if ($applicationConfig->debug->state ?? false) {
        if (empty($applicationConfig->debug->path)) {
            echo 'Exception: ', PHP_EOL, '所在文件:', $e->getFile(), PHP_EOL, '所在行:', $e->getLine(), PHP_EOL, '错误码:', $e->getCode(), PHP_EOL, '错误消息:', $e->getMessage(), PHP_EOL, PHP_EOL;
            if (!is_null($previous)) {
                echo '前一个Exception: ', PHP_EOL, '所在文件:', $previous->getFile(), PHP_EOL, '所在行:', $previous->getLine(), PHP_EOL, '错误码:', $previous->getCode(), PHP_EOL, '错误消息:', $previous->getMessage(), PHP_EOL, PHP_EOL;
            }
            exit();
        }
        $errorType = 'debug';
    } else {
        $errorType = 'error';
    }
    $errorFile = $applicationConfig->$errorType->path;
    $errorMessage = 'Exception: [所在文件:' . $e->getFile() . '] [所在行:' . $e->getLine() . '] [错误码:' . $e->getCode() . '] [错误消息:' . $e->getMessage() . '] '/* . PHP_EOL . '[异常追踪信息:' . $e->getTraceAsString() . ']' */;
    if (!is_null($previous)) {
        $errorMessage .= ' 前一个Exception: [所在文件:' . $previous->getFile() . '] [所在行:' . $previous->getLine() . '] [错误码:' . $previous->getCode() . '] [错误消息:' . $previous->getMessage() . '] '/* . PHP_EOL . '[异常追踪信息:' . $previous->getTraceAsString() . ']' */;
    }
    $console->di->get('logger', [
        $errorFile
    ])->$errorType($errorMessage);
}

9、cli/config/config_dev.php,cli程序配置文件,此文件也会和全局配置文件整合。

<?php
return [ 
    // 应用配置
    'application' => [ 
        'debug' => [ 
            'state' => true,
            'path' => '' 
        ],
        'error' => [ 
            'path' => BASE_PATH . 'runtime/cli/error/{YmdH}.log'
        ],
        // 自动加载
        'loader' => [ 
            'directories' => [ 
                BASE_PATH . 'cli/tasks/'
            ]
        ]
    ],
    // 服务配置
    'services' => [ 
        // 过滤器设置
        'filter' => [ 
            // 过滤类型,支持string、trim、absint、int、email、float、int!、float!、alphanum、striptags、lower、upper、url、special_chars
            'default_filter' => 'string,trim'
        ],
        // 文件日志,formatter常用line,adapter常用file
        'logger' => [ 
            'line' => [ 
                'format' => '[%date%][%type%] %message%',
                'dateFormat' => 'Y-m-d H:i:s'
            ],
            'file' => [ 
                'alert' => BASE_PATH . 'runtime/cli/logs/alert/{Y/m/d}/{YmdH}.log',
                'critical' => BASE_PATH . 'runtime/cli/logs/critical/{Y/m/d}/{YmdH}.log',
                'debug' => BASE_PATH . 'runtime/cli/logs/debug/{Y/m/d}/{YmdH}.log',
                'error' => BASE_PATH . 'runtime/cli/logs/error/{Y/m/d}/{YmdH}.log',
                'emergency' => BASE_PATH . 'runtime/cli/logs/emergency/{Y/m/d}/{YmdH}.log',
                'info' => BASE_PATH . 'runtime/cli/logs/info/{Y/m/d}/{YmdH}.log',
                'notice' => BASE_PATH . 'runtime/cli/logs/notice/{Y/m/d}/{YmdH}.log',
                'warning' => BASE_PATH . 'runtime/cli/logs/warning/{Y/m/d}/{YmdH}.log'
            ]
        ],
        // 加密配置
        'crypt' => [ 
            // 加密秘钥
            'key' => 'cli',
            // 填充方式,默认是0(PADDING_DEFAULT),1(PADDING_ANSI_X_923)、2(PADDING_PKCS7)、3(PADDING_ISO_10126)、4(PADDING_ISO_IEC_7816_4)、5(PADDING_ZERO)、6(PADDING_SPACE)
            'padding' => '',
            // 加密方法,默认是"aes-256-cfb"
            'cipher' => ''
        ],
        // 缓存配置
        'cache' => [ 
            'backend' => [ 
                // 数据缓存方式,支持memcache、file、redis、mongo、apc、apcu、libmemcached、memory、xcache
                'file' => [ 
                    'cache_dir' => BASE_PATH . 'runtime/cli/cache/'
                ]
            ]
        ],
        // 模型元数据缓存配置
        'models_metadata' => [
            'options' => [
                // 适配器,默认使用memory(内存),还支持apc、apcu、files、libmemcached、memcache、redis、session、xcache
                'adapter' => 'files',
                'meta_data_dir' => BASE_PATH . 'runtime/cli/models_metadata/'
            ]
        ],
        // 模型缓存配置
        'models_cache' => [
            'frontend' => [
                'adapter' => 'data',
                'lifetime' => 86400
            ],
            'backend' => [
                'adapter' => 'file',
                'cache_dir' => BASE_PATH . 'runtime/cli/models_cache/'
            ]
        ],
        // 安全配置
        'security' => [
            // 设置由openssl伪随机生成器生成的字节数
            'random_bytes' => 16,
            // 设置默认hash,0=7(CRYPT_BLOWFISH_Y),1(CRYPT_STD_DES),2(CRYPT_EXT_DES),3(CRYPT_MD5),4(CRYPT_BLOWFISH),5(CRYPT_BLOWFISH_A),6(CRYPT_BLOWFISH_X),8(CRYPT_SHA256),9(CRYPT_SHA512)
            'default_hash' => 7,
            'work_factor' =>8
        ]
    ]
];

10、cli/config/services.php文件。cli主要是dispatcher服务和router服务与web服务不同,单模块cli无需对路由进行必要配置。

<?php
/**
 * @desc 注册cli服务
 * @author: ZhaoYang
 * @date: 2018年6月17日 下午6:51:16
 */
// 引入配置文件
$config = require BASE_PATH . 'config/config_' . NOW_ENV . '.php';

// 引入cli配置文件
$cliConfig = require BASE_PATH . 'cli/config/config_' . NOW_ENV . '.php';

use Common\Common;
use Phalcon\Cache\Backend\Factory as CacheBackendFactory;
use Phalcon\Cache\Frontend\Factory as CacheFrontendFactory;
use Phalcon\Cli\Dispatcher;
use Phalcon\Config;
use Phalcon\Crypt;
use Phalcon\Db\Adapter\Pdo\Mysql;
use Phalcon\Db\Profiler;
use Phalcon\Di\FactoryDefault\Cli;
use Phalcon\Events\Manager as EventsManager;
use Phalcon\Logger\Adapter\File as LoggerAdapterFile;
use Phalcon\Logger\Formatter\Line as LoggerFormatterLine;
use Library\Plugins\DbProfilerPlugin;
use Phalcon\Text;

$di = new Cli();

/**
 * @desc 注册配置服务
 * @author: ZhaoYang
 * @date: 2018年6月17日 下午6:55:44
 */
$di->setShared('config', function () use ($config, $cliConfig) {
    $config = new Config($config);
    $config->merge(new Config($cliConfig));
    return $config;
});

/**
 * @desc 注册调度器服务
 * @author: ZhaoYang
 * @date: 2018年6月18日 下午4:38:04
 */
$di->setShared('dispatcher', function () {
    $dispatcher = new Dispatcher();
    return $dispatcher;
});

/**
 * @desc 注册性能分析组件
 * @author zhaoyang
 * @date 2018年5月20日 下午9:34:33
 */
$di->setShared('profiler', function () {
    $profiler = new Profiler();
    return $profiler;
});

/**
 * @desc 注册数据库(连接)服务
 * @author zhaoyang
 * @date 2018年5月14日 下午9:01:36
 */
$di->setShared('db', function () {
    $dbConfig = $this->getConfig()->services->db->toArray();
    $mysql = new Mysql($dbConfig);
    if ($dbConfig['logged'] ?? false) {
        $eventsManager = new EventsManager();
        $eventsManager->attach('db', new DbProfilerPlugin());
        $mysql->setEventsManager($eventsManager);
    }
    return $mysql;
});

/**
 * @desc 注册日志服务
 * @author zhaoyang
 * @date 2018年5月19日 下午6:20:36
 */
$di->set('logger', function (string $file = null, array $line = null) {
    $config = $this->getConfig()->services->logger;
    $linConfig = clone $config->line;
    !is_null($line) && $linConfig = $linConfig->merge(new Config($line));
    $loggerFormatterLine = new LoggerFormatterLine($linConfig->format, $linConfig->date_format);
    $fileConfig = $config->file;
    if (empty($file)) {
        $file = $fileConfig->info;
    } else if (array_key_exists($file, $fileConfig->toArray())) {
        $file = $fileConfig->$file;
    }
    $file = Common::dirFormat($file);
    $dir = dirname($file);
    $mkdirRes = Common::mkdir($dir);
    if (!$mkdirRes) {
        throw new \Exception('创建目录 ' . $dir . ' 失败');
    }
    $loggerAdapterFile = new LoggerAdapterFile($file);
    $loggerAdapterFile->setFormatter($loggerFormatterLine);
    return $loggerAdapterFile;
});

/**
 * @desc 注册加密服务
 * @author zhaoyang
 * @date 2018年5月28日 下午8:17:46
 */
$di->set('crypt', function (string $key = null, int $padding = null, string $cipher = null) {
    $cryptConfig = $this->getConfig()->services->crypt;
    $crypt = new Crypt();
    if (!empty($cryptConfig->key) || !empty($padding)) {
        $crypt->setKey($key ?? $cryptConfig->key);
    }
    if (!empty($cryptConfig->padding) || !empty($key)) {
        $crypt->setPadding($padding ?? $cryptConfig->padding);
    }
    if (!empty($cryptConfig->cipher) || !empty($cipher)) {
        $crypt->setCipher($cipher ?? $cryptConfig->cipher);
    }
    return $crypt;
});

/**
 * @desc 注册缓存
 * @author zhaoyang
 * @date 2018年5月30日 下午10:30:29
 */
$di->set('cache', function (array $options = []) {
    $cacheConfig = $this->getConfig()->services->cache;
    $frontendConfig = $cacheConfig->frontend;
    if (isset($options['frontend']['adapter'])) {
        $frontendOption = new Config($options['frontend']);
        if (array_key_exists($options['frontend']['adapter'], $frontendConfig->toArray())) {
            $frontendOptionClone = clone $frontendConfig->{$options['frontend']['adapter']};
            $frontendOptionClone->merge($frontendOption);
            $frontendOption = $frontendOptionClone;
        }
    } else {
        $frontendOption = clone $frontendConfig->data;
        $frontendOption->adapter = 'data';
    }
    $frontendOption = Common::convertArrKeyUnderline($frontendOption->toArray());
    if (version_compare(PHALCON_VERSION, '3.2.0', '>')) {
        $frontendCache = CacheFrontendFactory::load($frontendOption);
    } else {
        $frontendClassName = 'Phalcon\\Cache\\Frontend\\' . Text::camelize($frontendOption['adapter']);
        $frontendCache = new $frontendClassName($frontendOption);
    }
    $backendConfig = $cacheConfig->backend;
    if (isset($options['backend']['adapter'])) {
        $backendOption = new Config($options['backend']);
        if (array_key_exists($options['backend']['adapter'], $backendConfig->toArray())) {
            $backendOptionClone = clone $backendConfig->{$options['backend']['adapter']};
            $backendOptionClone->merge($backendOption);
            $backendOption = $backendOptionClone;
        }
    } else {
        $backendOption = clone $backendConfig->file;
        $backendOption->adapter = 'file';
    }
    if ($backendOption->adapter == 'file') {
        if (empty($dir = $backendOption->cache_dir)) {
            throw new \Exception('缓存目录不能为空');
        }
        $dir = Common::dirFormat($dir);
        $mkdirRes = Common::mkdir($dir);
        if (!$mkdirRes) {
            throw new \Exception('创建目录 ' . $dir . ' 失败');
        }
    }
    $backendOption = Common::convertArrKeyUnderline($backendOption->toArray());
    if (version_compare(PHALCON_VERSION, '3.2.0', '>')) {
        $backendOption['frontend'] = $frontendCache;
        $backendCache = CacheBackendFactory::load($backendOption);
    } else {
        $backendClassName = 'Phalcon\\Cache\\Backend\\' . Text::camelize($backendOption['adapter']);
        $backendCache = new $backendClassName($frontendCache, $backendOption);
    }
    return $backendCache;
});

/**
 * @desc 注册 modelsMetadata服务
 * @author zhaoyang
 * @date 2018年6月2日 下午10:39:43
 */
$di->setShared('modelsMetadata', function () {
    $modelsMetadataConfig = $this->getConfig()->services->models_metadata;
    $backendConfig = $this->getConfig()->services->cache->backend;
    $optionsArr = $modelsMetadataConfig->options->toArray();
    if (!isset($optionsArr['adapter'])) {
        throw new \Exception('modelsMetadata必须设置adapter');
    }
    if (array_key_exists($optionsArr['adapter'], $backendConfig->toArray())) {
        $backendOption = clone $backendConfig->{$optionsArr['adapter']};
        $optionsArr = $backendOption->merge(new Config($optionsArr))->toArray();
    }
    if ($optionsArr['adapter'] == 'files') {
        if (empty($optionsArr['meta_data_dir'])) {
            throw new \Exception('缓存目录不能为空');
        }
        $dir = Common::dirFormat($optionsArr['meta_data_dir']);
        $mkdirRes = Common::mkdir($dir);
        if (!$mkdirRes) {
            throw new \Exception('创建目录 ' . $dir . ' 失败');
        }
    }
    $optionsArr = Common::convertArrKeyUnderline($optionsArr);
    $modelsMetadataClassName = 'Phalcon\\Mvc\\Model\\MetaData\\' . Text::camelize($optionsArr['adapter']);
    $modelsMetadata = new $modelsMetadataClassName($optionsArr);
    return $modelsMetadata;
});

/**
 * @desc 注册modelsCache服务
 * @author zhaoyang
 * @date 2018年6月3日 下午6:22:31
 */
$di->set('modelsCache', function (array $options = []) {
    $modelsCacheConfig = clone $this->getConfig()->services->models_cache;
    !empty($options) && $modelsCacheConfig->merge(new Config($options));
    $options = $modelsCacheConfig->toArray();
    $modelsCache = $this->get('cache', [
        $options
    ]);
    return $modelsCache;
});

return $di;

至此全部完成。至于怎么使用,可以看之前的文章

码云地址:https://gitee.com/zhao_yang/phalcon_multi_module_framework

猜你喜欢

转载自blog.csdn.net/u014691098/article/details/80761057
今日推荐