Fixes #11462: Added support of filtering rules to yii\log\Target::$logVar, added \yii\helpers\BaseArrayHelper::filter() method

This commit is contained in:
Viktor Pikaev
2016-07-03 02:56:05 +04:00
committed by Alexander Makarov
parent 16e4cab8f1
commit 30bb9bd7c8
5 changed files with 198 additions and 8 deletions

View File

@ -21,6 +21,8 @@ Yii Framework 2 Change Log
- Enh #11438: Configurable `yii\helpers\Markdown` default flavor (mdmunir) - Enh #11438: Configurable `yii\helpers\Markdown` default flavor (mdmunir)
- Enh #11729: Added `yii\grid\CheckboxColumn::$cssClass` property to specify a class added to checkbox input (thiagotalma) - Enh #11729: Added `yii\grid\CheckboxColumn::$cssClass` property to specify a class added to checkbox input (thiagotalma)
- Bug #11459: Fixed flash messages not destroyed when `session.auto_start = 1` set in php.ini (cartmanchen) - Bug #11459: Fixed flash messages not destroyed when `session.auto_start = 1` set in php.ini (cartmanchen)
- Enh: Added support of filtering rules to `yii\log\Target::$logVar`. (Viktor Pikaev)
- Enh: Added `\yii\helpers\BaseArrayHelper::filter()` method. (Viktor Pikaev)
- Bug #11498: Fixed inability to save serialized object into PostgreSQL binary column (klimov-paul) - Bug #11498: Fixed inability to save serialized object into PostgreSQL binary column (klimov-paul)
- Bug #11507: Fixed `yii\validators\EachValidator::validateAttribute()` does not respect `skipOnEmpty` rule parameter (webdevsega) - Bug #11507: Fixed `yii\validators\EachValidator::validateAttribute()` does not respect `skipOnEmpty` rule parameter (webdevsega)
- Bug #11523: Fixed `yii\web\User::checkRedirectAcceptable()` to treat acceptable content type `*/*` as `*` (silverfire) - Bug #11523: Fixed `yii\web\User::checkRedirectAcceptable()` to treat acceptable content type `*/*` as `*` (silverfire)

View File

@ -755,4 +755,93 @@ class BaseArrayHelper
throw new InvalidParamException('Argument $needles must be an array or implement Traversable'); throw new InvalidParamException('Argument $needles must be an array or implement Traversable');
} }
} }
/**
* Filters array by specified rules.
*
* For example:
* ```php
* $array = [
* 'A' => [1, 2],
* 'B' => [
* 'C' => 1,
* 'D' => 2,
* ],
* 'E' => 1,
* ];
*
* $result = \yii\helpers\ArrayHelper::filter($array, ['A']);
* // $result will be:
* // [
* // 'A' => [1, 2],
* // ]
*
* $result = \yii\helpers\ArrayHelper::filter($array, ['A', 'B.C']);
* // $result will be:
* // [
* // 'A' => [1, 2],
* // 'B' => ['C' => 1],
* // ]
* ```
*
* $result = \yii\helpers\ArrayHelper::filter($array, ['B', '!B.C']);
* // $result will be:
* // [
* // 'B' => ['D' => 2],
* // ]
* ```
*
* @param array $array Source array.
* @param array $filters List of array keys which should be passed or removed from results.
* Each item should contains one of the next rules:
* - `var` - `$array['var']` will be passed to result.
* - `var.key` = only `$array['var']['key'] will be passed to result.
* - `!var.key` = `$array['var']['key'] will be removed from result.
* @return array Filtering array
*/
public static function filter($array, $filters)
{
$result = [];
$forbiddenVars = [];
foreach ($filters as $var) {
$keys = explode('.', $var);
$globalKey = $keys[0];
$localKey = isset($keys[1]) ? $keys[1] : null;
if ($globalKey[0] === '!') {
$forbiddenVars[] = [
substr($globalKey, 1),
$localKey,
];
continue;
}
if (empty($array[$globalKey])) {
continue;
}
if ($localKey === null) {
$result[$globalKey] = $array[$globalKey];
continue;
}
if (!isset($array[$globalKey][$localKey])) {
continue;
}
if (!isset($result[$globalKey])) {
$result[$globalKey] = [];
}
$result[$globalKey][$localKey] = $array[$globalKey][$localKey];
}
foreach ($forbiddenVars as $var) {
$globalKey=$var[0];
$localKey=$var[1];
if (isset($result[$globalKey])) {
unset($result[$globalKey][$localKey]);
}
}
return $result;
}
} }

View File

