Skip to content

Commit

Permalink
Fix autoLinkUrls() failing on long string in parens
Browse files Browse the repository at this point in the history
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 38f5433
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 10 deletions.
47 changes: 37 additions & 10 deletions src/View/Helper/TextHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
Expand All @@ -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;
}
Expand All @@ -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);
Expand All @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions tests/TestCase/View/Helper/TextHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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>'
]
];
}
Expand Down

0 comments on commit 38f5433

Please sign in to comment.