mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
API CHANGE HTTP::setGetVar() always returns absolute URLs. Use Director::makeRelative() to make them relative again.
API CHANGE HTTP::setGetVar() combines any GET parameters in PHP array notation (e.g. "foo[bar]=val") instead of replacing the whole array BUGFIX HTTP::setGetVar() uses parse_url() and http_build_query() to add query parameters to an existing URL, instead of doing its own regex-based parsing. This means existing GET parameters are correctly url encoded. git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/branches/2.4@98373 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
ff724b44de
commit
792b4cc1c4
115
core/HTTP.php
115
core/HTTP.php
@ -73,26 +73,59 @@ class HTTP {
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will try to include a GET parameter for an existing URL,
|
||||
* preserving existing parameters and fragments.
|
||||
* If no URL is given, falls back to $_SERVER['REQUEST_URI'].
|
||||
*
|
||||
* CAUTION: If the URL is determined to be relative,
|
||||
* it is prepended with Director::absoluteBaseURL().
|
||||
* This method will always return an absolute URL because
|
||||
* Director::makeRelative() can lead to inconsistent results.
|
||||
*
|
||||
* @param String $varname
|
||||
* @param String $varvalue
|
||||
* @param String $currentURL Relative or absolute URL (Optional).
|
||||
* @return String Absolute URL
|
||||
*/
|
||||
public static function setGetVar($varname, $varvalue, $currentURL = null) {
|
||||
$scriptbase = $currentURL ? $currentURL : $_SERVER['REQUEST_URI'];
|
||||
|
||||
$scriptbase = str_replace('&', '&', $scriptbase);
|
||||
$scriptbase = preg_replace('/\?' . quotemeta($varname) . '=([^&]*)&/', '?', $scriptbase);
|
||||
$scriptbase = preg_replace('/([\?&]+)' . quotemeta($varname) . '=([^&]*)/', null, $scriptbase);
|
||||
|
||||
$suffix = '';
|
||||
if(($hashPos = strpos($scriptbase,'#')) !== false) {
|
||||
$suffix .= substr($scriptbase, $hashPos);
|
||||
$scriptbase = substr($scriptbase, 0, $hashPos);
|
||||
$uri = $currentURL ? $currentURL : Director::makeRelative($_SERVER['REQUEST_URI']);
|
||||
|
||||
// We need absolute URLs for parse_url()
|
||||
if(Director::is_relative_url($uri)) $uri = Director::absoluteBaseURL() . $uri;
|
||||
|
||||
// try to parse uri
|
||||
$parts = parse_url($uri);
|
||||
if(!$parts) {
|
||||
throw new InvalidArgumentException("Can't parse URL: " . $uri);
|
||||
}
|
||||
|
||||
if($varvalue !== null) {
|
||||
$scriptbase .= (strrpos($scriptbase,'?') !== false) ? '&' : '?';
|
||||
$scriptbase .= "$varname=" . (isset($urlEncodeVarValue) ? urlencode($varvalue) : $varvalue);
|
||||
}
|
||||
// Parse params and add new variable
|
||||
$params = array();
|
||||
if(isset($parts['query'])) parse_str($parts['query'], $params);
|
||||
$params[$varname] = $varvalue;
|
||||
|
||||
$scriptbase = str_replace('&','&',$scriptbase);
|
||||
return $scriptbase . $suffix;
|
||||
// Recompile Uri
|
||||
$newUri = $parts['scheme'] . '://' . (
|
||||
isset($parts['user']) && $parts['user'] != '' && isset($parts['pass'])
|
||||
? $parts['user'] . ':' . $parts['pass'] . '@'
|
||||
: ''
|
||||
) .
|
||||
$parts['host'] . (
|
||||
isset($parts['path']) && $parts['path'] != ''
|
||||
? $parts['path']
|
||||
: ''
|
||||
) . (
|
||||
($params)
|
||||
? '?' . http_build_query($params)
|
||||
: ''
|
||||
) . (
|
||||
isset($parts['fragment']) && $parts['fragment'] != ''
|
||||
? '#' . $parts['fragment']
|
||||
: ''
|
||||
);
|
||||
|
||||
return $newUri;
|
||||
}
|
||||
|
||||
static function RAW_setGetVar($varname, $varvalue, $currentURL = null) {
|
||||
@ -100,31 +133,31 @@ class HTTP {
|
||||
return Convert::xml2raw($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for all tags with a specific attribute, then return the value of that attribute in a flat array.
|
||||
*
|
||||
* @param string $content
|
||||
* @param array $attributes an array of tags to attributes, for example "[a] => 'href', [div] => 'id'"
|
||||
* @return array
|
||||
*/
|
||||
public static function findByTagAndAttribute($content, $attributes) {
|
||||
$regexes = array();
|
||||
|
||||
foreach($attributes as $tag => $attribute) {
|
||||
$regexes[] = "/<{$tag} [^>]*$attribute *= *([\"'])(.*?)\\1[^>]*>/i";
|
||||
$regexes[] = "/<{$tag} [^>]*$attribute *= *([^ \"'>]+)/i";
|
||||
}
|
||||
|
||||
$result = array();
|
||||
|
||||
if($regexes) foreach($regexes as $regex) {
|
||||
if(preg_match_all($regex, $content, $matches)) {
|
||||
$result = array_merge_recursive($result, (isset($matches[2]) ? $matches[2] : $matches[1]));
|
||||
}
|
||||
}
|
||||
|
||||
return count($result) ? $result : null;
|
||||
}
|
||||
/**
|
||||
* Search for all tags with a specific attribute, then return the value of that attribute in a flat array.
|
||||
*
|
||||
* @param string $content
|
||||
* @param array $attributes an array of tags to attributes, for example "[a] => 'href', [div] => 'id'"
|
||||
* @return array
|
||||
*/
|
||||
public static function findByTagAndAttribute($content, $attributes) {
|
||||
$regexes = array();
|
||||
|
||||
foreach($attributes as $tag => $attribute) {
|
||||
$regexes[] = "/<{$tag} [^>]*$attribute *= *([\"'])(.*?)\\1[^>]*>/i";
|
||||
$regexes[] = "/<{$tag} [^>]*$attribute *= *([^ \"'>]+)/i";
|
||||
}
|
||||
|
||||
$result = array();
|
||||
|
||||
if($regexes) foreach($regexes as $regex) {
|
||||
if(preg_match_all($regex, $content, $matches)) {
|
||||
$result = array_merge_recursive($result, (isset($matches[2]) ? $matches[2] : $matches[1]));
|
||||
}
|
||||
}
|
||||
|
||||
return count($result) ? $result : null;
|
||||
}
|
||||
|
||||
static function getLinksIn($content) {
|
||||
return self::findByTagAndAttribute($content, array("a" => "href"));
|
||||
|
@ -10,56 +10,88 @@ class HTTPTest extends SapphireTest {
|
||||
/**
|
||||
* Tests {@link HTTP::getLinksIn()}
|
||||
*/
|
||||
public function testGetLinksIn() {
|
||||
$content = '
|
||||
<h2><a href="/">My Cool Site</a></h2>
|
||||
|
||||
<p>
|
||||
A boy went <a href="home/">home</a> to see his <span><a href="mother/">mother</a></span>. This
|
||||
involved a short <a href="$Journey">journey</a>, as well as some <a href="space travel">space travel</a>
|
||||
and <a href=unquoted>unquoted</a> events, as well as a <a href=\'single quote\'>single quote</a> from
|
||||
his <a href="/father">father</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
There were also some elements with extra <a class=attribute href=\'attributes\'>attributes</a> which
|
||||
played a part in his <a href=journey"extra id="JourneyLink">journey</a>. HE ALSO DISCOVERED THE
|
||||
<A HREF="CAPS LOCK">KEY</a>. Later he got his <a href="quotes \'mixed\' up">mixed up</a>.
|
||||
</p>
|
||||
';
|
||||
|
||||
$expected = array (
|
||||
'/', 'home/', 'mother/', '$Journey', 'space travel', 'unquoted', 'single quote', '/father', 'attributes',
|
||||
'journey', 'CAPS LOCK', 'quotes \'mixed\' up'
|
||||
);
|
||||
|
||||
$result = HTTP::getLinksIn($content);
|
||||
|
||||
// Results don't neccesarily come out in the order they are in the $content param.
|
||||
sort($result);
|
||||
sort($expected);
|
||||
|
||||
$this->assertTrue(is_array($result));
|
||||
$this->assertEquals($expected, $result, 'Test that all links within the content are found.');
|
||||
public function testGetLinksIn() {
|
||||
$content = '
|
||||
<h2><a href="/">My Cool Site</a></h2>
|
||||
|
||||
<p>
|
||||
A boy went <a href="home/">home</a> to see his <span><a href="mother/">mother</a></span>. This
|
||||
involved a short <a href="$Journey">journey</a>, as well as some <a href="space travel">space travel</a>
|
||||
and <a href=unquoted>unquoted</a> events, as well as a <a href=\'single quote\'>single quote</a> from
|
||||
his <a href="/father">father</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
There were also some elements with extra <a class=attribute href=\'attributes\'>attributes</a> which
|
||||
played a part in his <a href=journey"extra id="JourneyLink">journey</a>. HE ALSO DISCOVERED THE
|
||||
<A HREF="CAPS LOCK">KEY</a>. Later he got his <a href="quotes \'mixed\' up">mixed up</a>.
|
||||
</p>
|
||||
';
|
||||
|
||||
$expected = array (
|
||||
'/', 'home/', 'mother/', '$Journey', 'space travel', 'unquoted', 'single quote', '/father', 'attributes',
|
||||
'journey', 'CAPS LOCK', 'quotes \'mixed\' up'
|
||||
);
|
||||
|
||||
$result = HTTP::getLinksIn($content);
|
||||
|
||||
// Results don't neccesarily come out in the order they are in the $content param.
|
||||
sort($result);
|
||||
sort($expected);
|
||||
|
||||
$this->assertTrue(is_array($result));
|
||||
$this->assertEquals($expected, $result, 'Test that all links within the content are found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link HTTP::setGetVar()}
|
||||
*/
|
||||
public function testSetGetVar() {
|
||||
$expected = array (
|
||||
'/?foo=bar' => array('foo', 'bar', '/'),
|
||||
'/?baz=buz&foo=bar' => array('foo', 'bar', '/?baz=buz'),
|
||||
'/?buz=baz&foo=baz' => array('foo', 'baz', '/?foo=bar&buz=baz'),
|
||||
'/?foo=var' => array('foo', 'var', '/?foo=&foo=bar'),
|
||||
'/?foo[test]=var' => array('foo[test]', 'var', '/?foo[test]=another')
|
||||
// HACK No easy way to get the current URL without the query string or fragment
|
||||
$base = Director::absoluteBaseURL() . 'dev/tests/HTTPTest';
|
||||
|
||||
$this->assertEquals(
|
||||
$base . '?foo=bar',
|
||||
HTTP::setGetVar('foo', 'bar'),
|
||||
'Omitting a URL falls back to current URL'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
Director::absoluteBaseURL() . 'relative/url?foo=bar',
|
||||
HTTP::setGetVar('foo', 'bar', 'relative/url'),
|
||||
'Relative URL without slash prefix returns URL with absolute base'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
Director::absoluteBaseURL() . '/relative/url?foo=bar',
|
||||
HTTP::setGetVar('foo', 'bar', '/relative/url'),
|
||||
'Relative URL with slash prefix returns URL with absolute base'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
Director::absoluteBaseURL() . '/relative/url?baz=buz&foo=bar',
|
||||
HTTP::setGetVar('foo', 'bar', '/relative/url?baz=buz'),
|
||||
'Relative URL with existing query params, and new added key'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'http://test.com/?foo=new&buz=baz',
|
||||
HTTP::setGetVar('foo', 'new', 'http://test.com/?foo=old&buz=baz'),
|
||||
'Absolute URL without path and multipe existing query params, overwriting an existing parameter'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'http://test.com/?foo=new',
|
||||
HTTP::setGetVar('foo', 'new', 'http://test.com/?foo=&foo=old'),
|
||||
'Absolute URL and empty query param'
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
// http_build_query() escapes angular brackets, they should be correctly urldecoded by the browser client
|
||||
'http://test.com/?foo%5Btest%5D=one&foo%5Btest%5D=two',
|
||||
HTTP::setGetVar('foo[test]', 'two', 'http://test.com/?foo[test]=one'),
|
||||
'Absolute URL and PHP array query string notation'
|
||||
);
|
||||
|
||||
foreach($expected as $result => $args) {
|
||||
$this->assertEquals(
|
||||
call_user_func_array(array('HTTP', 'setGetVar'), $args), str_replace('&', '&', $result)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user