Skip to content

Commit

Permalink
Merge pull request #5556 from codeigniter4/hotfix-4.1.7
Browse files Browse the repository at this point in the history
Hotfix 4.1.7
  • Loading branch information
MGatner authored Jan 10, 2022
2 parents 6b2816c + 3e088a9 commit 99e0797
Show file tree
Hide file tree
Showing 22 changed files with 279 additions and 50 deletions.
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
# Changelog

## [v4.1.7](https://github.com/codeigniter4/CodeIgniter4/tree/v4.1.7) (2022-01-09)

[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.1.6...v4.1.7)

**Breaking Changes**

* fix: replace deprecated FILTER_SANITIZE_STRING by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5555

**Fixed Bugs**

* fix: BaseConnection::getConnectDuration() number_format(): Passing null to parameter by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5536
* Fix: Debug toolbar selectors by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/5544
* Fix: Toolbar. ciDebugBar.showTab() context. by @iRedds in https://github.com/codeigniter4/CodeIgniter4/pull/5554
* Refactor Database Collector display by @paulbalandan in https://github.com/codeigniter4/CodeIgniter4/pull/5553

## [v4.1.6](https://github.com/codeigniter4/CodeIgniter4/tree/v4.1.6) (2022-01-03)

[Full Changelog](https://github.com/codeigniter4/CodeIgniter4/compare/v4.1.5...v4.1.6)

**SECURITY**

* *Deserialization of Untrusted Data* found in the ``old()`` function was fixed. See the [Security advisory](https://github.com/codeigniter4/CodeIgniter4/security/advisories/GHSA-w6jr-wj64-mc9x) for more information.

**Breaking Changes**

* fix: Incorrect type `BaseBuilder::$tableName` by @kenjis in https://github.com/codeigniter4/CodeIgniter4/pull/5378
Expand Down
2 changes: 1 addition & 1 deletion system/CodeIgniter.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class CodeIgniter
/**
* The current version of CodeIgniter Framework
*/
public const CI_VERSION = '4.1.6';
public const CI_VERSION = '4.1.7';

private const MIN_PHP_VERSION = '7.3';

Expand Down
4 changes: 2 additions & 2 deletions system/Database/BaseConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -258,14 +258,14 @@ abstract class BaseConnection implements ConnectionInterface
*
* @var float
*/
protected $connectTime;
protected $connectTime = 0.0;

/**
* How long it took to establish connection.
*
* @var float
*/
protected $connectDuration;
protected $connectDuration = 0.0;

/**
* If true, no queries will actually be
Expand Down
55 changes: 39 additions & 16 deletions system/Debug/Toolbar/Collectors/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,19 @@ public static function collect(Query $query)
if (count(static::$queries) < $max) {
$queryString = $query->getQuery();

$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);

if (! is_cli()) {
// when called in the browser, the first two trace arrays
// are from the DB event trigger, which are unneeded
$backtrace = array_slice($backtrace, 2);
}

static::$queries[] = [
'query' => $query,
'string' => $queryString,
'duplicate' => in_array($queryString, array_column(static::$queries, 'string', null), true),
'trace' => debug_backtrace(),
'trace' => $backtrace,
];
}
}
Expand Down Expand Up @@ -134,23 +142,39 @@ public function display(): array
$data['queries'] = array_map(static function (array $query) {
$isDuplicate = $query['duplicate'] === true;

// Find the first line that doesn't include `system` in the backtrace
$line = [];
$firstNonSystemLine = '';

foreach ($query['trace'] as $index => &$line) {
// simplify file and line
if (isset($line['file'])) {
$line['file'] = clean_path($line['file']) . ':' . $line['line'];
unset($line['line']);
} else {
$line['file'] = '[internal function]';
}

// find the first trace line that does not originate from `system/`
if ($firstNonSystemLine === '' && strpos($line['file'], 'SYSTEMPATH') === false) {
$firstNonSystemLine = $line['file'];
}

foreach ($query['trace'] as &$traceLine) {
// Clean up the file paths
$traceLine['file'] = str_ireplace(APPPATH, 'APPPATH/', $traceLine['file']);
$traceLine['file'] = str_ireplace(SYSTEMPATH, 'SYSTEMPATH/', $traceLine['file']);
if (defined('VENDORPATH')) {
// VENDORPATH is not defined unless `vendor/autoload.php` exists
$traceLine['file'] = str_ireplace(VENDORPATH, 'VENDORPATH/', $traceLine['file']);
// simplify function call
if (isset($line['class'])) {
$line['function'] = $line['class'] . $line['type'] . $line['function'];
unset($line['class'], $line['type']);
}
$traceLine['file'] = str_ireplace(ROOTPATH, 'ROOTPATH/', $traceLine['file']);

if (strpos($traceLine['file'], 'SYSTEMPATH') !== false) {
continue;
if (strrpos($line['function'], '{closure}') === false) {
$line['function'] .= '()';
}
$line = empty($line) ? $traceLine : $line;

$line['function'] = str_repeat(chr(0xC2) . chr(0xA0), 8) . $line['function'];

// add index numbering padded with nonbreaking space
$indexPadded = str_pad(sprintf('%d', $index + 1), 3, ' ', STR_PAD_LEFT);
$indexPadded = preg_replace('/\s/', chr(0xC2) . chr(0xA0), $indexPadded);

$line['index'] = $indexPadded . str_repeat(chr(0xC2) . chr(0xA0), 4);
}

return [
Expand All @@ -159,8 +183,7 @@ public function display(): array
'duration' => ((float) $query['query']->getDuration(5) * 1000) . ' ms',
'sql' => $query['query']->debugToolbarDisplay(),
'trace' => $query['trace'],
'trace-file' => str_replace(ROOTPATH, '/', $line['file'] ?? ''),
'trace-line' => $line['line'] ?? '',
'trace-file' => $firstNonSystemLine,
'qid' => md5($query['query'] . microtime()),
];
}, static::$queries);
Expand Down
5 changes: 3 additions & 2 deletions system/Debug/Toolbar/Views/_database.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@
<tr class="{class}" title="{hover}" data-toggle="{qid}-trace">
<td class="narrow">{duration}</td>
<td>{! sql !}</td>
<td style="text-align: right">{trace-file}:<strong>{trace-line}</strong></td>
<td style="text-align: right"><strong>{trace-file}</strong></td>
</tr>
<tr class="muted" id="{qid}-trace" style="display:none">
<td></td>
<td colspan="2">
{trace}
{file}:<strong>{line}</strong><br/>
{index}<strong>{file}</strong><br/>
{function}<br/><br/>
{/trace}
</td>
</tr>
Expand Down
16 changes: 8 additions & 8 deletions system/Debug/Toolbar/Views/toolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ var ciDebugBar = {
document.getElementById('debug-icon-link').addEventListener('click', ciDebugBar.toggleToolbar, true);

// Allows to highlight the row of the current history request
var btn = document.querySelector('button[data-time="' + localStorage.getItem('debugbar-time') + '"]');
var btn = this.toolbar.querySelector('button[data-time="' + localStorage.getItem('debugbar-time') + '"]');
ciDebugBar.addClass(btn.parentNode.parentNode, 'current');

historyLoad = document.getElementsByClassName('ci-history-load');
historyLoad = this.toolbar.getElementsByClassName('ci-history-load');

for (var i = 0; i < historyLoad.length; i++)
{
Expand All @@ -52,15 +52,15 @@ var ciDebugBar = {
},

createListeners : function () {
var buttons = [].slice.call(document.querySelectorAll('#debug-bar .ci-label a'));
var buttons = [].slice.call(this.toolbar.querySelectorAll('.ci-label a'));

for (var i = 0; i < buttons.length; i++)
{
buttons[i].addEventListener('click', ciDebugBar.showTab, true);
}

// Hook up generic toggle via data attributes `data-toggle="foo"`
var links = document.querySelectorAll('[data-toggle]');
var links = this.toolbar.querySelectorAll('[data-toggle]');
for (var i = 0; i < links.length; i++)
{
links[i].addEventListener('click', ciDebugBar.toggleRows, true);
Expand Down Expand Up @@ -502,7 +502,7 @@ var ciDebugBar = {
},

setToolbarPosition: function () {
var btnPosition = document.getElementById('toolbar-position');
var btnPosition = this.toolbar.querySelector('#toolbar-position');

if (ciDebugBar.readCookie('debug-bar-position') === 'top')
{
Expand Down Expand Up @@ -531,7 +531,7 @@ var ciDebugBar = {
},

setToolbarTheme: function () {
var btnTheme = document.getElementById('toolbar-theme');
var btnTheme = this.toolbar.querySelector('#toolbar-theme');
var isDarkMode = window.matchMedia("(prefers-color-scheme: dark)").matches;
var isLightMode = window.matchMedia("(prefers-color-scheme: light)").matches;

Expand Down Expand Up @@ -627,7 +627,7 @@ var ciDebugBar = {

routerLink: function () {
var row, _location;
var rowGet = document.querySelectorAll('#debug-bar td[data-debugbar-route="GET"]');
var rowGet = this.toolbar.querySelectorAll('td[data-debugbar-route="GET"]');
var patt = /\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)/;

for (var i = 0; i < rowGet.length; i++)
Expand All @@ -653,7 +653,7 @@ var ciDebugBar = {
}
}

rowGet = document.querySelectorAll('#debug-bar td[data-debugbar-route="GET"] form');
rowGet = this.toolbar.querySelectorAll('td[data-debugbar-route="GET"] form');
for (var i = 0; i < rowGet.length; i++)
{
row = rowGet[i];
Expand Down
2 changes: 1 addition & 1 deletion system/Helpers/cookie_helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ function get_cookie($index, bool $xssClean = false)
{
$prefix = isset($_COOKIE[$index]) ? '' : config(App::class)->cookiePrefix;
$request = Services::request();
$filter = $xssClean ? FILTER_SANITIZE_STRING : FILTER_DEFAULT;
$filter = $xssClean ? FILTER_SANITIZE_FULL_SPECIAL_CHARS : FILTER_DEFAULT;

return $request->getCookie($prefix . $index, $filter);
}
Expand Down
29 changes: 26 additions & 3 deletions system/ThirdParty/Kint/Renderer/CliRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@ class CliRenderer extends TextRenderer
*/
public static $min_terminal_width = 40;

/**
* Which stream to check for VT100 support on windows.
*
* null uses STDOUT if it's defined
*
* @var null|resource
*/
public static $windows_stream = null;

protected static $terminal_width = null;

protected $windows_output = false;
Expand All @@ -69,8 +78,22 @@ public function __construct()
{
parent::__construct();

if (!self::$force_utf8) {
$this->windows_output = KINT_WIN;
if (!self::$force_utf8 && KINT_WIN) {
if (!KINT_PHP72) {
$this->windows_output = true;
} else {
$stream = self::$windows_stream;

if (!$stream && \defined('STDOUT')) {
$stream = STDOUT;
}

if (!$stream) {
$this->windows_output = true;
} else {
$this->windows_output = !\sapi_windows_vt100_support($stream);
}
}
}

if (!self::$terminal_width) {
Expand Down Expand Up @@ -153,7 +176,7 @@ protected function utf8ToWindows($string)
{
return \str_replace(
['', '', '', '', '', '', ''],
["\xda", "\xdc", "\xbf", "\xb3", "\xc0", "\xc4", "\xd9"],
[' ', '=', ' ', '|', ' ', '-', ' '],
$string
);
}
Expand Down
15 changes: 13 additions & 2 deletions system/ThirdParty/Kint/Renderer/RichRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ class RichRenderer extends Renderer

public static $always_pre_render = false;

public static $js_nonce = null;
public static $css_nonce = null;

protected $plugin_objs = [];
protected $expand = false;
protected $force_pre_render = false;
Expand Down Expand Up @@ -389,10 +392,18 @@ public function preRender()

switch ($type) {
case 'script':
$output .= '<script class="kint-rich-script">'.$contents.'</script>';
$output .= '<script class="kint-rich-script"';
if (null !== self::$js_nonce) {
$output .= ' nonce="'.\htmlspecialchars(self::$js_nonce).'"';
}
$output .= '>'.$contents.'</script>';
break;
case 'style':
$output .= '<style class="kint-rich-style">'.$contents.'</style>';
$output .= '<style class="kint-rich-style"';
if (null !== self::$css_nonce) {
$output .= ' nonce="'.\htmlspecialchars(self::$css_nonce).'"';
}
$output .= '>'.$contents.'</style>';
break;
default:
$output .= $contents;
Expand Down
10 changes: 10 additions & 0 deletions tests/system/Database/BaseConnectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,16 @@ public function testStoresConnectionTimings()
$this->assertGreaterThan(0.0, $db->getConnectDuration());
}

/**
* @see https://github.com/codeigniter4/CodeIgniter4/issues/5535
*/
public function testStoresConnectionTimingsNotConnected()
{
$db = new MockConnection($this->options);

$this->assertSame('0.000000', $db->getConnectDuration());
}

public function testMagicIssetTrue()
{
$db = new MockConnection($this->options);
Expand Down
61 changes: 61 additions & 0 deletions tests/system/Debug/Toolbar/Collectors/DatabaseTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

/**
* This file is part of CodeIgniter 4 framework.
*
* (c) CodeIgniter Foundation <admin@codeigniter.com>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

namespace CodeIgniter\Debug\Toolbar\Collectors;

use CodeIgniter\Database\Query;
use CodeIgniter\Test\CIUnitTestCase;
use PHPUnit\Framework\MockObject\MockObject;

/**
* @internal
*/
final class DatabaseTest extends CIUnitTestCase
{
public function testDisplay(): void
{
/** @var MockObject&Query $query */
$query = $this->getMockBuilder(Query::class)
->disableOriginalConstructor()
->getMock();

// set mock returns
$query->method('getQuery')->willReturn('SHOW TABLES;');
$query->method('debugToolbarDisplay')->willReturn('<strong>SHOW</strong> TABLES;');
$query->method('getDuration')->with(5)->willReturn('1.23456');

Database::collect($query); // <== $query will be called here
$collector = new Database();

$queries = $collector->display()['queries'];

$this->assertSame('1234.56 ms', $queries[0]['duration']);
$this->assertSame('<strong>SHOW</strong> TABLES;', $queries[0]['sql']);
$this->assertSame(clean_path(__FILE__) . ':35', $queries[0]['trace-file']);

foreach ($queries[0]['trace'] as $i => $trace) {
// since we added the index numbering
$this->assertArrayHasKey('index', $trace);
$this->assertSame(
sprintf('%s', $i + 1),
preg_replace(sprintf('/%s/', chr(0xC2) . chr(0xA0)), '', $trace['index'])
);

// since we merged file & line together
$this->assertArrayNotHasKey('line', $trace);
$this->assertArrayHasKey('file', $trace);

// since we dropped object & args in the backtrace for performance
// but args MAY STILL BE present in internal calls
$this->assertArrayNotHasKey('object', $trace);
}
}
}
1 change: 1 addition & 0 deletions user_guide_src/source/changelogs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ See all the changes.
.. toctree::
:titlesonly:

v4.1.7
v4.1.6
v4.1.5
v4.1.4
Expand Down
Loading

0 comments on commit 99e0797

Please sign in to comment.