正则表达式部分提取php代码((数组定义))

Posted

技术标签:

【中文标题】正则表达式部分提取php代码((数组定义))【英文标题】:Regular Expression to extract php code partially (( array definition )) 【发布时间】:2013-06-11 15:59:31 【问题描述】:

我将 php 代码((数组定义))存储在这样的字符串中

$code=' array(

  0  => "a",
 "a" => $GlobalScopeVar,
 "b" => array("nested"=>array(1,2,3)),  
 "c" => function() use (&$VAR)  return isset($VAR) ? "defined" : "undefined" ; ,

); ';

有一个正则表达式来提取这个数组??,我的意思是我想要类似的东西

$array=(  

  0  => '"a"',
 'a' => '$GlobalScopeVar',
 'b' => 'array("nested"=>array(1,2,3))',
 'c' => 'function() use (&$VAR)  return isset($VAR) ? "defined" : "undefined" ; ',

);

pD :: 我正在研究试图找到一个正则表达式,但什么也没找到。 pD2 :: *** 之神,现在让我赏金,我将提供 400 :3 pD3 :: 这将在内部应用程序中使用,我需要提取一些 php 文件的数组以部分“处理”,我尝试用这个 codepad.org/td6LVVme 解释

【问题讨论】:

eval,但你没有从我这里听到。 $array = eval('return '.$code) 没错,我刚刚注意到多余的单引号。在这种情况下,您可能不得不引入大炮:NikiC's PHP parser。但我不得不问——你不能换一种方式吗? @iim.hlk 我可能正在准备一个惊喜 :-) 另外我也准备了一些代码(应该是“魔法防水”)。它甚至适用于 @HamZa 的大型正则表达式失败的地方。 事实上,目前我正在考虑提供多个赏金,因为你的答案和@HamZa 的答案真的很好:2 【参考方案1】:

这样做的干净方法显然是使用标记器(但请记住,单独的标记器并不能解决问题)。

对于挑战,我打算使用正则表达式方法。

这个想法不是描述 PHP 语法,而是更多地以否定的方式描述它(换句话说,我只描述了基本的和需要的 PHP 结构来获得结果)。这种基本描述的优点是处理比函数、字符串、整数或布尔值更复杂的对象。结果是一个更灵活的模式,例如可以处理多行/单行 cmets、heredoc/nowdoc 语法:

<pre><?php

$code=' array(
  0   => "a",
  "a" => $GlobalScopeVar,
  "b" => array("nested"=>array(1,2,3)),  
  "c" => function() use (&$VAR)  return isset($VAR) ? "defined" : "undefined" ; ,
); ';

