Skip to content

Commit 5de7742

Browse files
committed
Merge remote-tracking branch 'upstream/develop' into 4.5
2 parents de116cc + 79c1b2f commit 5de7742

File tree

15 files changed

+136
-30
lines changed

15 files changed

+136
-30
lines changed

.github/workflows/test-phpunit.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,17 +95,17 @@ jobs:
9595
- SQLSRV
9696
- SQLite3
9797
mysql-version:
98-
- '5.7'
98+
- '8.0'
9999
include:
100100
- php-version: '8.1'
101101
db-platform: MySQLi
102-
mysql-version: '8.0'
102+
mysql-version: '5.7'
103103
- php-version: '8.3'
104104
composer-option: '--ignore-platform-req=php'
105105

106106
uses: ./.github/workflows/reusable-phpunit-test.yml # @TODO Extract to codeigniter4/.github repo
107107
with:
108-
job-name:
108+
job-name: ''
109109
php-version: ${{ matrix.php-version }}
110110
job-id: database-live-tests
111111
db-platform: ${{ matrix.db-platform }}
@@ -135,7 +135,7 @@ jobs:
135135

136136
uses: ./.github/workflows/reusable-phpunit-test.yml # @TODO Extract to codeigniter4/.github repo
137137
with:
138-
job-name:
138+
job-name: ''
139139
php-version: ${{ matrix.php-version }}
140140
job-id: separate-process-tests
141141
group-name: SeparateProcess

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
"phpunit/phpcov": "^8.2",
3434
"phpunit/phpunit": "^9.1",
3535
"predis/predis": "^1.1 || ^2.0",
36-
"rector/rector": "0.18.8",
36+
"rector/rector": "0.18.10",
3737
"vimeo/psalm": "^5.0"
3838
},
3939
"suggest": {

system/Common.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@
2525
use CodeIgniter\HTTP\IncomingRequest;
2626
use CodeIgniter\HTTP\RedirectResponse;
2727
use CodeIgniter\HTTP\RequestInterface;
28-
use CodeIgniter\HTTP\Response;
2928
use CodeIgniter\HTTP\ResponseInterface;
30-
use CodeIgniter\HTTP\URI;
3129
use CodeIgniter\Model;
3230
use CodeIgniter\Session\Session;
3331
use CodeIgniter\Test\TestLogger;

system/HTTP/Exceptions/HTTPException.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,19 @@ public static function forInvalidNegotiationType(string $type)
7272
return new static(lang('HTTP.invalidNegotiationType', [$type]));
7373
}
7474

75+
/**
76+
* Thrown in IncomingRequest when the json_decode() produces
77+
* an error code other than JSON_ERROR_NONE.
78+
*
79+
* @param string $error The error message
80+
*
81+
* @return static
82+
*/
83+
public static function forInvalidJSON(?string $error = null)
84+
{
85+
return new static(lang('HTTP.invalidJSON', [$error]));
86+
}
87+
7588
/**
7689
* For Message
7790
*

system/HTTP/IncomingRequest.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,10 +569,22 @@ public function getVar($index = null, $filter = null, $flags = null)
569569
* @see http://php.net/manual/en/function.json-decode.php
570570
*
571571
* @return array|bool|float|int|stdClass|null
572+
*
573+
* @throws HTTPException When the body is invalid as JSON.
572574
*/
573575
public function getJSON(bool $assoc = false, int $depth = 512, int $options = 0)
574576
{
575-
return json_decode($this->body ?? '', $assoc, $depth, $options);
577+
if ($this->body === null) {
578+
return null;
579+
}
580+
581+
$result = json_decode($this->body, $assoc, $depth, $options);
582+
583+
if (json_last_error() !== JSON_ERROR_NONE) {
584+
throw HTTPException::forInvalidJSON(json_last_error_msg());
585+
}
586+
587+
return $result;
576588
}
577589

