From 786e7e1f55319ade2a465daa483bb4c5ed2dc12a Mon Sep 17 00:00:00 2001
From: ignace nyamagana butera <nyamsprod@gmail.com>
Date: Thu, 2 Jan 2025 12:41:51 +0100
Subject: [PATCH] Fix URI RFC3986 parsing and invalid characters

---
 CHANGELOG.md      |  2 +-
 UriString.php     | 14 +++++++++++---
 UriStringTest.php | 18 +-----------------
 3 files changed, 13 insertions(+), 21 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index dc9e820..f0c1d96 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,7 +22,7 @@ All Notable changes to `League\Uri\Interfaces` will be documented in this file
 
 ### Fixed
 
-- `UriString::parse` will fail if the URI contains whitespace.
+- `UriString::parse` will fail if the URI contains whitespace or is the empty string.
 
 ### Deprecated
 
diff --git a/UriString.php b/UriString.php
index c8c691e..1b50f6f 100644
--- a/UriString.php
+++ b/UriString.php
@@ -68,7 +68,6 @@ final class UriString
      * @var array<string, array<string>>
      */
     private const URI_SHORTCUTS = [
-        '' => [],
         '#' => ['fragment' => ''],
         '?' => ['query' => ''],
         '?#' => ['query' => '', 'fragment' => ''],
@@ -278,7 +277,12 @@ public static function buildAuthority(array $components): ?string
      */
     public static function normalize(Stringable|string $uri): string
     {
-        $components = UriString::parse($uri);
+        $uri = (string) $uri;
+        if ('' === $uri) {
+            return '';
+        }
+
+        $components = self::parse($uri);
         if (null !== $components['scheme']) {
             $components['scheme'] = strtolower($components['scheme']);
         }
@@ -339,6 +343,10 @@ public static function normalizeAuthority(Stringable|string $authority): string
      */
     public static function resolve(Stringable|string $uri, Stringable|string|null $baseUri = null): string
     {
+        if ('' === $uri) {
+            $uri = $baseUri ?? throw new SyntaxError('The uri can not be the empty string when there\'s no base URI.');
+        }
+
         $uri = self::parse($uri);
         $baseUri = null !== $baseUri ? self::parse($baseUri) : $uri;
         if (null === $baseUri['scheme']) {
@@ -492,7 +500,7 @@ public static function parse(Stringable|string|int $uri): array
             return $components;
         }
 
-        if (1 === preg_match(self::REGEXP_INVALID_URI_CHARS, $uri)) {
+        if ('' === $uri || 1 === preg_match(self::REGEXP_INVALID_URI_CHARS, $uri)) {
             throw new SyntaxError(sprintf('The uri `%s` contains invalid characters', $uri));
         }
 
diff --git a/UriStringTest.php b/UriStringTest.php
index cb76d4c..6676ec6 100644
--- a/UriStringTest.php
+++ b/UriStringTest.php
@@ -579,19 +579,6 @@ public static function validUriProvider(): array
                     'fragment' => 'foo=1/bar=2',
                 ],
             ],
-            'empty string' => [
-                '',
-                [
-                    'scheme' => null,
-                    'user' => null,
-                    'pass' => null,
-                    'host' => null,
-                    'port' => null,
-                    'path' => '',
-                    'query' => null,
-                    'fragment' => null,
-                ],
-            ],
             'complex URI' => [
                 'htà+d/s:totot',
                 [
@@ -776,6 +763,7 @@ public static function invalidUriProvider(): array
             'invalid host with fullwidth escaped' =>  ['http://%ef%bc%85%ef%bc%94%ef%bc%91.com],'],
             //'invalid pseudo IDN to ASCII string' => ['http://xn--3/foo.'],
             'invalid IDN' => ['//:�@�����������������������������������������������������������������������������������������/'],
+            'empty string' => [''],
         ];
     }
 
@@ -944,10 +932,6 @@ public static function buildUriProvider(): array
                 'http://example.com#foo=1/bar=2',
                 'http://example.com#foo=1/bar=2',
             ],
-            'empty string' => [
-                '',
-                '',
-            ],
             'complex URI' => [
                 'htà+d/s:totot',
                 'htà+d/s:totot',