@ -10,6 +10,7 @@ namespace yii\log;
use Yii; use Yii;
use yii\base\Component; use yii\base\Component;
use yii\base\InvalidConfigException; use yii\base\InvalidConfigException;
use yii\helpers\ArrayHelper;
use yii\helpers\VarDumper; use yii\helpers\VarDumper;
use yii\web\Request; use yii\web\Request;
@ -57,6 +58,11 @@ abstract class Target extends Component
* @var array list of the PHP predefined variables that should be logged in a message. * @var array list of the PHP predefined variables that should be logged in a message.
* Note that a variable must be accessible via `$GLOBALS`. Otherwise it won't be logged. * Note that a variable must be accessible via `$GLOBALS`. Otherwise it won't be logged.
* Defaults to `['_GET', '_POST', '_FILES', '_COOKIE', '_SESSION', '_SERVER']`. * Defaults to `['_GET', '_POST', '_FILES', '_COOKIE', '_SESSION', '_SERVER']`.
* Each element should be in one of next forms:
* - `var` - `var` will be logged.
* - `var.key` - only `var[key]` key will be logged.
* - `!var.key` - `var[key]` key will be excluded.
*
*/ */
public $logVars = ['_GET', '_POST', '_FILES', '_COOKIE', '_SESSION', '_SERVER']; public $logVars = ['_GET', '_POST', '_FILES', '_COOKIE', '_SESSION', '_SERVER'];
/** /**
@ -122,14 +128,12 @@ abstract class Target extends Component
*/ */
protected function getContextMessage() protected function getContextMessage()
{ {
$context = []; $context = ArrayHelper::filter($GLOBALS,$this->logVars);
foreach ($this->logVars as $name) { $result = [];
if (!empty($GLOBALS[$name])) { foreach ($context as $key => $value) {
$context[] = "\${$name} = " . VarDumper::dumpAsString($GLOBALS[$name]); $result[] = "\${$key} = " . VarDumper::dumpAsString($value);
}
} }
return implode("\n\n", $result);
return implode("\n\n", $context);
} }
/** /**

View File

@ -778,5 +778,44 @@ class ArrayHelperTest extends TestCase
$this->assertFalse(ArrayHelper::isTraversable(null)); $this->assertFalse(ArrayHelper::isTraversable(null));
} }
public function testFilter()
{
$array = [
'A' => [
'B' => 1,
'C' => 2,
],
'G' => 1,
];
$this->assertEquals(ArrayHelper::filter($array, ['A']), [
'A' => [
'B' => 1,
'C' => 2,
],
]);
$this->assertEquals(ArrayHelper::filter($array, ['A.B']), [
'A' => [
'B' => 1,
],
]);
$this->assertEquals(ArrayHelper::filter($array, ['A', '!A.B']), [
'A' => [
'C' => 2,
],
]);
$this->assertEquals(ArrayHelper::filter($array, ['!A.B', 'A']), [
'A' => [
'C' => 2,
],
]);
$this->assertEquals(ArrayHelper::filter($array, ['A', 'G']), [
'A' => [
'B' => 1,
'C' => 2,
],
'G' => 1,
]);
$this->assertEquals(ArrayHelper::filter($array, ['X']), []);
$this->assertEquals(ArrayHelper::filter($array, ['X.Y']), []);
}
} }

View File

@ -75,6 +75,54 @@ class TargetTest extends TestCase
$this->assertEquals('test' . $e, static::$messages[$i++][0]); $this->assertEquals('test' . $e, static::$messages[$i++][0]);
} }
} }
public function testGetContextMessage()
{
$target = new TestTarget([
'logVars' => [
'A', '!A.A_b', 'A.A_d',
'B.B_a',
'C', 'C.C_a',
'D',
],
]);
$GLOBALS['A'] = [
'A_a' => 1,
'A_b' => 1,
'A_c' => 1,
];
$GLOBALS['B'] = [
'B_a' => 1,
'B_b' => 1,
'B_c' => 1,
];
$GLOBALS['C'] = [
'C_a' => 1,
'C_b' => 1,
'C_c' => 1,
];
$GLOBALS['E'] = [
'C_a' => 1,
'C_b' => 1,
'C_c' => 1,
];
$context = $target->getContextMessage();
$this->assertContains('A_a', $context);
$this->assertNotContains('A_b', $context);
$this->assertContains('A_c', $context);
$this->assertContains('B_a', $context);
$this->assertNotContains('B_b', $context);
$this->assertNotContains('B_c', $context);
$this->assertContains('C_a', $context);
$this->assertContains('C_b', $context);
$this->assertContains('C_c', $context);
$this->assertNotContains('D_a', $context);
$this->assertNotContains('D_b', $context);
$this->assertNotContains('D_c', $context);
$this->assertNotContains('E_a', $context);
$this->assertNotContains('E_b', $context);
$this->assertNotContains('E_c', $context);
}
} }
class TestTarget extends Target class TestTarget extends Target
@ -90,4 +138,12 @@ class TestTarget extends Target
TargetTest::$messages = array_merge(TargetTest::$messages, $this->messages); TargetTest::$messages = array_merge(TargetTest::$messages, $this->messages);
$this->messages = []; $this->messages = [];
} }
/**
* @inheritdoc
*/
public function getContextMessage()
{
return parent::getContextMessage();
}
} }