2017-12-15 5 views
2

Ich stoße derzeit auf ein Problem mit Regex unter bestimmten Umständen: Ich muss PHP-Quelldateien (insbesondere Klasse) analysieren, um nach Konstanten zu suchen, die in diesen Dateien definiert sind um sie wieder zur Ausgabe zu bringen.Wie man Lookbehind innerhalb einer Regex mit nicht fester Breite emuliert

Diese Konstanten können eine Dokumentation haben (und deshalb habe ich die Idee von Reflection verlassen, da das Abrufen von Konstanten über Reflection nur ihren Namen und ihren Wert zurückgibt), die innerhalb von Kommentarmarken verschickt werden können.

Ich habe es geschafft, die beiden separaten Teile der Regex zu erstellen (1 ist das Kommentar-Tag, das andere ist die const Deklaration), aber ich kann nicht beide erfolgreich verbinden: Es scheint, dass die erste Konstante innerhalb Die Datei enthält auch alle zuvor deklarierten Elemente, bis sie den allerersten Kommentarblock erreicht.

Meine regex ist wie folgt (ich bin kein regex Gott so fühlen sich frei, jede Kritik zu bringen):

((\t\)*(/\*+(.|\n)*\*/)\R+)?([\t| ]*(?|(public|protected|private)\s*)?const\s+([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*=\s*(.*);) 

Es geht die Probe-Test: Regex101

Falls der Anfangscode verschwindet :

/** 
* 
*/ 
class Test { 

    /** 
    * 
    */ 
    public const LOL = "damn"; 

    /** 
    * 
    */ 
    private const TEST = 5; 

    public const plop = "dong"; 
} 

ich für Tipps gibt und es sah, und ich habe über positive Lookbehind gelernt, aber von dem, was ich verstand, es funktioniert nur mit fester Breite Muster.

Ich habe keine Ideen mehr.

+0

* „(und das ist, warum ich die Idee der Reflexion verlassen, da nur Konstanten über Reflection Abrufen gibt ihren Namen und ihren Wert)“ * - Ab PHP 7.1.0, die Klasse ['ReflectionClassConstant'] (http://php.net/manual/en/class.reflectionclassconstant.php) ist verfügbar, und Sie können den Dokumentkommentar mit [ReflectionClassConstant :: getDocComment()' abrufen ] (http://php.net/manual/en/reflectionclassconstant.getdoccomment.php). ([kleines Beispiel] (https://3v4l.org/riCgp)) – salathe

+0

Hallo @salathe! Vielen Dank für Ihren Kommentar ! Gute Nachricht, aber was ich vergessen habe zu sagen war, dass die Dev-Umgebung dort auf 5.4 (ja, das ist scheiße) – Cr3aHal0

+0

Oh und @salathe Ich habe dies vergessen, aber abrufen Wert der Konstante über Reflection * kann konstante Deklaration und Struktur ändern (doppelt zitiert string mit geflüchtetem double quote zum Beispiel) deshalb habe ich es auf den ersten Blick nicht benutzt – Cr3aHal0

Antwort

1

ich einen mehrstufigen Ansatz bevorzugen würde: jede Klasse getrennt, dann suchen Sie nach Kommentaren (schließlich) und für die Konstanten.In Bezug auf die regex kann dies über

class\h*(?P<classname>\w+)[^{}]* # look for class literally and capture the name 
(\{ 
    (?:[^{}]*|(?2))*    # the whole block matches the class content 
\}) 

Siehe a demo on regex101.com erreicht werden.


nun auf die Kommentare und Konstanten

^\h* 
(?:(?P<comment>\Q/*\E(?s:.*?)\Q*/\E)(?s:.*?))? 
(?:public|private)\h*const\h* 
(?P<key>\w+)\h*=\h*(?P<value>[^;]+) 

a demo for this step on regex101.com auch sehen.


Der letzte Schritt wäre die Kommentare zu reinigen:

^\h*/?\*+\h*/? 

für die cleansing on regex101.com eine Demo sehen.


Schließlich werden Sie zwei Schleifen benötigen:

preg_match_all($regex_class, $source, $matches, PREG_SET_ORDER); 

foreach ($matches as $match) { 
    preg_match_all($const_class, $match[0], $constants, PREG_SET_ORDER); 
    foreach ($constants as $constant) { 
     $comment = preg_replace($clean_comment, '', $constant["comment"]); 

     # find the actual values here 
     echo "Class: {$match["classname"]}, Constant Name: {$constant["key"]}, Constant Value: {$constant["value"]}, Comment: $comment\n"; 
    } 
} 

Ein overall demo can be found on ideone.com.
Beachten Sie die einzelnen Regex-Modifikatoren im Demo und im Quellcode (speziell verbose und multiline!).


Sie können es in einem Array auch tun:

$result = []; 
preg_match_all($regex_class, $source, $matches, PREG_SET_ORDER); 

foreach ($matches as $match) { 
    preg_match_all($const_class, $match[0], $constants, PREG_SET_ORDER); 
    foreach ($constants as $constant) { 
     $comment = trim(preg_replace($clean_comment, '', $constant["comment"])); 
     $result[$match["classname"]][] = array('name' => $constant["key"], 'value' => $constant['value'], 'comment' => $comment); 
    } 
} 

print_r($result); 
+1

Hey @ Jan! Vielen Dank für deine Antwort, das wäre nicht etwas, zu dem ich sofort gesprungen wäre, aber ich kann nicht leugnen, dass es gut funktioniert! Ich weiß nicht, ob ich das hier machen werde, aber das ist eine sehr coole und vollständige Antwort, vielen Dank! – Cr3aHal0

+0

@ Cr3aHal0: Ich bin froh zu helfen. – Jan

1

Sie können es ohne positive Lookbehind tun: Sie haben einen Kommentar entsprechen, unmittelbar gefolgt von einer const Deklaration gefolgt:

(?:(?:^/\*\*$\s+)((?:^ ?\*.*$\s*?)+)(?:\s+^\*/$\s+))?^\s+(public|protected|private) const (\S+)\s+= ([^;]+);

Die erste Gruppe, die Sie in der Dokumentation abrufen können:

  • Kommentar Teil
    • (?:^/\*\*$\s+) Funde der Anfang eines Blocks Kommentar
    • ((?:^ ?\*.*$\s*?)+) die Gruppe den Inhalt Ihrer Kommentare
    • (?:\s+^\*/$\s+) Ende des Kommentars
  • Deklarationsteil enthält:
    • ^\s+ überspringen die Leerzeichen am Anfang die Zeile
    • (public|protected|private) const eine Gruppe zur Ermittlung der Sichtbarkeit
    • (\S+)\s+= ([^;]+); Gruppen für den Namen und den Wert
+0

Hey @Faibbus, vielen Dank für die Antwort! Auch wenn es nicht für jede Art von Kommentar passt (vor allem Zeilenkommentare, die von einer IDE generiert werden können), bietet dies einen guten Start und funktioniert bei wichtigen Fällen, also nochmals vielen Dank! – Cr3aHal0

+0

@ Cr3aHal0: danke für das Feedback. Ich wollte die meisten Kommentardekorationen entfernen, aber ich nehme an, dass Sie nur einen Blockkommentar oder einen einzeiligen Kommentar einfügen können. – Faibbus