Pierre
Pierre

Reputation: 13046

preg_match to show first match in foreach loop

I have an array of patterns:

$patterns= array(
    '#http://(www\.)?domain-1.com/.*#i',
    '#http://(www\.)?domain-2.com/.*#i',
    ...
);

and I have long string that contains multiple text and urls I want to match the first url occurred in the string, I only tried this :

foreach ($patterns as $pattern) {
    preg_match($pattern, $the_string, $match);
    echo '<pre>'; print_r($match); echo '</pre>';
}

It returns empty arrays where there are no match for some patterns and arrays that contains a url but depending on the order of the array $patterns,

how can I find any match of these patterns that occurred first.

Upvotes: 2

Views: 2482

Answers (3)

flec
flec

Reputation: 3019

I guess you're looking for this:

foreach ($patterns as $pattern) {
    if (preg_match($pattern, $the_string, $match)) {
        echo '<pre>'; print_r($match); echo '</pre>';
        break;
    }
}

UPDATE:

Then I think you should work with offsets linke this:

$matches = array();
foreach ($patterns as $pattern) {
    if (preg_match($pattern, $the_string, $match, PREG_OFFSET_CAPTURE)) {
        $matches[$match[0][1]] = $match[0][0];
    }
}
echo reset($matches);

Upvotes: 2

rodneyrehm
rodneyrehm

Reputation: 13557

You basically have three options:

  1. match a general URL pattern and then run that URL against the patterns you've got. If none matched, continue with the second result from the general pattern.
  2. run all your patterns with the PREG_OFFSET_CAPTURE flag to get the offset the pattern matched at. find the lowest offset, return your result
  3. combine your various patterns to a single pattern. Be aware that there are limits to the length of a pattern (64K in compiled form)

Option 2:

<?php

$text = "hello world http://www.domain-2.com/foo comes before http://www.domain-1.com/bar";
$patterns= array(
    '#http://(www\.)?domain-1.com/[^\s]*#i',
    '#http://(www\.)?domain-2.com/[^\s]*#i',
);

$match = null;
$offset = null;
foreach ($patterns as $pattern) {
    if (preg_match($pattern, $text, $matches, PREG_OFFSET_CAPTURE)) {
        if ($matches[0][1] < $offset || $offset === null) {
            $offset = $matches[0][1];
            $match = $matches[0][0];
        }
    }
}

var_dump($match);

beware that I changed your demo patterns. I replaced .* (anything) by [^\s]* (everything but space) to prevent the pattern from matching more than it's supposed to

Upvotes: 5

Robbie
Robbie

Reputation: 17710

I can't think of any way except evaluting all the strings one at a time, and grabbing the earliest:

$easliestPos = strlen($the_string) + 1;
$earliestMatch = false;

foreach ($patterns as $pattern) { 
    if (preg_match($pattern, $the_string, $match)) { 
        $myMatch = $match[0];
        $myMatchPos = strpos($myMatch, $the_string);
        if ($myMatchPos < $easliestPos ) {
            $easliestPos = $myMatchPos;
            $earliestMatch = $myMatch ;
        }
    }
 }

 if ($earliestMatch ) {
    echo $earliestMatch;
 }

Upvotes: 1

Related Questions