Fecshop 2017-10-20 09:35:50 1205次浏览 3条评论 2 0 0

Yii2 的配置文件是分散的,分散成很多碎的配置文件中,在项目中为了需要,可能又要添加很多配置文件 ,这么多的配置文件,在yii2初始化的时候,都需要进行array merge,需要一定的开销,如果提前合并成一个配置文件,就可以节省这些开销(前提是你的配置文件非常的多,譬如fecshop有大量的配置文件),下面是配置文件合并的示例代码:

<?php
error_reporting(E_ALL || ~E_NOTICE); //除去 E_NOTICE 之外的所有错误信息
//ini_set('session.cookie_domain', '.fancyecommerce.com'); //初始化域名,
$http = ($_SERVER['SERVER_PORT'] == 443) ? 'https' : 'http';
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
require __DIR__.'/../../vendor/autoload.php';
require __DIR__.'/../../vendor/fancyecommerce/fecshop/yii/Yii.php';
require __DIR__.'/../../common/config/bootstrap.php';
require __DIR__.'/../config/bootstrap.php';
$config = yii\helpers\ArrayHelper::merge(
    require(__DIR__.'/../../common/config/main.php'),
    require(__DIR__.'/../../common/config/main-local.php'),
    require(__DIR__.'/../config/main.php'),
    require(__DIR__.'/../config/main-local.php'),
    // fecshop services config
    require(__DIR__.'/../../vendor/fancyecommerce/fecshop/config/fecshop.php'),
    // fecshop module config
    require(__DIR__.'/../../vendor/fancyecommerce/fecshop/app/appfront/config/appfront.php'),
    // thrid part confing
    // common modules and services.
    require(__DIR__.'/../../common/config/fecshop_local.php'),
    // appadmin local modules and services.
    require(__DIR__.'/../config/fecshop_local.php')
);
$str = '<?php '.PHP_EOL;
$str .= 'return '.PHP_EOL;
//单个制表符用几个空格来表示
const TAB_DEFAULT_SPACES = 4;
/**
 * 获取某维的缩进空格字符串
 * @param $dimensional 维数,即当前在数组的第几层。
 * @return string 返回当前层的缩进空格的字符串
 */
function obtainSpaces($dimensional)
{
    $spaceNumber = $dimensional * TAB_DEFAULT_SPACES;
    $spaceStr = '';
    for ($index = 0; $index < $spaceNumber; $index++) {
        $spaceStr .= ' ';
    }
    return $spaceStr;
}
/**
 * @param $val 数组的键值对里的值(如果不是数组的时候的)
 * @return string 返回相应类型所对应的字符串
 */
function formatNotArray($val)
{
    if (is_string($val)) {
        return "'".$val."'";
    }
    if (is_bool($val)) {
        return $val? 'true' : 'false';
    }
    if (is_object($val)) {
        $post_log = '';
        ob_start();
        ob_implicit_flush(false);
        $func = new ReflectionFunction($val);
        $filename = $func->getFileName();
        $start_line = $func->getStartLine() - 1; // it's actually - 1, otherwise you wont get the function() block
        $end_line = $func->getEndLine();
        $length = $end_line - $start_line;
        $source = file($filename);
        $body = implode("", array_slice($source, $start_line, $length));
        echo $body;
        $post_log = ob_get_clean();
        return $post_log;
    }
    //if()
    return is_null($val)? "''" : $val;
}
/**
 * 格式化数组(格式化成字符串)
 * @param $arr 要格式化的数组
 * @param $dimensional 维度,即当前数组处于被嵌套在第几层中
 * @param $pre_sapces_str 上一维度的输出空格字符串
 * @param $curr_spaces_str 当前维度的输出空格字符串
 * @return string 数组格式化后所得字符串
 */
function formatArray($arr,$dimensional,$pre_sapces_str,$curr_spaces_str)
{
    $str = PHP_EOL.$pre_sapces_str.'['.PHP_EOL;
    $index = 1;
    foreach ($arr as $k => $v) {
        1 != $index && $str .= PHP_EOL;
        $index = -1;
        $key = is_string($k)? "'".$k."'" : $k;
        $value = '';
        if (is_array($v)) {
            $value = toPhpCode($v, $dimensional);
            $str .= $curr_spaces_str.$key.'=>'.$value.',';
        }else if(is_object($v)) {
            $value = formatNotArray($v);
            $str .= $curr_spaces_str.$value;
        }else {
            $value = formatNotArray($v);
            $str .= $curr_spaces_str.$key.'=>'.$value.',';
        }
        //$str .= $curr_spaces_str.$key.'=>'.$value.',';
    }
    $str .= PHP_EOL.$pre_sapces_str.']';
    return $str;
}
/**
 * 转成php代码
 * @param $arr 要转的数组
 * @param int $dimensional 维度,即当前数组处于被嵌套在第几层中
 * @return string 格式化后所得字符串
 */
