首先介绍我实现的是xhprof插件的日志转为无限树状图,先看效果图:
废话不多说,直接看代码:(辛辛苦苦敲了好久才搞定,逻辑比较多,新手多揣摩)
控制器:
1 <?php 2 3 namespace Admin\Controller; 4 7 8 class XhprofController extends AdminbaseController 9 { 10 function _initialize() 11 { 12 parent::_initialize(); 13 } 14 15 //获取当前文件目录 16 public function index() 17 { 18 $file = scandir("./public"); 19 $str = ".xhprof"; 20 foreach ($file as $value) { 21 if (preg_replace("/($str)/", "", $value) != $value) { 22 $dir[] = $value; 23 } 24 } 25 // $count = count($dir); 26 // $page = $this->page($count, 20); 27 // $this -> assign( "Page", $page->show( 'Admin' ) ); 28 $this->assign("dir", $dir); 29 $this->display(); 30 } 31 32 //获取适当的结构 33 public function tree($file_name) 34 { 35 $file_path = "./public/xhprof/$file_name"; 36 $file_name = str_replace('.', '', $file_name); 37 if (F($file_name)) { 38 $mainTree = F($file_name); 39 } else { 40 if (file_exists($file_path)) { 41 $fp = fopen($file_path, "r"); 42 $str = fread($fp, filesize($file_path));//指定读取大小,这里把整个文件内容读取出来 43 $array = json_decode($str, true); 44 $array_keys = array_keys($array); 45 //按照调用者整理数据 46 $calls = []; 47 foreach ($array_keys as $array_key) { 48 $caller = explode("==>", $array_key)[0]; 49 $beCall = explode("==>", $array_key)[1]; 50 if (!$calls[$caller]) { 51 $calls[$caller] = [ 52 "caller" => $caller, 53 "beCalls" => [] 54 ]; 55 } 56 $status = 0;//是否有子目录 57 foreach ($array_keys as $keyv) { 58 if (strpos($keyv, $beCall . '==>') !== false && strpos($keyv, "@") === false && $beCall !== null && $beCall !== "count") { 59 $status = 1; 60 } 61 } 62 if ($beCall !== null) { 63 $calls[$caller]['beCalls'][] = array('name' => $beCall, "status" => $status, 'data' => $array[$array_key]); 64 } 65 } 66 foreach ($calls as &$v) { 67 $tmpAll = 0; 68 foreach ($v['beCalls'] as $vv) { 69 $tmpAll += $vv['data']['wt']; 70 } 71 foreach ($v['beCalls'] as &$vv1) { 72 $vv1['data']['wtp'] = round($vv1['data']['wt'] * 100 / $tmpAll, 2); 73 $vv1['wtp'] = $vv1['data']['wt']; 74 } 75 } 76 $mainTree = $calls; 77 //缓存$mainTree 78 F($file_name, $mainTree); 79 } else { 80 $mainTree = ""; 81 } 82 } 83 array_multisort(array_column($mainTree["main()"]["beCalls"], "wtp"), SORT_DESC, array_column($mainTree["main()"]["beCalls"], "name"), SORT_DESC, $mainTree["main()"]["beCalls"]); 84 $this->assign("main", $array["main()"]); 85 $this->assign("mainTree", $mainTree["main()"]); 86 $this->assign("level", 1); 87 $this->assign("file_name", $file_name); 88 $this->assign("file_path", $file_path); 89 $this->display(); 90 } 91 92 public function ajax() 93 { 94 $note = htmlspecialchars_decode(I("post.note")); 95 $file_name = htmlspecialchars_decode(I("post.file_name")); 96 $file_path = htmlspecialchars_decode(I("post.file_path")); 97 //处理$mainTree 98 if (F($file_name)) { 99 $mainTree = F($file_name); 100 } else { 101 if (file_exists($file_path)) { 102 $fp = fopen($file_path, "r"); 103 $str = fread($fp, filesize($file_path));//指定读取大小,这里把整个文件内容读取出来 104 $array = json_decode($str, true); 105 $array_keys = array_keys($array); 106 //按照调用者整理数据 107 $calls = []; 108 foreach ($array_keys as $array_key) { 109 $caller = explode("==>", $array_key)[0]; 110 $beCall = explode("==>", $array_key)[1]; 111 if (!$calls[$caller]) { 112 $calls[$caller] = [ 113 "caller" => $caller, 114 "beCalls" => [] 115 ]; 116 } 117 $status = 0;//是否有子目录 118 foreach ($array_keys as $keyv) { 119 if (strpos($keyv, $beCall . '==>') !== false && strpos($keyv, "@") === false && $beCall !== null && $beCall !== "count") { 120 $status = 1; 121 } 122 } 123 if ($beCall !== null) { 124 $calls[$caller]['beCalls'][] = array('name' => $beCall, "status" => $status, 'data' => $array[$array_key]); 125 } 126 } 127 foreach ($calls as &$v) { 128 $tmpAll = 0; 129 foreach ($v['beCalls'] as $vv) { 130 $tmpAll += $vv['data']['wt']; 131 } 132 foreach ($v['beCalls'] as &$vv1) { 133 $vv1['data']['wtp'] = round($vv1['data']['wt'] * 100 / $tmpAll, 2); 134 $vv1['wtp'] = $vv1['data']['wt']; 135 } 136 } 137 $mainTree = $calls; 138 //缓存$mainTree 139 F($file_name, $mainTree); 140 } else { 141 $mainTree = ""; 142 } 143 } 144 array_multisort(array_column($mainTree[$note]["beCalls"], "wtp"), SORT_DESC, array_column($mainTree[$note]["beCalls"], "name"), SORT_DESC, $mainTree[$note]["beCalls"]); 145 $this->ajaxReturn($mainTree[$note]); 146 } 147 148 149 }
veiw界面:
1 </head> 2 <body> 3 <div class="wrap"> 4 <ul class="nav nav-tabs"> 5 <li class="active"><a href="">{:L('ADMIN_XHPROF')}</a></li> 6 </ul> 7 <form method="post" class="js-ajax-form" action="{:U('AdminTerm/listorders')}"> 8 <table class="table table-hover table-bordered table-list"> 9 <thead> 10 <tr> 11 <th width="2">ID</th> 12 <th width="50">{:L('FILE_NAME')}</th> 13 </tr> 14 </thead> 15 <tbody> 16 <volist name="dir" id="vo" key="k"> 17 <tr> 18 <th width="2">{$k}</th> 19 <th width="50"><a href="{:U('admin/xhprof/tree',array('file_name'=>$vo))}">{$vo}</a></th> 20 </tr> 21 </volist> 22 </tbody> 23 <tfoot> 24 <tr> 25 <th width="2">ID</th> 26 <th width="50">{:L('FILE_NAME')}</th> 27 </tr> 28 </tfoot> 29 </table> 30 <div class="pagination">{$Page}</div> 31 </form> 32 </div> 33 <script src="__PUBLIC__/js/common.js"></script> 34 </body> 35 </html>
1 <html> 2 <head> 3 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> 4 <title>Xhprof数据分析</title> 5 <link rel="stylesheet" type="text/css" href="__TMPL__Public/assets/css/bootstrap.min.css"/> 6 <link rel="stylesheet" type="text/css" href="__TMPL__Public/assets/css/xhprof.css"/> 7 <script type="text/javascript" src="__PUBLIC__/js/jquery.js"></script> 8 </head> 9 <body> 10 <div class="tree well"> 11 <ul> 12 <li> 13 <span class="main"> 14 <i class='icon-minus'> </i> <b class="name">main()</b> 15 第<b> 1 </b>级    16 ct:<b class="bw">{$main.ct}</b> 17 wt:<b class="red" >{$main.wt} (100%)</b> 18 cpu:<b class="bw">{$main.cpu}</b> 19 mu:<b class="bw">{$main.mu}</b> 20 pmu:<b class="bw">{$main.pmu}</b> 21 </span> 22 <foreach name="mainTree['beCalls']" item="vo"> 23 <ul> 24 <li class='parent_li'> 25 <span title='Collapse this branch' note="{$vo.name}" level="2" file_name={$file_name} file_path={$file_path}> 26 <if condition="$vo['status']==1"> 27 <i class="icon-plus"></i> 28 <else/> 29 <b style='width:14px;display: inline-block;'></b> 30 </if> 31 <div style="display: inline-block;"> 32 <b class="name" title={$vo.name}>{$vo.name}</b> 33 第<b> 2 </b>级    34 ct:<b class="ct">{$vo.data.ct}</b> 35 <if condition="$vo['data']['wtp'] gt 10"> 36 wt:<b class="red">{$vo.data.wt} ({$vo.data.wtp|round=2}%)</b> 37 <else/> 38 wt:<b class="wt" style="display: inline-block;min-width: 150px;">{$vo.data.wt} ({$vo.data.wtp|round=2}%)</b> 39 </if> 40 cpu:<b class="bw">{$vo.data.cpu}</b> 41 mu:<b class="bw">{$vo.data.mu}</b> 42 pmu:<b class="bw">{$vo.data.pmu}</b> 43 </div> 44 </span> 45 </li> 46 </ul> 47 </foreach> 48 </li> 49 </ul> 50 </div> 51 </body> 52 <script> 53 $(function () { 54 $('.tree li:has(ul)').addClass('parent_li').find(' > span').attr('title', 'Collapse this branch'); 55 $('body').on('click', "span", function (e) { 56 e.stopPropagation(); 57 if ($(this).attr('is_click') === 1) { 58 return false; 59 } 60 $(this).attr('is_click', 1); 61 var children = $(this).parent('li.parent_li').find(' > ul > li'); 62 if (children.is(":visible")) { 63 children.hide('fast'); 64 $(this).attr('title', 'Expand this branch').find(' > i').addClass('icon-plus').removeClass('icon-minus'); 65 $(this).attr('is_click', 0); 66 } else { 67 children.show('fast'); 68 $(this).attr('title', 'Collapse this branch').find(' > i').addClass('icon-minus').removeClass('icon-plus'); 69 if ($(this).hasClass('main')) { 70 $(this).attr('is_click', 0); 71 return; 72 } 73 if (!$(this).siblings('.child').length) { 74 do_ajax($(this)); 75 } else { 76 $(this).attr('is_click', 0); 77 } 78 } 79 }); 80 81 function do_ajax(e) { 82 var note_js = e.attr('note'); 83 var level_js = e.attr('level'); 84 var file_name_js = e.attr('file_name'); 85 var file_path_js = e.attr('file_path'); 86 $.ajax({ 87 type: "post", 88 cache: false, 89 dataType: "json", 90 url: "{:U('Admin/Xhprof/ajax')}", 91 data: { 92 note: note_js, 93 level: level_js, 94 file_name: file_name_js, 95 file_path: file_path_js 96 }, 97 success: function (data) { 98 if (data.beCalls !== null) { 99 var str = ""; 100 for (var i = 0; i < data.beCalls.length; i++) { 101 str += "<ul class='child'>"; 102 str += "<li class='parent_li'>"; 103 str += "<span title='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 + ">"; 104 if (data.beCalls[i].status == 1) { 105 str += "<i class='icon-plus' ></i>"; 106 } else { 107 str += "<b style='width:14px;display: inline-block;'> </b>"; 108 } 109 str += "<b class='name' title="+ data.beCalls[i].name + ">" + data.beCalls[i].name + '</b>第<b> ' + eval(Number(level_js) + 1) + "</b> 级   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>"; 110 str += '</li>'; 111 str += '</ul>'; 112 } 113 e.siblings('.child').remove(); 114 e.after(str); 115 e.attr('is_click', 0); 116 } 117 }, 118 error: function () { 119 } 120 }) 121 } 122 }); 123 </script> 124 </html>