Browse Source

Add Uri subclass to avoid dynamic properties

Remove the usage of dynamic properties on Uri by subclassing and making
the properties concrete. I'm not sure I like using the builder methods
to reconstruct the URI. Another solution I might explore is to create
proxy methods, but first I want to profile the runtime impacts of this
change.
Mark Story 4 years ago
parent
commit
68199cf635
2 changed files with 86 additions and 9 deletions
  1. 2 9
      src/Http/ServerRequestFactory.php
  2. 84 0
      src/Http/Uri.php

+ 2 - 9
src/Http/ServerRequestFactory.php

@@ -228,7 +228,7 @@ abstract class ServerRequestFactory implements ServerRequestFactoryInterface
      *
      * @param array $server The server parameters.
      * @param array $headers The normalized headers
-     * @return \Psr\Http\Message\UriInterface a constructed Uri
+     * @return \Cake\Http\Uri A constructed Uri
      */
     protected static function marshalUriFromSapi(array $server, array $headers): UriInterface
     {
@@ -248,14 +248,7 @@ abstract class ServerRequestFactory implements ServerRequestFactoryInterface
             $uri = $uri->withHost('localhost');
         }
 
-        // Splat on some extra attributes to save
-        // some method calls.
-        /** @psalm-suppress NoInterfaceProperties */
-        $uri->base = $base;
-        /** @psalm-suppress NoInterfaceProperties */
-        $uri->webroot = $webroot;
-
-        return $uri;
+        return Uri::fromLaminas($uri, $base, $webroot);
     }
 
     /**

+ 84 - 0
src/Http/Uri.php

@@ -0,0 +1,84 @@
+<?php
+declare(strict_types=1);
+
+namespace Cake\Http;
+
+use Laminas\Diactoros\Uri as LaminasUri;
+
+/**
+ * The base and webroot properties have piggybacked on the Uri for
+ * a long time. To preserve backwards compatibility and avoid dynamic
+ * property errors in PHP 8.2 we use this subclass.
+ */
+class Uri extends LaminasUri
+{
+    /**
+     * @var string
+     */
+    private $base = '';
+
+    /**
+     * @var string
+     */
+    private $webroot = '';
+
+    /**
+     * Create a subclassed Uri that includes additional CakePHP properties
+     *
+     * @param \Laminas\Diactoros\Uri $uri Uri instance to copy
+     * @param string $base The base path.
+     * @param string $webroot The webroot path.
+     * @return self
+     */
+    public static function fromLaminas(LaminasUri $uri, string $base, string $webroot): self
+    {
+        $copy = (new self())
+            ->withScheme($uri->getScheme())
+            ->withHost($uri->getHost())
+            ->withUserInfo($uri->getUserInfo())
+            ->withPort($uri->getPort())
+            ->withPath($uri->getPath())
+            ->withQuery($uri->getQuery())
+            ->withFragment($uri->getFragment());
+
+        $copy->base = $base;
+        $copy->webroot = $webroot;
+
+        return $copy;
+    }
+
+    /**
+     * Backwards compatible property read
+     *
+     * @param string $prop The property to read.
+     * @return string|null
+     */
+    public function __get($prop): ?string
+    {
+        if ($prop !== 'base' && $prop !== 'webroot') {
+            return null;
+        }
+
+        return $this->{$prop};
+    }
+
+    /**
+     * Get the application base path.
+     *
+     * @return string
+     */
+    public function getBase(): string
+    {
+        return $this->base;
+    }
+
+    /**
+     * Get the application webroot path.
+     *
+     * @return string
+     */
+    public function getWebroot(): string
+    {
+        return $this->webroot;
+    }
+}