千家信息网

php类库到sublime完成

发表于:2024-11-11 作者:千家信息网编辑
千家信息网最后更新 2024年11月11日,起因前一篇写了反射类生成php速查表。过年了在家也没事,想完成自己想要实现的sublime自动完成文件生成。就是sublime里输入关键字后按tab自动补全一个全的函数名和参数的,按tab可以切换一致
千家信息网最后更新 2024年11月11日php类库到sublime完成

起因

前一篇写了反射类生成php速查表。过年了在家也没事,想完成自己想要实现的sublime自动完成文件生成。就是sublime里输入关键字后按tab自动补全一个全的函数名和参数的,按tab可以切换一致到最后的。也是给别人的承诺,今年肯定会实现sublime的tp5插件的。

地址在 https://github.com/yangweijie/SublimeThinkPHP5.0

思路

搞清sublime 完成的写法

"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 文件里的。

completion 和snippet的区别在于 snippet只能一个文件一个补全。命令面版里有提示。

字符串

json 里直接字符串

tab占位

$0 完成的最后光标

tab 选中光标 ${1:XX} 1自增下标,: 选中的内容

trigger 触发器

表示什么文本末按tab会触发完成。

注意点

$ 换行之类的要转义

php如何获取

先上核心代码:

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框架定义的。

函数

// 获取定义的函数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,因为我想做的是所有文本编辑器的完成生成。先架构好结构,以后慢慢填坑。

先整体代码:

 $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吧。视图那没找到规律先手写的。

0