Skip to content

Commit 7ab7cf5

Browse files
authored
Merge branch 'codeigniter4:develop' into add-curlrequest-curlopt_ssl_verifyhost
2 parents e159bfd + fc500d0 commit 7ab7cf5

File tree

22 files changed

+216
-34
lines changed

22 files changed

+216
-34
lines changed

app/Views/errors/cli/error_exception.php

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,26 @@
33
use CodeIgniter\CLI\CLI;
44

55
// The main Exception
6-
CLI::newLine();
76
CLI::write('[' . get_class($exception) . ']', 'light_gray', 'red');
8-
CLI::newLine();
97
CLI::write($message);
10-
CLI::newLine();
118
CLI::write('at ' . CLI::color(clean_path($exception->getFile()) . ':' . $exception->getLine(), 'green'));
129
CLI::newLine();
1310

11+
$last = $exception;
12+
13+
while ($prevException = $last->getPrevious()) {
14+
$last = $prevException;
15+
16+
CLI::write(' Caused by:');
17+
CLI::write(' [' . get_class($prevException) . ']', 'red');
18+
CLI::write(' ' . $prevException->getMessage());
19+
CLI::write(' at ' . CLI::color(clean_path($prevException->getFile()) . ':' . $prevException->getLine(), 'green'));
20+
CLI::newLine();
21+
}
22+
1423
// The backtrace
1524
if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) {
16-
$backtraces = $exception->getTrace();
25+
$backtraces = $last->getTrace();
1726

1827
if ($backtraces) {
1928
CLI::write('Backtrace:', 'green');

app/Views/errors/html/error_exception.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,29 @@
4444
<?php endif; ?>
4545
</div>
4646

47+
<div class="container">
48+
<?php
49+
$last = $exception;
50+
51+
while ($prevException = $last->getPrevious()) {
52+
$last = $prevException;
53+
?>
54+
55+
<pre>
56+
Caused by:
57+
<?= esc(get_class($prevException)), esc($prevException->getCode() ? ' #' . $prevException->getCode() : '') ?>
58+
59+
<?= nl2br(esc($prevException->getMessage())) ?>
60+
<a href="https://www.duckduckgo.com/?q=<?= urlencode(get_class($prevException) . ' ' . preg_replace('#\'.*\'|".*"#Us', '', $prevException->getMessage())) ?>"
61+
rel="noreferrer" target="_blank">search &rarr;</a>
62+
<?= esc(clean_path($prevException->getFile()) . ':' . $prevException->getLine()) ?>
63+
</pre>
64+
65+
<?php
66+
}
67+
?>
68+
</div>
69+
4770
<?php if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE) : ?>
4871
<div class="container">
4972

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.10",
36+
"rector/rector": "0.18.11",
3737
"vimeo/psalm": "^5.0"
3838
},
3939
"suggest": {

phpstan-baseline.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2641,11 +2641,6 @@
26412641
'count' => 1,
26422642
'path' => __DIR__ . '/system/Model.php',
26432643
];
2644-
$ignoreErrors[] = [
2645-
'message' => '#^Return type \\(int\\|object\\|string\\|false\\) of method CodeIgniter\\\\Model\\:\\:insert\\(\\) should be covariant with return type \\(bool\\|int\\|string\\) of method CodeIgniter\\\\BaseModel\\:\\:insert\\(\\)$#',
2646-
'count' => 1,
2647-
'path' => __DIR__ . '/system/Model.php',
2648-
];
26492644
$ignoreErrors[] = [
26502645
'message' => '#^Accessing offset mixed directly on \\$_GET is discouraged\\.$#',
26512646
'count' => 1,

system/BaseModel.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -740,10 +740,12 @@ public function getInsertID()
740740
* Inserts data into the database. If an object is provided,
741741
* it will attempt to convert it to an array.
742742
*
743-
* @param array|object|null $data Data
744-
* @param bool $returnID Whether insert ID should be returned or not.
743+
* @param array|object|null $data Data
744+
* @phpstan-param row_array|object|null $data
745+
* @param bool $returnID Whether insert ID should be returned or not.
745746
*
746747
* @return bool|int|string insert ID or true on success. false on failure.
748+
* @phpstan-return ($returnID is true ? int|string|false : bool)
747749
*
748750
* @throws ReflectionException
749751
*/

system/Cache/Handlers/FileHandler.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,8 @@ public function deleteMatching(string $pattern)
150150
*/
151151
public function increment(string $key, int $offset = 1)
152152
{
153-
$key = static::validateKey($key, $this->prefix);
154-
$tmp = $this->getItem($key);
153+
$prefixedKey = static::validateKey($key, $this->prefix);
154+
$tmp = $this->getItem($prefixedKey);
155155

156156
if ($tmp === false) {
157157
$tmp = ['data' => 0, 'ttl' => 60];

system/Debug/BaseExceptionHandler.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,14 @@ abstract public function handle(
6767
*/
6868
protected function collectVars(Throwable $exception, int $statusCode): array
6969
{
70-
$trace = $exception->getTrace();
70+
// Get the first exception.
71+
$firstException = $exception;
72+
73+
while ($prevException = $firstException->getPrevious()) {
74+
$firstException = $prevException;
75+
}
76+
77+
$trace = $firstException->getTrace();
7178

7279
if ($this->config->sensitiveDataInTrace !== []) {
7380
$trace = $this->maskSensitiveData($trace, $this->config->sensitiveDataInTrace);

system/Debug/Exceptions.php

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -125,22 +125,31 @@ public function exceptionHandler(Throwable $exception)
125125
[$statusCode, $exitCode] = $this->determineCodes($exception);
126126

127127
if ($this->config->log === true && ! in_array($statusCode, $this->config->ignoreCodes, true)) {
128-
log_message('critical', "{message}\nin {exFile} on line {exLine}.\n{trace}", [
128+
log_message('critical', get_class($exception) . ": {message}\nin {exFile} on line {exLine}.\n{trace}", [
129129
'message' => $exception->getMessage(),
130130
'exFile' => clean_path($exception->getFile()), // {file} refers to THIS file
131131
'exLine' => $exception->getLine(), // {line} refers to THIS line
132132
'trace' => self::renderBacktrace($exception->getTrace()),
133133
]);
134+
135+
// Get the first exception.
136+
$last = $exception;
137+
138+
while ($prevException = $last->getPrevious()) {
139+
$last = $prevException;
140+
141+
log_message('critical', '[Caused by] ' . get_class($prevException) . ": {message}\nin {exFile} on line {exLine}.\n{trace}", [
142+
'message' => $prevException->getMessage(),
143+
'exFile' => clean_path($prevException->getFile()), // {file} refers to THIS file
144+
'exLine' => $prevException->getLine(), // {line} refers to THIS line
145+
'trace' => self::renderBacktrace($prevException->getTrace()),
146+
]);
147+
}
134148
}
135149

136150
$this->request = Services::request();
137151
$this->response = Services::response();
138152

139-
// Get the first exception.
140-
while ($prevException = $exception->getPrevious()) {
141-
$exception = $prevException;
142-
}
143-
144153
if (method_exists($this->config, 'handler')) {
145154
// Use new ExceptionHandler
146155
$handler = $this->config->handler($statusCode, $exception);
@@ -325,7 +334,14 @@ protected function render(Throwable $exception, int $statusCode)
325334
*/
326335
protected function collectVars(Throwable $exception, int $statusCode): array
327336
{
328-
$trace = $exception->getTrace();
337+
// Get the first exception.
338+
$firstException = $exception;
339+
340+
while ($prevException = $firstException->getPrevious()) {
341+
$firstException = $prevException;
342+
}
343+
344+
$trace = $firstException->getTrace();
329345

330346
if ($this->config->sensitiveDataInTrace !== []) {
331347
$trace = $this->maskSensitiveData($trace, $this->config->sensitiveDataInTrace);

system/Model.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -697,9 +697,11 @@ protected function shouldUpdate($data): bool
697697
* it will attempt to convert it to an array.
698698
*
699699
* @param array|object|null $data
700-
* @param bool $returnID Whether insert ID should be returned or not.
700+
* @phpstan-param row_array|object|null $data
701+
* @param bool $returnID Whether insert ID should be returned or not.
701702
*
702-
* @return BaseResult|false|int|object|string
703+
* @return false|int|object|string
704+
* @phpstan-return ($returnID is true ? int|string|false : bool)
703705
*
704706
* @throws ReflectionException
705707
*/

tests/system/Cache/Handlers/FileHandlerTest.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ protected function tearDown(): void
6767
chmod($this->config->file['storePath'] . DIRECTORY_SEPARATOR . $key, 0777);
6868
unlink($this->config->file['storePath'] . DIRECTORY_SEPARATOR . $key);
6969
}
70+
if (is_file($this->config->file['storePath'] . DIRECTORY_SEPARATOR . $this->config->prefix . $key)) {
71+
chmod($this->config->file['storePath'] . DIRECTORY_SEPARATOR . $this->config->prefix . $key, 0777);
72+
unlink($this->config->file['storePath'] . DIRECTORY_SEPARATOR . $this->config->prefix . $key);
73+
}
7074
}
7175

7276
rmdir($this->config->file['storePath']);
@@ -233,6 +237,22 @@ public function testIncrement(): void
233237
$this->assertSame(10, $this->handler->increment(self::$key3, 10));
234238
}
235239

240+
public function testIncrementWithDefaultPrefix(): void
241+
{
242+
$this->config->prefix = 'test_';
243+
$this->handler = new FileHandler($this->config);
244+
$this->handler->initialize();
245+
246+
$this->handler->save(self::$key1, 1);
247+
$this->handler->save(self::$key2, 'value');
248+
249+
$this->assertSame(11, $this->handler->increment(self::$key1, 10));
250+
$this->assertSame($this->handler->increment(self::$key1, 10), $this->handler->get(self::$key1));
251+
$this->assertFalse($this->handler->increment(self::$key2, 10));
252+
$this->assertSame(10, $this->handler->increment(self::$key3, 10));
253+
$this->assertSame($this->handler->increment(self::$key3, 10), $this->handler->get(self::$key3));
254+
}
255+
236256
public function testDecrement(): void
237257
{
238258
$this->handler->save(self::$key1, 10);
@@ -246,6 +266,21 @@ public function testDecrement(): void
246266
$this->assertSame(-1, $this->handler->decrement(self::$key3, 1));
247267
}
248268

269+
public function testDecrementWithDefaultPrefix(): void
270+
{
271+
$this->handler->save(self::$key1, 10);
272+
$this->handler->save(self::$key2, 'value');
273+
274+
// Line following commented out to force the cache to add a zero entry for key3
275+
// $this->fileHandler->save(self::$key3, 0);
276+
277+
$this->assertSame(9, $this->handler->decrement(self::$key1, 1));
278+
$this->assertSame($this->handler->decrement(self::$key1, 1), $this->handler->get(self::$key1));
279+
$this->assertFalse($this->handler->decrement(self::$key2, 1));
280+
$this->assertSame(-1, $this->handler->decrement(self::$key3, 1));
281+
$this->assertSame($this->handler->decrement(self::$key3, 1), $this->handler->get(self::$key3));
282+
}
283+
249284
public function testClean(): void
250285
{
251286
$this->handler->save(self::$key1, 1);

user_guide_src/source/incoming/restful.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ the controller that should be used:
5252

5353
.. literalinclude:: restful/003.php
5454

55+
.. literalinclude:: restful/017.php
56+
57+
.. literalinclude:: restful/018.php
58+
59+
See also :ref:`controllers-namespace`.
60+
5561
Change the Placeholder Used
5662
===========================
5763

@@ -122,6 +128,12 @@ the controller that should be used:
122128

123129
.. literalinclude:: restful/011.php
124130

131+
.. literalinclude:: restful/019.php
132+
133+
.. literalinclude:: restful/020.php
134+
135+
See also :ref:`controllers-namespace`.
136+
125137
Change the Placeholder Used
126138
===========================
127139

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<?php
22

3-
$routes->resource('photos', ['controller' => 'App\Gallery']);
4-
3+
$routes->resource('photos', ['controller' => 'Gallery']);
54
// Would create routes like:
6-
$routes->get('photos', 'App\Gallery::index');
5+
$routes->get('photos', '\App\Controllers\Gallery::index');
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<?php
22

3-
$routes->presenter('photos', ['controller' => 'App\Gallery']);
4-
3+
$routes->presenter('photos', ['controller' => 'Gallery']);
54
// Would create routes like:
6-
$routes->get('photos', 'App\Gallery::index');
5+
$routes->get('photos', '\App\Controllers\Gallery::index');
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
$routes->resource('photos', ['controller' => '\App\Gallery']);
4+
// Would create routes like:
5+
$routes->get('photos', '\App\Gallery::index');
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
use App\Controllers\Gallery;
4+
5+
$routes->resource('photos', ['namespace' => '', 'controller' => Gallery::class]);
6+
// Would create routes like:
7+
$routes->get('photos', '\App\Controllers\Gallery::index');
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
$routes->presenter('photos', ['controller' => '\App\Gallery']);
4+
// Would create routes like:
5+
$routes->get('photos', '\App\Gallery::index');
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
use App\Controllers\Gallery;
4+
5+
$routes->presenter('photos', ['namespace' => '', 'controller' => Gallery::class]);
6+
// Would create routes like:
7+
$routes->get('photos', '\App\Controllers\Gallery::index');

user_guide_src/source/incoming/routing.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ You can supply multiple verbs that a route should match by passing them in as an
8383
Specifying Route Handlers
8484
=========================
8585

86+
.. _controllers-namespace:
87+
8688
Controller's Namespace
8789
----------------------
8890

user_guide_src/source/installation/upgrade_444.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ Please refer to the upgrade instructions corresponding to your installation meth
1616
Mandatory File Changes
1717
**********************
1818

19+
Error Files
20+
===========
21+
22+
Update the following files to show correct error messages:
23+
24+
- app/Views/errors/cli/error_exception.php
25+
- app/Views/errors/html/error_exception.php
26+
1927
****************
2028
Breaking Changes
2129
****************

user_guide_src/source/models/entities.rst

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Using Entity Classes
33
#####################
44

5-
CodeIgniter supports Entity classes as a first-class citizen in it's database layer, while keeping
5+
CodeIgniter supports Entity classes as a first-class citizen, while keeping
66
them completely optional to use. They are commonly used as part of the Repository pattern, but can
77
be used directly with the :doc:`Model </models/model>` if that fits your needs better.
88

@@ -16,9 +16,17 @@ Entity Usage
1616

1717
At its core, an Entity class is simply a class that represents a single database row. It has class properties
1818
to represent the database columns, and provides any additional methods to implement the business logic for
19-
that row. The core feature, though, is that it doesn't know anything about how to persist itself. That's the
19+
that row.
20+
21+
.. note:: For ease of understanding, the explanation here is based on the case of
22+
using a database. However, Entity can also be used for data that does not come
23+
from a database.
24+
25+
The core feature, though, is that it doesn't know anything about how to persist itself. That's the
2026
responsibility of the model or the repository class. That way, if anything changes on how you need to save the
21-
object, you don't have to change how that object is used throughout the application. This makes it possible to
27+
object, you don't have to change how that object is used throughout the application.
28+
29+
This makes it possible to
2230
use JSON or XML files to store the objects during a rapid prototyping stage, and then easily switch to a
2331
database when you've proven the concept works.
2432

@@ -70,7 +78,7 @@ access them as if they were public properties. The base class, ``CodeIgniter\Ent
7078
well as providing the ability to check the properties with ``isset()``, or ``unset()`` the property, and keep track
7179
of what columns have changed since the object was created or pulled from the database.
7280

73-
.. note:: The Entity class stores the data in the property ``$attributes``.
81+
.. note:: The Entity class stores the data in the class property ``$attributes`` internally.
7482

7583
When the User is passed to the model's ``save()`` method, it automatically takes care of reading the properties
7684
and saving any changes to columns listed in the model's ``$allowedFields`` property. It also knows whether to create

0 commit comments

Comments
 (0)