mirror of
https://github.com/Lastorder-DC/rhymix.git
synced 2026-01-09 11:44:10 +09:00
Add URL-to-path and clean path conversion
This commit is contained in:
parent
86e91d116e
commit
9d2fe0270b
4 changed files with 140 additions and 8 deletions
|
|
@ -46,4 +46,24 @@ class FilenameFilter
|
|||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean a path to remove ./, ../, trailing slashes, etc.
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
public static function cleanPath($path)
|
||||
{
|
||||
$path = str_replace('\\', '/', $path);
|
||||
$path = preg_replace('@[\?#].+$@', '', $path);
|
||||
$path = preg_replace('@/{2,}@', '/', $path);
|
||||
$path = preg_replace('@/\.{3,}/@', '/', $path);
|
||||
$path = preg_replace('@/(\./)+@', '/', $path);
|
||||
while (preg_match('@/[^/]+/\.\.(?:/|$)@', $path, $matches))
|
||||
{
|
||||
$path = str_replace($matches[0], '/', $path);
|
||||
}
|
||||
return rtrim($path, '/');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,10 +18,7 @@ class URL
|
|||
*/
|
||||
public static function getCurrentURL(array $changes = array())
|
||||
{
|
||||
$proto = \RX_SSL ? 'https://' : 'http://';
|
||||
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
|
||||
$local = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/';
|
||||
$url = $proto . $host . $local;
|
||||
$url = self::getCurrentDomainURL(isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/');
|
||||
if (count($changes))
|
||||
{
|
||||
return self::modifyURL($url, $changes);
|
||||
|
|
@ -32,6 +29,19 @@ class URL
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current domain.
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
public static function getCurrentDomainURL($path = '/')
|
||||
{
|
||||
$proto = \RX_SSL ? 'https://' : 'http://';
|
||||
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
|
||||
return $proto . $host . '/' . ltrim($path, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a URL to its canonical format.
|
||||
*
|
||||
|
|
@ -42,9 +52,7 @@ class URL
|
|||
{
|
||||
if (preg_match('#^\.?/([^/]|$)#', $url) || !preg_match('#^(https?:|/)#', $url))
|
||||
{
|
||||
$proto = \RX_SSL ? 'https://' : 'http://';
|
||||
$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
|
||||
$url = $proto . $host . \RX_BASEURL . ltrim($url, './');
|
||||
$url = self::getCurrentDomainURL(\RX_BASEURL . ltrim($url, './'));
|
||||
}
|
||||
return preg_replace_callback('#^(https?:|)//([^/]+)#i', function($matches) {
|
||||
if ($matches[1] === '') $matches[1] = \RX_SSL ? 'https:' : 'http:';
|
||||
|
|
@ -125,6 +133,53 @@ class URL
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a server-side path to a URL.
|
||||
*
|
||||
* This method returns false if the path cannot be converted to a URL,
|
||||
* e.g. if the path is outside of the document root.
|
||||
*
|
||||
* @param string $path
|
||||
* @return string|false
|
||||
*/
|
||||
public static function fromServerPath($path)
|
||||
{
|
||||
$cleanpath = Filters\FilenameFilter::cleanPath($path);
|
||||
if (substr($path, -1) === '/')
|
||||
{
|
||||
$cleanpath .= '/';
|
||||
}
|
||||
$root = Filters\FilenameFilter::cleanPath($_SERVER['DOCUMENT_ROOT']);
|
||||
if ($cleanpath === $root)
|
||||
{
|
||||
return self::getCurrentDomainURL('/');
|
||||
}
|
||||
if (starts_with($root . '/', $cleanpath))
|
||||
{
|
||||
return self::getCurrentDomainURL(substr($cleanpath, strlen($root)));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a URL to a server-side path.
|
||||
*
|
||||
* This method returns false if the URL cannot be converted to a server-side path,
|
||||
* e.g. if the URL belongs to an external domain.
|
||||
*
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
public static function toServerPath($url)
|
||||
{
|
||||
$url = self::getCanonicalURL($url);
|
||||
if (!self::isInternalURL($url))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Filters\FilenameFilter::cleanPath($_SERVER['DOCUMENT_ROOT'] . parse_url($url, \PHP_URL_PATH));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode UTF-8 domain into IDNA (punycode)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -25,6 +25,17 @@ class URLTest extends \Codeception\TestCase\Test
|
|||
$this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/index.php?xe=sucks&l=ko', Rhymix\Framework\URL::getCurrentURL(array('l' => 'ko', 'foo' => null)));
|
||||
}
|
||||
|
||||
public function testGetCurrentDomainURL()
|
||||
{
|
||||
$protocol = \RX_SSL ? 'https://' : 'http://';
|
||||
$_SERVER['HTTP_HOST'] = 'www.rhymix.org';
|
||||
|
||||
$this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/', Rhymix\Framework\URL::getCurrentDomainURL());
|
||||
$this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/', Rhymix\Framework\URL::getCurrentDomainURL('/'));
|
||||
$this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/foo/bar', Rhymix\Framework\URL::getCurrentDomainURL('/foo/bar'));
|
||||
$this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/index.php?foo=bar', Rhymix\Framework\URL::getCurrentDomainURL('index.php?foo=bar'));
|
||||
}
|
||||
|
||||
public function testGetCanonicalURL()
|
||||
{
|
||||
$protocol = \RX_SSL ? 'https://' : 'http://';
|
||||
|
|
@ -82,6 +93,30 @@ class URLTest extends \Codeception\TestCase\Test
|
|||
// This function is checked in Security::checkCSRF()
|
||||
}
|
||||
|
||||
public function testURLFromServerPath()
|
||||
{
|
||||
$protocol = \RX_SSL ? 'https://' : 'http://';
|
||||
$_SERVER['HTTP_HOST'] = 'www.rhymix.org';
|
||||
|
||||
$this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL, Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR));
|
||||
$this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'index.php', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR . 'index.php'));
|
||||
$this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'foo/bar', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR . '/foo/bar'));
|
||||
$this->assertEquals(false, Rhymix\Framework\URL::fromServerPath('C:/Windows'));
|
||||
}
|
||||
|
||||
public function testURLToServerPath()
|
||||
{
|
||||
$protocol = \RX_SSL ? 'https://' : 'http://';
|
||||
$_SERVER['HTTP_HOST'] = 'www.rhymix.org';
|
||||
|
||||
$this->assertEquals(\RX_BASEDIR . 'index.php', Rhymix\Framework\URL::toServerPath($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'index.php'));
|
||||
$this->assertEquals(\RX_BASEDIR . 'foo/bar', Rhymix\Framework\URL::toServerPath($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . '/foo/bar?arg=baz'));
|
||||
$this->assertEquals(\RX_BASEDIR . 'foo/bar', Rhymix\Framework\URL::toServerPath('./foo/bar'));
|
||||
$this->assertEquals(\RX_BASEDIR . 'foo/bar', Rhymix\Framework\URL::toServerPath('foo/bar/../bar'));
|
||||
$this->assertEquals(false, Rhymix\Framework\URL::toServerPath('http://other.domain.com/'));
|
||||
$this->assertEquals(false, Rhymix\Framework\URL::toServerPath('//other.domain.com/'));
|
||||
}
|
||||
|
||||
public function testEncodeIdna()
|
||||
{
|
||||
$this->assertEquals('xn--9i1bl3b186bf9e.xn--3e0b707e', Rhymix\Framework\URL::encodeIdna('퓨니코드.한국'));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
use Rhymix\Framework\Filters\FilenameFilter;
|
||||
|
||||
class FilenameFilterTest extends \Codeception\TestCase\Test
|
||||
{
|
||||
public function testFilenameFilterClean()
|
||||
|
|
@ -35,8 +37,28 @@ class FilenameFilterTest extends \Codeception\TestCase\Test
|
|||
|
||||
foreach ($tests as $from => $to)
|
||||
{
|
||||
$result = Rhymix\Framework\Filters\FilenameFilter::clean($from);
|
||||
$result = FilenameFilter::clean($from);
|
||||
$this->assertEquals($to, $result);
|
||||
}
|
||||
}
|
||||
|
||||
public function testFilenameFilterCleanPath()
|
||||
{
|
||||
// Remove extra dots and slashes.
|
||||
$this->assertEquals('/usr/share/foo/bar.jpg', FilenameFilter::cleanPath('/usr/share/foo//./baz/../bar.jpg'));
|
||||
$this->assertEquals('/usr/share/foo/bar.jpg', FilenameFilter::cleanPath('/usr/share/foo/././baz/../../foo/bar.jpg'));
|
||||
$this->assertEquals('/usr/share', FilenameFilter::cleanPath('/usr/share/foo/..'));
|
||||
$this->assertEquals('/usr/share', FilenameFilter::cleanPath('/usr/share/foo/bar/../baz/../../'));
|
||||
|
||||
// Test Windows paths.
|
||||
$this->assertEquals('C:/Windows/Notepad.exe', FilenameFilter::cleanPath('C:\\Windows\\System32\\..\\Notepad.exe'));
|
||||
|
||||
// Do not remove .. if there is no parent directory.
|
||||
$this->assertEquals('C:/../foobar', FilenameFilter::cleanPath('C:\\..\foobar\\'));
|
||||
$this->assertEquals('/../foobar', FilenameFilter::cleanPath('/../foobar/'));
|
||||
|
||||
// Remove query strings and URL fragments.
|
||||
$this->assertEquals('index.php', FilenameFilter::cleanPath('index.php?foo=bar'));
|
||||
$this->assertEquals('index.php', FilenameFilter::cleanPath('index.php#baz'));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue