Commit 1106ad19 by Qiang Xue

Merge branch 'debug_module_improvements' of github.com:Ragazzo/yii2 into…

Merge branch 'debug_module_improvements' of github.com:Ragazzo/yii2 into Ragazzo-debug_module_improvements Conflicts: extensions/yii/debug/panels/DbPanel.php
parent 1e0e5f02
...@@ -52,6 +52,7 @@ class LogTarget extends Target ...@@ -52,6 +52,7 @@ class LogTarget extends Target
$manifest = unserialize(file_get_contents($indexFile)); $manifest = unserialize(file_get_contents($indexFile));
} }
$request = Yii::$app->getRequest(); $request = Yii::$app->getRequest();
$response = Yii::$app->getResponse();
$manifest[$this->tag] = $summary = [ $manifest[$this->tag] = $summary = [
'tag' => $this->tag, 'tag' => $this->tag,
'url' => $request->getAbsoluteUrl(), 'url' => $request->getAbsoluteUrl(),
...@@ -59,6 +60,8 @@ class LogTarget extends Target ...@@ -59,6 +60,8 @@ class LogTarget extends Target
'method' => $request->getMethod(), 'method' => $request->getMethod(),
'ip' => $request->getUserIP(), 'ip' => $request->getUserIP(),
'time' => time(), 'time' => time(),
'statusCode' => $response->statusCode,
'sqlCount' => $this->getSqlTotalCount(),
]; ];
$this->gc($manifest); $this->gc($manifest);
...@@ -102,4 +105,21 @@ class LogTarget extends Target ...@@ -102,4 +105,21 @@ class LogTarget extends Target
} }
} }
} }
/**
* Returns total sql count executed in current request. If database panel is not configured
* returns 0.
* @return integer
*/
protected function getSqlTotalCount()
{
if (!isset($this->module->panels['db'])) {
return 0;
}
$profileLogs = $this->module->panels['db']->save();
# / 2 because messages are in couple (begin/end)
return count($profileLogs['messages']) / 2;
}
} }
<?php
namespace yii\debug\components\search;
use yii\base\Component;
class Filter extends Component
{
/**
* @var array rules for matching filters in the way: [:fieldName => [rule1, rule2,..]]
*/
protected $rules = [];
/**
* Adds rules for filtering data. Match can be partial or exactly.
* @param string $name attribute name
* @param \yii\debug\components\search\matches\Base $rule
*/
public function addMatch($name, $rule)
{
if (empty($rule->value) && $rule->value !== 0) {
return;
}
$this->rules[$name][] = $rule;
}
/**
* Applies filter on given array and returns filtered data.
* @param array $data data to filter
* @return array filtered data
*/
public function filter(array $data)
{
$filtered = [];
foreach($data as $row)
{
if ($this->checkFilter($row)) {
$filtered[] = $row;
}
}
return $filtered;
}
/**
* Check if the given data satisfies filters.
* @param array $row
*/
public function checkFilter(array $row)
{
$matched = true;
foreach ($row as $name=>$value)
{
if (isset($this->rules[$name])) {
#check all rules for given attribute
foreach($this->rules[$name] as $rule)
{
if (!$rule->check($value)) {
$matched = false;
}
}
}
}
return $matched;
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\components\search\matches;
use yii\base\Component;
/**
* Base mathcer class for all matchers that will be used with filter.
*
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
abstract class Base extends Component implements MatcherInterface
{
/**
* @var mixed current value to check for the matcher
*/
public $value;
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\components\search\matches;
/**
*
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
class Exact extends Base
{
/**
* @var boolean if current matcher should consider partial mathc of given value.
*/
public $partial = false;
/**
* Checks if the given value is the same as base one or has partial match with base one.
* @param mixed $value
*/
public function check($value)
{
if (!$this->partial) {
return (mb_strtolower($this->value,'utf8') == mb_strtolower($value,'utf8'));
} else {
return (mb_strpos($value, $this->value) !== false);
}
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\components\search\matches;
/**
*
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
class Greater extends Base
{
/**
* Checks if the given value is the same as base one or has partial match with base one.
* @param mixed $value
*/
public function check($value)
{
return ($value > $this->value);
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\components\search\matches;
/**
*
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
class Lower extends Base
{
/**
* Checks if the given value is the same as base one or has partial match with base one.
* @param mixed $value
*/
public function check($value)
{
return ($value < $this->value);
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\components\search\matches;
/**
* MatcherInterface is the interface that should be implemented by all matchers that will be used in filter.
*
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
interface MatcherInterface
{
/**
* Check if the value is correct according current matcher.
* @param mixed $value
*/
public function check($value);
}
...@@ -10,6 +10,7 @@ namespace yii\debug\controllers; ...@@ -10,6 +10,7 @@ namespace yii\debug\controllers;
use Yii; use Yii;
use yii\web\Controller; use yii\web\Controller;
use yii\web\NotFoundHttpException; use yii\web\NotFoundHttpException;
use yii\debug\models\search\Debug;
/** /**
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
...@@ -38,7 +39,13 @@ class DefaultController extends Controller ...@@ -38,7 +39,13 @@ class DefaultController extends Controller
public function actionIndex() public function actionIndex()
{ {
return $this->render('index', ['manifest' => $this->getManifest()]); $searchModel = new Debug();
$dataProvider = $searchModel->search($_GET, $this->getManifest());
return $this->render('index', [
'dataProvider' => $dataProvider,
'searchModel' => $searchModel,
]);
} }
public function actionView($tag = null, $panel = null) public function actionView($tag = null, $panel = null)
......
<?php
namespace yii\debug\models\search;
use yii\base\Model;
use yii\data\ArrayDataProvider;
use yii\debug\components\search\Filter;
use yii\debug\components\search\matches;
/**
* Debug represents the model behind the search form about requests manifest data.
*/
class Debug extends Model
{
/**
* @var string tag attribute input search value
*/
public $tag;
/**
* @var string ip attribute input search value
*/
public $ip;
/**
* @var string method attribute input search value
*/
public $method;
/**
* @var integer ajax attribute input search value
*/
public $ajax;
/**
* @var string url attribute input search value
*/
public $url;
/**
* @var string status code attribute input search value
*/
public $statusCode;
/**
*
* @var integer sql count attribute input search value
*/
public $sqlCount;
/**
* @var array critical codes, used to determine grid row options.
*/
public $criticalCodes = [400, 404, 500];
public function rules()
{
return [
[['tag', 'ip', 'method', 'ajax', 'url','statusCode','sqlCount'], 'safe'],
];
}
/**
* @inheritdoc
*/
public function attributeLabels()
{
return [
'tag' => 'Tag',
'ip' => 'Ip',
'method' => 'Method',
'ajax' => 'Ajax',
'url' => 'url',
'statusCode' => 'Status code',
'sqlCount' => 'Total queries count',
];
}
/**
* Returns data provider with filled models. Filter applied if needed.
* @param type $params
* @param type $models
* @return \yii\data\ArrayDataProvider
*/
public function search($params, $models)
{
$dataProvider = new ArrayDataProvider([
'allModels' => $models,
'sort' => [
'attributes' => ['method', 'ip','tag','time','statusCode','sqlCount'],
],
'pagination' => [
'pageSize' => 10,
],
]);
if (!($this->load($params) && $this->validate())) {
return $dataProvider;
}
$filter = new Filter();
$this->addCondition($filter, 'tag', true);
$this->addCondition($filter, 'ip', true);
$this->addCondition($filter, 'method');
$this->addCondition($filter, 'ajax');
$this->addCondition($filter, 'url', true);
$this->addCondition($filter, 'statusCode');
$this->addCondition($filter, 'sqlCount');
$dataProvider->allModels = $filter->filter($models);
return $dataProvider;
}
/**
* Checks if the code is critical: 400 or greater, 500 or greater.
* @param integer $code
* @return bool
*/
public function isCodeCritical($code)
{
return in_array($code, $this->criticalCodes);
}
public function addCondition($filter,$attribute,$partial=false)
{
$value = $this->$attribute;
if (mb_strpos($value, '>') !== false)
{
$value = intval(str_replace('>', '', $value));
$filter->addMatch($attribute,new matches\Greater(['value' => $value]));
} elseif (mb_strpos($value, '<') !== false) {
$value = intval(str_replace('<', '', $value));
$filter->addMatch($attribute,new matches\Lower(['value' => $value]));
} else {
$filter->addMatch($attribute,new matches\Exact(['value' => $value, 'partial' => $partial]));
}
}
}
<?php <?php
use Yii;
use yii\helpers\Html; use yii\helpers\Html;
use yii\grid\GridView;
use yii\data\ArrayDataProvider;
/** /**
* @var \yii\web\View $this * @var \yii\web\View $this
...@@ -19,28 +22,61 @@ $this->title = 'Yii Debugger'; ...@@ -19,28 +22,61 @@ $this->title = 'Yii Debugger';
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<h1>Available Debug Data</h1> <h1>Available Debug Data</h1>
<table class="table table-condensed table-bordered table-striped table-hover" style="table-layout: fixed;">
<thead> <?php
<tr>
<th style="width: 120px;">Tag</th> $timeFormatter = extension_loaded('intl') ? Yii::createObject(['class' => 'yii\i18n\Formatter']) : Yii::$app->formatter;
<th style="width: 170px;">Time</th>
<th style="width: 120px;">IP</th> echo GridView::widget([
<th style="width: 70px;">Method</th> 'dataProvider' => $dataProvider,
<th>URL</th> 'filterModel' => $searchModel,
</tr> 'rowOptions' => function ($model, $key, $index, $grid) use ($searchModel)
</thead> {
<tbody> if ($searchModel->isCodeCritical($model['statusCode']))
<?php foreach ($manifest as $tag => $data): ?> return ['class'=>'danger'];
<tr> },
<td><?= Html::a($tag, ['view', 'tag' => $tag]) ?></td> 'columns' => [
<td><?= date('Y-m-d h:i:sa', $data['time']) ?></td> ['class' => 'yii\grid\SerialColumn'],
<td><?= $data['ip'] ?></td> [
<td><?= $data['method'] ?></td> 'attribute' => 'tag',
<td><?= $data['url'] ?></td> 'value' => function ($data)
</tr> {
<?php endforeach; ?> return Html::a($data['tag'], ['view', 'tag' => $data['tag']]);
</tbody> },
</table> 'format' => 'html',
],
[
'attribute' => 'time',
'value' => function ($data) use ($timeFormatter)
{
return $timeFormatter->asDateTime($data['time'], 'long');
},
],
'ip',
[
'attribute' => 'sqlCount',
'label' => 'Total queries count'
],
[
'attribute' => 'method',
'filter' => ['get' => 'GET', 'post' => 'POST', 'delete' => 'DELETE', 'put' => 'PUT', 'head' => 'HEAD']
],
[
'attribute'=>'ajax',
'value' => function ($data)
{
return $data['ajax'] ? 'Yes' : 'No';
},
'filter' => ['No', 'Yes'],
],
'url',
[
'attribute' => 'statusCode',
'filter' => [200=>200, 404=>404, 403=>403, 500=>500],
'label' => 'Status code'
],
],
]); ?>
</div> </div>
</div> </div>
</div> </div>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment