php類庫速查表升級
歷史回顧
我感嘆於laravel的生態完好,tp5的類api的缺失(以前thinkphp3時代,幫助公司用apigen NetBeans裡生成過一個3.1版本的api)。
在我走之後,估計沒人用NetBeans了 所以就沒人做升級。
然後我覺得laravel的速查表不錯,見下圖:
於是就想到了把他移植到tp5上,作為自己回到thinkphp5開發上的第一個貢獻。
記得那個時候是清明前開始動手的。使用最土的,靜態頁面,一個個類手動去編輯。
後來發現類太多了,我每次在sublime 裡 先開啟一個類,複製出來臨時檔案,處理好,貼上到靜態index.html裡,太麻煩了,要處理左側的導航,又要定位插入的位置。 於是乎在五一放假時 我換了個思路,我不搞靜態了,我搞動態的。
於是在海豚裡建了一個外掛,
以章節的形式新增。終於效率上去了,五一時候抽空把內容全補全了。
釋出到了 掘金上,獲得了17個贊。
進擊的速查表
本來以為完成了。結果老大們太積極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= newReflectionClass($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= newReflectionClass($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" }}
在獲取時還可以傳屬性型別進行過濾:
getMethods(ReflectionMethod::IS_STATIC | ReflectionMethod::IS_FINAL);var_dump($methods);?>?phpclass>
拿到方法後,我們需要獲得類的方法的公有私有、靜態等屬性。
因為我在顯示時做了方法不同型別的區分演示。
$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']);
我先獲取了原有方法的文件資訊和引數資訊,並且按照我需要的進行格式化。
獲取引數的要注意,返回的是引數陣列
官方示例:
getParameters() AS $arg) { if($_REQUEST[$arg->name]) $fire_args[$arg->name]=$_REQUEST[$arg->name]; else $fire_args[$arg->name]=null; } return call_user_func_array(array($class, $method), $fire_args);}?>?phppublic>
主要我們獲取到引數名稱 然後結合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 ''; }
有的方法沒引數直接返回空,有的有引數,我們們想拼接處 引數字串 比如
php不會返回
foo($a)
這串的。得自己處理主要留意引數的 是否有預設值、是否引用
?phpclass>
getName
isOptional
isDefaultValueAvailable
getDefaultValue
在獲取預設值引數的時候 我發現有的時候會拋異常,而且都報的是內部類的。
所以一定要try catch 去獲取預設值。
至此 整個獲取某些類的 方法及方法註釋和引數資訊 的功能全部實現。
大家如果想獲取某一組class的 速查表 只需 修改get_core_class返回值,只要是tp5裡自動載入的類。都可以解析。
整體思路,根據類陣列獲取方法->方法文件、獲取引數->獲取引數資訊
這樣,我就可以安心的偷懶了。下次tp升級,我直接composer update一下,重新整理一下首頁,拿到靜態html 更新到我的 gh-pages 分支。就完成了新框架速寫表的更新。
一勞永逸啊。
尾聲
具體原始碼參考 :
檢視速查表 直接:
歡迎給我提供建議。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4729/viewspace-2805145/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- PHP版本升級:從php7.1升級到php7.2PHP
- 升級php7.3PHP
- rac 升級crs 升級資料庫軟體,升級資料庫資料庫
- 使用webtatic源升級phpWebPHP
- Homestead 升級PHP版本PHP
- Mac brew 升級 PHP版本MacPHP
- 升級mac的PHP版本MacPHP
- yum升級php到5.3PHP
- Java類反射再次升級Java反射
- Mac 升級 PHP 到 7.4 版本MacPHP
- php5.6 升級至7.2.7PHP
- php5.3升級指令碼PHP指令碼
- 資料庫升級之-Dataguard滾動升級資料庫
- 生產庫升級:oracle 9.2.0.1升級oracle 9.2.0.8Oracle
- 資料庫升級資料庫
- ♀♀資料庫升級♀♀資料庫
- LNMP架構php升級問題及解決方法 php-5.3.10升級到php-5.4.0薦LNMP架構PHP
- Centos 7升級 PHP7 到 PHP8CentOSPHP
- centos下升級php5.3到php5.6CentOSPHP
- Ubuntu PHP7.0 升級到 7.1UbuntuPHP
- Oracle 資料庫升級Oracle資料庫
- [PHP 安全] OWASP 維護的 PHP 安全配置速查表PHP
- oracle資料庫升級11.2.0.3升級到11.2.0.4Oracle資料庫
- MacOS下PHP7.1升級到PHP7.4.15MacPHP
- 升級 PHP7 過程記錄PHP
- Tumblr的PHP 7升級經驗分享PHP
- 卡巴斯基提供升級包 解決病毒庫升級
- 常用的PHP類庫—PHP開發者必備PHP
- 資料庫升級和工具資料庫
- 資料庫升級之-XTTS資料庫TTS
- 資料庫升級報錯資料庫
- Centos5.6簡單方法升級php到php5.3.3CentOSPHP
- 【PHP升級】CentOS6.3編譯安裝 PHP5.4.38PHPCentOS編譯
- 升級 PHP 7.4 帶來的兩個大坑PHP
- Linux平滑編譯升級php至5.5.0Linux編譯PHP
- linux伺服器PHP升級到7Linux伺服器PHP
- 靜默方式安裝、升級oracle(三): 升級資料庫軟體及資料庫Oracle資料庫
- android資料庫如何進行版本升級?架構之資料庫框架升級Android資料庫架構框架