Регулярное выражение для частичного извлечения php-кода ((определение массива))

У меня есть 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 :: боги stackoverflow, позвольте мне наполнить это сейчас, и я буду предлагать 400: 3
pD3 :: это будет использоваться во внутреннем приложении, где мне нужно извлечь массив из некоторого php-файла, который будет «обработан» по частям, я попробую объяснить с этим codepad.org/td6LVVme

Даже когда вы запрашивали регулярное выражение, он работает также с чистым PHP. token_get_all здесь ключевая функция. Для проверки регулярного выражения @ ответа HamZa .

Преимущество здесь в том, что оно более динамично, чем регулярное выражение. Регулярное выражение имеет статический шаблон, а с помощью token_get_all вы можете решить после каждого отдельного токена, что делать. Он даже избегает одиночных кавычек и обратных косых черт, когда это необходимо, что не будет делать регулярное выражение.

Кроме того, в regex у вас есть, даже когда прокомментировано, проблемы, чтобы представить, что он должен делать; что делает код намного легче понять, когда вы смотрите на 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

Итак, вот регулярное выражение 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 

Я добавил несколько замечаний, отметим, что вам нужно использовать 3 модификатора:
x : давайте сделаем комментарии 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" ) 

Онлайн-демо-версия regex Онлайн-демо-версия

Известная ошибка (исправлена)

  $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) 

Онлайн-демонстрация ошибок

кредиты

Идет к Барту Кирсу для его рекурсивного шаблона, чтобы соответствовать вложенным скобкам.

Совет

Возможно, вам стоит пойти с парсером, поскольку регулярные выражения чувствительны. @bwoebi проделал большую работу в своем ответе .

Чистый способ сделать это, очевидно, использовать токенизатор (но имейте в виду, что только токенизатор не решает проблему).

Для задачи я использую регулярный подход.

Идея состоит не в том, чтобы описать синтаксис PHP, а в том, чтобы описать его отрицательным образом (другими словами, я описываю только основные и необходимые структуры PHP для получения результата) . Преимущество этого базового описания состоит в том, чтобы иметь дело с более сложными объектами, чем с функциями, строками, целыми числами или булевыми. Результатом является более гибкий шаблон, который может иметь дело, например, с комментариями нескольких / одиночных строк, синтаксисом 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"; } } , <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"; } } 

Вот что вы просили, очень компактный. Пожалуйста, дайте мне знать, хотите ли вы какие-нибудь хитрости.

КОД (вы можете запустить это прямо в 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 содержит ваш массив.

Тебе нравится?

Пожалуйста, дайте мне знать, если у вас есть какие-либо вопросы или вы нуждаетесь в настройках. 🙂

Только для этой ситуации:

 $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" ; } )