PHP Regulären Ausdruck anpassen

BartTheDevil89

Devilution Media
ID: 87739
L
2 Mai 2006
3.960
103
Hallo,

bin gerade dabei mich etwas mit nem mini bbcode - System zu befassen. Dabei bin ich jetzt allerdings auf ein Problem getroffen. Denn ich habe folgenden Aufbau:

https://www.domain.de
[domain]https://www.domain.de[/domain]
[domain=https://www.domain.de]Testbegriff[/domain]

Also diese 3 Links können auftreten. Daraus möchte ich jetzt folgendes machen können:

[domain]https://www.domain.de[/domain]
[domain]https://www.domain.de[/domain]
[domain=https://www.domain.de]Testbegriff[/domain]

Wie löse ich das am besten? Soweit bin ich schon bekommen:

PHP:
$pattern = "/(((http[s]?:\/\/)|(www\.))?(([a-z][-a-z0-9]+\.)?[a-z][-a-z0-9]+\.[a-z]+(\.[a-z]{2,2})?)\/?[a-z0-9._\/~#&=;%+?-]+[a-z0-9\/#=?]{1,1})/is";
$string = preg_replace($pattern, " [domain]$1[/domain]", $string);

Damit erkenne ich jetzt also schon alle Domains, die auftreten. Damit macht er mir aber derzeit jetzt folgendes Bild:

[domain]https://www.domain.de[/domain]
[domain][domain]https://www.domain.de[/domain][/domain]
[domain=[domain]https://www.domain.de[/domain]]Testbegriff[/domain]

Demnach muss ich also ja den regulären Ausdruck um folgendes erweitern:

( Nicht [domain] ODER Nicht [domain= ) (Hier dann jetzt mein jetziger regulärer Ausdruck) (Hier Nicht [/domain])

Müsste ich mit den 3 Teilen doch so hinbekommen, dass er mir das einfach umwandelt, oder? Bzw. wie setz ich das am sinnvollsten jetzt da oben um? Mein Versuch war dann folgender:

PHP:
$pattern = "/((([domain])|([domain=))?((http[s]?:\/\/)|(www\.))?(([a-z][-a-z0-9]+\.)?[a-z][-a-z0-9]+\.[a-z]+(\.[a-z]{2,2})?)\/?[a-z0-9._\/~#&=;%+?-]+[a-z0-9\/#=?]{1,1})/is";
$string = preg_replace($pattern, " [domain]$1[/domain]", $string);

Allerdings ist das ja nur teilweise vollständig bzw. nicht ganz fertig, da ich hier nicht weiterkomme. :(

Hoffe ihr könnt vielleicht helfen.
 
Ich weiss nicht, ob's das performanteste ist, aber dafür dürfte der negative Look Ahead/Behind passend sein. Vereinfachte Darstellung:
Code:
/[COLOR="DarkRed"](?<!\[domain(?:=.*?)?\])[/COLOR]<url_regexp>[COLOR="Green"](?!\[\/domain\])[/COLOR]/
Untested, aber sollte zumindest die Richtung vorgeben.
 
Ne andere Idee:
Code:
/[COLOR="Green"](.*?)[/COLOR]<url_regexp>[COLOR="Green"](.*?)[/COLOR]/
Wenn man jetzt preg_replace_callback() nimmt, kann man in der Funktion auswerten, was zu tun is.

Ich bin mir jetzt spontan nur nicht sicher, ob das funktioniert, wenn mehrere URL-Ausdrücke vorkommen :think: Evtl. müsste mans dann noch etwas anpassen.
 
(.*?) würde in dieser Form durch auf eine leere Menge reduziert werden, wenn mich nicht alles täuscht. Immerhin ist es der kleinste passende Treffer.

Wenn ich grad drüber nachdenke, ist so ein strikt RegExp-basierter Parser vermutlich eh nicht das Nonplusultra. Momentan wird ja ausgeschlossen, dass URLs in [domain]-Tags nicht erneut ersetzt werden.
Code:
[domain][tag]https://www.example.de[/tag][/domain]
Dieser Code hingegen würde wieder zu Problemen führen...
 
Wenn ich grad drüber nachdenke, ist so ein strikt RegExp-basierter Parser vermutlich eh nicht das Nonplusultra. Momentan wird ja ausgeschlossen, dass URLs in [domain]-Tags nicht erneut ersetzt werden.
Code:
[domain][tag]https://www.example.de[/tag][/domain]
Dieser Code hingegen würde wieder zu Problemen führen...

Gut solche Probleme werde ich wahrscheinlich nie ganz vermeiden können. Mein letzter Versuch war jetzt folgender:

$pattern = "/(?<!\[domain(?:=.*?)?\])/((([a-z][-a-z0-9]+\.)?[a-z][-a-z0-9]+\.[a-z]+(\.[a-z]{2,2})?)\/?[a-z0-9._\/~#&=;%+?-]+[a-z0-9\/#=?]{1,1})/is(?!\[\/domain\])/";

Da schein ich aber irgendwo was verdreht zu haben, denn unkown Modifier "(".

Aber mal zum Grundproblem, vielleicht fällt auch jemandem was ein:

Ich hab ein normales Eingabefeld, was Code (ähnlich wie BBCode) zulässt. Dort kann entweder sowas reinkommen mit Markierung:

Test mit [domain]https://www.domain.de[/domain] oder auch so nem [domain=https://www.domain.de]Link[/domain]

Variante 2 ist dann ohne Markierung:
Test mit https://www.domain.de bzw. www.domain.de

Variante 3 ist dann mit teilweiser Markierung:
Test mit [domain]https://www.domain.de[/domain] bzw. www.domain.de oder https://www.domain.de

Ziel ist eben am Ende technisch es immer zu schaffen, dass die Dinger markiert sind.
 
Du hast bei dem RegExp den hinteren Teil falsch reinkopiert. Guck mal nach /is/, dort endet der eigentlich RegExp und nach den Modifieren kommt dann halt noch fälschlicherweise der Look Ahead.

Bringt aber eh nix, wie mir grad aufgefallen ist. Die URL im Parameter des [domain]-Tags würde trotzdem noch ersetzt werden und wenn mich nicht alles täuscht, müssen Look Aheads/Behinds auch eine feste Länge haben, was ich vorhin nicht bedacht hatte.

Dann bleibt wohl nur folgende Methode:

Du ersetzst alle gültigen [domain]-Tags durch entsprechende Platzhalter, machst dann den URL-Erkennungsschritt und ersetzt anschliessend die Platzhalter wieder durch die eigentlichen Tags.
 
(.*?) würde in dieser Form durch auf eine leere Menge reduziert werden, wenn mich nicht alles täuscht. Immerhin ist es der kleinste passende Treffer.
Stimmt. Meine Überlegung vorhin war, ungierig machen, sonst frisst der Punkt evtl. andere Domains mit bzw. matcht dann eben von Anfang bis Ende komplett.
 
Nee, mit [domain]-Tags meinte ich alle Varianten davon.

Mal als ungetesteten Beispielcode:
PHP:
$replaces = array();

preg_match_all('~\[domain(?:=.*?)?\].*?\[/domain\]~', $string, $matches);
foreach ($matches[0] as $match)
{
    $replacement = '##domain:'.md5(uniqid('domain', true)).'##';
    $replaces[$replacement] = $match;
}
$string = str_replace(array_values($replaces), array_keys($replaces), $string);

// URL-Ersetzungsschritt

$string = str_replace(array_keys($replaces), array_values($replaces), $string);
 
Zuletzt bearbeitet:
Nee, mit [domain]-Tags meinte ich alle Varianten davon.

Mal als ungetesteten Beispielcode:
PHP:
$replaces = array();

preg_match_all('~\[domain(?:=.*?)?\].*?\[/domain\]~', $string, $matches);
foreach ($matches as $match)
{
    $replacement = '##domain:'.md5(uniqid('domain', true)).'##';
    $replaces[$replacement] = $match[0];
}
$string = str_replace(array_values($replaces), array_keys($replaces), $string);

// URL-Ersetzungsschritt

$string = str_replace(array_keys($replaces), array_values($replaces), $string);

Ah ok so meinst das...also vom Prinzip nicht schlecht. Aber der hat noch Probleme damit:

[domain=https://www.domain.de]Titel...[/domain]

Also ein [domain]https://...[/domain] wandelt er sich um, aber die obere Variante nicht.
 
Probier mal stattdessen:
Code:
~\[domain(?:=[COLOR="Red"][^\]]+?[/COLOR])?\].*?\[/domain\]~
 
Problem grad gefunden. Folgender Aufbau:

PHP:
			$string = "https://www.domain.de

https://www.domain.de

https://www2.domain.de

domain.de

[domain]https://www.link.de[/domain]

[domain]https://www.link.de[/domain]

[domain=https://www.testlink.de]Testlink[/domain]";
			$replaces = array();
			preg_match_all('~\[domain(?:=[^\]]+?)?\].*?\[/domain\]~', $string, $matches);
			foreach ($matches as $match)
			{
			    $replacement = '##domain:'.md5(uniqid('domain', true)).'##';
			    $replaces[$replacement] = $match[0];
			}
			$string = str_replace(array_values($replaces), array_keys($replaces), $string);
			echo "String jetzt: $string";

Dabei ist mir folgendes aufgefallen: Wenn ich nur die Variante [domain= nehme, dann läuft es...aber wenn ich vorher ein [domain] drin habe, dann läuft es nicht.
 
Das liegt daran, dass ich in dem ersten Code gestern noch 'nen Bug hatte (falsch über die $matches iteriert), das wohl im Post editiert habe, aber dann vergessen habe, das auch noch zu posten.

Hier die korrigierte, funktionierende Version:
PHP:
<?php
	$string = "https://www.domain.de

https://www.domain.de

https://www2.domain.de

domain.de

[domain]https://www.link.de[/domain]

[domain]https://www.link.de[/domain]

[domain=https://www.testlink.de]Testlink[/domain]";

	$replaces = array();
	preg_match_all('~\[domain(?:=[^\]]+?)?\].*?\[/domain\]~', $string, $matches);
	foreach ($matches[0] as $match)
	{
		$replacement = '##domain:'.md5(uniqid('domain', true)).'##';
		$replaces[$replacement] = $match;
	}
	$string = str_replace(array_values($replaces), array_keys($replaces), $string);
	echo "String jetzt: $string";