Skip to content

Commit 7ea9372

Browse files
committed
Merge remote-tracking branch 'upstream/develop' into 4.4
2 parents 0b989f1 + 315f8c6 commit 7ea9372

File tree

19 files changed

+201
-78
lines changed

19 files changed

+201
-78
lines changed

admin/framework/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"psr/log": "^1.1"
1414
},
1515
"require-dev": {
16-
"kint-php/kint": "^5.0.1",
16+
"kint-php/kint": "^5.0.3",
1717
"codeigniter/coding-standard": "^1.5",
1818
"fakerphp/faker": "^1.9",
1919
"friendsofphp/php-cs-fixer": "3.13.0",

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"psr/log": "^1.1"
1414
},
1515
"require-dev": {
16-
"kint-php/kint": "^5.0.1",
16+
"kint-php/kint": "^5.0.3",
1717
"codeigniter/coding-standard": "^1.5",
1818
"fakerphp/faker": "^1.9",
1919
"friendsofphp/php-cs-fixer": "3.13.0",
@@ -25,7 +25,7 @@
2525
"phpunit/phpcov": "^8.2",
2626
"phpunit/phpunit": "^9.1",
2727
"predis/predis": "^1.1 || ^2.0",
28-
"rector/rector": "0.15.11",
28+
"rector/rector": "0.15.12",
2929
"vimeo/psalm": "^5.0"
3030
},
3131
"suggest": {

contributing/signing.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,7 @@ bash shell to use the **-S** option to force the secure signing.
5353

5454
Regardless of how you sign a commit, commit messages are important too.
5555
See [Contribution Workflow](./workflow.md#commit-messages) for details.
56+
57+
## GPG-Signing Old Commits
58+
59+
See [Contribution Workflow](./workflow.md#gpg-signing-old-commits).

phpstan-baseline.neon.dist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ parameters:
217217

218218
-
219219
message: "#^Call to an undefined method CodeIgniter\\\\Router\\\\RouteCollectionInterface\\:\\:getDefaultNamespace\\(\\)\\.$#"
220-
count: 3
220+
count: 2
221221
path: system/Router/Router.php
222222

223223
-

system/Debug/Exceptions.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ class Exceptions
6868
*/
6969
protected $response;
7070

71+
private ?Throwable $exceptionCaughtByExceptionHandler = null;
72+
7173
/**
7274
* @param CLIRequest|IncomingRequest $request
7375
*/
@@ -113,6 +115,8 @@ public function initialize()
113115
*/
114116
public function exceptionHandler(Throwable $exception)
115117
{
118+
$this->exceptionCaughtByExceptionHandler = $exception;
119+
116120
[$statusCode, $exitCode] = $this->determineCodes($exception);
117121

118122
if ($this->config->log === true && ! in_array($statusCode, $this->config->ignoreCodes, true)) {
@@ -191,6 +195,13 @@ public function shutdownHandler()
191195

192196
['type' => $type, 'message' => $message, 'file' => $file, 'line' => $line] = $error;
193197

198+
if ($this->exceptionCaughtByExceptionHandler) {
199+
$message .= "\n【Previous Exception】\n"
200+
. get_class($this->exceptionCaughtByExceptionHandler) . "\n"
201+
. $this->exceptionCaughtByExceptionHandler->getMessage() . "\n"
202+
. $this->exceptionCaughtByExceptionHandler->getTraceAsString();
203+
}
204+
194205
if (in_array($type, [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE], true)) {
195206
$this->exceptionHandler(new ErrorException($message, 0, $type, $file, $line));
196207
}

system/HTTP/IncomingRequest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ protected function parseQueryString(): string
329329
$uri = $_SERVER['QUERY_STRING'] ?? @getenv('QUERY_STRING');
330330

331331
if (trim($uri, '/') === '') {
332-
return '';
332+
return '/';
333333
}
334334

335335
if (strncmp($uri, '/', 1) === 0) {

system/Helpers/form_helper.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -741,17 +741,20 @@ function validation_show_error(string $field, string $template = 'single'): stri
741741
$config = config('Validation');
742742
$view = Services::renderer();
743743

744-
$errors = validation_errors();
744+
$errors = array_filter(validation_errors(), static fn ($key) => preg_match(
745+
'/^' . str_replace(['\.\*', '\*\.'], ['\..+', '.+\.'], preg_quote($field, '/')) . '$/',
746+
$key
747+
), ARRAY_FILTER_USE_KEY);
745748

746-
if (! array_key_exists($field, $errors)) {
749+
if ($errors === []) {
747750
return '';
748751
}
749752

750753
if (! array_key_exists($template, $config->templates)) {
751754
throw ValidationException::forInvalidTemplate($template);
752755
}
753756

754-
return $view->setVar('error', $errors[$field])
757+
return $view->setVar('error', implode("\n", $errors))
755758
->render($config->templates[$template]);
756759
}
757760
}

system/Router/Router.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,19 +155,20 @@ public function __construct(RouteCollectionInterface $routes, ?Request $request
155155
}
156156

157157
/**
158+
* Finds the controller method corresponding to the URI.
159+
*
160+
* @param string|null $uri URI path relative to baseURL
161+
*
158162
* @return Closure|string Controller classname or Closure
159163
*
160164
* @throws PageNotFoundException
161165
* @throws RedirectException
162166
*/
163167
public function handle(?string $uri = null)
164168
{
165-
// If we cannot find a URI to match against, then
166-
// everything runs off of its default settings.
169+
// If we cannot find a URI to match against, then set it to root (`/`).
167170
if ($uri === null || $uri === '') {
168-
return strpos($this->controller, '\\') === false
169-
? $this->collection->getDefaultNamespace() . $this->controller
170-
: $this->controller;
171+
$uri = '/';
171172
}
172173

173174
// Decode URL-encoded string

system/Router/RouterInterface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public function __construct(RouteCollectionInterface $routes, ?Request $request
2727
/**
2828
* Finds the controller method corresponding to the URI.
2929
*
30-
* @param string $uri
30+
* @param string|null $uri URI path relative to baseURL
3131
*
3232
* @return Closure|string Controller classname or Closure
3333
*/

system/ThirdParty/Kint/Utils.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,9 @@ public static function composerGetExtras(string $key = 'kint'): array
118118
continue;
119119
}
120120

121-
foreach ($packages as $package) {
121+
// Composer 2.0 Compatibility: packages are now wrapped into a "packages" top level key instead of the whole file being the package array
122+
// @see https://getcomposer.org/upgrade/UPGRADE-2.0.md
123+
foreach ($packages['packages'] ?? $packages as $package) {
122124
if (isset($package['extra'][$key]) && \is_array($package['extra'][$key])) {
123125
$extras = \array_replace($extras, $package['extra'][$key]);
124126
}

tests/system/HTTP/IncomingRequestDetectingTest.php

Lines changed: 61 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -31,127 +31,158 @@ protected function setUp(): void
3131

3232
$_POST = $_GET = $_SERVER = $_REQUEST = $_ENV = $_COOKIE = $_SESSION = [];
3333

34-
$origin = 'http://www.example.com/index.php/woot?code=good#pos';
35-
34+
// The URI object is not used in detectPath().
35+
$origin = 'http://www.example.com/index.php/woot?code=good#pos';
3636
$this->request = new IncomingRequest(new App(), new URI($origin), null, new UserAgent());
3737
}
3838

3939
public function testPathDefault()
4040
{
41-
$this->request->uri = '/index.php/woot?code=good#pos';
41+
// /index.php/woot?code=good#pos
4242
$_SERVER['REQUEST_URI'] = '/index.php/woot';
4343
$_SERVER['SCRIPT_NAME'] = '/index.php';
44-
$expected = 'woot';
44+
45+
$expected = 'woot';
4546
$this->assertSame($expected, $this->request->detectPath());
4647
}
4748

48-
public function testPathEmpty()
49+
public function testPathDefaultEmpty()
4950
{
50-
$this->request->uri = '/';
51+
// /
5152
$_SERVER['REQUEST_URI'] = '/';
5253
$_SERVER['SCRIPT_NAME'] = '/index.php';
53-
$expected = '/';
54+
55+
$expected = '/';
5456
$this->assertSame($expected, $this->request->detectPath());
5557
}
5658

5759
public function testPathRequestURI()
5860
{
59-
$this->request->uri = '/index.php/woot?code=good#pos';
61+
// /index.php/woot?code=good#pos
6062
$_SERVER['REQUEST_URI'] = '/index.php/woot';
6163
$_SERVER['SCRIPT_NAME'] = '/index.php';
62-
$expected = 'woot';
64+
65+
$expected = 'woot';
6366
$this->assertSame($expected, $this->request->detectPath('REQUEST_URI'));
6467
}
6568

6669
public function testPathRequestURINested()
6770
{
68-
$this->request->uri = '/ci/index.php/woot?code=good#pos';
71+
// I'm not sure but this is a case of Apache config making such SERVER
72+
// values?
73+
// The current implementation doesn't use the value of the URI object.
74+
// So I removed the code to set URI. Therefore, it's exactly the same as
75+
// the method above as a test.
76+
// But it may be changed in the future to use the value of the URI object.
77+
// So I don't remove this test case.
78+
79+
// /ci/index.php/woot?code=good#pos
6980
$_SERVER['REQUEST_URI'] = '/index.php/woot';
7081
$_SERVER['SCRIPT_NAME'] = '/index.php';
71-
$expected = 'woot';
82+
83+
$expected = 'woot';
7284
$this->assertSame($expected, $this->request->detectPath('REQUEST_URI'));
7385
}
7486

7587
public function testPathRequestURISubfolder()
7688
{
77-
$this->request->uri = '/ci/index.php/popcorn/woot?code=good#pos';
89+
// /ci/index.php/popcorn/woot?code=good#pos
7890
$_SERVER['REQUEST_URI'] = '/ci/index.php/popcorn/woot';
7991
$_SERVER['SCRIPT_NAME'] = '/ci/index.php';
80-
$expected = 'popcorn/woot';
92+
93+
$expected = 'popcorn/woot';
8194
$this->assertSame($expected, $this->request->detectPath('REQUEST_URI'));
8295
}
8396

8497
public function testPathRequestURINoIndex()
8598
{
86-
$this->request->uri = '/sub/example';
99+
// /sub/example
87100
$_SERVER['REQUEST_URI'] = '/sub/example';
88101
$_SERVER['SCRIPT_NAME'] = '/sub/index.php';
89-
$expected = 'example';
102+
103+
$expected = 'example';
90104
$this->assertSame($expected, $this->request->detectPath('REQUEST_URI'));
91105
}
92106

93107
public function testPathRequestURINginx()
94108
{
95-
$this->request->uri = '/ci/index.php/woot?code=good#pos';
109+
// /ci/index.php/woot?code=good#pos
96110
$_SERVER['REQUEST_URI'] = '/index.php/woot?code=good';
97111
$_SERVER['SCRIPT_NAME'] = '/index.php';
98-
$expected = 'woot';
112+
113+
$expected = 'woot';
99114
$this->assertSame($expected, $this->request->detectPath('REQUEST_URI'));
100115
}
101116

102117
public function testPathRequestURINginxRedirecting()
103118
{
104-
$this->request->uri = '/?/ci/index.php/woot';
119+
// /?/ci/index.php/woot
105120
$_SERVER['REQUEST_URI'] = '/?/ci/woot';
106121
$_SERVER['SCRIPT_NAME'] = '/index.php';
107-
$expected = 'ci/woot';
122+
123+
$expected = 'ci/woot';
108124
$this->assertSame($expected, $this->request->detectPath('REQUEST_URI'));
109125
}
110126

111127
public function testPathRequestURISuppressed()
112128
{
113-
$this->request->uri = '/woot?code=good#pos';
129+
// /woot?code=good#pos
114130
$_SERVER['REQUEST_URI'] = '/woot';
115131
$_SERVER['SCRIPT_NAME'] = '/';
116-
$expected = 'woot';
132+
133+
$expected = 'woot';
117134
$this->assertSame($expected, $this->request->detectPath('REQUEST_URI'));
118135
}
119136

120137
public function testPathQueryString()
121138
{
122-
$this->request->uri = '/?/ci/index.php/woot';
123-
$_SERVER['REQUEST_URI'] = '/?/ci/woot';
139+
// /index.php?/ci/woot
140+
$_SERVER['REQUEST_URI'] = '/index.php?/ci/woot';
124141
$_SERVER['QUERY_STRING'] = '/ci/woot';
125142
$_SERVER['SCRIPT_NAME'] = '/index.php';
126-
$expected = 'ci/woot';
143+
144+
$expected = 'ci/woot';
145+
$this->assertSame($expected, $this->request->detectPath('QUERY_STRING'));
146+
}
147+
148+
public function testPathQueryStringWithQueryString()
149+
{
150+
// /index.php?/ci/woot?code=good#pos
151+
$_SERVER['REQUEST_URI'] = '/index.php?/ci/woot?code=good';
152+
$_SERVER['QUERY_STRING'] = '/ci/woot?code=good';
153+
$_SERVER['SCRIPT_NAME'] = '/index.php';
154+
155+
$expected = 'ci/woot';
127156
$this->assertSame($expected, $this->request->detectPath('QUERY_STRING'));
128157
}
129158

130159
public function testPathQueryStringEmpty()
131160
{
132-
$this->request->uri = '/?/ci/index.php/woot';
133-
$_SERVER['REQUEST_URI'] = '/?/ci/woot';
161+
// /index.php?
162+
$_SERVER['REQUEST_URI'] = '/index.php?';
134163
$_SERVER['QUERY_STRING'] = '';
135164
$_SERVER['SCRIPT_NAME'] = '/index.php';
136-
$expected = '';
165+
166+
$expected = '/';
137167
$this->assertSame($expected, $this->request->detectPath('QUERY_STRING'));
138168
}
139169

140170
public function testPathPathInfo()
141171
{
142-
$this->request->uri = '/index.php/woot?code=good#pos';
172+
// /index.php/woot?code=good#pos
143173
$this->request->setGlobal('server', [
144174
'PATH_INFO' => null,
145175
]);
146176
$_SERVER['REQUEST_URI'] = '/index.php/woot';
147177
$_SERVER['SCRIPT_NAME'] = '/index.php';
148-
$expected = 'woot';
178+
179+
$expected = 'woot';
149180
$this->assertSame($expected, $this->request->detectPath('PATH_INFO'));
150181
}
151182

152183
public function testPathPathInfoGlobal()
153184
{
154-
$this->request->uri = '/index.php/woot?code=good#pos';
185+
// /index.php/woot?code=good#pos
155186
$this->request->setGlobal('server', [
156187
'PATH_INFO' => 'silliness',
157188
]);

tests/system/Helpers/FormHelperTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,25 @@ public function testValidationShowError()
996996
$this->assertSame('<span class="help-block">The ID field is required.</span>' . "\n", $html);
997997
}
998998

999+
public function testValidationShowErrorForWildcards()
1000+
{
1001+
$validation = Services::validation();
1002+
$validation->setRule('user.*.name', 'Name', 'required')
1003+
->run([
1004+
'user' => [
1005+
'friends' => [
1006+
['name' => 'Name1'],
1007+
['name' => ''],
1008+
['name' => 'Name2'],
1009+
],
1010+
],
1011+
]);
1012+
1013+
$html = validation_show_error('user.*.name');
1014+
1015+
$this->assertSame('<span class="help-block">The Name field is required.</span>' . "\n", $html);
1016+
}
1017+
9991018
public function testFormParseFormAttributesTrue()
10001019
{
10011020
$expected = 'readonly ';

0 commit comments

Comments
 (0)