578590
/**

system/Images/Handlers/GDHandler.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ public function save(?string $target = null, int $quality = 90): bool
227227

228228
// for png and webp we can actually preserve transparency
229229
if (in_array($this->image()->imageType, $this->supportTransparency, true)) {
230+
imagepalettetotruecolor($this->resource);
230231
imagealphablending($this->resource, false);
231232
imagesavealpha($this->resource, true);
232233
}

system/Language/en/HTTP.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
// IncomingRequest
2121
'invalidNegotiationType' => '"{0}" is not a valid negotiation type. Must be one of: media, charset, encoding, language.',
22+
'invalidJSON' => 'Failed to parse JSON string. Error: {0}',
2223

2324
// Message
2425
'invalidHTTPProtocol' => 'Invalid HTTP Protocol Version: {0}',

system/Test/FilterTestTrait.php

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -125,35 +125,42 @@ protected function getFilterCaller($filter, string $position): Closure
125125
throw new InvalidArgumentException('Invalid filter position passed: ' . $position);
126126
}
127127

128+
if ($filter instanceof FilterInterface) {
129+
$filterInstances = [$filter];
130+
}
131+
128132
if (is_string($filter)) {
129133
// Check for an alias (no namespace)
130134
if (strpos($filter, '\\') === false) {
131135
if (! isset($this->filtersConfig->aliases[$filter])) {
132136
throw new RuntimeException("No filter found with alias '{$filter}'");
133137
}
134138

135-
$filterClasses = $this->filtersConfig->aliases[$filter];
139+
$filterClasses = (array) $this->filtersConfig->aliases[$filter];
140+
} else {
141+
// FQCN
142+
$filterClasses = [$filter];
136143
}
137144

138-
$filterClasses = (array) $filterClasses;
139-
}
145+
$filterInstances = [];
140146

141-
foreach ($filterClasses as $class) {
142-
// Get an instance
143-
$filter = new $class();
147+
foreach ($filterClasses as $class) {
148+
// Get an instance
149+
$filter = new $class();
144150

145-
if (! $filter instanceof FilterInterface) {
146-
throw FilterException::forIncorrectInterface(get_class($filter));
151+
if (! $filter instanceof FilterInterface) {
152+
throw FilterException::forIncorrectInterface(get_class($filter));
153+
}
154+
155+
$filterInstances[] = $filter;
147156
}
148157
}
149158

150159
$request = clone $this->request;
151160

152161
if ($position === 'before') {
153-
return static function (?array $params = null) use ($filterClasses, $request) {
154-
foreach ($filterClasses as $class) {
155-
$filter = new $class();
156-
162+
return static function (?array $params = null) use ($filterInstances, $request) {
163+
foreach ($filterInstances as $filter) {
157164
$result = $filter->before($request, $params);
158165

159166
// @TODO The following logic is in Filters class.
@@ -177,10 +184,8 @@ protected function getFilterCaller($filter, string $position): Closure
177184

178185
$response = clone $this->response;
179186

180-
return static function (?array $params = null) use ($filterClasses, $request, $response) {
181-
foreach ($filterClasses as $class) {
182-
$filter = new $class();
183-
187+
return static function (?array $params = null) use ($filterInstances, $request, $response) {
188+
foreach ($filterInstances as $filter) {
184189
$result = $filter->after($request, $response, $params);
185190

186191
// @TODO The following logic is in Filters class.

tests/_support/Images/rocket.png

55.6 KB
Loading

tests/system/HTTP/IncomingRequestTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use CodeIgniter\Test\CIUnitTestCase;
1919
use Config\App;
2020
use InvalidArgumentException;
21+
use JsonException;
2122
use TypeError;
2223

2324
/**
@@ -528,6 +529,32 @@ public function testGetJSONReturnsNullFromNullBody(): void
528529
$this->assertNull($request->getJSON());
529530
}
530531

532+
public function testGetJSONWithInvalidJSONString(): void
533+
{
534+
$this->expectException(HTTPException::class);
535+
$this->expectExceptionMessage('Failed to parse JSON string. Error: Syntax error');
536+
537+
$config = new App();
538+
$config->baseURL = 'http://example.com/';
539+
$json = 'Invalid JSON string';
540+
$request = $this->createRequest($config, $json);
541+
542+
$request->getJSON();
543+
}
544+
545+
public function testGetJSONWithJsonThrowOnErrorAndInvalidJSONString(): void
546+
{
547+
$this->expectException(JsonException::class);
548+
$this->expectExceptionMessage('Syntax error');
549+
550+
$config = new App();
551+
$config->baseURL = 'http://example.com/';
552+
$json = 'Invalid JSON string';
553+
$request = $this->createRequest($config, $json);
554+
555+
$request->getJSON(false, 512, JSON_THROW_ON_ERROR);
556+
}
557+
531558
public function testCanGrabGetRawInput(): void
532559
{
533560
$rawstring = 'username=admin001&role=administrator&usepass=0';

tests/system/Images/GDHandlerTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,15 @@ public function testImageConvert(): void
412412
$this->assertSame(exif_imagetype($this->start . 'work/ci-logo.png'), IMAGETYPE_PNG);
413413
}
414414

415+
public function testImageConvertPngToWebp(): void
416+
{
417+
$this->handler->withFile($this->origin . 'rocket.png');
418+
$this->handler->convert(IMAGETYPE_WEBP);
419+
$saved = $this->start . 'work/rocket.webp';
420+
$this->handler->save($saved);
421+
$this->assertSame(exif_imagetype($saved), IMAGETYPE_WEBP);
422+
}
423+
415424
public function testImageReorientLandscape(): void
416425
{
417426
for ($i = 0; $i <= 8; $i++) {

tests/system/Test/FilterTestTraitTest.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public function testGetCallerInvalidPosition(): void
6363
$this->getFilterCaller('test-customfilter', 'banana');
6464
}
6565

66-
public function testCallerSupportArray(): void
66+
public function testCallerSupportsArray(): void
6767
{
6868
$this->filtersConfig->aliases['test-customfilter'] = [Customfilter::class];
6969

@@ -73,6 +73,22 @@ public function testCallerSupportArray(): void
7373
$this->assertSame('http://hellowworld.com', $result->getBody());
7474
}
7575

76+
public function testCallerSupportsClassname(): void
77+
{
78+
$caller = $this->getFilterCaller(Customfilter::class, 'before');
79+
$result = $caller();
80+
81+
$this->assertSame('http://hellowworld.com', $result->getBody());
82+
}
83+
84+
public function testCallerSupportsFilterInstance(): void
85+
{
86+
$caller = $this->getFilterCaller(new Customfilter(), 'before');
87+
$result = $caller();
88+
89+
$this->assertSame('http://hellowworld.com', $result->getBody());
90+
}
91+
7692
public function testCallerUsesClonedInstance(): void
7793
{
7894
$caller = $this->getFilterCaller('test-customfilter', 'before');

tests/system/Validation/ValidationTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace CodeIgniter\Validation;
1313

14+
use CodeIgniter\HTTP\Exceptions\HTTPException;
1415
use CodeIgniter\HTTP\IncomingRequest;
1516
use CodeIgniter\HTTP\SiteURI;
1617
use CodeIgniter\HTTP\UserAgent;
@@ -884,6 +885,25 @@ public function testJsonInput(): void
884885
unset($_SERVER['CONTENT_TYPE']);
885886
}
886887

888+
public function testJsonInputInvalid(): void
889+
{
890+
$this->expectException(HTTPException::class);
891+
$this->expectExceptionMessage('Failed to parse JSON string. Error: Syntax error');
892+
893+
$config = new App();
894+
$json = 'invalid';
895+
$request = new IncomingRequest($config, new URI(), $json, new UserAgent());
896+
$request->setHeader('Content-Type', 'application/json');
897+
898+
$rules = [
899+
'role' => 'if_exist|max_length[5]',
900+
];
901+
$this->validation
902+
->withRequest($request->withMethod('POST'))
903+
->setRules($rules)
904+
->run();
905+
}
906+
887907
/**
888908
* @see https://github.com/codeigniter4/CodeIgniter4/issues/6466
889909
*/

