Mohammad Umer Mohammad Umer - 9 months ago 44
PHP Question

Timestamp convert into shortcode with regex

I am using this code to detect timestamp from content.

$pattern = '/(?<!:)\d{1,2}:\d{2}(?::\d{2})?(?!(?::\d{2})?\s*[ap]\.?m\.?)(?!.*?["\'])/';
$replacement = '[spp-timestamp time="$0"]';
$foundTimestamp = preg_replace ($pattern,$replacement, $content);

To detect timestamp to shortcode [spp-timestamp time="00:00"]

Just want to add more logic into this to not select timestamp from selected div for example

<div class="sm2-inline-duration timestamp">0:00</div> and
<div class="sm2-inline-time timestamp">0:00</div>

for example if i put class "timestamp" in any div it should not selected with regex.

Is it possible ?

Right now its is detecting stamps

00:00, 00:00:00, 0:00, 0:00:00, and not selecting with Am or PM

Answer Source

You need to parse the HTML with DOMDocument with XPath that will allow you to use a simpler regex for your replacement. //div[not(contains(@class, "timestamp"))] xpath expression will prevent modifying the contents of the div elements that contain timestamp substring in their class attributes.

$html = <<<DATA
<div class="sm2-inline-duration timestamp">0:00</div>
<div class="sm2-inline-time timestamp">0:00</div>
<div class="sm2-inline-duration">0:00</div>
<div class="sm2-inline-duration">Do not touch this</div>

$dom = new DOMDocument('1.0', 'UTF-8');

$pattern = '/(?<!:)\d{1,2}:\d{2}(?::\d{2})?(?!(?::\d{2})?\s*[ap]\.?m\.?)/i';
$replacement = '[spp-timestamp time="$0"]';

$xpath = new DOMXPath($dom);
$divs = $xpath->query('//div[not(contains(@class, "timestamp"))]');

foreach($divs as $div) { 
   $div->nodeValue = preg_replace ($pattern,$replacement, $div->nodeValue);

echo $dom->saveHTML();

See the PHP demo

For educational purposes, here is a regex-based approach as this will not work in production safely with arbitrary HTML.

The idea is to replace the unsupported negative lookbehind of inifinite width with an optional capturing group, and check inside preg_replace_callback if the group matched, and use the appropriate replacement (if matched, we need to keep the original match text, else, replace with our custom replacement).

$pattern = '/(<div\s+[^<]*?\bclass="[^<"]*\btimestamp\b[^<]*?>[^<]*)?(?<!:)\d{1,2}:\d{2}(?::\d{2})?(?!(?::\d{2})?\s*[ap]\.?m\.?)(?!.*?["\'])/';
$replacement = '[spp-timestamp time="$0"]';
$foundTimestamp = preg_replace_callback($pattern,function($m) {
    return empty($m[1]) ? '[spp-timestamp time="'. $m[0] . '"]' : $m[0];
}, $content);
echo $foundTimestamp;

See another PHP demo