$pattern = <<<'EOD'
~
# elements
(?(DEFINE)
    # comments
    (?<comMulti> /\* .*? (?:\*/|\z) )                                              # multiline comment
    (?<comInlin> (?://|\#) \N* $ )                                                 # inline comment
    (?<comments> \g<comMulti> | \g<comInlin> )

    # strings
    (?<strDQ> " (?>[^"\\]+|\\.)* ")                                                # double quote string
    (?<strSQ> ' (?>[^'\\]+|\\.)* ')                                                # single quote string
    (?<strHND> <<<(["']?)([a-zA-Z]\w*)\g-2 (?>\R \N*)*? \R \g-1 ;? (?=\R|$) )  # heredoc and nowdoc syntax
    (?<string> \g<strDQ> | \g<strSQ> | \g<strHND> )

    # brackets
    (?<braCrl>  (?> \g<nobracket> | \g<brackets> )*  )
    (?<braRnd> \( (?> \g<nobracket> | \g<brackets> )* \) )
    (?<braSqr> \[ (?> \g<nobracket> | \g<brackets> )* ] )
    (?<brackets> \g<braCrl> | \g<braRnd> | \g<braSqr> )

    # nobracket: content between brackets except other brackets
    (?<nobracket> (?> [^][)("'</\#]+ | \g<comments> | / | \g<string> | <+ )+ )

    # ignored elements
    (?<s> \s+ | \g<comments> )
)

# array components
(?(DEFINE)    
    # key
    (?<key> [0-9]+ | \g<string> )

    # value
    (?<value> (?> [^][)("'</\#,\s]+ | \g<s> | / | \g<string> | <+ | \g<brackets> )+? (?=\g<s>*[,)]) )
)
(?J)
(?: \G (?!\A)(?<!\)) | array \g<s>* \( ) \g<s>* \K

    (?: (?<key> \g<key> ) \g<s>* => \g<s>* )? (?<value> \g<value> ) \g<s>* (?:,|,?\g<s>*(?<stop> \) ))
~xsm
EOD;


if (preg_match_all($pattern, $code, $m, PREG_SET_ORDER)) 
    foreach($m as $v) 
        echo "\n<strong>Whole match:</strong> " . $v[0]
           . "\n<strong>Key</strong>:\t" . $v['key']
           . "\n<strong>Value</strong>:\t" . $v['value'] . "\n";
        if (isset($v['stop']))
            echo "\n<strong>done</strong>\n\n"; 

    

【讨论】:

嘿,兄弟,我对这个解决方案有一点问题,我认为只需要稍微修复一下,可以检查一下:ideone.com/FKrPxS,也许是空的($v['stop ']) 有条件的,感谢您的宝贵时间。【参考方案2】:

这是您所要求的,非常紧凑。 如果您需要任何调整,请告诉我。

代码(您可以直接在 php 中运行)

$code=' array(
  0  => "a",
 "a" => $GlobalScopeVar,
 "b" => array("nested"=>array(1,2,3)),  
 "c" => function() use (&$VAR)  return isset($VAR) ? "defined" : "undefined" ; ,

); ';

$regex = "~(?xm)
^[\s'\"]*([^'\"\s]+)['\"\s]*
=>\s*+
(.*?)\s*,?\s*$~";

if(preg_match_all($regex,$code,$matches,PREG_SET_ORDER)) 
    $array=array();
    foreach($matches as $match) 
        $array[$match[1]] = $match[2];
    

    echo "<pre>";
    print_r($array);
    echo "</pre>";

 // END IF

输出

Array
(
    [0] => "a"
    [a] => $GlobalScopeVar
    [b] => array("nested"=>array(1,2,3))
    [c] => function() use (&$VAR)  return isset($VAR) ? "defined" : "undefined" ; 
)

$array 包含你的数组。

你喜欢吗?

如果您有任何问题或需要调整,请告诉我。 :)

【讨论】:

嘿兄弟!直到现在我才看到这个答案,看起来真的很好,事实上我一直喜欢紧凑的代码((用几行做更多的事情)),目前我无法测试这个,但我非常感谢你的努力,谢谢 :) 【参考方案3】:

即使您要求使用正则表达式,它也适用于纯 PHP。 token_get_all 是这里的关键功能。对于正则表达式检查@HamZa's answer out。

这里的优点是它比正则表达式更具动态性。正则表达式具有静态模式,而使用 token_get_all,您可以在每个标记之后决定要做什么。它甚至在必要时转义单引号和反斜杠,这是正则表达式不会做的。

另外,在正则表达式中,即使在被评论时,你也很难想象它应该做什么;当您查看 PHP 代码时,代码的作用会更容易理解。

$code = ' array(

  0  => "a",
  "a" => $GlobalScopeVar,
  "b" => array("nested"=>array(1,2,3)),  
  "c" => function() use (&$VAR)  return isset($VAR) ? "defined" : "undefined" ; ,
  "string_literal",
  12345

); ';

$token = token_get_all("<?php ".$code);
$newcode = "";

$i = 0;
while (++$i < count($token))  // enter into array; then start.
        if (is_array($token[$i]))
                $newcode .= $token[$i][1];
        else
                $newcode .= $token[$i];

        if ($token[$i] == "(") 
                $ending = ")";
                break;
        
        if ($token[$i] == "[") 
                $ending = "]";
                break;
        


// init variables
$escape = 0;
$wait_for_non_whitespace = 0;
$parenthesis_count = 0;
$entry = "";

// main loop
while (++$i < count($token)) 
        // don't match commas in func($a, $b)
        if ($token[$i] == "(" || $token[$i] == "") // ( -> normal parenthesis;  -> closures
                $parenthesis_count++;
        if ($token[$i] == ")" || $token[$i] == "")
                $parenthesis_count--;

        // begin new string after T_DOUBLE_ARROW
        if (!$escape && $wait_for_non_whitespace && (!is_array($token[$i]) || $token[$i][0] != T_WHITESPACE)) 
                $escape = 1;
                $wait_for_non_whitespace = 0;
                $entry .= "'";
        

        // here is a T_DOUBLE_ARROW, there will be a string after this
        if (is_array($token[$i]) && $token[$i][0] == T_DOUBLE_ARROW && !$escape) 
                $wait_for_non_whitespace = 1;
        

        // entry ended: comma reached
        if (!$parenthesis_count && $token[$i] == "," || ($parenthesis_count == -1 && $token[$i] == ")" && $ending == ")") || ($ending == "]" && $token[$i] == "]")) 
                // go back to the first non-whitespace
                $whitespaces = "";
                if ($parenthesis_count == -1 || ($ending == "]" && $token[$i] == "]")) 
                        $cut_at = strlen($entry);
                        while ($cut_at && ord($entry[--$cut_at]) <= 0x20); // 0x20 == " "
                        $whitespaces = substr($entry, $cut_at + 1, strlen($entry));
                        $entry = substr($entry, 0, $cut_at + 1);
                

                // $escape == true means: there was somewhere a T_DOUBLE_ARROW
                if ($escape) 
                        $escape = 0;
                        $newcode .= $entry."'";
                 else 
                        $newcode .= "'".addcslashes($entry, "'\\")."'";
                

                $newcode .= $whitespaces.($parenthesis_count?")":(($ending == "]" && $token[$i] == "]")?"]":","));

                // reset
                $entry = "";
         else 
                // add actual token to $entry
                if (is_array($token[$i])) 
                        $addChar = $token[$i][1];
                 else 
                        $addChar = $token[$i];
                

                if ($entry == "" && $token[$i][0] == T_WHITESPACE) 
                        $newcode .= $addChar;
                 else 
                        $entry .= $escape?str_replace(array("'", "\\"), array("\\'", "\\\\"), $addChar):$addChar;
                
        


//append remaining chars like whitespaces or ;
$newcode .= $entry;

print $newcode;

演示地址:http://3v4l.org/qe4Q1

应该输出:

array(

  0  => '"a"',
  "a" => '$GlobalScopeVar',
  "b" => 'array("nested"=>array(1,2,3))',  
  "c" => 'function() use (&$VAR)  return isset($VAR) ? "defined" : "undefined" ; ',
  '"string_literal"',
  '12345'

) 

您可以,要获取数组的数据,print_r(eval("return $newcode;")); 来获取数组的条目:

Array
(
    [0] => "a"
    [a] => $GlobalScopeVar
    [b] => array("nested"=>array(1,2,3))
    [c] => function() use (&$VAR)  return isset($VAR) ? "defined" : "undefined" ; 
    [1] => "string_literal"
    [2] => 12345
)

【讨论】:

嘿!!,我喜欢你在这里做的魔法*,我不在乎这是否不是 RegEx 解决方案,因为它确实有效,事实上,喜欢你超越所需解决方案的方式((RegEx))并提供我需要的东西......我测试了它,显然没有错误*,所以,恭喜!您为这段美丽的代码奖励了 +250。 ((是的,你应该等一下,因为我要等 23 小时才能先给 Hamza +250 分)) @iim.hlk 不,魔术,只是处理由 token_get_all() 返回的数组;-P 我会等待赏金;-) 我正在尝试开始一个 250 的新兔子来“支付给你属于你的东西”,但只给了我 500 分的选择:c @iim.hlk 你不知道吗,如果你在同一个问题上给予另一个赏金,你必须加倍赏金? @iim.hlk 现在:你会做什么?是否给予赏金(我真的写了这段代码来获得赏金:|)?【参考方案4】:

正则表达式

这是我想出的 MEGA 正则表达式:

\s*                                     # white spaces
########################## KEYS START ##########################
(?:                                     # We\'ll use this to make keys optional
(?P<keys>                               # named group: keys
\d+                                     # match digits
|                                       # or
"(?(?=\\\\")..|[^"])*"                  # match string between "", works even 4 escaped ones "hello \" world"
|                                       # or
\'(?(?=\\\\\')..|[^\'])*\'              # match string between \'\', same as above :p
|                                       # or
\$\w+(?:\[(?:[^[\]]|(?R))*\])*          # match variables $_POST, $var, $var["foo"], $var["foo"]["bar"], $foo[$bar["fail"]]
)                                       # close group: keys
########################## KEYS END ##########################
\s*                                     # white spaces
=>                                      # match =>
)?                                      # make keys optional
\s*                                     # white spaces
########################## VALUES START ##########################
(?P<values>                             # named group: values
\d+                                     # match digits
|                                       # or
"(?(?=\\\\")..|[^"])*"                  # match string between "", works even 4 escaped ones "hello \" world"
|                                       # or
\'(?(?=\\\\\')..|[^\'])*\'              # match string between \'\', same as above :p
|                                       # or
\$\w+(?:\[(?:[^[\]]|(?R))*\])*          # match variables $_POST, $var, $var["foo"], $var["foo"]["bar"], $foo[$bar["fail"]]
|                                       # or
array\s*\((?:[^()]|(?R))*\)             # match an array()
|                                       # or
\[(?:[^[\]]|(?R))*\]                    # match an array, new PHP array syntax: [1, 3, 5] is the same as array(1,3,5)
|                                       # or
(?:function\s+)?\w+\s*                  # match functions: helloWorld, function name
(?:\((?:[^()]|(?R))*\))                 # match function parameters (wut), (), (array(1,2,4))
(?:(?:\s*use\s*\((?:[^()]|(?R))*\)\s*)? # match use(&$var), use($foo, $bar) (optionally)
\(?:[^]|(?R))*\                     # match  whatever
)?;?                                    # match ; (optionally)
)                                       # close group: values
########################## VALUES END ##########################
\s*                                     # white spaces

我放了一些 cmets,注意你需要使用 3 个修饰符:x:让我做 cmets s :用点匹配换行符 i : 不区分大小写

PHP

$code='array(0  => "a", 123 => 123, $_POST["hello"][\'world\'] => array("is", "actually", "An array !"), 1234, \'got problem ?\', 
 "a" => $GlobalScopeVar, $test_further => function test($noway)echo "this works too !!!";, "yellow" => "blue",
 "b" => array("nested"=>array(1,2,3), "nested"=>array(1,2,3),"nested"=>array(1,2,3)), "c" => function() use (&$VAR)  return isset($VAR) ? "defined" : "undefined" ; 
  "bug", "fixed", "mwahahahaa" => "Yeaaaah"
);'; // Sample data

$code = preg_replace('#(^\s*array\s*\(\s*)|(\s*\)\s*;?\s*$)#s', '', $code); // Just to get ride of array( at the beginning, and ); at the end

preg_match_all('~
\s*                                     # white spaces
########################## KEYS START ##########################
(?:                                     # We\'ll use this to make keys optional
(?P<keys>                               # named group: keys
\d+                                     # match digits
|                                       # or
"(?(?=\\\\")..|[^"])*"                  # match string between "", works even 4 escaped ones "hello \" world"
|                                       # or
\'(?(?=\\\\\')..|[^\'])*\'              # match string between \'\', same as above :p
|                                       # or
\$\w+(?:\[(?:[^[\]]|(?R))*\])*          # match variables $_POST, $var, $var["foo"], $var["foo"]["bar"], $foo[$bar["fail"]]
)                                       # close group: keys
########################## KEYS END ##########################
\s*                                     # white spaces
=>                                      # match =>
)?                                      # make keys optional
\s*                                     # white spaces
########################## VALUES START ##########################
(?P<values>                             # named group: values
\d+                                     # match digits
|                                       # or
"(?(?=\\\\")..|[^"])*"                  # match string between "", works even 4 escaped ones "hello \" world"
|                                       # or
\'(?(?=\\\\\')..|[^\'])*\'              # match string between \'\', same as above :p
|                                       # or
\$\w+(?:\[(?:[^[\]]|(?R))*\])*          # match variables $_POST, $var, $var["foo"], $var["foo"]["bar"], $foo[$bar["fail"]]
|                                       # or
array\s*\((?:[^()]|(?R))*\)             # match an array()
|                                       # or
\[(?:[^[\]]|(?R))*\]                    # match an array, new PHP array syntax: [1, 3, 5] is the same as array(1,3,5)
|                                       # or
(?:function\s+)?\w+\s*                  # match functions: helloWorld, function name
(?:\((?:[^()]|(?R))*\))                 # match function parameters (wut), (), (array(1,2,4))
(?:(?:\s*use\s*\((?:[^()]|(?R))*\)\s*)? # match use(&$var), use($foo, $bar) (optionally)
\(?:[^]|(?R))*\                     # match  whatever
)?;?                                    # match ; (optionally)
)                                       # close group: values
########################## VALUES END ##########################
\s*                                     # white spaces
~xsi', $code, $m); // Matching :p

print_r($m['keys']); // Print keys
print_r($m['values']); // Print values


// Since some keys may be empty in case you didn't specify them in the array, let's fill them up !
foreach($m['keys'] as $index => &$key)
    if($key === '')
        $key = 'made_up_index_'.$index;
    

$results = array_combine($m['keys'], $m['values']);
print_r($results); // printing results

输出

Array
(
    [0] => 0
    [1] => 123
    [2] => $_POST["hello"]['world']
    [3] => 
    [4] => 
    [5] => "a"
    [6] => $test_further
    [7] => "yellow"
    [8] => "b"
    [9] => "c"
    [10] => 
    [11] => 
    [12] => "mwahahahaa"
    [13] => "this is"
)
Array
(
    [0] => "a"
    [1] => 123
    [2] => array("is", "actually", "An array !")
    [3] => 1234
    [4] => 'got problem ?'
    [5] => $GlobalScopeVar
    [6] => function test($noway)echo "this works too !!!";
    [7] => "blue"
    [8] => array("nested"=>array(1,2,3), "nested"=>array(1,2,3),"nested"=>array(1,2,3))
    [9] => function() use (&$VAR)  return isset($VAR) ? "defined" : "undefined" ; 
    [10] => "bug"
    [11] => "fixed"
    [12] => "Yeaaaah"
    [13] => "a test"
)
Array
(
    [0] => "a"
    [123] => 123
    [$_POST["hello"]['world']] => array("is", "actually", "An array !")
    [made_up_index_3] => 1234
    [made_up_index_4] => 'got problem ?'
    ["a"] => $GlobalScopeVar
    [$test_further] => function test($noway)echo "this works too !!!";
    ["yellow"] => "blue"
    ["b"] => array("nested"=>array(1,2,3), "nested"=>array(1,2,3),"nested"=>array(1,2,3))
    ["c"] => function() use (&$VAR)  return isset($VAR) ? "defined" : "undefined" ; 
    [made_up_index_10] => "bug"
    [made_up_index_11] => "fixed"
    ["mwahahahaa"] => "Yeaaaah"
    ["this is"] => "a test"
)

Online regex demo Online php demo

已知错误(已修复)

    $code='array("aaa", "sdsd" => "dsdsd");'; // fail
    $code='array(\'aaa\', \'sdsd\' => "dsdsd");'; // fail
    $code='array("aaa", \'sdsd\' => "dsdsd");'; // succeed
    // Which means, if a value with no keys is followed
    // by key => value and they are using the same quotation
    // then it will fail (first value gets merged with the key)

Online bug demo

学分

转到Bart Kiers 以获取他的recursive pattern 以匹配嵌套括号。

建议

您可能应该使用解析器,因为正则表达式很敏感。 @bwoebi 在他的 answer 中做得很好。

【讨论】:

@bwoebi 解析不出来,我提供了一个扩展的例子。 我对它进行了彻底的测试,它应该可以工作,如果你发现了一个错误,请不要犹豫。 @bwoebi 有意与否,正则表达式是关于匹配某些模式。如果我没有写它来匹配$"variable"(例如),那么它就不会匹配。基本上我尽力匹配所有可能的案例,但由于我不是高级php编码器,我忘记了一些案例(我什至不知道)。 @HamZa 查看所有可能的语言:lxr.php.net/xref/PHP_TRUNK/Zend/zend_language_parser.y#282 @HamZa 你的努力值得至少 250,我知道这个 RegEx 可以改进,但目前只适用于许多情况。 ((我也会给bwoebi 250))【参考方案5】:

仅针对这种情况:

$code=' array(

  0=>"a",
  "a"=>$GlobalScopeVar,
  "b"=>array("nested"=>array(1,2,3)),  
  "c"=>function() use (&$VAR)  return isset($VAR) ? "defined" : "undefined" ; ,

); ';

preg_match_all('#\s*(.*?)\s*=>\s*(.*?)\s*,?\s*$#m', $code, $m);
$array = array_combine($m[1], $m[2]);
print_r($array);

输出:

Array
(
    [0] => "a"
    ["a"] => $GlobalScopeVar
    ["b"] => array("nested"=>array(1,2,3))
    ["c"] => function() use (&$VAR)  return isset($VAR) ? "defined" : "undefined" ; 
)

【讨论】:

你为什么说“只是为了这种情况”??,这个正则表达式不能提取任何类型的结构正确的数组?? @iim.hlk 对于this 之类的东西会失败。基本上每个“元素”都需要换行才能成功。 天哪!,听起来很脏,但我可以提供 400 个代表来获得那个神奇的防水无敌正则表达式...想想看,拥有 +6000 个代表你会抓住所有的女士((偶尔一个 JB )) :$ @iim.hlk 抱歉,正则表达式不知道“防水”,你需要一个解析器。

以上是关于正则表达式部分提取php代码((数组定义))的主要内容,如果未能解决你的问题,请参考以下文章

PHP正则表达式取文本中间内容。

PHP数组——数组正则表达式数组预定义数组

从字符串正则表达式 PHP 中提取数值 [关闭]

PHP数组(正则表达式数组预定义数组)

10.23上午 PHP数组(数组正则表达式数组预定义数组)

PHP 正则表达式匹配 img ,PHP 正则提取或替换图片 img 标记中的任意属性。