我是正则表达式的新手,这对我来说太先进了.所以我在这里问专家.
问题 我想从php define()中检索常量/值
DEFINE('TEXT', 'VALUE');
基本上我想要一个正则表达式能够返回常量的名称,并从上面的行返回常量的值.只是文字和价值.这甚至可能吗?
为什么我需要它?我正在处理语言文件,我希望得到所有的夫妻(名字,价值)并把它们放在数组中.我设法用str_replace()和trim()等来完成它.但是这种方式很长,我相信使用单行正则表达式可以使它变得更容易.
注意:VALUE也可能包含转义单引号.例:
DEFINE('TEXT', 'J\'ai');
我希望我不要求太复杂的事情.:)
问候
对于任何类型的基于语法的解析,正则表达式通常是一个糟糕的解决方案.即使是大量的语法(比如算术)也有嵌套,而且正在嵌套(特别是)正则表达式才会失败.
幸运的是,PHP通过token_get_all()函数访问PHP解释器使用的相同词法分析器,为您提供了更好的解决方案.给它一个PHP代码的字符流,它将它解析为令牌("lexemes"),你可以用一个非常简单的有限状态机做一些简单的解析.
运行这个程序(它作为test.php运行,所以它自己尝试).该文件故意格式错误,所以你可以看到它轻松处理.
define('CONST1', 'value' ); define (CONST2, 'value2'); define( 'CONST3', time()); define('define', 'define'); define("test", VALUE4); define('const5', // 'weird declaration' ) ; define('CONST7', 3.14); define ( /* comment */ 'foo', 'bar'); $defn = 'blah'; define($defn, 'foo'); define( 'CONST4', define('CONST5', 6)); header('Content-Type: text/plain'); $defines = array(); $state = 0; $key = ''; $value = ''; $file = file_get_contents('test.php'); $tokens = token_get_all($file); $token = reset($tokens); while ($token) { // dump($state, $token); if (is_array($token)) { if ($token[0] == T_WHITESPACE || $token[0] == T_COMMENT || $token[0] == T_DOC_COMMENT) { // do nothing } else if ($token[0] == T_STRING && strtolower($token[1]) == 'define') { $state = 1; } else if ($state == 2 && is_constant($token[0])) { $key = $token[1]; $state = 3; } else if ($state == 4 && is_constant($token[0])) { $value = $token[1]; $state = 5; } } else { $symbol = trim($token); if ($symbol == '(' && $state == 1) { $state = 2; } else if ($symbol == ',' && $state == 3) { $state = 4; } else if ($symbol == ')' && $state == 5) { $defines[strip($key)] = strip($value); $state = 0; } } $token = next($tokens); } foreach ($defines as $k => $v) { echo "'$k' => '$v'\n"; } function is_constant($token) { return $token == T_CONSTANT_ENCAPSED_STRING || $token == T_STRING || $token == T_LNUMBER || $token == T_DNUMBER; } function dump($state, $token) { if (is_array($token)) { echo "$state: " . token_name($token[0]) . " [$token[1]] on line $token[2]\n"; } else { echo "$state: Symbol '$token'\n"; } } function strip($value) { return preg_replace('!^([\'"])(.*)\1$!', '$2', $value); } ?>
输出:
'CONST1' => 'value' 'CONST2' => 'value2' 'CONST3' => 'time' 'define' => 'define' 'test' => 'VALUE4' 'const5' => 'weird declaration' 'CONST7' => '3.14' 'foo' => 'bar' 'CONST5' => '6'
这基本上是一个查找模式的有限状态机:
function name ('define') open parenthesis constant comma constant close parenthesis
在PHP源文件的词汇流中,将两个常量视为(名称,值)对.这样做它处理嵌套的define()语句(根据结果)并忽略空格和注释以及跨多行工作.
注意:我的deliberatley使它忽略了函数和变量是常量名称或值的情况,但你可以根据需要将它扩展到它.
值得指出的是,PHP在字符串方面非常宽容.它们可以用单引号,双引号或(在某些情况下)声明,完全没有引号.这可以(正如Gumbo所指出的那样)是一个对常量的模糊参考引用,你无法知道它是什么(无论如何都没有保证),给你一个chocie:
忽略那种字符串样式(T_STRING);
查看是否已使用该名称声明常量并替换它的值.你无法知道其他文件被调用了什么,也无法处理有条件创建的任何定义,所以你不能肯定地说任何事物肯定是不变的,也不是它有什么价值; 要么
你可以忍受这些可能是常量(这是不可能的)的可能性,并将它们视为字符串.
我个人会去(1)然后(3).