设为首页 收藏本站
查看: 1628|回复: 0

[经验分享] php类库速查表升级

[复制链接]

尚未签到

发表于 2018-12-13 10:58:54 | 显示全部楼层 |阅读模式
历史回顾
我感叹于laravel的生态完好,tp5的类api的缺失(以前thinkphp3时代,帮助公司用apigen NetBeans里生成过一个3.1版本的api)。
DSC0000.png
在我走之后,估计没人用NetBeans了 所以就没人做升级。
然后我觉得laravel的速查表不错,见下图:
DSC0001.png
于是就想到了把他移植到tp5上,作为自己回到thinkphp5开发上的第一个贡献。
记得那个时候是清明前开始动手的。使用最土的,静态页面,一个个类手动去编辑。
后来发现类太多了,我每次在sublime 里 先打开一个类,复制出来临时文件,处理好,粘贴到静态index.html里,太麻烦了,要处理左侧的导航,又要定位插入的位置。 于是乎在五一放假时 我换了个思路,我不搞静态了,我搞动态的。
于是在海豚里建了一个插件,
DSC0002.png
DSC0003.png
DSC0004.png
DSC0005.png
以章节的形式添加。终于效率上去了,五一时候抽空把内容全补全了。
发布到了 掘金上,获得了17个赞。
DSC0006.png
进击的速查表
本来以为完成了。结果老大们太积极7月4日又发布了5.0.10,然后在开发5.1dev版。
既然老大把我的速查表都挂到了tp官网上。我也不能让用的人等,看旧的5.0.7版、
可是我不想在人工去对比改了哪个类,增加还是删除了哪些方法。20多章呢。
我想 “代码的问题应该由代码解决”。于是想到了php的注解、反射类。
正好项目用到了一个 crada/php-apidoc 的api文档生成工具。
于是我就开始动手实现这通过类反射信息获取方法的工程。
反射的资料
PHP 5 具有完整的反射 API,添加了对类、接口、函数、方法和扩展进行反向工程的能力。 此外,反射 API 提供了方法来取出函数、类和方法中的文档注释。
官网手册提到了反射有这多类:
Reflection
ReflectionClass
ReflectionZendExtension
ReflectionExtension
ReflectionFunction
ReflectionFunctionAbstract
ReflectionMethod
ReflectionObject
ReflectionParameter
ReflectionProperty
ReflectionType
ReflectionGenerator
Reflector
ReflectionException
实现思路
// TODO 获取待处理的类命名空间数据// TODO 遍历类 反射// TODO 获取类的信息 (名称、方法列表)// TODO 遍历方法列表, 获取方法的类型 和注释我看了一遍反射是先反射类 再找到方法 再找到方法的参数
类的获取
tp5的核心类 都在一个目录
于是我想到了用glob遍历
public function get_core_class(){
$class_path = CORE_PATH;
$before_cwd = getcwd();
chdir($class_path);
$names = glob('*.php');
$ret = [];foreach ($names as $key => $name) {
$ret[] = 'think\\'. str_ireplace('.php', '', $name);
}
chdir($before_cwd);return $ret;
}反射类的初始化要传的是类的实际命名空间路径。
$class= new\ReflectionClass($className); 这样。然后有以下方法:
ReflectionClass::__construct — 初始化 ReflectionClass 类
ReflectionClass::export — 导出一个类
ReflectionClass::getConstant — 获取定义过的一个常量
ReflectionClass::getConstants — 获取一组常量
ReflectionClass::getConstructor — 获取类的构造函数
ReflectionClass::getDefaultProperties — 获取默认属性
ReflectionClass::getDocComment — 获取文档注释
ReflectionClass::getEndLine — 获取最后一行的行数
ReflectionClass::getExtension — 根据已定义的类获取所在扩展的 ReflectionExtension 对象
ReflectionClass::getExtensionName — 获取定义的类所在的扩展的名称
ReflectionClass::getFileName — 获取定义类的文件名
ReflectionClass::getInterfaceNames — 获取接口(interface)名称
ReflectionClass::getInterfaces — 获取接口
ReflectionClass::getMethod — 获取一个类方法的 ReflectionMethod。
ReflectionClass::getMethods — 获取方法的数组
ReflectionClass::getModifiers — 获取类的修饰符
ReflectionClass::getName — 获取类名
ReflectionClass::getNamespaceName — 获取命名空间的名称
ReflectionClass::getParentClass — 获取父类
ReflectionClass::getProperties — 获取一组属性
ReflectionClass::getProperty — 获取类的一个属性的 ReflectionProperty
ReflectionClass::getShortName — 获取短名
ReflectionClass::getStartLine — 获取起始行号
ReflectionClass::getStaticProperties — 获取静态(static)属性
ReflectionClass::getStaticPropertyValue — 获取静态(static)属性的值
ReflectionClass::getTraitAliases — 返回 trait 别名的一个数组
ReflectionClass::getTraitNames — 返回这个类所使用 traits 的名称的数组
ReflectionClass::getTraits — 返回这个类所使用的 traits 数组
ReflectionClass::hasConstant — 检查常量是否已经定义
ReflectionClass::hasMethod — 检查方法是否已定义
ReflectionClass::hasProperty — 检查属性是否已定义
ReflectionClass::implementsInterface — 接口的实现
ReflectionClass::inNamespace — 检查是否位于命名空间中
ReflectionClass::isAbstract — 检查类是否是抽象类(abstract)
ReflectionClass::isAnonymous — 检查类是否是匿名类
ReflectionClass::isCloneable — 返回了一个类是否可复制
ReflectionClass::isFinal — 检查类是否声明为 final
ReflectionClass::isInstance — 检查类的实例
ReflectionClass::isInstantiable — 检查类是否可实例化
ReflectionClass::isInterface — 检查类是否是一个接口(interface)
ReflectionClass::isInternal — 检查类是否由扩展或核心在内部定义
ReflectionClass::isIterateable — 检查是否可迭代(iterateable)
ReflectionClass::isSubclassOf — 检查是否为一个子类
ReflectionClass::isTrait — 返回了是否为一个 trait
ReflectionClass::isUserDefined — 检查是否由用户定义的
ReflectionClass::newInstance — 从指定的参数创建一个新的类实例
ReflectionClass::newInstanceArgs — 从给出的参数创建一个新的类实例。
ReflectionClass::newInstanceWithoutConstructor — 创建一个新的类实例而不调用它的构造函数
ReflectionClass::setStaticPropertyValue — 设置静态属性的值
ReflectionClass::__toString — 返回 ReflectionClass 对象字符串的表示形式。
这些方法看着是静态,其实可以 $class->getShortName() 直接使用。
我主要需要拿到类简写名和方法。
public function generate($classNames){
config('default_return_type', 'json');// TODO 获取待处理的类命名空间数据
// TODO 遍历类 反射
// TODO 获取类的信息 (名称、方法列表)
// TODO 遍历方法列表, 获取方法的类型 和注释
$outputs = [];foreach ($classNames as $k => $className) {
$class= new\ReflectionClass($className);
$key = $class->getShortName();// dump($key);
$outputs[$key] = $this->getClassAnnotation($class);
}return $outputs;
}getClassAnonation 方法就是我用来获取类的全部方法的信息的方法。
方法相关
拿到反射类之后 想获取反射方法,得实例化 ReflectionMethod 类。
// 获取类的注释信息
public function getClassAnnotation($class){
$ret = ['hasPublicMethods'=>0,
];
$ret['name'] = $class->getName();
$methods = $class->getMethods();foreach ($methods as $key => $method) {
$class = $method->class;
$method_name = $method->name;
$rm = new \ReflectionMethod($class, $method_name);// 忽略构造和析构
if($rm->isConstructor() || $rm->isDestructor()){continue;
}
$foo = [];
$foo['docComment'] = $rm->getDocComment();
$foo['docComment_formated'] = $this->parseMethodDoc($foo['docComment']);
$foo['args'] = $rm->getParameters();
$foo['args_formated'] = $this->parseParameters($class, $method_name, $foo['args']);if($rm->isPublic()){
$type = $rm->isStatic()? 'public_static' : 'public_public';
}else{
$type = $rm->isStatic()? 'private_static' : 'private_public';
}// 只在hasPublicMethods 为0时更新值,保证设置1后不会被复写为0
if(empty($ret['hasPublicMethods'])){
$ret['hasPublicMethods'] = stripos($type, '_public') !== false;
}
$foo['type'] = $type;
$ret['methods'][$method_name] = $foo;
}return $ret;// $className = 'think\\App';
// $class = new \ReflectionClass($className);
// config('default_return_type', 'json');
// 类名
// return $class->name;
// ReflectionClass 实例的一个字符串表示形式
// return $class->__toString();
// 同上
// return \ReflectionClass::export($className, 1);
// 获取类常量
// return json_encode($class->getConstants());
// 获取构造方法
// return $class->getConstructor();
// 类名相关
// var_dump($class->inNamespace());
// var_dump($class->getName());
// var_dump($class->getNamespaceName());
// var_dump($class->getShortName());
# 文件相关
// getFileName
// getExtensionName
// 属性相关
// return $class->getDefaultProperties();
// return $class->getProperties(\ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED);
// const integer IS_STATIC = 1 ;
// const integer IS_PUBLIC = 256 ;
// const integer IS_PROTECTED = 512 ;
// const integer IS_PRIVATE = 1024 ;
// return $class->getStaticProperties();
// 类注释
// return $class->getDocComment();
}先获取全部的类方法:
$class->getMethods();
官网的例子:
array(3) {
  [0]=>
  &object(ReflectionMethod)#2 (2) {
    ["name"]=>    string(11) "firstMethod"
    ["class"]=>    string(5) "Apple"
  }
  [1]=>
  &object(ReflectionMethod)#3 (2) {
    ["name"]=>    string(12) "secondMethod"
    ["class"]=>    string(5) "Apple"
  }
  [2]=>
  &object(ReflectionMethod)#4 (2) {
    ["name"]=>    string(11) "thirdMethod"
    ["class"]=>    string(5) "Apple"
  }
}在获取时还可以传属性类型进行过滤:
拿到方法后,我们需要获得类的方法的公有私有、静态等属性。
DSC0007.png
因为我在显示时做了方法不同类型的区分演示。
$rm = new \ReflectionMethod($class, $method_name);// 忽略构造和析构if($rm->isConstructor() || $rm->isDestructor()){continue;
}先过滤掉构造和析构方法。
$foo = [];$foo['docComment'] = $rm->getDocComment();$foo['docComment_formated'] = $this->parseMethodDoc($foo['docComment']);$foo['args'] = $rm->getParameters();$foo['args_formated'] = $this->parseParameters($class, $method_name, $foo['args']);我先获取了原有方法的文档信息和参数信息,并且按照我需要的进行格式化。
获取参数的要注意,返回的是参数数组
官方示例:
主要咱获取到参数名称 然后结合methodName 去实例化 ReflectionParameter 来获取参数信息
类的参数信息
public function parseParameters($class, $method, $args){if($args){
$args_str = [];foreach ($args as $key => $arg) {
$p = new \ReflectionParameter(array($class, $method), $key);// 判断是否引用参数
if($p->isPassedByReference()){
$arg_str_new = "&\$".$p->getName();
}else{
$arg_str_new = "\$".$p->getName();
}if ($p->isOptional() && $p->isDefaultValueAvailable()) {
$a_clsss = $class;// 获取某些内部类的参数会抛异常,且异常时$class会变化不是我们想知道的哪个类方法一场了
try{
$defaul = $p->getDefaultValue();
$arg_str_new .= is_array($defaul) ? ' = '. '[]': ' = '. var_export($defaul, 1);
}catch(\Exception $e){
trace($p->isVariadic());
trace($a_clsss.'/'.$method.'_'.$key);
}
}
$args_str[] = $arg_str_new;
}return implode(', ', $args_str);
}return '';
}有的方法没参数直接返回空,有的有参数,咱们想拼接处 参数字符串 比如

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-650874-1-1.html 上篇帖子: 经典开源PHP项目 下篇帖子: php 编译小错误
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表