首先介绍我实现的是xhprof插件的日志转为无限树状图,先看效果图:

\"\"

废话不多说,直接看代码:(辛辛苦苦敲了好久才搞定,逻辑比较多,新手多揣摩)

控制器:

<?php

namespace Admin\\Controller;

class XhprofController extends Admin Controller
{
    function _initialize()
    {
        parent::_initialize();
    }

    //获取当前文件目录
    public function index()
    {
        $file = scandir(\"./public/xhprof\");
        $str = \".xhprof\";
        foreach ($file as $value) {
            if (preg_replace(\"/($str)/\", \"\", $value) != $value) {
                $dir[] = $value;
            }
        }
        $this->assign(\"dir\", $dir);
        $this->display();
    }

    //获取适当的结构
    public function tree($file_name)
    {
        $file_path = \"./public/xhprof/$file_name\";
        $file_name = str_replace(\'.\', \'\', $file_name);
        if (F($file_name)) {
            $mainTree = F($file_name);
        } else {
            if (file_exists($file_path)) {
                $fp = fopen($file_path, \"r\");
                $str = fread($fp, filesize($file_path));//指定读取大小,这里把整个文件内容读取出来
                $array = json_decode($str, true);
                $array_keys = array_keys($array);
                //按照调用者整理数据
                $calls = [];
                foreach ($array_keys as $array_key) {
                    $caller = explode(\"==>\", $array_key)[0];
                    $beCall = explode(\"==>\", $array_key)[1];
                    if (!$calls[$caller]) {
                        $calls[$caller] = [
                            \"caller\" => $caller,
                            \"beCalls\" => []
                        ];
                    }
                    $status = 0;//是否有子目录
                    foreach ($array_keys as $keyv) {
                        if (strpos($keyv, $beCall . \'==>\') !== false && strpos($keyv, \"@\") === false && $beCall !== null && $beCall !== \"count\") {
                            $status = 1;
                        }
                    }
                    if ($beCall !== null) {
                        $calls[$caller][\'beCalls\'][] = array(\'name\' => $beCall, \"status\" => $status, \'data\' => $array[$array_key]);
                    }
                }
                foreach ($calls as &$v) {
                    $tmpAll = 0;
                    foreach ($v[\'beCalls\'] as $vv) {
                        $tmpAll += $vv[\'data\'][\'wt\'];
                    }
                    foreach ($v[\'beCalls\'] as &$vv1) {
                        $vv1[\'data\'][\'wtp\'] = round($vv1[\'data\'][\'wt\'] * 100 / $tmpAll, 2);
                        $vv1[\'wtp\'] = $vv1[\'data\'][\'wt\'];
                    }
                }
                $mainTree = $calls;
                //缓存$mainTree
                F($file_name, $mainTree);
            } else {
                $mainTree = \"\";
            }
        }
        array_multisort(array_column($mainTree[\"main()\"][\"beCalls\"], \"wtp\"), SORT_DESC, array_column($mainTree[\"main()\"][\"beCalls\"], \"name\"), SORT_DESC, $mainTree[\"main()\"][\"beCalls\"]);
        $this->assign(\"main\", $array[\"main()\"]);
        $this->assign(\"mainTree\", $mainTree[\"main()\"]);
        $this->assign(\"level\", 1);
        $this->assign(\"file_name\", $file_name);
        $this->assign(\"file_path\", $file_path);
        $this->display();
    }

    public function ajax()
    {
        $note = htmlspecialchars_decode(I(\"post.note\"));
        $file_name = htmlspecialchars_decode(I(\"post.file_name\"));
        $file_path = htmlspecialchars_decode(I(\"post.file_path\"));
        //处理$mainTree
        if (F($file_name)) {
            $mainTree = F($file_name);
        } else {
            if (file_exists($file_path)) {
                $fp = fopen($file_path, \"r\");
                $str = fread($fp, filesize($file_path));//指定读取大小,这里把整个文件内容读取出来
                $array = json_decode($str, true);
                $array_keys = array_keys($array);
                //按照调用者整理数据
                $calls = [];
                foreach ($array_keys as $array_key) {
                    $caller = explode(\"==>\", $array_key)[0];
                    $beCall = explode(\"==>\", $array_key)[1];
                    if (!$calls[$caller]) {
                        $calls[$caller] = [
                            \"caller\" => $caller,
                            \"beCalls\" => []
                        ];
                    }
                    $status = 0;//是否有子目录
                    foreach ($array_keys as $keyv) {
                        if (strpos($keyv, $beCall . \'==>\') !== false && strpos($keyv, \"@\") === false && $beCall !== null && $beCall !== \"count\") {
                            $status = 1;
                        }
                    }
                    if ($beCall !== null) {
                        $calls[$caller][\'beCalls\'][] = array(\'name\' => $beCall, \"status\" => $status, \'data\' => $array[$array_key]);
                    }
                }
                foreach ($calls as &$v) {
                    $tmpAll = 0;
                    foreach ($v[\'beCalls\'] as $vv) {
                        $tmpAll += $vv[\'data\'][\'wt\'];
                    }
                    foreach ($v[\'beCalls\'] as &$vv1) {
                        $vv1[\'data\'][\'wtp\'] = round($vv1[\'data\'][\'wt\'] * 100 / $tmpAll, 2);
                        $vv1[\'wtp\'] = $vv1[\'data\'][\'wt\'];
                    }
                }
                $mainTree = $calls;
                //缓存$mainTree
                F($file_name, $mainTree);
            } else {
                $mainTree = \"\";
            }
        }
        array_multisort(array_column($mainTree[$note][\"beCalls\"], \"wtp\"), SORT_DESC, array_column($mainTree[$note][\"beCalls\"], \"name\"), SORT_DESC, $mainTree[$note][\"beCalls\"]);
        $this->ajaxReturn($mainTree[$note]);
    }


}





veiw界面:

<html>
<head>
    <  http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
    < >Xhprof数据分析</ >
    <  rel=\"stylesheet\" type=\"text/css\" href=\"__TMPL__Public/assets/css/bootstrap.min.css\"/>
    <  rel=\"stylesheet\" type=\"text/css\" href=\"__TMPL__Public/assets/css/xhprof.css\"/>
    <  type=\"text/ \" src=\"__PUBLIC__/js/jquery.js\"></ >
</head>
<body>
<div class=\"tree well\">
    <ul>
        <li>
            <span class=\"main\">
                <i class=\'icon-minus\'> </i>  <b class=\"name\">main()</b>
                            第<b>&nbsp;1&nbsp;</b>级&emsp;&emsp;&emsp;
                            ct:<b class=\"bw\">{$main.ct}</b>
                            wt:<b class=\"red\" >{$main.wt} (100%)</b>
                            cpu:<b class=\"bw\">{$main.cpu}</b>
                            mu:<b class=\"bw\">{$main.mu}</b>
                            pmu:<b class=\"bw\">{$main.pmu}</b>
            </span>
            <foreach name=\"mainTree[\'beCalls\']\" item=\"vo\">
                <ul>
                    <li class=\'parent_li\'>
                        <span  =\'Collapse this branch\' note=\"{$vo.name}\" level=\"2\" file_name={$file_name} file_path={$file_path}>
                             <if condition=\"$vo[\'status\']==1\">
                                 <i class=\"icon-plus\"></i>
                                 <else/>
                                 <b style=\'width:14px;display: inline-block;\'></b>
                             </if>
                            <div style=\"display: inline-block;\">
                             <b class=\"name\"  ={$vo.name}>{$vo.name}</b>
                             第<b>&nbsp;2&nbsp;</b>级&emsp;&emsp;&emsp;
                             ct:<b class=\"ct\">{$vo.data.ct}</b>
                             <if condition=\"$vo[\'data\'][\'wtp\'] gt 10\">
                                 wt:<b class=\"red\">{$vo.data.wt} ({$vo.data.wtp|round=2}%)</b>
                                 <else/>
                                 wt:<b class=\"wt\" style=\"display: inline-block;min-width: 150px;\">{$vo.data.wt} ({$vo.data.wtp|round=2}%)</b>
                             </if>
                            cpu:<b class=\"bw\">{$vo.data.cpu}</b>
                            mu:<b class=\"bw\">{$vo.data.mu}</b>
                            pmu:<b class=\"bw\">{$vo.data.pmu}</b>
                            </div>
                        </span>
                    </li>
                </ul>
            </foreach>
        </li>
    </ul>
</div>
</body>
< >
    $(function () {
        $(\'.tree li:has(ul)\').addClass(\'parent_li\').find(\' > span\').attr(\' \', \'Collapse this branch\');
        $(\'body\').on(\'click\', \"span\", function (e) {
            e.stopPropagation();
            if ($(this).attr(\'is_click\') === 1) {
                return false;
            }
            $(this).attr(\'is_click\', 1);
            var children = $(this).parent(\'li.parent_li\').find(\' > ul > li\');
            if (children.is(\":visible\")) {
                children.hide(\'fast\');
                $(this).attr(\' \', \'Expand this branch\').find(\' > i\').addClass(\'icon-plus\').removeClass(\'icon-minus\');
                $(this).attr(\'is_click\', 0);
            } else {
                children.show(\'fast\');
                $(this).attr(\' \', \'Collapse this branch\').find(\' > i\').addClass(\'icon-minus\').removeClass(\'icon-plus\');
                if ($(this).hasClass(\'main\')) {
                    $(this).attr(\'is_click\', 0);
                    return;
                }
                if (!$(this).siblings(\'.child\').length) {
                    do_ajax($(this));
                } else {
                    $(this).attr(\'is_click\', 0);
                }
            }
        });

        function do_ajax(e) {
            var note_js = e.attr(\'note\');
            var level_js = e.attr(\'level\');
            var file_name_js = e.attr(\'file_name\');
            var file_path_js = e.attr(\'file_path\');
            $.ajax({
                type: \"post\",
                cache: false,
                dataType: \"json\",
                url: \"{:U(\'Admin/Xhprof/ajax\')}\",
                data: {
                    note: note_js,
                    level: level_js,
                    file_name: file_name_js,
                    file_path: file_path_js
                },
                success: function (data) {
                    if (data.beCalls !== null) {
                        var str = \"\";
                        for (var i = 0; i < data.beCalls.length; i++) {
                            str += \"<ul class=\'child\'>\";
                            str += \"<li class=\'parent_li\'>\";
                            str += \"<span  =\'Collapse this branch\' class=\'load\'   note=\" + data.beCalls[i].name + \" level= \" + eval(Number(level_js) + 1) + \" file_name= \" + file_name_js + \" file_path= \" + file_path_js + \">\";
                            if (data.beCalls[i].status == 1) {
                                str += \"<i  class=\'icon-plus\' ></i>\";
                            } else {
                                str += \"<b style=\'width:14px;display: inline-block;\'> </b>\";
                            }
                            str += \"<b class=\'name\'  =\"+ data.beCalls[i].name + \">\" + data.beCalls[i].name + \'</b>第<b>&nbsp;\' + eval(Number(level_js) + 1) + \"</b>&nbsp;级&emsp;&emsp;&emsp;ct:<b class=\'ct\'>\" + data.beCalls[i].data.ct + \"</b>  wt:<b  class= \" + (data.beCalls[i].data.wtp < 10 ? \'wt\' : \'red\') + \" >\" + data.beCalls[i].data.wt + \"(\" + data.beCalls[i].data.wtp + \"%)</b>  cpu:<b class=\'bw\' >\" + data.beCalls[i].data.cpu + \"</b>  mu:<b class=\'bw\'>\" + data.beCalls[i].data.mu + \"</b>   pmu:<b class=\'bw\'>\" + data.beCalls[i].data.pmu + \"</b></span>\";
                            str += \'</li>\';
                            str += \'</ul>\';
                        }
                        e.siblings(\'.child\').remove();
                        e.after(str);
                        e.attr(\'is_click\', 0);
                    }
                },
                error: function () {
                }
            })
        }
    });
</ >
</html>

 

收藏 打印