PHP 里用 Tokenizer 实现更好的 highlight_string


作者:郑凯

一个能有这么多用途的模块 Tokenizer 被我无视到现在,直到最近才醒过味来

比方说 PHP 代码高亮,一直用的 highlight_string,可实际上这是一个非常粗糙的函数,只能区分四种颜色:default、string、keyword、comment,而用了 Tokenizer,(虽然没必要,但是)如果你愿意的话,可以标记出一百多种颜色

简单的代码实现如下:

tokenizer.php

效果图片,左边是 PHP 页面,右边是我编辑器

highlight

简单的说下过程:

token_get_all 把整个文件转成大数组后,每个 item 要么是基本符号(如 ;= 之类的),要么是数组,下标 0 1 2 分别是 token id、原字符串、原始行号,其中 token id 可以用 token_name() 转回实体名,我将其显示为 <span class="实体名">原字符串</span>,以交给 CSS 来控制

其中需要注意一点,我认为 HTML 最合适的行号显示方式是 <ol><li> 标签,我见过一个 JS + CSS 的高亮控件,其行号是文本的,也就是说如果你直接鼠标划一段拷贝的话,会连行号一起拷上,如果不想这样就必须点最上面的 <div> 画的拷贝按钮,虽然最终可以不拷贝行号了,但方式实在是别扭(就像 Flash 里没法右键一样恼人)。但基于 <li> 的话就有个问题,需要你自己来处理转出来的 token 中的换行问题,需要写成两个 <li> 而不是简单的在 <li> 中加 <br />

我的最终效果可以保证,当你在浏览器里看我那个 PHP 生成的 HTML 高亮代码的话,你直接 Ctrl+ACtrl+C,得到的文本跟源代码是完全一致的,包括缩进。

代码只是个简单 demo,因为 token name 还需要自己来重新归类,例如我们在编辑器里设定高亮的时候只需要说,关键字是什么颜色,函数是什么颜色,而在这里你需要自己来找出哪些是关键字,你会在我的代码的结尾看到巨长的 CSS 选择。又或者如果需要区分内置函数和自定义函数,那肯定需要字典了。


关于缩进是另外一个话题,我认识的绝大部分人都是用 4 个空格代替缩进,这是让我非常疑惑的问题,除非按照 Pear 里那种傻逼的缩进规则外(如果 array( 后有换行,则下一行不是比前一行多一个缩进,而是一直多到 array 的第一个字母 a 的位置,我见过由于过长的命名导致一个仅二维的数组就有五十多个空格做缩进),其他情况下缩进所代表的空格数无论怎么设都是一致的。用空格取代真正的缩进有点像用一张图片来取代 <hr>——你本来可以在 CSS(编辑器)里设定它的表现的。当然,这个问题只有硬性规定,没有讨论结果的。就像 Linux 要求 tab,而 Python 建议4空格,可他们都很有影响力

我在代码里是额外对缩进加了层 <span class="tab">,并定义样式 font-size 是正常字体的一半,以控制它看起来像 4 个空格


其实是看到老王说到 代码审计 时才想起 Tokenizer

另外,如果 phpDoc 能及时改用 Tokenizer 而不是自己解析字符串的话,就算更新缓慢,至少也能向后兼容 PHP 5.3 的代码了(只是新语法忽略,至少不会异常中止)