From 6611a30d907cf292749a727fe183423be1271a41 Mon Sep 17 00:00:00 2001 From: Felix Becker Date: Mon, 10 Oct 2016 12:13:42 +0200 Subject: [PATCH] Throw exception on invalid file URI --- src/utils.php | 20 +++++++++++++++++--- tests/FormatterTest.php | 4 ++-- tests/Server/TextDocument/FormattingTest.php | 9 ++++++--- tests/Utils/FileUriTest.php | 19 +++++++++++++++++-- 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/utils.php b/src/utils.php index 034c125..3c058e2 100644 --- a/src/utils.php +++ b/src/utils.php @@ -3,6 +3,8 @@ declare(strict_types = 1); namespace LanguageServer; +use InvalidArgumentException; + /** * Recursively Searches files with matching filename, starting at $path. * @@ -29,18 +31,30 @@ function findFilesRecursive(string $path, string $pattern): array { */ function pathToUri(string $filepath): string { $filepath = trim(str_replace('\\', '/', $filepath), '/'); - $filepath = implode('/', array_map('urlencode', explode('/', $filepath))); + $parts = explode('/', $filepath); + // Don't %-encode the colon after a Windows drive letter + $first = array_shift($parts); + if (substr($first, -1) !== ':') { + $first = urlencode($first); + } + $parts = array_map('urlencode', $parts); + array_unshift($parts, $first); + $filepath = implode('/', $parts); return 'file:///' . $filepath; } /** * Transforms URI into file path - * + * * @param string $uri * @return string */ function uriToPath(string $uri) { - $filepath = urldecode(parse_url($uri)['path']); + $fragments = parse_url($uri); + if ($fragments === null || !isset($fragments['scheme']) || $fragments['scheme'] !== 'file') { + throw new InvalidArgumentException("Not a valid file URI: $uri"); + } + $filepath = urldecode($fragments['path']); return strpos($filepath, ':') === false ? $filepath : str_replace('/', '\\', $filepath); } diff --git a/tests/FormatterTest.php b/tests/FormatterTest.php index 6f19bcb..af7efd4 100644 --- a/tests/FormatterTest.php +++ b/tests/FormatterTest.php @@ -16,7 +16,7 @@ class FormatterTest extends TestCase $input = file_get_contents(__DIR__ . '/../fixtures/format.php'); $output = file_get_contents(__DIR__ . '/../fixtures/format_expected.php'); - $edits = $formatter->format($input, 'whatever'); + $edits = $formatter->format($input, 'file:///whatever'); $this->assertSame($output, $edits[0]->newText); } @@ -25,7 +25,7 @@ class FormatterTest extends TestCase $formatter = new Formatter(); $expected = file_get_contents(__DIR__ . '/../fixtures/format_expected.php'); - $edits = $formatter->format($expected, 'whatever'); + $edits = $formatter->format($expected, 'file:///whatever'); $this->assertSame([], $edits); } } diff --git a/tests/Server/TextDocument/FormattingTest.php b/tests/Server/TextDocument/FormattingTest.php index 0b23753..957f4a6 100644 --- a/tests/Server/TextDocument/FormattingTest.php +++ b/tests/Server/TextDocument/FormattingTest.php @@ -7,6 +7,7 @@ use PHPUnit\Framework\TestCase; use LanguageServer\Tests\MockProtocolStream; use LanguageServer\{Server, Client, LanguageClient, Project}; use LanguageServer\Protocol\{TextDocumentIdentifier, TextDocumentItem, FormattingOptions}; +use function LanguageServer\{pathToUri, uriToPath}; class FormattingTest extends TestCase { @@ -27,19 +28,21 @@ class FormattingTest extends TestCase $client = new LanguageClient(new MockProtocolStream()); $project = new Project($client); $textDocument = new Server\TextDocument($project, $client); + $path = realpath(__DIR__ . '/../../../fixtures/format.php'); + $uri = pathToUri($path); // Trigger parsing of source $textDocumentItem = new TextDocumentItem(); - $textDocumentItem->uri = 'whatever'; + $textDocumentItem->uri = $uri; $textDocumentItem->languageId = 'php'; $textDocumentItem->version = 1; - $textDocumentItem->text = file_get_contents(__DIR__ . '/../../../fixtures/format.php'); + $textDocumentItem->text = file_get_contents($path); $textDocument->didOpen($textDocumentItem); // how code should look after formatting $expected = file_get_contents(__DIR__ . '/../../../fixtures/format_expected.php'); // Request formatting - $result = $textDocument->formatting(new TextDocumentIdentifier('whatever'), new FormattingOptions()); + $result = $textDocument->formatting(new TextDocumentIdentifier($uri), new FormattingOptions()); $this->assertEquals([0 => [ 'range' => [ 'start' => [ diff --git a/tests/Utils/FileUriTest.php b/tests/Utils/FileUriTest.php index 0a077a2..a9d3559 100644 --- a/tests/Utils/FileUriTest.php +++ b/tests/Utils/FileUriTest.php @@ -4,6 +4,7 @@ declare(strict_types = 1); namespace LanguageServer\Tests\Utils; use PHPUnit\Framework\TestCase; +use InvalidArgumentException; use function LanguageServer\{pathToUri, uriToPath}; class FileUriTest extends TestCase @@ -24,11 +25,11 @@ class FileUriTest extends TestCase // special chars are escaped $uri = pathToUri('c:/path/to/file/dürüm döner.php'); - $this->assertEquals('file:///c%3A/path/to/file/d%C3%BCr%C3%BCm+d%C3%B6ner.php', $uri); + $this->assertEquals('file:///c:/path/to/file/d%C3%BCr%C3%BCm+d%C3%B6ner.php', $uri); //backslashes are transformed $uri = pathToUri('c:\\foo\\bar.baz'); - $this->assertEquals('file:///c%3A/foo/bar.baz', $uri); + $this->assertEquals('file:///c:/foo/bar.baz', $uri); } public function testUriToPath() @@ -51,4 +52,18 @@ class FileUriTest extends TestCase $uri = 'file:///c:/foo/bar.baz'; $this->assertEquals('c:\\foo\\bar.baz', uriToPath($uri)); } + + public function testUriToPathForUnknownProtocol() + { + $this->expectException(InvalidArgumentException::class); + $uri = 'vfs:///whatever'; + uriToPath($uri); + } + + public function testUriToPathForInvalidProtocol() + { + $this->expectException(InvalidArgumentException::class); + $uri = 'http://www.google.com'; + uriToPath($uri); + } }