Skip to content

Commit 4fcc5ff

Browse files
authored
refactor: fix the latent LSP violations in HTTP (#10463)
* refactor: fix the latent LSP violations in `HTTP` * add upgrade instructions * call `File::__construct` in `UploadedFile`'s constructor
1 parent 78510c1 commit 4fcc5ff

10 files changed

Lines changed: 95 additions & 29 deletions

File tree

system/HTTP/Files/UploadedFile.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ public function __construct(string $path, string $originalName, ?string $mimeTyp
123123
* @param bool $overwrite State for indicating whether to overwrite the previously generated file with the same
124124
* name or not.
125125
*
126-
* @return bool
126+
* @return static
127127
*/
128128
public function move(string $targetPath, ?string $name = null, bool $overwrite = false)
129129
{
@@ -165,7 +165,9 @@ public function move(string $targetPath, ?string $name = null, bool $overwrite =
165165
$this->path = $targetPath;
166166
$this->name = basename($destination);
167167

168-
return true;
168+
parent::__construct($destination, false);
169+
170+
return $this;
169171
}
170172

171173
/**

system/HTTP/Files/UploadedFileInterface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public function __construct(string $path, string $originalName, ?string $mimeTyp
6262
* @param string $targetPath Path to which to move the uploaded file.
6363
* @param string|null $name the name to rename the file to.
6464
*
65-
* @return bool
65+
* @return static
6666
*
6767
* @throws InvalidArgumentException if the $path specified is invalid.
6868
* @throws RuntimeException on the second or subsequent call to the method.

system/HTTP/IncomingRequest.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,6 @@ class IncomingRequest extends Request
5656
* everything this cares about (and the router, etc) is the portion
5757
* AFTER the baseURL. So, if hosted in a sub-folder this will
5858
* appear different than actual URI path. If you need that use getPath().
59-
*
60-
* @var URI
6159
*/
6260
protected $uri;
6361

system/HTTP/OutgoingRequest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class OutgoingRequest extends Message implements OutgoingRequestInterface
3030
/**
3131
* A URI instance.
3232
*
33-
* @var URI|null
33+
* @var URI
3434
*/
3535
protected $uri;
3636

@@ -41,7 +41,7 @@ class OutgoingRequest extends Message implements OutgoingRequestInterface
4141
*/
4242
public function __construct(
4343
string $method,
44-
?URI $uri = null,
44+
URI $uri,
4545
array $headers = [],
4646
$body = null,
4747
string $version = '1.1',
@@ -110,7 +110,7 @@ public function withMethod($method)
110110
/**
111111
* Retrieves the URI instance.
112112
*
113-
* @return URI|null
113+
* @return URI
114114
*/
115115
public function getUri()
116116
{

tests/system/HTTP/Files/FileMovingTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,34 @@ public function testMoved(): void
215215
$this->assertTrue($file->hasMoved());
216216
}
217217

218+
public function testPathnameAfterMoveReflectsTheMovedFile(): void
219+
{
220+
$finalFilename = 'fileA.txt';
221+
service('superglobals')->setFilesArray([
222+
'userfile1' => [
223+
'name' => $finalFilename,
224+
'type' => 'text/plain',
225+
'size' => 124,
226+
'tmp_name' => '/tmp/fileA.txt',
227+
'error' => 0,
228+
],
229+
]);
230+
231+
$collection = new FileCollection();
232+
233+
$destination = $this->destination;
234+
235+
if (! is_dir($destination)) {
236+
mkdir($destination, 0777, true);
237+
}
238+
239+
$file = $collection->getFile('userfile1');
240+
$this->assertInstanceOf(UploadedFile::class, $file);
241+
242+
$file->move($destination, $file->getName(), false);
243+
$this->assertSame($destination . '/' . $finalFilename, $file->getPathname());
244+
}
245+
218246
public function testStore(): void
219247
{
220248
$finalFilename = 'fileA';

user_guide_src/source/changelogs/v4.8.0.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ update your implementations to include the new methods or method changes to ensu
5555
- **Cache:** ``CodeIgniter\Cache\CacheInterface::remember()`` now accepts a TTL callable. Custom implementations of ``CacheInterface`` must update the ``$ttl`` parameter type from ``int`` to ``callable|int``.
5656
- **Database:** ``CodeIgniter\Database\ConnectionInterface`` now requires the ``afterCommit()``, ``afterRollback()``, ``inTransaction()``, and ``transaction()`` methods.
5757
- **HTTP:** ``CodeIgniter\HTTP\ResponseInterface`` now requires the ``stream()`` and ``eventStream()`` methods, which create streaming and SSE responses. See :ref:`streaming-responses`.
58+
- **HTTP:** ``CodeIgniter\HTTP\Files\UploadedFileInterface::move()`` now returns ``static`` instead of ``bool``. The previous ``bool`` return was incompatible with ``CodeIgniter\Files\File::move()``, which ``UploadedFile`` extends, so no implementation could satisfy both. See :doc:`../installation/upgrade_480` for the migration.
5859
- **Logging:** ``CodeIgniter\Log\Handlers\HandlerInterface::handle()`` now requires a third parameter ``array $context = []``. Any custom log handler that overrides ``handle()`` - whether implementing ``HandlerInterface`` directly or extending a built-in handler class - must add the parameter to its ``handle()`` method signature.
5960
- **Security:** The ``SecurityInterface``'s ``verify()`` method now has a native return type of ``static``.
6061
- **Validation:** ``CodeIgniter\Validation\ValidationInterface`` now requires the ``getValidatedInput()`` method, which returns a ``CodeIgniter\Input\ValidatedInput`` instance.
@@ -73,6 +74,7 @@ Method Signature Changes
7374
- **Config:** ``CodeIgniter\Config\Services::request()`` no longer accepts any parameter.
7475
- **Database:** The following methods have had their signatures updated to remove deprecated parameters:
7576
- ``CodeIgniter\Database\Forge::_createTable()`` no longer accepts the deprecated ``$ifNotExists`` parameter. The method signature is now ``_createTable(string $table, array $attributes)``.
77+
- **HTTP:** ``CodeIgniter\HTTP\OutgoingRequest::__construct()`` now requires the ``$uri`` parameter, which was previously ``?URI $uri = null``. Passing ``null`` only worked when a ``Host`` header was supplied in the same call, since the constructor otherwise dereferences the URI to set that header. Consequently ``OutgoingRequest::getUri()`` now returns ``URI`` instead of ``URI|null``, matching ``OutgoingRequestInterface``. See :doc:`../installation/upgrade_480` for the migration.
7678
- **Model:** ``CodeIgniter\BaseModel`` now requires the ``chunkRows()``, ``chunkById()``, and ``chunkRowsById()`` methods. Custom classes extending ``BaseModel`` directly must implement them.
7779
- **Session:** The ``$max_lifetime`` parameter of the following ``gc()`` methods now has the native ``int`` type, matching ``SessionHandlerInterface``: ``ArrayHandler::gc()``, ``DatabaseHandler::gc()``, ``FileHandler::gc()``, ``MemcachedHandler::gc()``, ``PostgreHandler::gc()``, ``RedisHandler::gc()``.
7880

user_guide_src/source/installation/upgrade_480.rst

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,61 @@ Previously, returning a non-integer value from a command run through ``spark`` w
2727
Starting with v4.8.0, this behavior is still supported but will trigger a deprecation notice. Commands should now return an integer exit code
2828
to ensure proper behavior across all platforms.
2929

30+
Uploaded File Move Return Type
31+
==============================
32+
33+
``CodeIgniter\HTTP\Files\UploadedFileInterface::move()`` now returns ``static``
34+
instead of ``bool``, matching ``CodeIgniter\Files\File::move()`` which
35+
``UploadedFile`` extends.
36+
37+
If you have a custom implementation of ``UploadedFileInterface``, or a class
38+
extending ``UploadedFile`` that overrides ``move()``, return the instance
39+
instead of ``true``:
40+
41+
.. code-block:: php
42+
43+
// Before
44+
public function move(string $targetPath, ?string $name = null, bool $overwrite = false)
45+
{
46+
// ...
47+
48+
return true;
49+
}
50+
51+
// After
52+
public function move(string $targetPath, ?string $name = null, bool $overwrite = false)
53+
{
54+
// ...
55+
56+
return $this;
57+
}
58+
59+
Calling code that only tests the result, such as ``if ($file->move($path))``,
60+
needs no change because the returned instance is truthy. Code comparing the
61+
result strictly against ``true`` must be updated.
62+
63+
Outgoing Request Constructor
64+
============================
65+
66+
``CodeIgniter\HTTP\OutgoingRequest::__construct()`` now requires the ``$uri``
67+
parameter, which was previously ``?URI $uri = null``. Consequently
68+
``OutgoingRequest::getUri()`` now returns ``URI`` instead of ``URI|null``.
69+
70+
Passing ``null`` only worked when a ``Host`` header was supplied in the same
71+
call, because the constructor's host check short-circuits before dereferencing
72+
the URI. Such calls must now pass a ``URI``:
73+
74+
.. code-block:: php
75+
76+
// Before
77+
$request = new OutgoingRequest('GET', null, ['Host' => 'example.com']);
78+
79+
// After
80+
$request = new OutgoingRequest('GET', new URI('http://example.com'), ['Host' => 'example.com']);
81+
82+
Any other call that omitted ``$uri`` or passed ``null`` already failed with
83+
``Call to a member function getHost() on null``, so it needs no migration.
84+
3085
*********************
3186
Breaking Enhancements
3287
*********************

utils/phpstan-baseline/loader.neon

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# total 692 errors
1+
# total 689 errors
22

33
includes:
44
- argument.type.neon
@@ -8,7 +8,6 @@ includes:
88
- deadCode.unreachable.neon
99
- function.resultUnused.neon
1010
- method.childParameterType.neon
11-
- method.childReturnType.neon
1211
- method.notFound.neon
1312
- missingType.callable.neon
1413
- missingType.iterableValue.neon

utils/phpstan-baseline/method.childReturnType.neon

Lines changed: 0 additions & 13 deletions
This file was deleted.

utils/phpstan-baseline/property.phpDocType.neon

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# total 22 errors
1+
# total 21 errors
22

33
parameters:
44
ignoreErrors:
@@ -77,11 +77,6 @@ parameters:
7777
count: 1
7878
path: ../../system/HTTP/Exceptions/RedirectException.php
7979

80-
-
81-
message: '#^PHPDoc type CodeIgniter\\HTTP\\URI of property CodeIgniter\\HTTP\\IncomingRequest\:\:\$uri is not the same as PHPDoc type CodeIgniter\\HTTP\\URI\|null of overridden property CodeIgniter\\HTTP\\OutgoingRequest\:\:\$uri\.$#'
82-
count: 1
83-
path: ../../system/HTTP/IncomingRequest.php
84-
8580
-
8681
message: '#^PHPDoc type string of property CodeIgniter\\Session\\Handlers\\FileHandler\:\:\$savePath is not the same as PHPDoc type array\<string, mixed\>\|string of overridden property CodeIgniter\\Session\\Handlers\\BaseHandler\:\:\$savePath\.$#'
8782
count: 1

0 commit comments

Comments
 (0)