Skip to content

Commit d845f71

Browse files
committed
Merge remote-tracking branch 'upstream/develop' into 4.3
2 parents 36bf74c + 42e5b69 commit d845f71

File tree

18 files changed

+232
-41
lines changed

18 files changed

+232
-41
lines changed

contributing/workflow.md

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ are working in.
174174
At some point, you will decide that your feature branch is complete, or
175175
that it could benefit from a review by fellow developers.
176176

177-
**Note:**
177+
> **Note**
178178
> Remember to sync your local repo with the shared one before pushing!
179179
It is a lot easier to resolve conflicts at this stage.
180180

@@ -213,7 +213,7 @@ Make sure that the PR title is helpful for the maintainers and other
213213
developers. Add any comments appropriate, for instance asking for
214214
review.
215215

216-
**Note:**
216+
> **Note**
217217
> If you do not provide a title or description for your PR, the odds of it being summarily rejected
218218
rise astronomically.
219219

@@ -259,6 +259,12 @@ Synchronize your repository:
259259
> git push origin develop
260260
```
261261

262+
(Optional) Create a new branch as a backup, just in case:
263+
264+
```console
265+
> git branch fix/problem123.bk fix/problem123
266+
```
267+
262268
Bring your feature branch up to date:
263269

264270
```console
@@ -276,6 +282,60 @@ And finally push your local branch to your GitHub repository:
276282
> git push --force-with-lease origin fix/problem123
277283
```
278284

285+
## If you sent to the wrong branch
286+
287+
If you have sent a PR to the wrong branch, you need to create a new PR branch.
288+
289+
When you have the PR branch `feat-abc` and you should have sent the PR to `4.3`,
290+
but you created the PR branch from `develop` and sent a PR.
291+
292+
Copy the IDs of any commits you made that you want to keep:
293+
294+
```console
295+
> git log
296+
```
297+
298+
Update your `4.3` branch:
299+
300+
```console
301+
> git switch 4.3
302+
> git fetch upstream
303+
> git merge upstream/4.3
304+
> git push origin 4.3
305+
```
306+
307+
Create a new branch `feat-ab.new` from the correct branch `4.3`:
308+
309+
```console
310+
> git switch -c feat-abc.new 4.3
311+
```
312+
313+
Cherry-pick the commits you did:
314+
315+
```console
316+
> git cherry-pick <commit_id> <commit_id> <commit_id> ...
317+
```
318+
319+
Rename the PR branch `feat-abc`:
320+
321+
```console
322+
> git branch -m feat-abc feat-abc.old
323+
```
324+
325+
Rename the new branch `feat-abc.new` to `feat-abc`.
326+
327+
```console
328+
> git branch -m feat-abc.new feat-abc
329+
```
330+
331+
Force push.
332+
333+
```console
334+
> git push --force-with-lease origin feat-abc
335+
```
336+
337+
On the GitHub PR page, change the base branch to the correct branch `4.3`.
338+
279339
## Cleanup
280340

281341
If your PR is accepted and merged into the shared repository, you can

system/CodeIgniter.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -947,8 +947,10 @@ protected function gatherOutput(?Cache $cacheConfig = null, $returned = null)
947947

948948
if ($returned instanceof DownloadResponse) {
949949
// Turn off output buffering completely, even if php.ini output_buffering is not off
950-
while (ob_get_level() > 0) {
951-
ob_end_clean();
950+
if (ENVIRONMENT !== 'testing') {
951+
while (ob_get_level() > 0) {
952+
ob_end_clean();
953+
}
952954
}
953955

954956
$this->response = $returned;

system/Database/BaseConnection.php

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,10 +1406,41 @@ public function listTables(bool $constrainByPrefix = false)
14061406

14071407
/**
14081408
* Determine if a particular table exists
1409+
*
1410+
* @param bool $cached Whether to use data cache
14091411
*/
1410-
public function tableExists(string $tableName): bool
1412+
public function tableExists(string $tableName, bool $cached = true): bool
14111413
{
1412-
return in_array($this->protectIdentifiers($tableName, true, false, false), $this->listTables(), true);
1414+
if ($cached === true) {
1415+
return in_array($this->protectIdentifiers($tableName, true, false, false), $this->listTables(), true);
1416+
}
1417+
1418+
if (false === ($sql = $this->_listTables(false, $tableName))) {
1419+
if ($this->DBDebug) {
1420+
throw new DatabaseException('This feature is not available for the database you are using.');
1421+
}
1422+
1423+
return false;
1424+
}
1425+
1426+
$tableExists = $this->query($sql)->getResultArray() !== [];
1427+
1428+
// if cache has been built already
1429+
if (! empty($this->dataCache['table_names'])) {
1430+
$key = array_search(
1431+
strtolower($tableName),
1432+
array_map('strtolower', $this->dataCache['table_names']),
1433+
true
1434+
);
1435+
1436+
// table doesn't exist but still in cache - lets reset cache, it can be rebuilt later
1437+
// OR if table does exist but is not found in cache
1438+
if (($key !== false && ! $tableExists) || ($key === false && $tableExists)) {
1439+
$this->resetDataCache();
1440+
}
1441+
}
1442+
1443+
return $tableExists;
14131444
}
14141445

14151446
/**
@@ -1576,9 +1607,11 @@ abstract public function insertID();
15761607
/**
15771608
* Generates the SQL for listing tables in a platform-dependent manner.
15781609
*
1610+
* @param string|null $tableName If $tableName is provided will return only this table if exists.
1611+
*
15791612
* @return false|string
15801613
*/
1581-
abstract protected function _listTables(bool $constrainByPrefix = false);
1614+
abstract protected function _listTables(bool $constrainByPrefix = false, ?string $tableName = null);
15821615

15831616
/**
15841617
* Generates a platform-specific query string so that the column names can be fetched.

system/Database/Forge.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ public function createTable(string $table, bool $ifNotExists = false, array $att
498498
}
499499

500500
// If table exists lets stop here
501-
if ($ifNotExists === true && $this->db->tableExists($table)) {
501+
if ($ifNotExists === true && $this->db->tableExists($table, false)) {
502502
$this->reset();
503503

504504
return true;

system/Database/MySQLi/Connection.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,11 +368,17 @@ public function escapeLikeStringDirect($str)
368368
/**
369369
* Generates the SQL for listing tables in a platform-dependent manner.
370370
* Uses escapeLikeStringDirect().
371+
*
372+
* @param string|null $tableName If $tableName is provided will return only this table if exists.
371373
*/
372-
protected function _listTables(bool $prefixLimit = false): string
374+
protected function _listTables(bool $prefixLimit = false, ?string $tableName = null): string
373375
{
374376
$sql = 'SHOW TABLES FROM ' . $this->escapeIdentifiers($this->database);
375377

378+
if ($tableName !== null) {
379+
return $sql . ' LIKE ' . $this->escape($tableName);
380+
}
381+
376382
if ($prefixLimit !== false && $this->DBPrefix !== '') {
377383
return $sql . " LIKE '" . $this->escapeLikeStringDirect($this->DBPrefix) . "%'";
378384
}

system/Database/OCI8/Connection.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,17 @@ public function affectedRows(): int
242242

243243
/**
244244
* Generates the SQL for listing tables in a platform-dependent manner.
245+
*
246+
* @param string|null $tableName If $tableName is provided will return only this table if exists.
245247
*/
246-
protected function _listTables(bool $prefixLimit = false): string
248+
protected function _listTables(bool $prefixLimit = false, ?string $tableName = null): string
247249
{
248250
$sql = 'SELECT "TABLE_NAME" FROM "USER_TABLES"';
249251

252+
if ($tableName !== null) {
253+
return $sql . ' WHERE "TABLE_NAME" LIKE ' . $this->escape($tableName);
254+
}
255+
250256
if ($prefixLimit !== false && $this->DBPrefix !== '') {
251257
return $sql . ' WHERE "TABLE_NAME" LIKE \'' . $this->escapeLikeString($this->DBPrefix) . "%' "
252258
. sprintf($this->likeEscapeStr, $this->likeEscapeChar);

system/Database/Postgre/Connection.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,17 @@ protected function _escapeString(string $str): string
205205

206206
/**
207207
* Generates the SQL for listing tables in a platform-dependent manner.
208+
*
209+
* @param string|null $tableName If $tableName is provided will return only this table if exists.
208210
*/
209-
protected function _listTables(bool $prefixLimit = false): string
211+
protected function _listTables(bool $prefixLimit = false, ?string $tableName = null): string
210212
{
211213
$sql = 'SELECT "table_name" FROM "information_schema"."tables" WHERE "table_schema" = \'' . $this->schema . "'";
212214

215+
if ($tableName !== null) {
216+
return $sql . ' AND "table_name" LIKE ' . $this->escape($tableName);
217+
}
218+
213219
if ($prefixLimit !== false && $this->DBPrefix !== '') {
214220
return $sql . ' AND "table_name" LIKE \''
215221
. $this->escapeLikeString($this->DBPrefix) . "%' "

system/Database/SQLSRV/Connection.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,14 +193,20 @@ public function insertID(): int
193193

194194
/**
195195
* Generates the SQL for listing tables in a platform-dependent manner.
196+
*
197+
* @param string|null $tableName If $tableName is provided will return only this table if exists.
196198
*/
197-
protected function _listTables(bool $prefixLimit = false): string
199+
protected function _listTables(bool $prefixLimit = false, ?string $tableName = null): string
198200
{
199201
$sql = 'SELECT [TABLE_NAME] AS "name"'
200202
. ' FROM [INFORMATION_SCHEMA].[TABLES] '
201203
. ' WHERE '
202204
. " [TABLE_SCHEMA] = '" . $this->schema . "' ";
203205

206+
if ($tableName !== null) {
207+
return $sql .= ' AND [TABLE_NAME] LIKE ' . $this->escape($tableName);
208+
}
209+
204210
if ($prefixLimit === true && $this->DBPrefix !== '') {
205211
$sql .= " AND [TABLE_NAME] LIKE '" . $this->escapeLikeString($this->DBPrefix) . "%' "
206212
. sprintf($this->likeEscapeStr, $this->likeEscapeChar);

system/Database/SQLite3/Connection.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,17 @@ protected function _escapeString(string $str): string
161161

162162
/**
163163
* Generates the SQL for listing tables in a platform-dependent manner.
164+
*
165+
* @param string|null $tableName If $tableName is provided will return only this table if exists.
164166
*/
165-
protected function _listTables(bool $prefixLimit = false): string
167+
protected function _listTables(bool $prefixLimit = false, ?string $tableName = null): string
166168
{
169+
if ($tableName !== null) {
170+
return 'SELECT "NAME" FROM "SQLITE_MASTER" WHERE "TYPE" = \'table\''
171+
. ' AND "NAME" NOT LIKE \'sqlite!_%\' ESCAPE \'!\''
172+
. ' AND "NAME" LIKE ' . $this->escape($tableName);
173+
}
174+
167175
return 'SELECT "NAME" FROM "SQLITE_MASTER" WHERE "TYPE" = \'table\''
168176
. ' AND "NAME" NOT LIKE \'sqlite!_%\' ESCAPE \'!\''
169177
. (($prefixLimit !== false && $this->DBPrefix !== '')

system/Test/Mock/MockConnection.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,10 @@ public function insertID(): int
179179

180180
/**
181181
* Generates the SQL for listing tables in a platform-dependent manner.
182+
*
183+
* @param string|null $tableName If $tableName is provided will return only this table if exists.
182184
*/
183-
protected function _listTables(bool $constrainByPrefix = false): string
185+
protected function _listTables(bool $constrainByPrefix = false, ?string $tableName = null): string
184186
{
185187
return '';
186188
}

tests/_support/Filters/Customfilter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class Customfilter implements \CodeIgniter\Filters\FilterInterface
1818
{
1919
public function before(RequestInterface $request, $arguments = null)
2020
{
21-
$request->url = 'http://hellowworld.com';
21+
$request->appendBody('http://hellowworld.com');
2222

2323
return $request;
2424
}

tests/system/CodeIgniterTest.php

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,33 @@ public function testControllersCanReturnResponseObject()
191191
$this->assertStringContainsString("You want to see 'about' page.", $output);
192192
}
193193

194+
/**
195+
* @see https://github.com/codeigniter4/CodeIgniter4/issues/6358
196+
*/
197+
public function testControllersCanReturnDownloadResponseObject()
198+
{
199+
$_SERVER['argv'] = ['index.php', 'pages/about'];
200+
$_SERVER['argc'] = 2;
201+
202+
$_SERVER['REQUEST_URI'] = '/pages/about';
203+
204+
// Inject mock router.
205+
$routes = Services::routes();
206+
$routes->add('pages/(:segment)', static function ($segment) {
207+
$response = Services::response();
208+
209+
return $response->download('some.txt', 'some text', true);
210+
});
211+
$router = Services::router($routes, Services::request());
212+
Services::injectMock('router', $router);
213+
214+
ob_start();
215+
$this->codeigniter->useSafeOutput(true)->run();
216+
$output = ob_get_clean();
217+
218+
$this->assertSame('some text', $output);
219+
}
220+
194221
public function testControllersRunFilterByClassName()
195222
{
196223
$_SERVER['argv'] = ['index.php', 'pages/about'];
@@ -200,7 +227,7 @@ public function testControllersRunFilterByClassName()
200227

201228
// Inject mock router.
202229
$routes = Services::routes();
203-
$routes->add('pages/about', static fn () => Services::request()->url, ['filter' => Customfilter::class]);
230+
$routes->add('pages/about', static fn () => Services::request()->getBody(), ['filter' => Customfilter::class]);
204231

205232
$router = Services::router($routes, Services::request());
206233
Services::injectMock('router', $router);
@@ -210,6 +237,8 @@ public function testControllersRunFilterByClassName()
210237
$output = ob_get_clean();
211238

212239
$this->assertStringContainsString('http://hellowworld.com', $output);
240+
241+
$this->resetServices();
213242
}
214243

215244
public function testResponseConfigEmpty()

tests/system/Database/Forge/CreateTableTest.php

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,26 +21,6 @@
2121
*/
2222
final class CreateTableTest extends CIUnitTestCase
2323
{
24-
public function testCreateTableWithExists()
25-
{
26-
$dbMock = $this->getMockBuilder(MockConnection::class)
27-
->setConstructorArgs([[]])
28-
->onlyMethods(['listTables'])
29-
->getMock();
30-
$dbMock
31-
->method('listTables')
32-
->willReturn(['foo']);
33-
34-
$forge = new class ($dbMock) extends Forge {
35-
protected $createTableIfStr = false;
36-
};
37-
38-
$forge->addField('id');
39-
$actual = $forge->createTable('foo', true);
40-
41-
$this->assertTrue($actual);
42-
}
43-
4424
public function testCreateTableWithDefaultRawSql()
4525
{
4626
$sql = <<<'SQL'

0 commit comments

Comments
 (0)