php 通过反射技术,把类中的方法转换为速查手册文档
<?php
// 文件:class2handbook.php
/**
* php 通过反射技术,把类中的方法转换为速查手册文档。
* 功能:遍历目录中的类文件,通过类的反射技术,把类的所有方法输出到速查手册文档文件
* 注意:一次只能同时处理一个目录,而且类的命名空间要相同
*/
// 类名
$className = 'CommonHelper';
// 类的命名空间
$nameSpace = 'MyApp\\Lib\\';
// 输出保存的文件
$toFilePath = './handbook_temp.md';
// 是否批量处理
$isBatch = true;
if ($isBatch) {
$files = fastScanDir('../../src/lib/');
foreach ($files as $filename => $fileinfo) {
$className = basename($filename, '.php');
include '../../src/lib/' . $className . '.php';
makeHandbook($className, $nameSpace, $toFilePath);
}
} else {
include '../../src/lib/' . $className . '.php';
makeHandbook($className, $nameSpace, $toFilePath);
}
/**
* 反射类对象并输出所有方法到速查手册文件
* @param string $className 类名
* @param string $nameSpace 命令空间
* @param string $toFilePath 输出的文件路径
* @return void
*/
function makeHandbook($className, $nameSpace, $toFilePath)
{
$docArr = [];
$objClass = new ReflectionClass($nameSpace . $className);
$docArr[] = $className . ' ' . parseComment($objClass->getDocComment()); // 获取类的注释文档
$docArr[] = '命名空间: ' . $objClass->getNamespaceName(); // 获取类的命名空间
$docArr[] = '~~~php';
$methods = $objClass->getMethods();
foreach ($methods as $objMethod) {
$docArr[] = '// ' . parseComment($objMethod->getDocComment()); // 获取方法的注释文档
$protected = '';
if ($objMethod->isPrivate()) {
$protected = 'private ';
}
if ($objMethod->isProtected()) {
$protected = 'protected ';
}
if ($objMethod->isPublic()) {
$protected = 'public ';
}
if ($objMethod->isStatic()) {
$docArr[] = $protected . $className . '::' . $objMethod->getName() . '(' . parseParameters($objMethod) . ')';
} else {
$docArr[] = $protected . '$instance->' . $objMethod->getName() . '(' . parseParameters($objMethod) . ')';
}
}
$docArr[] = '~~~';
$docArr[] = ''; // 换行
$docArr[] = ''; // 换行
// 自动创建目录
$toDir = dirname($toFilePath);
if (!is_dir($toDir)) {
mkdir($toDir);
}
$content = implode(PHP_EOL, $docArr);
file_put_contents($toFilePath, $content, FILE_APPEND);
echo $className, ' done.', PHP_EOL;
}
/**
* 解析注释文档,提取第一行的内容
* @param string $comment 注释,风格是多行注释(\/** ... *\/)
* @return string 返回第一行注释内容
*/
function parseComment($comment)
{
$comments = explode(PHP_EOL, $comment);
$comment = '';
if (isset($comments[1])) {
$comment = trim(trim(trim($comments[1]), '*'));
}
return $comment;
}
/**
* 解析类的方法的参数列表
* @param \ReflectionMethod 方法反射类
* @return string 返回方法参数的字符串
*/
function parseParameters($objMethod)
{
$paramsArr = [];
$objParams = $objMethod->getParameters();
foreach ($objParams as $objParam) {
$paramStr = '$' . $objParam->getName();
// $objParam->isDefaultValueConstant() // 默认值是否是枚举类型 method($opts=Foo::OPTION_MULTIGET);
// 是否有默认值
if ($objParam->isDefaultValueAvailable()) {
$defaultValue = $objParam->getDefaultValue();
switch (gettype($defaultValue)) {
case 'string':
$paramStr .= sprintf(" = '%s'", $defaultValue);
break;
case 'array':
$paramStr .= ' = ' . varExport($defaultValue);
break;
case 'NULL':
$paramStr .= ' = null';
break;
case 'boolean':
$paramStr .= ' = ' . var_export($defaultValue, true);
break;
default: // integer, float, object
$paramStr .= ' = ' . $defaultValue;
break;
}
}
$defaultValue = $objParam->isOptional();
$paramsArr[] = $paramStr;
}
return implode(', ', $paramsArr);
}
/**
* var_export() 方法的现代风格版
* 说明:原始版:array(), 现代版:[]
* @param mixed $var
* @param string $indent 缩进内容,默认是空格
* @param boolean $isCompact 是否紧凑模式,即数组是否换行
* @return string
*/
function varExport($var, $indent = '', $isCompact = true)
{
switch (gettype($var)) {
case "string":
return '\'' . addcslashes($var, "\\\$\"\r\n\t\v\f") . '\'';
case "array":
if (count($var) == 0) {
return '[]';
}
$newLine = $isCompact ? '' : "\n";
$itemSpace = $isCompact ? "$indent " : "$indent ";
$indexed = array_keys($var) === range(0, count($var) - 1);
$r = [];
foreach ($var as $key => $value) {
$r[] = $itemSpace . ($indexed ? "" : varExport($key) . " => ") . varExport($value, $itemSpace);
}
return "[$newLine" . implode(",$newLine", $r) . "$newLine" . $indent . "]";
case "boolean":
return $var ? "TRUE" : "FALSE";
default:
return var_export($var, true);
}
}
/**
* PHP 高效遍历文件夹(大量文件不会卡死,带文件名排序功能)
* @param string $path 目录路径
* @param integer $level 目录深度层级
* @param boolean $showfile 是否显示文件(否则只遍历显示目录)
* @param array $skips 要忽略的文件路径集合
* @param integer $deepth 扫描深度
*/
function fastScanDir($path = './', $level = 0, $showfile = true, $skips = array(), $deepth = 0)
{
if (!file_exists($path) || ($deepth && $level > $deepth)) {
return array();
}
$path = str_replace('//', '/', $path . '/');
$file = new \FilesystemIterator($path);
$filename = '';
$icon = ''; // 树形层级图形
if ($level > 0) {
$icon = ('|' . str_repeat('--', $level));
}
$outarr = array();
foreach ($file as $fileinfo) {
$filename = iconv('GBK', 'utf-8', $fileinfo->getFilename()); // 解决中文乱码
$filepath = $path . $filename;
if ($fileinfo->isDir()) {
if (!($skips && in_array($filepath . '/', $skips))) {
$outarr[$filename] = array('path' => $filepath, 'type' => 'dir', 'icon' => $icon);
$outarr[$filename]['children'] = fastScanDir($filepath, $level + 1, $showfile);
}
continue;
}
if ($showfile && !($skips && !in_array($filepath, $skips))) {
$outarr[$filename] = array('path' => $filepath, 'type' => 'file', 'icon' => $icon);
}
}
if ($outarr) {
ksort($outarr);
}
return $outarr;
}