/** * Process phrases intelligently found within a HTML text (such as adding links). * * @staticvar array $usedpharses * @param string $text the text that we are filtering * @param array $link_array an array of filterobjects * @param array $ignoretagsopen an array of opening tags that we should ignore while filtering * @param array $ignoretagsclose an array of corresponding closing tags * @param bool $overridedefaultignore True to only use tags provided by arguments * @return string **/ function filter_phrases($text, &$link_array, $ignoretagsopen = NULL, $ignoretagsclose = NULL, $overridedefaultignore = false) { global $CFG; static $usedphrases; $ignoretags = array(); // To store all the enclosig tags to be completely ignored. $tags = array(); // To store all the simple tags to be ignored. if (!$overridedefaultignore) { // A list of open/close tags that we should not replace within // Extended to include <script>, <textarea>, <select> and <a> tags // Regular expression allows tags with or without attributes $filterignoretagsopen = array('<head>', '<nolink>', '<span class="nolink">', '<script(\\s[^>]*?)?>', '<textarea(\\s[^>]*?)?>', '<select(\\s[^>]*?)?>', '<a(\\s[^>]*?)?>'); $filterignoretagsclose = array('</head>', '</nolink>', '</span>', '</script>', '</textarea>', '</select>', '</a>'); } else { // Set an empty default list. $filterignoretagsopen = array(); $filterignoretagsclose = array(); } // Add the user defined ignore tags to the default list. if (is_array($ignoretagsopen)) { foreach ($ignoretagsopen as $open) { $filterignoretagsopen[] = $open; } foreach ($ignoretagsclose as $close) { $filterignoretagsclose[] = $close; } } // Invalid prefixes and suffixes for the fullmatch searches // Every "word" character, but the underscore, is a invalid suffix or prefix. // (nice to use this because it includes national characters (accents...) as word characters. $filterinvalidprefixes = '([^\\W_])'; $filterinvalidsuffixes = '([^\\W_])'; // Double up some magic chars to avoid "accidental matches" $text = preg_replace('/([#*%])/', '\\1\\1', $text); //Remove everything enclosed by the ignore tags from $text filter_save_ignore_tags($text, $filterignoretagsopen, $filterignoretagsclose, $ignoretags); // Remove tags from $text filter_save_tags($text, $tags); // Time to cycle through each phrase to be linked $size = sizeof($link_array); for ($n = 0; $n < $size; $n++) { $linkobject =& $link_array[$n]; // Set some defaults if certain properties are missing // Properties may be missing if the filterobject class has not been used to construct the object if (empty($linkobject->phrase)) { continue; } // Avoid integers < 1000 to be linked. See bug 1446. $intcurrent = intval($linkobject->phrase); if (!empty($intcurrent) && strval($intcurrent) == $linkobject->phrase && $intcurrent < 1000) { continue; } // All this work has to be done ONLY it it hasn't been done before if (!$linkobject->work_calculated) { if (!isset($linkobject->hreftagbegin) or !isset($linkobject->hreftagend)) { $linkobject->work_hreftagbegin = '<span class="highlight"'; $linkobject->work_hreftagend = '</span>'; } else { $linkobject->work_hreftagbegin = $linkobject->hreftagbegin; $linkobject->work_hreftagend = $linkobject->hreftagend; } // Double up chars to protect true duplicates // be cleared up before returning to the user. $linkobject->work_hreftagbegin = preg_replace('/([#*%])/', '\\1\\1', $linkobject->work_hreftagbegin); if (empty($linkobject->casesensitive)) { $linkobject->work_casesensitive = false; } else { $linkobject->work_casesensitive = true; } if (empty($linkobject->fullmatch)) { $linkobject->work_fullmatch = false; } else { $linkobject->work_fullmatch = true; } // Strip tags out of the phrase $linkobject->work_phrase = strip_tags($linkobject->phrase); // Double up chars that might cause a false match -- the duplicates will // be cleared up before returning to the user. $linkobject->work_phrase = preg_replace('/([#*%])/', '\\1\\1', $linkobject->work_phrase); // Set the replacement phrase properly if ($linkobject->replacementphrase) { //We have specified a replacement phrase // Strip tags $linkobject->work_replacementphrase = strip_tags($linkobject->replacementphrase); } else { //The replacement is the original phrase as matched below $linkobject->work_replacementphrase = '$1'; } // Quote any regular expression characters and the delimiter in the work phrase to be searched $linkobject->work_phrase = preg_quote($linkobject->work_phrase, '/'); // Work calculated $linkobject->work_calculated = true; } // If $CFG->filtermatchoneperpage, avoid previously (request) linked phrases if (!empty($CFG->filtermatchoneperpage)) { if (!empty($usedphrases) && in_array($linkobject->work_phrase, $usedphrases)) { continue; } } // Regular expression modifiers $modifiers = $linkobject->work_casesensitive ? 's' : 'isu'; // works in unicode mode! // Do we need to do a fullmatch? // If yes then go through and remove any non full matching entries if ($linkobject->work_fullmatch) { $notfullmatches = array(); $regexp = '/' . $filterinvalidprefixes . '(' . $linkobject->work_phrase . ')|(' . $linkobject->work_phrase . ')' . $filterinvalidsuffixes . '/' . $modifiers; preg_match_all($regexp, $text, $list_of_notfullmatches); if ($list_of_notfullmatches) { foreach (array_unique($list_of_notfullmatches[0]) as $key => $value) { $notfullmatches['<*' . $key . '*>'] = $value; } if (!empty($notfullmatches)) { $text = str_replace($notfullmatches, array_keys($notfullmatches), $text); } } } // Finally we do our highlighting if (!empty($CFG->filtermatchonepertext) || !empty($CFG->filtermatchoneperpage)) { $resulttext = preg_replace('/(' . $linkobject->work_phrase . ')/' . $modifiers, $linkobject->work_hreftagbegin . $linkobject->work_replacementphrase . $linkobject->work_hreftagend, $text, 1); } else { $resulttext = preg_replace('/(' . $linkobject->work_phrase . ')/' . $modifiers, $linkobject->work_hreftagbegin . $linkobject->work_replacementphrase . $linkobject->work_hreftagend, $text); } // If the text has changed we have to look for links again if ($resulttext != $text) { // Set $text to $resulttext $text = $resulttext; // Remove everything enclosed by the ignore tags from $text filter_save_ignore_tags($text, $filterignoretagsopen, $filterignoretagsclose, $ignoretags); // Remove tags from $text filter_save_tags($text, $tags); // If $CFG->filtermatchoneperpage, save linked phrases to request if (!empty($CFG->filtermatchoneperpage)) { $usedphrases[] = $linkobject->work_phrase; } } // Replace the not full matches before cycling to next link object if (!empty($notfullmatches)) { $text = str_replace(array_keys($notfullmatches), $notfullmatches, $text); unset($notfullmatches); } } // Rebuild the text with all the excluded areas if (!empty($tags)) { $text = str_replace(array_keys($tags), $tags, $text); } if (!empty($ignoretags)) { $ignoretags = array_reverse($ignoretags); // Reversed so "progressive" str_replace() will solve some nesting problems. $text = str_replace(array_keys($ignoretags), $ignoretags, $text); } // Remove the protective doubleups $text = preg_replace('/([#*%])(\\1)/', '\\1', $text); // Add missing javascript for popus $text = filter_add_javascript($text); return $text; }
/** * Given some text this function converts any URLs it finds into HTML links * * @param string $text Passed in by reference. The string to be searched for urls. */ function convert_urls_into_links(&$text) { //I've added img tags to this list of tags to ignore. //See MDL-21168 for more info. A better way to ignore tags whether or not //they are escaped partially or completely would be desirable. For example: //<a href="blah"> //<a href="blah"> //<a href="blah"> $filterignoretagsopen = array('<a\\s[^>]+?>'); $filterignoretagsclose = array('</a>'); filter_save_ignore_tags($text, $filterignoretagsopen, $filterignoretagsclose, $ignoretags); // Check if we support unicode modifiers in regular expressions. Cache it. // TODO: this check should be a environment requirement in Moodle 2.0, as far as unicode // chars are going to arrive to URLs officially really soon (2010?) // Original RFC regex from: http://www.bytemycode.com/snippets/snippet/796/ // Various ideas from: http://alanstorm.com/url_regex_explained // Unicode check, negative assertion and other bits from Moodle. static $unicoderegexp; if (!isset($unicoderegexp)) { $unicoderegexp = @preg_match('/\\pL/u', 'a'); // This will fail silenty, returning false, } $unicoderegexp = false; //force non use of unicode modifiers. MDL-21296 if ($unicoderegexp) { //We can use unicode modifiers $text = preg_replace('#(?<!=["\'])(((http(s?))://)(((([\\pLl0-9]([\\pLl0-9]|-)*[\\pLl0-9]|[\\pLl0-9])\\.)+([\\pLl]([\\pLl0-9]|-)*[\\pLl0-9]|[\\pLl]))|(([0-9]{1,3}\\.){3}[0-9]{1,3}))(:[\\pL0-9]*)?(/([\\pLl0-9\\.!$&\'\\(\\)*+,;=_~:@-]|%[a-fA-F0-9]{2})*)*(\\?([\\pLl0-9\\.!$&\'\\(\\)*+,;=_~:@/?-]|%[a-fA-F0-9]{2})*)?(\\#[\\pLl0-9\\.!$&\'\\(\\)*+,;=_~:@/?-]*)?)(?<![,\\.;])#iu', '<a href="\\1" target="_blank">\\1</a>', $text); $text = preg_replace('#(?<!=["\']|//)((www\\.([\\pLl0-9]([\\pLl0-9]|-)*[\\pLl0-9]|[\\pLl0-9])\\.)+([\\pLl]([\\pLl0-9]|-)*[\\pLl0-9]|[\\pLl])(:[\\pL0-9]*)?(/([\\pLl0-9\\.!$&\'\\(\\)*+,;=_~:@-]|%[a-fA-F0-9]{2})*)*(\\?([\\pLl0-9\\.!$&\'\\(\\)*+,;=_~:@/?-]|%[a-fA-F0-9]{2})*)?(\\#[\\pLl0-9\\.!$&\'\\(\\)*+,;=_~:@/?-]*)?)(?<![,\\.;])#iu', '<a href="http://\\1" target="_blank">\\1</a>', $text); } else { //We cannot use unicode modifiers $text = preg_replace('#(?<!=["\'])(((http(s?))://)(((([a-z0-9]([a-z0-9]|-)*[a-z0-9]|[a-z0-9])\\.)+([a-z]([a-z0-9]|-)*[a-z0-9]|[a-z]))|(([0-9]{1,3}\\.){3}[0-9]{1,3}))(:[a-zA-Z0-9]*)?(/([a-z0-9\\.!$&\'\\(\\)*+,;=_~:@-]|%[a-f0-9]{2})*)*(\\?([a-z0-9\\.!$&\'\\(\\)*+,;=_~:@/?-]|%[a-fA-F0-9]{2})*)?(\\#[a-z0-9\\.!$&\'\\(\\)*+,;=_~:@/?-]*)?)(?<![,\\.;])#i', '<a href="\\1" target="_blank">\\1</a>', $text); $text = preg_replace('#(?<!=["\']|//)((www\\.([a-z0-9]([a-z0-9]|-)*[a-z0-9]|[a-z0-9])\\.)+([a-z]([a-z0-9]|-)*[a-z0-9]|[a-z])(:[a-zA-Z0-9]*)?(/([a-z0-9\\.!$&\'\\(\\)*+,;=_~:@-]|%[a-f0-9]{2})*)*(\\?([a-z0-9\\.!$&\'\\(\\)*+,;=_~:@/?-]|%[a-fA-F0-9]{2})*)?(\\#[a-z0-9\\.!$&\'\\(\\)*+,;=_~:@/?-]*)?)(?<![,\\.;])#i', '<a href="http://\\1" target="_blank">\\1</a>', $text); } if (!empty($ignoretags)) { $ignoretags = array_reverse($ignoretags); /// Reversed so "progressive" str_replace() will solve some nesting problems. $text = str_replace(array_keys($ignoretags), $ignoretags, $text); } }
/** * Given some text this function converts any URLs it finds into HTML links * * @param string $text Passed in by reference. The string to be searched for urls. */ protected function convert_urls_into_links(&$text) { //I've added img tags to this list of tags to ignore. //See MDL-21168 for more info. A better way to ignore tags whether or not //they are escaped partially or completely would be desirable. For example: //<a href="blah"> //<a href="blah"> //<a href="blah"> $filterignoretagsopen = array('<a\\s[^>]+?>'); $filterignoretagsclose = array('</a>'); filter_save_ignore_tags($text, $filterignoretagsopen, $filterignoretagsclose, $ignoretags); // Check if we support unicode modifiers in regular expressions. Cache it. // TODO: this check should be a environment requirement in Moodle 2.0, as far as unicode // chars are going to arrive to URLs officially really soon (2010?) // Original RFC regex from: http://www.bytemycode.com/snippets/snippet/796/ // Various ideas from: http://alanstorm.com/url_regex_explained // Unicode check, negative assertion and other bits from Moodle. static $unicoderegexp; if (!isset($unicoderegexp)) { $unicoderegexp = @preg_match('/\\pL/u', 'a'); // This will fail silently, returning false, } // TODO MDL-21296 - use of unicode modifiers may cause a timeout $urlstart = '(?:http(s)?://|(?<!://)(www\\.))'; $domainsegment = '(?:[\\pLl0-9][\\pLl0-9-]*[\\pLl0-9]|[\\pLl0-9])'; $numericip = '(?:(?:[0-9]{1,3}\\.){3}[0-9]{1,3})'; $port = '(?::\\d*)'; $pathchar = '(?:[\\pL0-9\\.!$&\'\\(\\)*+,;=_~:@-]|%[a-f0-9]{2})'; $path = "(?:/{$pathchar}*)*"; $querystring = '(?:\\?(?:[\\pL0-9\\.!$&\'\\(\\)*+,;=_~:@/?-]|%[a-fA-F0-9]{2})*)'; $fragment = '(?:\\#(?:[\\pL0-9\\.!$&\'\\(\\)*+,;=_~:@/?-]|%[a-fA-F0-9]{2})*)'; // Lookbehind assertions. // Is not HTML attribute or CSS URL property. Unfortunately legit text like "url(http://...)" will not be a link. $lookbehindstart = "(?<!=[\"']|\\burl\\([\"' ]|\\burl\\()"; $lookbehindend = "(?<![]),.;])"; $regex = "{$lookbehindstart}{$urlstart}((?:{$domainsegment}\\.)+{$domainsegment}|{$numericip})" . "({$port}?{$path}{$querystring}?{$fragment}?){$lookbehindend}"; if ($unicoderegexp) { $regex = '#' . $regex . '#ui'; } else { $regex = '#' . preg_replace(array('\\pLl', '\\PL'), 'a-z', $regex) . '#i'; } $text = preg_replace($regex, '<a href="http$1://$2$3$4" class="_blanktarget">$0</a>', $text); if (!empty($ignoretags)) { $ignoretags = array_reverse($ignoretags); /// Reversed so "progressive" str_replace() will solve some nesting problems. $text = str_replace(array_keys($ignoretags), $ignoretags, $text); } if ($this->get_global_config('embedimages')) { // now try to inject the images, this code was originally in the mediapluing filter // this may be useful only if somebody relies on the fact the links in FORMAT_MOODLE get converted // to URLs which in turn change to real images $search = '/<a href="([^"]+\\.(jpg|png|gif))" class="_blanktarget">([^>]*)<\\/a>/is'; $text = preg_replace_callback($search, 'filter_urltolink_img_callback', $text); } }
/** * Given some text this function converts any URLs it finds into HTML links * * @param string $text Passed in by reference. The string to be searched for urls. */ protected function convert_urls_into_links(&$text) { //I've added img tags to this list of tags to ignore. //See MDL-21168 for more info. A better way to ignore tags whether or not //they are escaped partially or completely would be desirable. For example: //<a href="blah"> //<a href="blah"> //<a href="blah"> $filterignoretagsopen = array('<a\\s[^>]+?>'); $filterignoretagsclose = array('</a>'); filter_save_ignore_tags($text, $filterignoretagsopen, $filterignoretagsclose, $ignoretags); // Check if we support unicode modifiers in regular expressions. Cache it. // TODO: this check should be a environment requirement in Moodle 2.0, as far as unicode // chars are going to arrive to URLs officially really soon (2010?) // Original RFC regex from: http://www.bytemycode.com/snippets/snippet/796/ // Various ideas from: http://alanstorm.com/url_regex_explained // Unicode check, negative assertion and other bits from Moodle. static $unicoderegexp; if (!isset($unicoderegexp)) { $unicoderegexp = @preg_match('/\\pL/u', 'a'); // This will fail silently, returning false, } // TODO MDL-21296 - use of unicode modifiers may cause a timeout $urlstart = '(?:http(s)?://|(?<!://)(www\\.))'; $domainsegment = '(?:[\\pLl0-9][\\pLl0-9-]*[\\pLl0-9]|[\\pLl0-9])'; $numericip = '(?:(?:[0-9]{1,3}\\.){3}[0-9]{1,3})'; $port = '(?::\\d*)'; $pathchar = '(?:[\\pL0-9\\.!$&\'\\(\\)*+,;=_~:@-]|%[a-f0-9]{2})'; $path = "(?:/{$pathchar}*)*"; $querystring = '(?:\\?(?:[\\pL0-9\\.!$&\'\\(\\)*+,;=_~:@/?-]|%[a-fA-F0-9]{2})*)'; $fragment = '(?:\\#(?:[\\pL0-9\\.!$&\'\\(\\)*+,;=_~:@/?-]|%[a-fA-F0-9]{2})*)'; // Lookbehind assertions. // Is not HTML attribute or CSS URL property. Unfortunately legit text like "url(http://...)" will not be a link. $lookbehindend = "(?<![]),.;])"; $regex = "{$urlstart}((?:{$domainsegment}\\.)+{$domainsegment}|{$numericip})" . "({$port}?{$path}{$querystring}?{$fragment}?){$lookbehindend}"; if ($unicoderegexp) { $regex = '#' . $regex . '#ui'; } else { $regex = '#' . preg_replace(array('\\pLl', '\\PL'), 'a-z', $regex) . '#i'; } // Locate any HTML tags. $matches = preg_split('/(<[^<|>]*>)/i', $text, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); // Iterate through the tokenized text to handle chunks (html and content). foreach ($matches as $idx => $chunk) { // Nothing to do. We skip completely any html chunk. if (strpos(trim($chunk), '<') === 0) { continue; } // Nothing to do. We skip any content chunk having any of these attributes. if (preg_match('#(background=")|(action=")|(style="background)|(href=")|(src=")|(url [(])#', $chunk)) { continue; } // Arrived here, we want to process every word in this chunk. $text = $chunk; $words = explode(' ', $text); foreach ($words as $idx2 => $word) { // ReDoS protection. Stop processing if a word is too large. if (strlen($word) < 4096) { $words[$idx2] = preg_replace($regex, '<a href="http$1://$2$3$4" class="_blanktarget">$0</a>', $word); } } $text = implode(' ', $words); // Copy the result back to the array. $matches[$idx] = $text; } $text = implode('', $matches); if (!empty($ignoretags)) { $ignoretags = array_reverse($ignoretags); /// Reversed so "progressive" str_replace() will solve some nesting problems. $text = str_replace(array_keys($ignoretags), $ignoretags, $text); } if (get_config('filter_urltolink', 'embedimages')) { // now try to inject the images, this code was originally in the mediapluing filter // this may be useful only if somebody relies on the fact the links in FORMAT_MOODLE get converted // to URLs which in turn change to real images $search = '/<a href="([^"]+\\.(jpg|png|gif))" class="_blanktarget">([^>]*)<\\/a>/is'; $text = preg_replace_callback($search, 'filter_urltolink_img_callback', $text); } }
/** * Given some text this function converts any URLs it finds into HTML links * * @param string $text Passed in by reference. The string to be searched for urls. */ protected function convert_urls_into_links(&$text) { //I've added img tags to this list of tags to ignore. //See MDL-21168 for more info. A better way to ignore tags whether or not //they are escaped partially or completely would be desirable. For example: //<a href="blah"> //<a href="blah"> //<a href="blah"> $filterignoretagsopen = array('<a\\s[^>]+?>'); $filterignoretagsclose = array('</a>'); filter_save_ignore_tags($text, $filterignoretagsopen, $filterignoretagsclose, $ignoretags); // Check if we support unicode modifiers in regular expressions. Cache it. // TODO: this check should be a environment requirement in Moodle 2.0, as far as unicode // chars are going to arrive to URLs officially really soon (2010?) // Original RFC regex from: http://www.bytemycode.com/snippets/snippet/796/ // Various ideas from: http://alanstorm.com/url_regex_explained // Unicode check, negative assertion and other bits from Moodle. static $unicoderegexp; if (!isset($unicoderegexp)) { $unicoderegexp = @preg_match('/\\pL/u', 'a'); // This will fail silently, returning false, } //todo: MDL-21296 - use of unicode modifiers may cause a timeout if ($unicoderegexp) { //We can use unicode modifiers $text = preg_replace('#(?<!=["\'])(((http(s?))://)(((([\\pLl0-9]([\\pLl0-9]|-)*[\\pLl0-9]|[\\pLl0-9])\\.)+([\\pLl]([\\pLl0-9]|-)*[\\pLl0-9]|[\\pLl]))|(([0-9]{1,3}\\.){3}[0-9]{1,3}))(:[\\pL0-9]*)?(/([\\pLl0-9\\.!$&\'\\(\\)*+,;=_~:@-]|%[a-fA-F0-9]{2})*)*(\\?([\\pLl0-9\\.!$&\'\\(\\)*+,;=_~:@/?-]|%[a-fA-F0-9]{2})*)?(\\#[\\pLl0-9\\.!$&\'\\(\\)*+,;=_~:@/?-]*)?)(?<![,.;])#iu', '<a href="\\1" class="_blanktarget">\\1</a>', $text); $text = preg_replace('#(?<!=["\']|//)((www\\.([\\pLl0-9]([\\pLl0-9]|-)*[\\pLl0-9]|[\\pLl0-9])\\.)+([\\pLl]([\\pLl0-9]|-)*[\\pLl0-9]|[\\pLl])(:[\\pL0-9]*)?(/([\\pLl0-9\\.!$&\'\\(\\)*+,;=_~:@-]|%[a-fA-F0-9]{2})*)*(\\?([\\pLl0-9\\.!$&\'\\(\\)*+,;=_~:@/?-]|%[a-fA-F0-9]{2})*)?(\\#[\\pLl0-9\\.!$&\'\\(\\)*+,;=_~:@/?-]*)?)(?<![,.;])#iu', '<a href="http://\\1" class="_blanktarget">\\1</a>', $text); } else { //We cannot use unicode modifiers $text = preg_replace('#(?<!=["\'])(((http(s?))://)(((([a-z0-9]([a-z0-9]|-)*[a-z0-9]|[a-z0-9])\\.)+([a-z]([a-z0-9]|-)*[a-z0-9]|[a-z]))|(([0-9]{1,3}\\.){3}[0-9]{1,3}))(:[a-zA-Z0-9]*)?(/([a-z0-9\\.!$&\'\\(\\)*+,;=_~:@-]|%[a-f0-9]{2})*)*(\\?([a-z0-9\\.!$&\'\\(\\)*+,;=_~:@/?-]|%[a-fA-F0-9]{2})*)?(\\#[a-z0-9\\.!$&\'\\(\\)*+,;=_~:@/?-]*)?)(?<![,.;])#i', '<a href="\\1" class="_blanktarget">\\1</a>', $text); $text = preg_replace('#(?<!=["\']|//)((www\\.([a-z0-9]([a-z0-9]|-)*[a-z0-9]|[a-z0-9])\\.)+([a-z]([a-z0-9]|-)*[a-z0-9]|[a-z])(:[a-zA-Z0-9]*)?(/([a-z0-9\\.!$&\'\\(\\)*+,;=_~:@-]|%[a-f0-9]{2})*)*(\\?([a-z0-9\\.!$&\'\\(\\)*+,;=_~:@/?-]|%[a-fA-F0-9]{2})*)?(\\#[a-z0-9\\.!$&\'\\(\\)*+,;=_~:@/?-]*)?)(?<![,.;])#i', '<a href="http://\\1" class="_blanktarget">\\1</a>', $text); } if (!empty($ignoretags)) { $ignoretags = array_reverse($ignoretags); /// Reversed so "progressive" str_replace() will solve some nesting problems. $text = str_replace(array_keys($ignoretags), $ignoretags, $text); } if ($this->get_global_config('embedimages')) { // now try to inject the images, this code was originally in the mediapluing filter // this may be useful only if somebody relies on the fact the links in FORMAT_MOODLE get converted // to URLs which in turn change to real images $search = '/<a href="([^"]+\\.(jpg|png|gif))" class="_blanktarget">([^>]*)<\\/a>/is'; $text = preg_replace_callback($search, 'filter_urltolink_img_callback', $text); } }