Skip to content
Permalink
Browse files

Fix autoLinkUrls() failing on long string in parens

When a URL contained a long string in parenthesis, the old pattern hit
its backtrace limit and failed to do anything. This new pattern doesn't
have nearly as much backtracking. I had to use some additional
replacement logic to correctly handle URLs wrapped in punctuation.

Refs #9916
  • Loading branch information...
markstory committed Dec 28, 2016
1 parent e5e3dfb commit 38f5433b98fe8fb735e5f22aa87fefe35d771741
Showing with 41 additions and 10 deletions.
  1. +37 −10 src/View/Helper/TextHelper.php
  2. +4 −0 tests/TestCase/View/Helper/TextHelperTest.php
@@ -117,9 +117,21 @@ public function autoLinkUrls($text, array $options = [])
$this->_placeholders = [];
$options += ['escape' => true];
$pattern = '#(?<!href="|src="|">)((?:https?|ftp|nntp)://[\p{L}0-9.\-_:]+' .
'(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+' .
'(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?«»“”‘’]))#i';
$pattern = '/(?:(?<!href="|src="|">)
(?>
(
(?<left>[\[<(]) # left paren,brace
(?>
# Lax match URL
(?<url>(?:https?|ftp|nntp):\/\/[\p{L}0-9.\-_:]+(?:[\/?][\p{L}0-9.\-_:\/?=&>\[\]()#@]+)?)
(?<right>[\])>]) # right paren,brace
)
)
|
(?<url_bare>(?P>url)) # A bare URL. Use subroutine
)
)/ixu';
$text = preg_replace_callback(
$pattern,
[&$this, '_insertPlaceHolder'],
@@ -146,8 +158,20 @@ public function autoLinkUrls($text, array $options = [])
*/
protected function _insertPlaceHolder($matches)
{
$key = md5($matches[0]);
$this->_placeholders[$key] = $matches[0];
$match = $matches[0];
$envelope = ['', ''];
if (isset($matches['url'])) {
$match = $matches['url'];
$envelope = [$matches['left'], $matches['right']];
}
if (isset($matches['url_bare'])) {
$match = $matches['url_bare'];
}
$key = md5($match);
$this->_placeholders[$key] = [
'content' => $match,
'envelope' => $envelope
];
return $key;
}
@@ -162,12 +186,13 @@ protected function _insertPlaceHolder($matches)
protected function _linkUrls($text, $htmlOptions)
{
$replace = [];
foreach ($this->_placeholders as $hash => $url) {
$link = $url;
foreach ($this->_placeholders as $hash => $content) {
$link = $url = $content['content'];
$envelope = $content['envelope'];
if (!preg_match('#^[a-z]+\://#i', $url)) {
$url = 'http://' . $url;
}
$replace[$hash] = $this->Html->link($link, $url, $htmlOptions);
$replace[$hash] = $envelope[0] . $this->Html->link($link, $url, $htmlOptions) . $envelope[1];
}
return strtr($text, $replace);
@@ -184,8 +209,10 @@ protected function _linkUrls($text, $htmlOptions)
protected function _linkEmails($text, $options)
{
$replace = [];
foreach ($this->_placeholders as $hash => $url) {
$replace[$hash] = $this->Html->link($url, 'mailto:' . $url, $options);
foreach ($this->_placeholders as $hash => $content) {
$url = $content['content'];
$envelope = $content['envelope'];
$replace[$hash] = $envelope[0] . $this->Html->link($url, 'mailto:' . $url, $options) . $envelope[1];
}
return strtr($text, $replace);
@@ -347,6 +347,10 @@ public static function autoLinkProvider()
[
"Text with partial www.cakephp.org\r\nwww.cakephp.org urls and CRLF",
"Text with partial <a href=\"http://www.cakephp.org\">www.cakephp.org</a>\r\n<a href=\"http://www.cakephp.org\">www.cakephp.org</a> urls and CRLF"
],
[
'https://nl.wikipedia.org/wiki/Exploit_(computerbeveiliging)',
'<a href="https://nl.wikipedia.org/wiki/Exploit_(computerbeveiliging)">https://nl.wikipedia.org/wiki/Exploit_(computerbeveiliging)</a>'
]
];
}

0 comments on commit 38f5433

Please sign in to comment.
You can’t perform that action at this time.