Regex mümkün define () içeriğini ayrıştırmak için?

5 Cevap php

Ben regex için çok yeni, ve bu yol benim için çok ileri düzeydedir. Yani ben buraya uzmanlar soruyorum.

Problem I would like to retrieve the constants / values from a php define()

DEFINE('TEXT', 'VALUE');

Temelde ben sürekli adını ve yukarıdaki hattan sabit değerini iade edebilmek için bir regex istiyorum. Sadece METİN ve DEĞER. Bu bile mümkün mü?

Neden ihtiyacınız? Ben dil dosyası ile uğraşıyorum ve tüm çiftler (name, value) almak ve dizideki onları koymak istiyorum. Ben str_replace ile bunu () ve () vb Döşeme başardı. Ama bu şekilde uzun ve ben bu regex tek hattı ile daha kolay yapılabilir eminim.

Not: DEĞER yanı kaçtı tek tırnak içerebilir. Örnek:

DEFINE('TEXT', 'J\'ai');

Ben çok karmaşık bir şey soran değilim umuyoruz. :)

Selamlar

5 Cevap

Gramer tabanlı ayrıştırma her türlü için, düzenli ifadeler genellikle korkunç bir çözümdür. (Aritmetik gibi) Hatta smple gramerler yuvalama var ve (özellikle) bu düzenli ifadeler sadece devrilmesine yuva üzerinde bulunuyor.

Neyse PHP token_get_all() function aracılığıyla PHP yorumlayıcı tarafından kullanılan aynı sözel çözümleyici erişim vererek sizin için çok, çok daha iyi bir çözüm sağlar. PHP kodu bir karakter akışı vermek ve bunu oldukça basit bir finite state machine ile basit ayrıştırma biraz yapabilirsiniz, hangi belirteçleri ("lexemeler") ayrıştırmak gerekir.

Bu programı (kendisi üzerinde dener yüzden test.php olarak çalıştırılır) çalıştırın. Eğer kolaylıkla o kolları görebilirsiniz böylece dosya kasıtlı kötü biçimlendirilir.

<?
    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);
}
?>

Çıktı:

'CONST1' => 'value'
'CONST2' => 'value2'
'CONST3' => 'time'
'define' => 'define'
'test' => 'VALUE4'
'const5' => 'weird declaration'
'CONST7' => '3.14'
'foo' => 'bar'
'CONST5' => '6'

Bu temelde bir örüntü arar bir sonlu durum makinesi:

function name ('define')
open parenthesis
constant
comma
constant
close parenthesis

Bir PHP kaynak dosya ve davranır bir (isim, değer) çifti olarak iki sabitler sözcük akışında. Bunu yaparken (sonuçlarına göre) iç içe (define) ifadeleri kolları ve boşlukları ve yorumlar yanı sıra birden fazla hatları üzerinden çalışan sayar.

Note: Ben deliberatley fonksiyonlar ve değişkenler sabit isimleri veya değerlerdir zaman davayı görmezden ama istediğiniz gibi o bunu uzatabilirsiniz yaptık.

Ayrıca bu dizeleri gelince PHP oldukça bağışlayıcı olduğuna işaret değer. Onlar hiç tırnak ile tek tırnak, çift tırnak ya da (bazı durumlarda) ile ilan edilebilir. Bu sabit belirsiz bir referans referans olması (bamya tarafından işaret olarak) olabilir ve size ait chocie veren (zaten hiçbir garanti yolu), hangi bilmenin bir yolu var:

  1. Dizeleri (T_STRING) bu tarz almamak;
  2. Sabit zaten bu adla ilan edilmiştir görmesini ve değer yerine. Eğer ne de bir şey kesinlikle ne değeri, ne de bir sabit olup olmadığını kesin olarak söyleyemem böylece koşullu oluşturulan herhangi tanımlar işleyebilir ama diğer dosyaları adı olmuştur bilmek hiçbir yolu yok; veya
  3. Sadece bu sabitler (muhtemel) ve sadece dizeleri gibi onları tedavi olabilir ihtimali ile yaşayabilir.

Şahsen ben (1) sonra gitmek istiyorum (3).

