Problem:
Find key-value pairs separated by :
but ignore escaped colons, i.e. \:
.
Solution:
Negative lookbehinds – see Regex Tutorial – Lookaheads and Lookbehinds. “Positive” means “must match”, “Negative” means “must not match”. Over here, we are “looking behind” the backslash to ensure that a colon is not matched.
Single match in a string:
<?php
// 4 backslashes in PHP = 2 backslashes in pattern string = escaped (literal) backslash in regex
$regex = '/(.*(?<!\\\\)):(.*)/';
$str = 'a:b\:c';
$isMatch = preg_match($regex, $str, $matches);
var_dump($matches);
// array (size=3)
// 0 => string 'a:b\:c' (length=6)
// 1 => string 'a' (length=1)
// 2 => string 'b\:c' (length=4)
Multiple matches in multi-line text enclosed by braces:
<?php
$regex = '/\{([a-z\-_\\\\:]*(?<!\\\\)):([a-z\-_\\\\:]*)\}/i';
$str = <<<'EOD'
{a:b\:c} {d\:e:f}
{date_format:Y-m-d\TH\:i\:s}
y:z
EOD;
$hasMatch = preg_match_all($regex, $str, $matches, PREG_SET_ORDER);
var_dump($matches);
// array (size=3)
// 0 =>
// array (size=3)
// 0 => string '{a:b\:c}' (length=8)
// 1 => string 'a' (length=1)
// 2 => string 'b\:c' (length=4)
// 1 =>
// array (size=3)
// 0 => string '{d\:e:f}' (length=8)
// 1 => string 'd\:e' (length=4)
// 2 => string 'f' (length=1)
// 2 =>
// array (size=3)
// 0 => string '{date_format:Y-m-d\TH\:i\:s}' (length=28)
// 1 => string 'date_format' (length=11)
// 2 => string 'Y-m-d\TH\:i\:s' (length=14)