php动态高亮web源代码
注:配置好不允许高亮的文件名,安全第一
#php实现动态展示目录树结构源代码
适用于开放源代码,结合html缓存使用效果更佳,因循环较多不适合放首页
能力有限没实现行号
效果截图
代码
4个文件放统一目录
1.php,1.html,net.js,showsource.php
1.php
<?php
header('Content-Type: text/html; charset=utf-8');define('root',str_replace('\\','/',realpath(__DIR__.'/../..')));
$dir = root;
$res = [];
$root_dir = $dir;
$res = tree($res,$dir);
//$res = preg_replace('/^'.preg_quote($dir,'/').'/', 'root', $res);
$data = base64_encode(json_encode($res));
require '1.html';
function tree(&$res,$dir)
{global $root_dir;if(count($res)===0){$res[]=['id'=>sha1($dir),'pid'=>0,'type'=>'dir','handle'=>$dir,'name'=>substr($dir,strripos($dir,'/')+1,strlen($dir)),];}$handles = array_merge(glob($dir . '/.*'), glob($dir . '/*'));$handles = preg_replace('/^[\s\S]*\/\.$/','',$handles);$handles = preg_replace('/^[\s\S]*\/\.\.$/','',$handles);$handles = array_filter($handles);sort($handles);foreach ($handles as $item){if(is_dir($item)){$res[]=['id'=>sha1($item),'pid'=>sha1($dir),'type'=>'dir','handle'=>preg_replace('/^'.preg_quote($root_dir,'/').'/','root',$item),'name'=>substr($item,strripos($item,'/')+1,strlen($item)),];tree($res,$item);}else{$res[]=['id'=>sha1($item),'pid'=>sha1($dir),'type'=>'file','handle'=>preg_replace('/^'.preg_quote($root_dir,'/').'/','root',$item),'name'=>substr($item,strripos($item,'/')+1,strlen($item)),];}}return $res;
}function dump($s=null,$return = false)
{ob_start();var_dump($s);$res = ob_get_clean();$res = preg_replace('/'.preg_quote(']=>','/').'\n[\s]+/m', '] => ', $res);switch (php_sapi_name()){case 'cgi-fcgi':$res = preg_replace('/ /U', "\t", $res);$res = '<pre><code>'.$res.'</code></pre>';if($return)return $res;echo $res;break;case 'cli':if($return)return $res;echo $res;break;}
}
1.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>show source</title><style>.tree {--spacing: 1.5rem;--radius: 10px;padding-left: 0;padding-right: 10px;}.tree li {display: block;position: relative;padding-left: calc(2 * var(--spacing) - var(--radius) - 2px);}.tree ul {margin-left: calc(var(--radius) - var(--spacing));padding-left: 0;}.tree ul li {border-left: 2px solid #ddd;}.tree ul li:last-child {border-color: transparent;}.tree ul li::before {content: '';display: block;position: absolute;top: calc(var(--spacing) / -2);left: -2px;width: calc(var(--spacing) + 2px);height: calc(var(--spacing) + 1px);border: solid #ddd;border-width: 0 0 2px 2px;}.tree summary {display: block;cursor: pointer;}.tree summary::marker,.tree summary::-webkit-details-marker {display: none;}.tree summary:focus {outline: none;}.tree summary:focus-visible {outline: 1px dotted #000;}.tree summary::before {content: '';display: block;position: absolute;top: calc(var(--spacing) / 2 - var(--radius));left: calc(var(--spacing) - var(--radius) - 1px);width: calc(2 * var(--radius));height: calc(2 * var(--radius));border-radius: 50%;background: #ddd;}.tree li a::after {content: '';display: block;position: absolute;top: calc(var(--spacing) / 2 - var(--radius));left: calc(var(--spacing) - var(--radius) - 1px);width: calc(2 * var(--radius));height: calc(2 * var(--radius));border-radius: 50%;background: #ddd;}.tree .active a::after {content: 'z';z-index: 1;background: #696;color: #fff;line-height: calc(2 * var(--radius) - 2px);text-align: center;}.tree .active a{color: #0a67fb;}.tree summary::before {content: '+';z-index: 1;background: #696;color: #fff;line-height: calc(2 * var(--radius) - 2px);text-align: center;}.tree details[open] > summary::before {content: '−';}.tree ul li a {display: block;text-decoration: none;color: #222222;}.tree summary:hover{color: royalblue;}.tree a:hover{color: royalblue;}body{background: #fff;}.file-menu{background: #eee;}.source{/*background: royalblue;*/}.main{width: 100%;display: inline-flex;}.main .left{width: 30%;}.main .right{width: 70%;}.source{padding-left: 10px;}</style>
</head>
<body>
<div class="main"><div class="left"><div class="file-menu"><ul id="file-list" class="tree"></ul></div></div><div class="right"><div id="source" class="source"></div></div>
</div>
<script src="/static/js/net.js"></script>
<script>net = new Net();var data = "<?php echo $data; ?>";data = JSON.parse(atob(data));var tree = treeFile(data);initFileList(tree);function showSource(obj) {document.querySelectorAll('#file-list .active').forEach(function (active) {active.classList.remove('active');});obj.parentElement.classList.add('active');let post_data = {file:obj.dataset.file};net.post('showsource.php','data='+JSON.stringify(post_data),function (data) {data = JSON.parse(data);if(data.code!==200){document.getElementById("source").textContent=data.msg;return;}document.getElementById("source").innerHTML='<pre>'+data.data+'</pre>';});}function treeFile(data) {for (let i = 0; i < data.length; i++) {data[i].childnodes = [];data[i].list = [];for (let j = 0; j < data.length; j++) {if (data[j].pid === data[i].id) {switch (data[j].type) {case 'dir':data[i].childnodes.push(data[j]);break;case 'file':data[i].list.push(data[j]);break;}}}}return data[0];}function initFileList(data) {var ul = '';ul = getFileList(data, ul);document.getElementById("file-list").innerHTML = ul;document.querySelectorAll('#file-list a').forEach(function (link) {link.addEventListener('click', function (event) {event.preventDefault();showSource(link);});});}function getFileList(obj, ul) {ul += '<li>';ul += '<details>';ul += '<summary>' + obj.name + '</summary>';ul += '<ul>';if (!(obj.childnodes.length === 0)) {for (let key in obj.childnodes) {ul = getFileList(obj.childnodes[key], ul);}}if (!(obj.list.length === 0)) {for (let key in obj.list) {ul += '<li><a href="#" data-file="'+obj.list[key].handle+'">' + obj.list[key].name + '</a></li>';}}ul += '</ul>';ul += '</details>';ul += '</li>';return ul;}</script>
</body>
</html>
net.js
function Net() {this.xhr = new XMLHttpRequest();this.get = function (url, func, pram = {}) {xhr = this.xhr;xhr.onreadystatechange = function () {if (this.readyState == 4) {pram.code = this.status;if (this.status == 200) {func(this.responseText, pram);} else {func(this.responseText, pram);}}};xhr.open('get', url, false);xhr.send()};this.post = function (url, data, func,pram = {}) {xhr = this.xhr;xhr.onreadystatechange = function () {if (this.readyState == 4) {pram.code = this.status;if (this.status == 200) {func(this.responseText,pram);} else {func(this.responseText,pram);}}};xhr.open("POST", url, false);xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");xhr.send(data)}
}
showsource.php
<?php
header('Content-Type: text/html; charset=utf-8');if(empty($_POST['data'])){die;
}
$data = $_POST['data'];
$data = json_decode($data,true);
if(empty($data['file'])){die;
}define('root',str_replace('\\','/',realpath(__DIR__.'/../..')));
$dir = root;
$root_dir = $dir;$pass_files = ['root/app/conf.php',
];
$allow_ext = ['html','js','css','php','txt','xml','json',
];$file = $data['file'];if(in_array($file,$pass_files)){outSource(500,'涉及配置不允许高亮','');die;
}
if(strpos($file,'.')!==false){$file_ext = substr($file,strripos($file,'.')+1,strlen($file));if(!in_array($file_ext,$allow_ext)){outSource(500,'只允许高亮'.implode('|',$allow_ext).'后缀文件','');die;}
}$file = preg_replace('/^root/',$root_dir,$file);
if(!is_file($file)){outSource(500,'文件不存在','');die;
}$code = highlight_file($file,true);
outSource(200,'',$code);function outSource($code,$msg,$data)
{echo json_encode(['code'=>$code,'msg'=>$msg,'data'=>$data,],JSON_UNESCAPED_UNICODE);
}
注:配置好不允许高亮的文件名,安全第一