Bu mümkündür, ama ben bunun yerine kullanacağınız get_defined_constants(). Ama tüm çevirileri (T ile başlayan tüm çevirileri gibi) ortak bir şey var, bu yüzden diğer sabitler dışında bunları söyleyebilirim emin olun.

Metin ile değil her sorunun bir regexp ile çözülmesi gerektiğini, bu yüzden size ulaşmak istediğiniz devlet değil, nasıl öneririm.

Yani, yerine yararlı veya yerine tamamen undebuggable regexpi kullanarak değil gerçekten php ayrıştırıcı kullanarak, neden basit bir ayrıştırıcı yazmak değil mi?

<?php

$str = "define('nam\\'e', 'va\\\\\\'lue');\ndefine('na\\\\me2', 'value\\'2');\nDEFINE('a', 'b');";

function getDefined($str) {
    $lines = array();
    preg_match_all('#^define[(][ ]*(.*?)[ ]*[)];$#mi', $str, $lines);

    $res = array();
    foreach ($lines[1] as $cnt) {
    	$p = 0;
    	$key = parseString($cnt, $p);
    	// Skip comma
    	$p++;
    	// Skip space
    	while ($cnt{$p} == " ") {
    		$p++;
    	}
    	$value = parseString($cnt, $p);

    	$res[$key] = $value;
    }

    return $res;
}

function parseString($s, &$p) {
    $quotechar = $s[$p];
    if (! in_array($quotechar, array("'", '"'))) {
    	throw new Exception("Invalid quote character '" . $quotechar . "', input is " . var_export($s, true) . " @ " . $p);
    }

    $len = strlen($s);
    $quoted = false;
    $res = "";

    for ($p++;$p < $len;$p++) {
    	if ($quoted) {
    		$quoted = false;
    		$res .= $s{$p};
    	} else {
    		if ($s{$p} == "\\") {
    			$quoted = true;
    			continue;
    		}
    		if ($s{$p} == $quotechar) {
    			$p++;
    			return $res;
    		}
    		$res .= $s{$p};
    	}
    }

    throw new Exception("Premature end of line");
}

var_dump(getDefined($str));

Çıktı:

array(3) {
  ["nam'e"]=>
  string(7) "va\'lue"
  ["na\me2"]=>
  string(7) "value'2"
  ["a"]=>
  string(1) "b"
}

define aramaları bulmak için bu normal bir ifade deneyin:

 /\bdefine\(\s*("(?:[^"\\]+|\\(?:\\\\)*.)*"|'(?:[^'\\]+|\\(?:\\\\)*.)*')\s*,\s*("(?:[^"\\]+|\\(?:\\\\)*.)*"|'(?:[^'\\]+|\\(?:\\\\)*.)*')\s*\);/is

Yani:

$pattern = '/\\bdefine\\(\\s*("(?:[^"\\\\]+|\\\\(?:\\\\\\\\)*.)*"|\'(?:[^\'\\\\]+|\\\\(?:\\\\\\\\)*.)*\')\\s*,\\s*("(?:[^"\\\\]+|\\\\(?:\\\\\\\\)*.)*"|\'(?:[^\'\\\\]+|\\\\(?:\\\\\\\\)*.)*\')\\s*\\);/is';
$str = '<?php define(\'foo\', \'bar\'); define("define(\\\'foo\\\', \\\'bar\\\')", "define(\'foo\', \'bar\')"); ?>';
preg_match_all($pattern, $str, $matches, PREG_SET_ORDER);
var_dump($matches);

I eval kötü olduğunu biliyoruz. Ama bu dize ifadeleri değerlendirmek için en iyi yolu:

$constants = array();
foreach ($matches as $match) {
    eval('$constants['.$match[1].'] = '.$match[1].';');
}
var_dump($constants);

Sen regex karmaşıklığı ile denize gitmek gerek olmayabilir - Böyle bir şey muhtemelen yeterli olacaktır

 /DEFINE\('(.*?)',\s*'(.*)'\);/

İşte bunu kullanmak nasıl gösteren bir PHP örnek bulunuyor

$lines=file("myconstants.php");
foreach($lines as $line) {
    $matches=array();
    if (preg_match('/DEFINE\(\'(.*?)\',\s*\'(.*)\'\);/i', $line, $matches)) {
        $name=$matches[1];
        $value=$matches[2];

        echo "$name = $value\n";
    }

}