preg_replace mail.ru redirect

There is a text full of such links. You need to somehow extract a direct url from it and write it down in place of the redirect. Please help with the compilation of the regular season!

<a href="http://win.mail.ru/cgi-bin/link?check=1&amp;url=http%3A%2F%2Fredirect.subscribe.ru%2Flaw.russia.review.consdailyrus%2C2215%2F20110128105418%2Fn%2Fm17587277%2F-%2Fmy.consultant.ru%2Fcabinet%2F%3Fmode%3Dstat%3Bclick%3Bd%3D2011-01-27%3Br%3Dfd%3Bs%3Dsubscribe%3Bdst%3Dhttp%253A%252F%252Fwww.consultant.ru%252Flaw%252Freview%252Flink%252F%253Fid%253D961912">   </a>

Added.

Thank you! I managed to fix the final link without redirects by adding this:

foreach($res[1] as $n => $link)
    if (!empty($res[2][$n])) {
        $url = urldecode($res[2][$n]);
        if ( (preg_match('/redirect.subscribe.ru/is', $url)) > 0 ) {
            parse_str(parse_url($url, PHP_URL_QUERY), $url);
            $url = $url['mode'];
            $l=strpos($url, 'dst=');
            $url = str_split($url,$l+4);
            $result = $url[1].$url[2];
        } else {
            $result = $url;
        }
        $text = str_replace($link, $result, $text);
    }

Answer 1, authority 100%

<?
$text = '<a href="http://win.mail.ru/cgi-bin/link?check=1&amp;url=http%3A%2F%2Fredirect.subscribe.ru%2Flaw.russia.review.consdailyrus%2C2215%2F20110128105418%2Fn%2Fm17587277%2F-%2Fmy.consultant.ru%2Fcabinet%2F%3Fmode%3Dstat%3Bclick%3Bd%3D2011-01-27%3Br%3Dfd%3Bs%3Dsubscribe%3Bdst%3Dhttp%253A%252F%252Fwww.consultant.ru%252Flaw%252Freview%252Flink%252F%253Fid%253D961912">   </a>
<a href="http://win.mail.ru/cgi-bin/link?check=1&amp;url=http%3A%2F%2Fredirect.subscribe.ru%2Flaw.russia.review.consdailyrus%2C2215%2F20110128105418%2Fn%2Fm17587277%2F-%2Fmy.consultant.ru%2Fcabinet%2F%3Fmode%3Dstat%3Bclick%3Bd%3D2011-01-27%3Br%3Dfd%3Bs%3Dsubscribe%3Bdst%3Dhttp%253A%252F%252Fwww.consultant.ru%252Flaw%252Freview%252Flink%252F%253Fid%253D961912123">    123</a>';
echo '<h3></h3>';
echo '<hr />';
echo $text;
echo '<hr />';
$exp = '/<a.*href="(http.*url=([^"]+))"[^>]*>/i';    
$res = $res2 = array();
preg_match_all($exp, $text, $res);
if (!empty($res[1]))
  foreach($res[1] as $n => $link)
    if (!empty($res[2][$n]))
      $text = str_replace($link, urldecode($res[2][$n]), $text);
echo '<h3></h3>';
echo '<hr />';
echo $text;
echo '<hr />';
?>

The only thing is that there are 2 redirects in the link (mail.ru + subscribe.ru) and the second one is harder to catch. But before the first code above processes.


Answer 2, authority 100%

In my opinion, in this case it is easier to use xml parser.

$xml = new SimpleXMLElement($text);
//       href
foreach ($xml->xpath('//a[@href]') as $a) {
    //    query
    if ( !($query = parse_url($a['href'], PHP_URL_QUERY)) ) {
        continue;
    }
    //  query,    
    parse_str($query, $params);
    //     ,   
    if (isset($params['url'])) {
        $a['href'] = $params['url'];
    }
}
var_dump($xml->asXML());

UPD. If you really want to replace it by regular expression, then it’s best to use preg_replace_callback()

$result = preg_replace_callback('/(<a\b[^>]+href=)(\S+)\b([^>]*>)/i', function($m) {
        $url = htmlspecialchars_decode(trim($m[2], '"\''));
        if ( !($query = parse_url($url, PHP_URL_QUERY)) ) {
            return $m[0];
        }
        parse_str($query, $params);
        if (!isset($params['url'])) {
            return $m[0];
        }
        $url = $params['url'];
        $parts = parse_url($url);
        if ($parts['host'] !== 'redirect.subscribe.ru') {
            return $m[1].$url.$m[3];
        }
        parse_str($parts['query'], $params);
        if (!isset($params['mode']) || !preg_match('/;dst=(.+)/', $params['mode'], $n)) {
            return $m[1].$url.$m[3];
        }
        return $m[1].$n[1].$m[3];
}, $text);

Answer 3, authority 50%

Regular expressions can only search for the link itself. The link from the url parameter must be decoded with urldecode.

Create the appropriate regular expression based on the already prepared ones. For example, from SO. Or based on many others.