前一篇寫了反射類生成php速查表。過年了在家也沒事,想完成本身想要實現的sublime自動完成文件生成。就是sublime裏輸入關鍵字後按tab自動補全一個全的函數名和參數的,按tab能夠切換一致到最後的。也是給別人的承諾,今年確定會實現sublime的tp5插件的。
地址在 https://github.com/yangweijie/SublimeThinkPHP5.0php
"HTML_FILE_SUFFIX", "TEMPLATE_NAME", { "trigger": "_ad", "contents": "protected function _after_delete(\\$data,\\$options) {\n ${1:}\n}$0" }, { "trigger": "_af", "contents": "protected function _after_find(&\\$result,\\$options) {\n ${1:}\n}$0" }, { "trigger": "_ai", "contents": "protected function _after_insert(\\$data,\\$options) {\n ${1:}\n}$0" }, { "trigger": "_as", "contents": "protected function _after_select(&\\$result,\\$options){\n ${1:foreach(\\$result as &\\$record)\\{\n ${2:\\$this->_after_find(\\$record,\\$options);}\n \\}}\n}$0" }, { "trigger": "_au", "contents": "protected function _after_update(\\$data,\\$options) {\n ${1:}\n}$0" }, { "trigger": "_bi", "contents": "protected function _before_insert(&\\$data,\\$options) {\n ${1:}\n}$0" }, { "trigger": "_bu", "contents": "protected function _before_update(&\\$data,\\$options) {\n ${1:}\n}$0" }, { "trigger": "->_empty", "contents": "\\$this->_empty()$0" }, { "trigger": "_get", "contents": "_get('${1:\\$name}')$0" }, { "trigger": "_post", "contents": "_post('${1:\\$name}')$0" }, { "trigger": "->_sql", "contents": "->_sql('${1:\\$name}')$0" }, { "trigger": "->addAll", "contents": "->addAll(\\$${1:dataList},\\$${2:options},\\$${3:replace})$0" },
這個是寫在Sublime的ThinkPHP 插件裏 php.sublime-completions 文件裏的。git
completion 和snippet的區別在於 snippet只能一個文件一個補全。命令面版裏有提示。github
json 裏直接字符串sql
$0
完成的最後光標thinkphp
tab 選中光標 ${1:XX}
1自增下標,: 選中的內容json
表示什麼文本末按tab會觸發完成。數組
$ 換行之類的要轉義swoole
先上核心代碼:架構
<?php namespace util; class SnippetBuilder { public $cme; public function __construct($classes) { // if (false == defined('THINK_VERSION')) { // throw new \Exception('請在thinkphp應用中執行本類', 1); // } $this->cme = new ClassMethodExtractor(); // $this->framework_type = version_compare(THINK_VERSION, '5') >= 0 ? 5 : 3; $this->classes = $classes; } public function buildAll($path) { $consts = $this->getConsts(); $functions = $this->getFunctions(); $classes = $this->classes; $this->ret = []; if ($consts) { $this->buildConsts($consts); } if ($functions) { $this->buildFunction($functions); } if ($classes) { foreach ($classes as $class) { $class2 = $this->cme->getClassAnnotation(new \ReflectionClass($class)); $this->buildClass($class2); } } if ($this->ret) { file_put_contents($path, $this->parseAll($this->ret)); } else { exit('沒有可生成的內容'); } } // 獲取常量數組 public function getConsts() { $all_consts = get_defined_constants(true); return array_keys($all_consts['user']); } // 生成常量完成 public function buildConsts($consts) { foreach ($consts as $key => &$const) { $this->ret[] = $const; } } // 生成類的完成 public function buildClass($classes) { } // 獲取定義的函數 public function getFunctions() { $arr = get_defined_functions(); $ret = []; foreach ($arr['user'] as $key => $name) { $foo = []; $refFunc = new \ReflectionFunction($name); $foo['args'] = $refFunc->getParameters(); $ret[$name] = $foo; } return $ret; } // 生成函數 public function buildFunction($functions) { } }
// 獲取常量數組 public function getConsts() { $all_consts = get_defined_constants(true); return array_keys($all_consts['user']); }
php有函數,且會區分core user 和擴展的。咱們只須要user定義,也就是tp框架定義的。app
// 獲取定義的函數 public function getFunctions() { $arr = get_defined_functions(); $ret = []; foreach ($arr['user'] as $key => $name) { $foo = []; $refFunc = new \ReflectionFunction($name); $foo['args'] = $refFunc->getParameters(); $ret[$name] = $foo; } return $ret; }
php也有個獲取所有函數名的方法。一樣只拿user
而後新的函數反射類 new \ReflectionFunction($name);
獲取它的參數 $refFunc->getParameters();
$all_class = get_declared_classes();
這回沒有user好事了,不過能夠反射類isInternal區分。
// 獲取所有用戶定義的類 public function getAllUserClasses(){ $all_class = get_declared_classes(); $ret = []; foreach ($all_class as $class) { $rf = new \ReflectionClass($class); if(false == $rf->isInternal()){ if('app\index\controller\Index' == $class){ continue; } $ret[] = $class; } } return $ret; }
if ($classes) { foreach ($classes as $class) { $class2 = $this->cme->getClassAnnotation(new \ReflectionClass($class)); $this->buildClass($class2); } }
用cme類提取出類和對應方法及參數。
我寫了 SublimeSnippetBuilder來繼承我以前的 SnippetBuilder,由於我想作的是全部文本編輯器的完成生成。先架構好結構,之後慢慢填坑。
先總體代碼:
<?php namespace util; class SublimeSnippetBuilder extends SnippetBuilder { // 生成函數 public function buildFunction($functions) { foreach ($functions as $name => $fun) { $args_arr = [$name, '(', ')']; if ($fun['args']) { $args_arr = [$name, '(']; $index = 1; foreach ($fun['args'] as $key => $arg) { $p = new \ReflectionParameter($name, $key); if ($p->isPassedByReference()) { $arg_str_new = '\&\\$' . $p->getName(); } else { $arg_str_new = '\\$' . $p->getName(); } if ($p->isOptional() && $p->isDefaultValueAvailable()) { // 獲取某些內部類的參數會拋異常,且異常時$class會變化不是咱們想知道的哪一個類方法一場了 try { $defaul = $p->getDefaultValue(); $arg_str_new .= is_array($defaul) ? ' = []' : ' = ' . var_export($defaul, 1); } catch (\Exception $e) { trace($p->isVariadic()); trace($name . '_' . $key); } } if ($index == 1) { $p_str = sprintf('${%d:%s}', $index, $arg_str_new); } else { $p_str = sprintf('${%d: ,%s}', $index, $arg_str_new); } $args_arr[] = $p_str; $index++; } $args_arr[] = ')'; } $contens = implode('', $args_arr) . '$0'; $foo = [ 'trigger' => $name, 'contents' => $contens, ]; $this->ret[] = $foo; } } public function buildClass($class) { if($class['methods']){ foreach ($class['methods'] as $name => $fun) { switch ($fun['type']) { case 'public_static': case 'private_static': $trigger_name = "::{$name}"; break; case 'public_public': case 'private_public': $trigger_name = "->{$name}"; break; default: $trigger_name = ''; break; } $args_arr = [$trigger_name, '(', ')']; if (empty($fun['args']) == false) { $args_arr = [$trigger_name, '(']; $index = 1; foreach ($fun['args'] as $key => $p) { if ($p->isPassedByReference()) { $arg_str_new = '\&\\$' . $p->getName(); } else { $arg_str_new = '\\$' . $p->getName(); } if ($p->isOptional() && $p->isDefaultValueAvailable()) { // 獲取某些內部類的參數會拋異常,且異常時$class會變化不是咱們想知道的哪一個類方法一場了 try { $defaul = $p->getDefaultValue(); $arg_str_new .= is_array($defaul) ? ' = []' : ' = ' . var_export($defaul, 1); } catch (\Exception $e) { trace($p->isVariadic()); trace($name . '_' . $key); } } if ($index == 1) { $p_str = sprintf('${%d:%s}', $index, $arg_str_new); } else { $p_str = sprintf('${%d: ,%s}', $index, $arg_str_new); } $args_arr[] = $p_str; $index++; } $args_arr[] = ')'; } $contens = implode('', $args_arr) . '$0'; $foo = [ 'trigger' => $trigger_name, 'contents' => $contens, ]; $this->ret[] = $foo; } } } public function parseAll($ret) { // dump($ret); $ret = [ "scope" => "source.php - variable.other.php", "completions" => $ret, ]; return json_encode($ret, JSON_PRETTY_PRINT); } }
其實 就是生成對應的多維數組,數字索引爲常量,函數方法爲多維數組。
函數的trigger就是函數名,類無非::
、->
後跟方法名
而後區分代參不帶參,帶參再遍歷參數,區分引用和默認值的狀況。用了sprintf 方法拼接${index:content} 這種格式。
最終生成json,用了JSON_PRETTY_PRINT 格式化輸出後寫文件。
總體思路仍是很清晰的。
其餘語言的能夠借鑑。
效果:
一會兒兩千行,手寫得累死。
其實,你們只需擴展getAllUserClasses 就能夠生成其餘類的,好比swoole的。
題外話,新插件提交wbond 開始用branch 結果不推薦了要tag。我折騰了幾回才經過ci。人家不高興了。不給我過,有能力的直接git clone吧。視圖那沒找到規律先手寫的。