Commit 18be2212 by Alexander Makarov

Refactored debug module, added all missing docs

parent 5fbd4ce7
...@@ -9,6 +9,8 @@ namespace yii\debug; ...@@ -9,6 +9,8 @@ namespace yii\debug;
use yii\web\AssetBundle; use yii\web\AssetBundle;
/** /**
* Debugger asset bundle
*
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
......
...@@ -72,6 +72,13 @@ class LogTarget extends Target ...@@ -72,6 +72,13 @@ class LogTarget extends Target
$this->updateIndexFile($indexFile, $summary); $this->updateIndexFile($indexFile, $summary);
} }
/**
* Updates index file with summary log data
*
* @param string $indexFile path to index file
* @param array $summary summary log data
* @throws \yii\base\InvalidConfigException
*/
private function updateIndexFile($indexFile, $summary) private function updateIndexFile($indexFile, $summary)
{ {
touch($indexFile); touch($indexFile);
......
...@@ -50,7 +50,9 @@ class Module extends \yii\base\Module ...@@ -50,7 +50,9 @@ class Module extends \yii\base\Module
*/ */
public $historySize = 50; public $historySize = 50;
/**
* @inheritdoc
*/
public function init() public function init()
{ {
parent::init(); parent::init();
...@@ -69,13 +71,16 @@ class Module extends \yii\base\Module ...@@ -69,13 +71,16 @@ class Module extends \yii\base\Module
} }
} }
/**
* @inheritdoc
*/
public function beforeAction($action) public function beforeAction($action)
{ {
Yii::$app->getView()->off(View::EVENT_END_BODY, [$this, 'renderToolbar']); Yii::$app->getView()->off(View::EVENT_END_BODY, [$this, 'renderToolbar']);
unset(Yii::$app->getLog()->targets['debug']); unset(Yii::$app->getLog()->targets['debug']);
$this->logTarget = null; $this->logTarget = null;
if ($this->checkAccess($action)) { if ($this->checkAccess()) {
return parent::beforeAction($action); return parent::beforeAction($action);
} elseif ($action->id === 'toolbar') { } elseif ($action->id === 'toolbar') {
return false; return false;
...@@ -84,6 +89,11 @@ class Module extends \yii\base\Module ...@@ -84,6 +89,11 @@ class Module extends \yii\base\Module
} }
} }
/**
* Renders mini-toolbar at the end of page body.
*
* @param \yii\base\Event $event
*/
public function renderToolbar($event) public function renderToolbar($event)
{ {
if (!$this->checkAccess() || Yii::$app->getRequest()->getIsAjax()) { if (!$this->checkAccess() || Yii::$app->getRequest()->getIsAjax()) {
...@@ -99,6 +109,10 @@ class Module extends \yii\base\Module ...@@ -99,6 +109,10 @@ class Module extends \yii\base\Module
echo '<script>' . $view->renderPhpFile(__DIR__ . '/assets/toolbar.js') . '</script>'; echo '<script>' . $view->renderPhpFile(__DIR__ . '/assets/toolbar.js') . '</script>';
} }
/**
* Checks if current user is allowed to access the module
* @return boolean if access is granted
*/
protected function checkAccess() protected function checkAccess()
{ {
$ip = Yii::$app->getRequest()->getUserIP(); $ip = Yii::$app->getRequest()->getUserIP();
...@@ -111,6 +125,9 @@ class Module extends \yii\base\Module ...@@ -111,6 +125,9 @@ class Module extends \yii\base\Module
return false; return false;
} }
/**
* @return array default set of panels
*/
protected function corePanels() protected function corePanels()
{ {
return [ return [
......
...@@ -73,6 +73,11 @@ class Panel extends Component ...@@ -73,6 +73,11 @@ class Panel extends Component
return null; return null;
} }
/**
* Loads data into the panel
*
* @param mixed $data
*/
public function load($data) public function load($data)
{ {
$this->data = $data; $this->data = $data;
......
<?php <?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\components\search; namespace yii\debug\components\search;
use yii\base\Component; use yii\base\Component;
use yii\debug\components\search\matchers\MatcherInterface;
/**
* Provides array filtering capabilities.
*
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
class Filter extends Component class Filter extends Component
{ {
/** /**
* @var array rules for matching filters in the way: [:fieldName => [rule1, rule2,..]] * @var array rules for matching filters in the way: [:fieldName => [rule1, rule2,..]]
*/ */
protected $rules = []; protected $rules = [];
/** /**
* Adds rules for filtering data. Match can be partial or exactly. * Adds data filtering rule.
*
* @param string $name attribute name * @param string $name attribute name
* @param \yii\debug\components\search\matches\Base $rule * @param MatcherInterface $rule
*/ */
public function addMatch($name, $rule) public function addMatcher($name, MatcherInterface $rule)
{ {
if (empty($rule->value) && $rule->value !== 0) { if ($rule->hasValue()) {
return;
}
$this->rules[$name][] = $rule; $this->rules[$name][] = $rule;
} }
}
/** /**
* Applies filter on given array and returns filtered data. * Applies filter on a given array and returns filtered data.
*
* @param array $data data to filter * @param array $data data to filter
* @return array filtered data * @return array filtered data
*/ */
...@@ -36,7 +47,7 @@ class Filter extends Component ...@@ -36,7 +47,7 @@ class Filter extends Component
$filtered = []; $filtered = [];
foreach ($data as $row) { foreach ($data as $row) {
if ($this->checkFilter($row)) { if ($this->passesFilter($row)) {
$filtered[] = $row; $filtered[] = $row;
} }
} }
...@@ -45,28 +56,25 @@ class Filter extends Component ...@@ -45,28 +56,25 @@ class Filter extends Component
} }
/** /**
* Check if the given data satisfies filters. * Checks if the given data satisfies filters.
* @param array $row *
* @param array $row data
* @return boolean if data passed filtering
*/ */
public function checkFilter(array $row) private function passesFilter(array $row)
{ {
$matched = true;
foreach ($row as $name => $value) { foreach ($row as $name => $value) {
if (isset($this->rules[$name])) { if (isset($this->rules[$name])) {
// check all rules for a given attribute
#check all rules for given attribute
foreach ($this->rules[$name] as $rule) { foreach ($this->rules[$name] as $rule) {
if (!$rule->check($value)) { /** @var MatcherInterface $rule */
$matched = false; if (!$rule->match($value)) {
return false;
} }
} }
} }
} }
return $matched; return true;
} }
} }
...@@ -5,22 +5,36 @@ ...@@ -5,22 +5,36 @@
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\debug\components\search\matches; namespace yii\debug\components\search\matchers;
use yii\base\Component; use yii\base\Component;
/** /**
* Base mathcer class for all matchers that will be used with filter. * Base class for matchers that are used in a filter.
* *
* @author Mark Jebri <mark.github@yandex.ru> * @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0 * @since 2.0
*/ */
abstract class Base extends Component implements MatcherInterface abstract class Base extends Component implements MatcherInterface
{ {
/**
* @var mixed base value to check
*/
protected $baseValue;
/** /**
* @var mixed current value to check for the matcher * @inheritdoc
*/ */
public $value; public function setValue($value)
{
$this->baseValue = $value;
}
/**
* @inheritdoc
*/
public function hasValue()
{
return !empty($this->baseValue) || $this->baseValue === 0;
}
} }
...@@ -5,23 +5,21 @@ ...@@ -5,23 +5,21 @@
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\debug\components\search\matches; namespace yii\debug\components\search\matchers;
/** /**
* Checks if the given value is greater than the base one.
* *
* @author Mark Jebri <mark.github@yandex.ru> * @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0 * @since 2.0
*/ */
class Greater extends Base class GreaterThan extends Base
{ {
/** /**
* Checks if the given value is the same as base one or has partial match with base one. * @inheritdoc
* @param mixed $value
*/ */
public function check($value) public function match($value)
{ {
return ($value > $this->value); return ($value > $this->baseValue);
} }
} }
...@@ -5,23 +5,21 @@ ...@@ -5,23 +5,21 @@
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\debug\components\search\matches; namespace yii\debug\components\search\matchers;
/** /**
* Checks if the given value is lower than the base one.
* *
* @author Mark Jebri <mark.github@yandex.ru> * @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0 * @since 2.0
*/ */
class Lower extends Base class LowerThan extends Base
{ {
/** /**
* Checks if the given value is the same as base one or has partial match with base one. * @inheritdoc
* @param mixed $value
*/ */
public function check($value) public function match($value)
{ {
return ($value < $this->value); return ($value < $this->baseValue);
} }
} }
...@@ -5,21 +5,35 @@ ...@@ -5,21 +5,35 @@
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\debug\components\search\matches; namespace yii\debug\components\search\matchers;
/** /**
* MatcherInterface is the interface that should be implemented by all matchers that will be used in filter. * MatcherInterface should be implemented by all matchers that are used in a filter.
* *
* @author Mark Jebri <mark.github@yandex.ru> * @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0 * @since 2.0
*/ */
interface MatcherInterface interface MatcherInterface
{ {
/**
* Checks if the value passed matches base value.
*
* @param mixed $value value to be matched
* @return boolean if there is a match
*/
public function match($value);
/** /**
* Check if the value is correct according current matcher. * Sets base value to match against
*
* @param mixed $value * @param mixed $value
*/ */
public function check($value); public function setValue($value);
/**
* Checks if base value is set
*
* @return boolean if base value is set
*/
public function hasValue();
} }
...@@ -5,32 +5,30 @@ ...@@ -5,32 +5,30 @@
* @license http://www.yiiframework.com/license/ * @license http://www.yiiframework.com/license/
*/ */
namespace yii\debug\components\search\matches; namespace yii\debug\components\search\matchers;
/** /**
* Checks if the given value is exactly or partially same as the base one.
* *
* @author Mark Jebri <mark.github@yandex.ru> * @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0 * @since 2.0
*/ */
class Exact extends Base class SameAs extends Base
{ {
/** /**
* @var boolean if current matcher should consider partial match of given value. * @var boolean if partial match should be used.
*/ */
public $partial = false; public $partial = false;
/** /**
* Checks if the given value is the same as base one or has partial match with base one. * @inheritdoc
* @param mixed $value
*/ */
public function check($value) public function match($value)
{ {
if (!$this->partial) { if (!$this->partial) {
return (mb_strtolower($this->value, 'utf8') == mb_strtolower($value, 'utf8')); return (mb_strtolower($this->baseValue, 'utf8') == mb_strtolower($value, 'utf8'));
} else { } else {
return (mb_strpos(mb_strtolower($value, 'utf8'), mb_strtolower($this->value,'utf8')) !== false); return (mb_strpos(mb_strtolower($value, 'utf8'), mb_strtolower($this->baseValue, 'utf8')) !== false);
} }
} }
} }
...@@ -13,11 +13,16 @@ use yii\web\NotFoundHttpException; ...@@ -13,11 +13,16 @@ use yii\web\NotFoundHttpException;
use yii\debug\models\search\Debug; use yii\debug\models\search\Debug;
/** /**
* Debugger controller
*
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class DefaultController extends Controller class DefaultController extends Controller
{ {
/**
* @inheritdoc
*/
public $layout = 'main'; public $layout = 'main';
/** /**
* @var \yii\debug\Module * @var \yii\debug\Module
...@@ -28,6 +33,9 @@ class DefaultController extends Controller ...@@ -28,6 +33,9 @@ class DefaultController extends Controller
*/ */
public $summary; public $summary;
/**
* @inheritdoc
*/
public function actions() public function actions()
{ {
$actions = []; $actions = [];
......
<?php <?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\models\search; namespace yii\debug\models\search;
use yii\base\Model; use yii\base\Model;
use yii\debug\components\search\Filter; use yii\debug\components\search\Filter;
use yii\debug\components\search\matches; use yii\debug\components\search\matchers;
/**
* Base search model
*
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
class Base extends Model class Base extends Model
{ {
/** /**
* @param Filter $filter * Adds filtering condition for a given attribute
* @param string $attribute *
* @param boolean $partial * @param Filter $filter filter instance
* @param string $attribute attribute to filter
* @param boolean $partial if partial match should be used
*/ */
public function addCondition($filter, $attribute, $partial = false) public function addCondition(Filter $filter, $attribute, $partial = false)
{ {
$value = $this->$attribute; $value = $this->$attribute;
if (mb_strpos($value, '>') !== false) { if (mb_strpos($value, '>') !== false) {
$value = intval(str_replace('>', '', $value)); $value = intval(str_replace('>', '', $value));
$filter->addMatch($attribute, new matches\Greater(['value' => $value])); $filter->addMatcher($attribute, new matchers\GreaterThan(['value' => $value]));
} elseif (mb_strpos($value, '<') !== false) { } elseif (mb_strpos($value, '<') !== false) {
$value = intval(str_replace('<', '', $value)); $value = intval(str_replace('<', '', $value));
$filter->addMatch($attribute, new matches\Lower(['value' => $value])); $filter->addMatcher($attribute, new matchers\LowerThan(['value' => $value]));
} else { } else {
$filter->addMatch($attribute, new matches\Exact(['value' => $value, 'partial' => $partial])); $filter->addMatcher($attribute, new matchers\SameAs(['value' => $value, 'partial' => $partial]));
} }
} }
} }
<?php <?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\models\search; namespace yii\debug\models\search;
...@@ -6,13 +11,16 @@ use yii\data\ArrayDataProvider; ...@@ -6,13 +11,16 @@ use yii\data\ArrayDataProvider;
use yii\debug\components\search\Filter; use yii\debug\components\search\Filter;
/** /**
* Db represents the model behind the search form about current request database queries. * Search model for current request database queries.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/ */
class Db extends Base class Db extends Base
{ {
/** /**
* @var string type attribute input search value * @var string type of the input search value
*/ */
public $type; public $type;
...@@ -21,6 +29,9 @@ class Db extends Base ...@@ -21,6 +29,9 @@ class Db extends Base
*/ */
public $query; public $query;
/**
* @inheritdoc
*/
public function rules() public function rules()
{ {
return [ return [
...@@ -41,8 +52,9 @@ class Db extends Base ...@@ -41,8 +52,9 @@ class Db extends Base
/** /**
* Returns data provider with filled models. Filter applied if needed. * Returns data provider with filled models. Filter applied if needed.
* @param array $params *
* @param array $models * @param array $params an array of parameter values indexed by parameter names
* @param array $models data to return provider for
* @return \yii\data\ArrayDataProvider * @return \yii\data\ArrayDataProvider
*/ */
public function search($params, $models) public function search($params, $models)
...@@ -69,5 +81,4 @@ class Db extends Base ...@@ -69,5 +81,4 @@ class Db extends Base
return $dataProvider; return $dataProvider;
} }
} }
<?php <?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\models\search; namespace yii\debug\models\search;
...@@ -6,7 +11,11 @@ use yii\data\ArrayDataProvider; ...@@ -6,7 +11,11 @@ use yii\data\ArrayDataProvider;
use yii\debug\components\search\Filter; use yii\debug\components\search\Filter;
/** /**
* Debug represents the model behind the search form about requests manifest data. * Search model for requests manifest data.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/ */
class Debug extends Base class Debug extends Base
{ {
...@@ -51,6 +60,9 @@ class Debug extends Base ...@@ -51,6 +60,9 @@ class Debug extends Base
*/ */
public $criticalCodes = [400, 404, 500]; public $criticalCodes = [400, 404, 500];
/**
* @inheritdoc
*/
public function rules() public function rules()
{ {
return [ return [
...@@ -76,8 +88,8 @@ class Debug extends Base ...@@ -76,8 +88,8 @@ class Debug extends Base
/** /**
* Returns data provider with filled models. Filter applied if needed. * Returns data provider with filled models. Filter applied if needed.
* @param array $params * @param array $params an array of parameter values indexed by parameter names
* @param array $models * @param array $models data to return provider for
* @return \yii\data\ArrayDataProvider * @return \yii\data\ArrayDataProvider
*/ */
public function search($params, $models) public function search($params, $models)
...@@ -110,13 +122,13 @@ class Debug extends Base ...@@ -110,13 +122,13 @@ class Debug extends Base
} }
/** /**
* Checks if the code is critical: 400 or greater, 500 or greater. * Checks if code is critical.
*
* @param integer $code * @param integer $code
* @return bool * @return boolean
*/ */
public function isCodeCritical($code) public function isCodeCritical($code)
{ {
return in_array($code, $this->criticalCodes); return in_array($code, $this->criticalCodes);
} }
} }
<?php <?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\models\search; namespace yii\debug\models\search;
...@@ -6,11 +11,14 @@ use yii\data\ArrayDataProvider; ...@@ -6,11 +11,14 @@ use yii\data\ArrayDataProvider;
use yii\debug\components\search\Filter; use yii\debug\components\search\Filter;
/** /**
* Log represents the model behind the search form about current request log. * Search model for current request log.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/ */
class Log extends Base class Log extends Base
{ {
/** /**
* @var string ip attribute input search value * @var string ip attribute input search value
*/ */
...@@ -26,6 +34,9 @@ class Log extends Base ...@@ -26,6 +34,9 @@ class Log extends Base
*/ */
public $message; public $message;
/**
* @inheritdoc
*/
public function rules() public function rules()
{ {
return [ return [
...@@ -47,8 +58,9 @@ class Log extends Base ...@@ -47,8 +58,9 @@ class Log extends Base
/** /**
* Returns data provider with filled models. Filter applied if needed. * Returns data provider with filled models. Filter applied if needed.
* @param array $params *
* @param array $models * @param array $params an array of parameter values indexed by parameter names
* @param array $models data to return provider for
* @return \yii\data\ArrayDataProvider * @return \yii\data\ArrayDataProvider
*/ */
public function search($params, $models) public function search($params, $models)
...@@ -73,5 +85,4 @@ class Log extends Base ...@@ -73,5 +85,4 @@ class Log extends Base
return $dataProvider; return $dataProvider;
} }
} }
<?php <?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\models\search; namespace yii\debug\models\search;
...@@ -6,11 +11,14 @@ use yii\data\ArrayDataProvider; ...@@ -6,11 +11,14 @@ use yii\data\ArrayDataProvider;
use yii\debug\components\search\Filter; use yii\debug\components\search\Filter;
/** /**
* Profile represents the model behind the search form about current request profiling log. * Search model for current request profiling log.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/ */
class Profile extends Base class Profile extends Base
{ {
/** /**
* @var string method attribute input search value * @var string method attribute input search value
*/ */
...@@ -21,6 +29,9 @@ class Profile extends Base ...@@ -21,6 +29,9 @@ class Profile extends Base
*/ */
public $info; public $info;
/**
* @inheritdoc
*/
public function rules() public function rules()
{ {
return [ return [
...@@ -41,8 +52,9 @@ class Profile extends Base ...@@ -41,8 +52,9 @@ class Profile extends Base
/** /**
* Returns data provider with filled models. Filter applied if needed. * Returns data provider with filled models. Filter applied if needed.
* @param array $params *
* @param array $models * @param array $params an array of parameter values indexed by parameter names
* @param array $models data to return provider for
* @return \yii\data\ArrayDataProvider * @return \yii\data\ArrayDataProvider
*/ */
public function search($params, $models) public function search($params, $models)
...@@ -69,5 +81,4 @@ class Profile extends Base ...@@ -69,5 +81,4 @@ class Profile extends Base
return $dataProvider; return $dataProvider;
} }
} }
...@@ -18,26 +18,45 @@ use yii\debug\Panel; ...@@ -18,26 +18,45 @@ use yii\debug\Panel;
*/ */
class ConfigPanel extends Panel class ConfigPanel extends Panel
{ {
/**
* @inheritdoc
*/
public function getName() public function getName()
{ {
return 'Configuration'; return 'Configuration';
} }
/**
* Returns Yii logo ready to use in `<img src="`
*
* @return string base64 representation of the image
*/
public static function getYiiLogo() public static function getYiiLogo()
{ {
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAeCAYAAADQBxWhAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAADcZJREFUeAEAtg1J8gHaKRUAAP8AAAEGAQACDgMAAQgDAAIIAQABCQMAAQgEAAIKAwACCAQAAQgCAAIJBQACCQQAAgkFAAIJBwAQCwkA0hgAANAO+gAM/AEAAQABAPn++wD2/PkA+f38Of3+/Wb+//9S/v/+sQABAV4DAAEAAAAAAAQAAAAAAAAAAAD/AAAA/wAAAP8AGwD/ABoA/wAAAP8A5gD/AOUA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AAAAAAB+BgIEgf8BAwD9//4A/v/9Xfz+/hEAAABNAAAAAAQA/wAAAP8AMgADAGEAAwE7AQUBJwAGAgoBBQIAAQUB9gADAdoAAQDFAP//sQD//7sAAAAAAAAAAAAAAAAAAAAA/wD/AP///wD//wAA/wD/AAAAAH4HAwWBAP8AAP7//wD9//4A/f8DAAIB/Uz9//1mAQECmgHaKRUAAAIApwMPBFgCDAQAAQsCAAIJAwABBwMAAgkDAAEHAwABBQIAAQYDAAEFA8cCCAOEAggFtgIKBwAQCgkAzhsBANQO+wAP+wEA/gD/QP///78AAAAA/gD+AP7+/wD9//4A/P/+AP39/gD7//zp/gD/GAQCCAIAAAQA5wAJAgABCAIAAQcCAAEGAwACCAIAAAcCAAEHAgABBgQAAgcEAAIGAjkABAK1AAEAnwD//2f8AP77FPwHABACAwAEBAAAAP/+jgD//wD/AAAA/f/+AP4B/gD9//4AAv79APwB/QAA/f8X/P7+PgQCCgMAAAIBzgAGAQABBgEAAgUCAAIGAQABBgIAAQYDAAIFBAAABwQAAQcCAAEGAwABBQUAAQQCYQEDAiv6Af9HFvgD8A/+AQD2A/4hBf4CMQAAAQD/AP4A/v//AP7+/gD8//4AAgECAAL/AAAB/wAAAgD+RgQACwMAAP8AwwIFAQABBgIAAQYCAAAHAwABBgMAAQUDAAEHAwABBgIAAgYDAAEGBQACBgQAAgUEAAAFAjb9AwG+CPz+ORv6BfndDgMsBvsBAAAAAAD/AP4A/v/+APwB/gAC//0AAv4CAAL+AAAAAwEAAAH8FAICBgEAAgYA4QAEAscBBQIAAQYCAAEFAgAABAIAAQUDAAEFAwACBgMAAQYFAAIGBAABBwQAAAgEAAIHBQACCAYx/gMBpR7zAAAP/wbaBAUHAAcEBQAGAwYABgMGAAcDBQAFAwUABAMDAAQCBQAFAgMABAED/wICDAQAAgwFAAIGAngBAwEAAAUCAAEDAQACBQIAAQUCAAEFAgABBQQAAQYDAAEHBAACBgQAAgUDAAEGAwACBwUA/wn+U/0FHlULABjZBQX74AYDBwAGBAUABQMFAAUDBAAGAgUABQIEAAUCAwAEAQQABAID6AIABQEAAAYBAAAEAcIAAwGZAQMBAAAEAgAABAMAAgUCAAEEAgABBAIAAgQDAAEEAwABBQIAAQYDAAIHBQACBgQAAwYEAP8KAKIHAhEABwQChgYEBQAGAgUABwMFAAUCBQADAgMABQIEAAMCAwADAgMAAwIEugIA/wAAAP8AAAD+/wAAAABoAAMBqgIEAgABBAIAAAMBAAEEAwAABAMAAQUDAAEFAgAABAMAAgUEAAEFBAABBgUAAAcKAAUG8QgH/A93B/4amwYF/f8FAwYABAIDAAUDBAAEAgMAAwIDAAMBAgACAQHkBQIDxwIAAAAAAAAAAAAAAAAAAAAAAQABVwACAnsBAwH0AQMCAAEEAgABBAIAAAMCAAEDAgACBAMAAQUDAAEEAwABBQQAAgcFAP4FBQADAPqABfwaAQQDBbEEAwUAAwMFAAMCAwAEAgMAAwECAAMBAgACAQKaBAIDAAIAAAAAAAAAAAAAAAAAAAAAAAAAAP8A/4YAAAAvAQIBhQABAcoBAgIAAgMCAAEDAgABBAMAAAMDAAEEAwABBQQAAAcCAPwECwD9AgAIAf8LUQQBEaYGAwEAAwIEAAICAgACAgIAAQECAAECAvEDAgOTBAIDAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADeAAAAfAABADcAAgAx/wIAdwACArUAAgL3AQICAAEDAwABAwMAAAYCAPkCDgD8AgoA/QAIbP//Ec0EBAD7AgECAAIBAgACAAIAAgABAAEAAXEEAgPwBQIFAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQAAAAigAAAEwBAAAxAAIBYgACArMDA/v8AAXzAPcADwD9AgkA/gIJQf//BBsCAfrZAf8CAAAAAAAAAAAA/wAAuAEBAp8FAgUABAIEAAIA//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAwAHAffZBgHtnwQD8k4ABPQp1vVFpvYCFgANCPUA/QIIAPr9Eyb8/AOx/wH7AP///wD+//7nAQEAWQUCBAAEAgQABAIEAAT98esAAQYJAAMLEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAIACQH3AAME7AD4AigA3/0sANPtLAD5Ag3c5AE5GxcK8QAzEsgAFAnzAPH7ESMC/ATg/v/1AP3+AP7///9PAQAB8AIBAQAAAAAAAAAAAAOGorAA+9zPAPvg1wADFBQAAgYCAAEGAwAABQYACwT4AA0F9AAIB/UA8QIXANf8LgCp+WIAtvNKAOP3GwDu/BmLEAPuWvT8CgDh9iYABwX+ABUN+PD8++EL+/zuNP3/A08A//+//wD/AAAAAAAAAAAAAAAAAAH///8A+ubdAOdzRQD/7t8AESYZAA0UCAACCPwA8A4iANsJLwDRBC8A2P0rAN37IgAIAfYABv70AA0LBkERCwe+BwQCAAkHAAAAAwkA+wMRADEZ7N0qCYfF9/jR0/4CFoz///wA/f3+AAAAAAAAAAAAAAAAAAH///8A/gAAAPXn4QD90bsA58y1APH38wAIEAkApA5sANICMgD//QAACQD1AA0C8wD//wAABAICEQsIBN4IBgQQBwUCAAkGAwAJBgIAAwQGAP0DFgAuEqk+FQbDw/j+GAD///0A/v7+AAAAAAAAAAAAAAAAAAH///8A+vv7AP4FBQAIAAAAlL7hAJC+6AAZEgwA/gACAAr/9AABAAAAAQD/AP8AAQD+//8ADQgFqw0IBlQIBQMACAYDAAgFBAAHBgMACgYBAAYFBP8BBA0XAwH+6/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAQAAAP729gAACgYA6/T4AOf1+gAQDPYAA//8APIA/wAAAAEAAAAAAP3//wAGAAE5BQECVAEA+wD8/v8A/f7/AP7+/wD9//4A/v8EAAr+/OYD/fLr/v8CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAA/v0AOx0HAIkA+ADf7/sABgMBAAAAAAAAAAAAAAAAAP4A/wAMBAR+AP8AAP3//wD8/v8A/f7+AP7+/wD9/v8A/v7/AP//BLgC/P4A//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAD+/P4ANyENABgPAwDh4/QAAAsEAAAAAAAAAAAAAP8AAAAAABTyCAVI/f//ABX//gAO/v4A/v4EAP3+/wD8//8A/v//AP8AAJICAQEA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAD2+v0ADxAIAEUlEACc0+0AAAwEAAD+AAAAAAAAAAEAAAD//1b49/oA5P3/APn+/wAW/v8AD/3/AP3+/wD8/v4A/f4Fofn8/dL//v8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAD/AAAAAPv9AE4oEQCcCwQAAP3/AAACAQAAAAAAAAAAAAABATAAAP8A/fr7AOj9/gD3/gYAGP//ABH+/gAK/QTfBgMCZwEAAgD9/v8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAABAAAA8fH5AE0zFwA3MRYAAOv3AAAAAAAA/v8AAP7/AAD/ABgAAgEAAAMBAAD9/QDx8/gA4fT5AOX3+tv4/P4/AwEB+QMCAQADAgEAAwIBAAMCAQADAgEAAwIBAAMCAQADAgEAAwIBAAIAAAAAAQEBAAAAAAAAAAAA9/7/AAAAAACGRB0AAAQDAAD5/wAA/gEAAP4BAAAAAA4A/gAAAPr/AAD4/wAC+fwA+Pb4qfH7/jgDAgHjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT///8G/Pz8+gAAAAD///8AAgAAAPHt9wBCKBEAdFIfAMbZ7AARCwYADQkCAM7d9xzg6foABQ0D8SkVA7spHA+grNnxtfv8/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT9/f3+////AQQEBAEAAAABBAQE/f0BAQAABQcASiMNAN3g5wAbDQQADAf/AOgNAXosEgkMAQgAsA4GAe4SEAUA/P8BAAQBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAP//vNz1LVdvDhUAAAAASUVORK5CYII='; return 'data:image/png;base64,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';
} }
/**
* @inheritdoc
*/
public function getSummary() public function getSummary()
{ {
return Yii::$app->view->render('panels/config/summary', ['panel' => $this]); return Yii::$app->view->render('panels/config/summary', ['panel' => $this]);
} }
/**
* @inheritdoc
*/
public function getDetail() public function getDetail()
{ {
return Yii::$app->view->render('panels/config/detail', ['panel' => $this]); return Yii::$app->view->render('panels/config/detail', ['panel' => $this]);
} }
/**
* Returns data about extensions
*
* @return array
*/
public function getExtensions() public function getExtensions()
{ {
$data = []; $data = [];
...@@ -47,6 +66,9 @@ class ConfigPanel extends Panel ...@@ -47,6 +66,9 @@ class ConfigPanel extends Panel
return $data; return $data;
} }
/**
* @inheritdoc
*/
public function save() public function save()
{ {
return [ return [
......
...@@ -20,7 +20,6 @@ use yii\debug\models\search\Db; ...@@ -20,7 +20,6 @@ use yii\debug\models\search\Db;
*/ */
class DbPanel extends Panel class DbPanel extends Panel
{ {
/** /**
* @var array db queries info extracted to array as models, to use with data provider. * @var array db queries info extracted to array as models, to use with data provider.
*/ */
...@@ -31,11 +30,17 @@ class DbPanel extends Panel ...@@ -31,11 +30,17 @@ class DbPanel extends Panel
*/ */
private $_timings; private $_timings;
/**
* @inheritdoc
*/
public function getName() public function getName()
{ {
return 'Database'; return 'Database';
} }
/**
* @inheritdoc
*/
public function getSummary() public function getSummary()
{ {
$timings = $this->calculateTimings(); $timings = $this->calculateTimings();
...@@ -50,6 +55,9 @@ class DbPanel extends Panel ...@@ -50,6 +55,9 @@ class DbPanel extends Panel
]); ]);
} }
/**
* @inheritdoc
*/
public function getDetail() public function getDetail()
{ {
$searchModel = new Db(); $searchModel = new Db();
...@@ -63,7 +71,8 @@ class DbPanel extends Panel ...@@ -63,7 +71,8 @@ class DbPanel extends Panel
} }
/** /**
* Calculates given request profile messages timings. * Calculates given request profile timings.
*
* @return array timings [token, category, timestamp, traces, nesting level, elapsed time] * @return array timings [token, category, timestamp, traces, nesting level, elapsed time]
*/ */
protected function calculateTimings() protected function calculateTimings()
...@@ -74,6 +83,9 @@ class DbPanel extends Panel ...@@ -74,6 +83,9 @@ class DbPanel extends Panel
return $this->_timings; return $this->_timings;
} }
/**
* @inheritdoc
*/
public function save() public function save()
{ {
$target = $this->module->logTarget; $target = $this->module->logTarget;
...@@ -82,7 +94,8 @@ class DbPanel extends Panel ...@@ -82,7 +94,8 @@ class DbPanel extends Panel
} }
/** /**
* Returns total queries time. * Returns total query time.
*
* @param array $timings * @param array $timings
* @return integer total time * @return integer total time
*/ */
...@@ -98,8 +111,8 @@ class DbPanel extends Panel ...@@ -98,8 +111,8 @@ class DbPanel extends Panel
} }
/** /**
* Returns array of models that represents logs of the current request. Can be used with data providers, * Returns an array of models that represents logs of the current request.
* like yii\data\ArrayDataProvider. * Can be used with data providers such as \yii\data\ArrayDataProvider.
* @return array models * @return array models
*/ */
protected function getModels() protected function getModels()
...@@ -110,7 +123,7 @@ class DbPanel extends Panel ...@@ -110,7 +123,7 @@ class DbPanel extends Panel
foreach($timings as $seq => $dbTiming) { foreach($timings as $seq => $dbTiming) {
$this->_models[] = [ $this->_models[] = [
'type' => $this->detectQueryType($dbTiming['info']), 'type' => $this->getQueryType($dbTiming['info']),
'query' => $dbTiming['info'], 'query' => $dbTiming['info'],
'duration' => ($dbTiming['duration'] * 1000), // in milliseconds 'duration' => ($dbTiming['duration'] * 1000), // in milliseconds
'trace' => $dbTiming['trace'], 'trace' => $dbTiming['trace'],
...@@ -123,16 +136,15 @@ class DbPanel extends Panel ...@@ -123,16 +136,15 @@ class DbPanel extends Panel
} }
/** /**
* Detects databse timing type. Detecting is produced through simple parsing to the first space|tab|new row. * Returns databse query type.
* First word before space is timing type. If there is no such words, timing will have empty type. *
* @param string $timing timing procedure string * @param string $timing timing procedure string
* @return string query type select|insert|delete|etc * @return string query type such as select, insert, delete, etc.
*/ */
protected function detectQueryType($timing) protected function getQueryType($timing)
{ {
$timing = ltrim($timing); $timing = ltrim($timing);
preg_match('/^([a-zA-z]*)/', $timing, $matches); preg_match('/^([a-zA-z]*)/', $timing, $matches);
return count($matches) ? $matches[0] : ''; return count($matches) ? $matches[0] : '';
} }
} }
...@@ -20,22 +20,30 @@ use yii\debug\models\search\Log; ...@@ -20,22 +20,30 @@ use yii\debug\models\search\Log;
*/ */
class LogPanel extends Panel class LogPanel extends Panel
{ {
/** /**
* @var array log messages extracted to array as models, to use with data provider. * @var array log messages extracted to array as models, to use with data provider.
*/ */
private $_models; private $_models;
/**
* @inheritdoc
*/
public function getName() public function getName()
{ {
return 'Logs'; return 'Logs';
} }
/**
* @inheritdoc
*/
public function getSummary() public function getSummary()
{ {
return Yii::$app->view->render('panels/log/summary', ['data' => $this->data, 'panel' => $this]); return Yii::$app->view->render('panels/log/summary', ['data' => $this->data, 'panel' => $this]);
} }
/**
* @inheritdoc
*/
public function getDetail() public function getDetail()
{ {
$searchModel = new Log(); $searchModel = new Log();
...@@ -48,6 +56,9 @@ class LogPanel extends Panel ...@@ -48,6 +56,9 @@ class LogPanel extends Panel
]); ]);
} }
/**
* @inheritdoc
*/
public function save() public function save()
{ {
$target = $this->module->logTarget; $target = $this->module->logTarget;
...@@ -56,9 +67,10 @@ class LogPanel extends Panel ...@@ -56,9 +67,10 @@ class LogPanel extends Panel
} }
/** /**
* Returns array of models that represents logs of the current request. Can be used with data providers, * Returns an array of models that represents logs of the current request.
* like yii\data\ArrayDataProvider. * Can be used with data providers, such as \yii\data\ArrayDataProvider.
* @param boolean $refresh if needed to build models from log messages and refresh them. *
* @param boolean $refresh if need to build models from log messages and refresh them.
* @return array models * @return array models
*/ */
protected function getModels($refresh = false) protected function getModels($refresh = false)
...@@ -78,5 +90,4 @@ class LogPanel extends Panel ...@@ -78,5 +90,4 @@ class LogPanel extends Panel
} }
return $this->_models; return $this->_models;
} }
} }
...@@ -25,11 +25,17 @@ class ProfilingPanel extends Panel ...@@ -25,11 +25,17 @@ class ProfilingPanel extends Panel
*/ */
private $_models; private $_models;
/**
* @inheritdoc
*/
public function getName() public function getName()
{ {
return 'Profiling'; return 'Profiling';
} }
/**
* @inheritdoc
*/
public function getSummary() public function getSummary()
{ {
return Yii::$app->view->render('panels/profile/summary', [ return Yii::$app->view->render('panels/profile/summary', [
...@@ -39,6 +45,9 @@ class ProfilingPanel extends Panel ...@@ -39,6 +45,9 @@ class ProfilingPanel extends Panel
]); ]);
} }
/**
* @inheritdoc
*/
public function getDetail() public function getDetail()
{ {
$searchModel = new Profile(); $searchModel = new Profile();
...@@ -53,6 +62,9 @@ class ProfilingPanel extends Panel ...@@ -53,6 +62,9 @@ class ProfilingPanel extends Panel
]); ]);
} }
/**
* @inheritdoc
*/
public function save() public function save()
{ {
$target = $this->module->logTarget; $target = $this->module->logTarget;
...@@ -65,7 +77,7 @@ class ProfilingPanel extends Panel ...@@ -65,7 +77,7 @@ class ProfilingPanel extends Panel
} }
/** /**
* Returns array of profiling models that can be used in data provider. * Returns array of profiling models that can be used in a data provider.
* @return array models * @return array models
*/ */
protected function getModels() protected function getModels()
...@@ -87,5 +99,4 @@ class ProfilingPanel extends Panel ...@@ -87,5 +99,4 @@ class ProfilingPanel extends Panel
} }
return $this->_models; return $this->_models;
} }
} }
...@@ -19,21 +19,33 @@ use yii\debug\Panel; ...@@ -19,21 +19,33 @@ use yii\debug\Panel;
*/ */
class RequestPanel extends Panel class RequestPanel extends Panel
{ {
/**
* @inheritdoc
*/
public function getName() public function getName()
{ {
return 'Request'; return 'Request';
} }
/**
* @inheritdoc
*/
public function getSummary() public function getSummary()
{ {
return Yii::$app->view->render('panels/request/summary', ['panel' => $this]); return Yii::$app->view->render('panels/request/summary', ['panel' => $this]);
} }
/**
* @inheritdoc
*/
public function getDetail() public function getDetail()
{ {
return Yii::$app->view->render('panels/request/detail', ['panel' => $this]); return Yii::$app->view->render('panels/request/detail', ['panel' => $this]);
} }
/**
* @inheritdoc
*/
public function save() public function save()
{ {
$headers = Yii::$app->getRequest()->getHeaders(); $headers = Yii::$app->getRequest()->getHeaders();
...@@ -96,5 +108,4 @@ class RequestPanel extends Panel ...@@ -96,5 +108,4 @@ class RequestPanel extends Panel
'SESSION' => empty($_SESSION) ? [] : $_SESSION, 'SESSION' => empty($_SESSION) ? [] : $_SESSION,
]; ];
} }
} }
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