user_guide_src/source/changelogs/v4.4.4.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ and Traditional rules validate data of non-string types.
3131
Message Changes
3232
***************
3333

34+
- Added ``HTTP.invalidJSON`` error message.
35+
3436
*******
3537
Changes
3638
*******

user_guide_src/source/libraries/validation.rst

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,7 +1012,13 @@ valid_cc_number Yes Verifies that the credit card number matches
10121012
Rules for File Uploads
10131013
======================
10141014

1015-
These validation rules enable you to do the basic checks you might need to verify that uploaded files meet your business needs.
1015+
When you validate uploaded files, you must use the rules specifically created for
1016+
file validation.
1017+
1018+
.. important:: Only rules that listed in the table below can be used to validate
1019+
files. Therefore, adding any general rules, like ``permit_empty``, to file
1020+
validation rules array or string, the file validation will not work correctly.
1021+
10161022
Since the value of a file upload HTML field doesn't exist, and is stored in the ``$_FILES`` global, the name of the input field will
10171023
need to be used twice. Once to specify the field name as you would for any other rule, but again as the first parameter of all
10181024
file upload related rules::
@@ -1054,7 +1060,3 @@ is_image Yes Fails if the file cannot be determined to be
10541060
======================= ========== ============================================= ===================================================
10551061

10561062
The file validation rules apply for both single and multiple file uploads.
1057-
1058-
.. note:: Only rules specifically created for file validation (like the ones listed in the table above) can be used to validate files.
1059-
Therefore, adding any general rules, like ``permit_empty``, to file validation rules array or string, the file validation will not
1060-
work correctly.

0 commit comments

Comments
 (0)