function toPhpCode($arr, $dimensional = 0)
{
    if (!is_array($arr)) {
        return formatNotArray($arr);
    }
    $pre_sapces_str = obtainSpaces($dimensional);
    $dimensional++;
    $curr_spaces_str = obtainSpaces($dimensional);
    return formatArray($arr,$dimensional,$pre_sapces_str,$curr_spaces_str);
}
$str .= toPhpCode($config);
$str .= ';';
file_put_contents('../merge_config.php', $str);
echo 'generate merge config file success';

详细可以参看:https://github.com/fecshop/yii2_fecshop_app_advanced/blob/master/environments/prod/appfront/web/index-merge-config.php

  • 评论于 2017-12-20 15:53 举报

    配置文件合并的难点,在于数组配置的值不是字符数字这种类型,而是function(),因此需要用到反射机制。

    也就是方法

    function formatNotArray($val)
    {
        if (is_string($val)) {
            return "'".$val."'";
        }
        if (is_bool($val)) {
            return $val? 'true' : 'false';
        }
        if (is_object($val)) {
            $post_log = '';
            ob_start();
            ob_implicit_flush(false);
            $func = new ReflectionFunction($val);
            $filename = $func->getFileName();
            $start_line = $func->getStartLine() - 1; // it's actually - 1, otherwise you wont get the function() block
            $end_line = $func->getEndLine();
            $length = $end_line - $start_line;
            $source = file($filename);
            $body = implode("", array_slice($source, $start_line, $length));
            echo $body;
            $post_log = ob_get_clean();
            return $post_log;
        }
        //if()
        return is_null($val)? "''" : $val;
    }
    

    配置中的function,譬如:log component配置:

    'log' =>[
                # 追踪级别
                # 消息跟踪级别
                # 在开发的时候,通常希望看到每个日志消息来自哪里。这个是能够被实现的,通过配置 log 组件的 yii\log\Dispatcher::traceLevel 属性, 就像下面这样:
                'traceLevel' => 3,
    
                # 通过 yii\log\Logger 对象,日志消息被保存在一个数组里。为了这个数组的内存消耗, 当数组积累了一定数量的日志消息,日志对象每次都将刷新被记录的消息到 log targets 中。 你可以通过配置 log 组件的 yii\log\Dispatcher::flushInterval 属性来自定义数量
                'flushInterval' => 1,
    
                'targets' => [
                    'file' =>[
                        //'levels' => ['trace'],
                        'categories' => ['fecshop_debug'],
                        'class' => 'yii\log\FileTarget',
                        # 当 yii\log\Logger 对象刷新日志消息到 log targets 的时候,它们并 不能立即获取导出的消息。相反,消息导出仅仅在一个日志目标累积了一定数量的过滤消息的时候才会发生。你可以通过配置 个别的 log targets 的 yii\log\Target::exportInterval 属性来 自定义这个数量,就像下面这样:
                        'exportInterval' => 1,
                        # 输出文件
                        'logFile' => '@appfront/runtime/fecshop_logs/fecshop_debug.log',
                        # 你可以通过配置 yii\log\Target::prefix 的属性来自定义格式,这个属性是一个PHP可调用体返回的自定义消息前缀
                        'prefix' => function ($message) {
                            return $message;
                        },
                        # 除了消息前缀以外,日志目标也可以追加一些上下文信息到每组日志消息中。 默认情况下,这些全局的PHP变量的值被包含在:$_GET, $_POST, $_FILES, $_COOKIE,$_SESSION 和 $_SERVER 中。 你可以通过配置 yii\log\Target::logVars 属性适应这个行为,这个属性是你想要通过日志目标包含的全局变量名称。 举个例子,下面的日志目标配置指明了只有 $_SERVER 变量的值将被追加到日志消息中。
                        # 你可以将 logVars 配置成一个空数组来完全禁止上下文信息包含。或者假如你想要实现你自己提供上下文信息的方式, 你可以重写 yii\log\Target::getContextMessage() 方法。
                         'logVars' => [],
                    ],
                ],
            ],
    

    上面配置中的function就是一个对象:

     function ($message) {
                            return $message;
                        }
    

    上面的function formatNotArray($val) 函数中的if(is_object()),会通过反射机制,得到function的字符串。

  • 评论于 2017-12-20 15:55 举报

    欢迎关注fecshop, github地址:https://github.com/fecshop

  • 评论于 2018-04-04 16:51 举报

    提问说说链接:http://www.yiichina.com/feed/30116

    问题:php 如何输出源代码,除了把变量作为一个字符串以外? 大侠们帮忙想想 先谢谢啦

    回答:

    @yulong9025

    我猜的不错的话,你应该是想,读取一个php的变量,譬如

    1.这个变量是数组,然后把这个数组的内容输出出来,而不是手动用字符串拼起来赋值给变量

    2.这个变量是对象,想把这个对象的class代码都输出打印出来

    你想要的是否是这样?如果是,那么我来回答你的问题

    思路:对于数组,可以用字符串拼接的方式用函数封装起来,用递归的方式一次性输出,对于对象,需要用到反射

    我有一个详细的实现,也就是把Fecshop开源商城的N个配置文件合并成一个配置文件,详细查看:http://www.yiichina.com/tutorial/1493

    希望可以帮助到你,有兴趣可以了解下Fecshop!

您需要登录后才可以评论。登录 | 立即注册