This commit is contained in:
2020-02-01 16:47:12 +07:00
commit 4c619ad6e6
16739 changed files with 3329179 additions and 0 deletions

View File

@@ -0,0 +1,140 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\panels;
use Yii;
use yii\base\InvalidConfigException;
use yii\helpers\Html;
use yii\debug\Panel;
use yii\web\AssetBundle;
use yii\web\AssetManager;
/**
* Debugger panel that collects and displays asset bundles data.
*
* @author Artur Fursa <arturfursa@gmail.com>
* @since 2.0
*/
class AssetPanel extends Panel
{
/**
* @inheritdoc
*/
public function getName()
{
return 'Asset Bundles';
}
/**
* @inheritdoc
*/
public function getSummary()
{
return Yii::$app->view->render('panels/assets/summary', ['panel' => $this]);
}
/**
* @inheritdoc
*/
public function getDetail()
{
return Yii::$app->view->render('panels/assets/detail', ['panel' => $this]);
}
/**
* @inheritdoc
*/
public function save()
{
$bundles = Yii::$app->view->assetManager->bundles;
if (empty($bundles)) { // bundles can be false
return [];
}
$data = [];
foreach ($bundles as $name => $bundle) {
if ($bundle instanceof AssetBundle) {
$bundleData = (array) $bundle;
if (isset($bundleData['publishOptions']['beforeCopy']) && $bundleData['publishOptions']['beforeCopy'] instanceof \Closure) {
$bundleData['publishOptions']['beforeCopy'] = '\Closure';
}
if (isset($bundleData['publishOptions']['afterCopy']) && $bundleData['publishOptions']['afterCopy'] instanceof \Closure) {
$bundleData['publishOptions']['afterCopy'] = '\Closure';
}
$data[$name] = $bundleData;
}
}
return $data;
}
/**
* @inheritdoc
*/
public function isEnabled()
{
try {
Yii::$app->view->assetManager;
} catch (InvalidConfigException $exception) {
return false;
}
return true;
}
/**
* Additional formatting for view.
*
* @param AssetBundle[] $bundles Array of bundles to formatting.
*
* @return AssetBundle[]
*/
protected function format(array $bundles)
{
foreach ($bundles as $bundle) {
$this->cssCount += count($bundle->css);
$this->jsCount += count($bundle->js);
array_walk($bundle->css, function(&$file, $key, $userdata) {
$file = Html::a($file, $userdata->baseUrl . '/' . $file, ['target' => '_blank']);
}, $bundle);
array_walk($bundle->js, function(&$file, $key, $userdata) {
$file = Html::a($file, $userdata->baseUrl . '/' . $file, ['target' => '_blank']);
}, $bundle);
array_walk($bundle->depends, function(&$depend) {
$depend = Html::a($depend, '#' . $depend);
});
$this->formatOptions($bundle->publishOptions);
$this->formatOptions($bundle->jsOptions);
$this->formatOptions($bundle->cssOptions);
}
return $bundles;
}
/**
* Format associative array of params to simple value.
*
* @param array $params
*
* @return array
*/
protected function formatOptions(array &$params)
{
if (!is_array($params)) {
return $params;
}
foreach ($params as $param => $value) {
$params[$param] = Html::tag('strong', '\'' . $param . '\' => ') . (string) $value;
}
return $params;
}
}

View File

@@ -0,0 +1,109 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\panels;
use Yii;
use yii\debug\Panel;
/**
* Debugger panel that collects and displays application configuration and environment.
*
* @property array $extensions This property is read-only.
* @property array $phpInfo This property is read-only.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class ConfigPanel extends Panel
{
/**
* @inheritdoc
*/
public function getName()
{
return 'Configuration';
}
/**
* @inheritdoc
*/
public function getSummary()
{
return Yii::$app->view->render('panels/config/summary', ['panel' => $this]);
}
/**
* @inheritdoc
*/
public function getDetail()
{
return Yii::$app->view->render('panels/config/detail', ['panel' => $this]);
}
/**
* Returns data about extensions
*
* @return array
*/
public function getExtensions()
{
$data = [];
foreach ($this->data['extensions'] as $extension) {
$data[$extension['name']] = $extension['version'];
}
ksort($data);
return $data;
}
/**
* Returns the BODY contents of the phpinfo() output
*
* @return array
*/
public function getPhpInfo()
{
ob_start();
phpinfo();
$pinfo = ob_get_contents();
ob_end_clean();
$phpinfo = preg_replace('%^.*<body>(.*)</body>.*$%ms', '$1', $pinfo);
$phpinfo = str_replace('<table', '<div class="table-responsive"><table class="table table-condensed table-bordered table-striped table-hover config-php-info-table" ', $phpinfo);
$phpinfo = str_replace('</table>', '</table></div>', $phpinfo);
return $phpinfo;
}
/**
* @inheritdoc
*/
public function save()
{
return [
'phpVersion' => PHP_VERSION,
'yiiVersion' => Yii::getVersion(),
'application' => [
'yii' => Yii::getVersion(),
'name' => Yii::$app->name,
'version' => Yii::$app->version,
'language' => Yii::$app->language,
'sourceLanguage' => Yii::$app->sourceLanguage,
'charset' => Yii::$app->charset,
'env' => YII_ENV,
'debug' => YII_DEBUG,
],
'php' => [
'version' => PHP_VERSION,
'xdebug' => extension_loaded('xdebug'),
'apc' => extension_loaded('apc'),
'memcache' => extension_loaded('memcache'),
'memcached' => extension_loaded('memcached'),
],
'extensions' => Yii::$app->extensions,
];
}
}

View File

@@ -0,0 +1,347 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\panels;
use Yii;
use yii\base\InvalidConfigException;
use yii\debug\Panel;
use yii\helpers\ArrayHelper;
use yii\log\Logger;
use yii\debug\models\search\Db;
/**
* Debugger panel that collects and displays database queries performed.
*
* @property array $profileLogs This property is read-only.
* @property string $summaryName Short name of the panel, which will be use in summary. This property is
* read-only.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class DbPanel extends Panel
{
/**
* @var integer the threshold for determining whether the request has involved
* critical number of DB queries. If the number of queries exceeds this number,
* the execution is considered taking critical number of DB queries.
*/
public $criticalQueryThreshold;
/**
* @var string the name of the database component to use for executing (explain) queries
*/
public $db = 'db';
/**
* @var array the default ordering of the database queries. In the format of
* [ property => sort direction ], for example: [ 'duration' => SORT_DESC ]
* @since 2.0.7
*/
public $defaultOrder = [
'seq' => SORT_ASC
];
/**
* @var array the default filter to apply to the database queries. In the format
* of [ property => value ], for example: [ 'type' => 'SELECT' ]
* @since 2.0.7
*/
public $defaultFilter = [];
/**
* @var array db queries info extracted to array as models, to use with data provider.
*/
private $_models;
/**
* @var array current database request timings
*/
private $_timings;
/**
* @inheritdoc
*/
public function init()
{
$this->actions['db-explain'] = [
'class' => 'yii\\debug\\actions\\db\\ExplainAction',
'panel' => $this,
];
}
/**
* @inheritdoc
*/
public function getName()
{
return 'Database';
}
/**
* @return string short name of the panel, which will be use in summary.
*/
public function getSummaryName()
{
return 'DB';
}
/**
* @inheritdoc
*/
public function getSummary()
{
$timings = $this->calculateTimings();
$queryCount = count($timings);
$queryTime = number_format($this->getTotalQueryTime($timings) * 1000) . ' ms';
return Yii::$app->view->render('panels/db/summary', [
'timings' => $this->calculateTimings(),
'panel' => $this,
'queryCount' => $queryCount,
'queryTime' => $queryTime,
]);
}
/**
* @inheritdoc
*/
public function getDetail()
{
$searchModel = new Db();
if (!$searchModel->load(Yii::$app->request->getQueryParams())) {
$searchModel->load($this->defaultFilter, '');
}
$models = $this->getModels();
$dataProvider = $searchModel->search($models);
$dataProvider->getSort()->defaultOrder = $this->defaultOrder;
$sumDuplicates = $this->sumDuplicateQueries($models);
return Yii::$app->view->render('panels/db/detail', [
'panel' => $this,
'dataProvider' => $dataProvider,
'searchModel' => $searchModel,
'hasExplain' => $this->hasExplain(),
'sumDuplicates' => $sumDuplicates,
]);
}
/**
* Calculates given request profile timings.
*
* @return array timings [token, category, timestamp, traces, nesting level, elapsed time]
*/
public function calculateTimings()
{
if ($this->_timings === null) {
$this->_timings = Yii::getLogger()->calculateTimings(isset($this->data['messages']) ? $this->data['messages'] : []);
}
return $this->_timings;
}
/**
* @inheritdoc
*/
public function save()
{
return ['messages' => $this->getProfileLogs()];
}
/**
* Returns all profile logs of the current request for this panel. It includes categories such as:
* 'yii\db\Command::query', 'yii\db\Command::execute'.
* @return array
*/
public function getProfileLogs()
{
$target = $this->module->logTarget;
return $target->filterMessages($target->messages, Logger::LEVEL_PROFILE, ['yii\db\Command::query', 'yii\db\Command::execute']);
}
/**
* Returns total query time.
*
* @param array $timings
* @return int total time
*/
protected function getTotalQueryTime($timings)
{
$queryTime = 0;
foreach ($timings as $timing) {
$queryTime += $timing['duration'];
}
return $queryTime;
}
/**
* Returns an array of models that represents logs of the current request.
* Can be used with data providers such as \yii\data\ArrayDataProvider.
* @return array models
*/
protected function getModels()
{
if ($this->_models === null) {
$this->_models = [];
$timings = $this->calculateTimings();
$duplicates = $this->countDuplicateQuery($timings);
foreach ($timings as $seq => $dbTiming) {
$this->_models[] = [
'type' => $this->getQueryType($dbTiming['info']),
'query' => $dbTiming['info'],
'duration' => ($dbTiming['duration'] * 1000), // in milliseconds
'trace' => $dbTiming['trace'],
'timestamp' => ($dbTiming['timestamp'] * 1000), // in milliseconds
'seq' => $seq,
'duplicate' => $duplicates[$dbTiming['info']],
];
}
}
return $this->_models;
}
/**
* Return associative array, where key is query string
* and value is number of occurrences the same query in array.
*
* @param $timings
* @return array
* @since 2.0.13
*/
public function countDuplicateQuery($timings)
{
$query = ArrayHelper::getColumn($timings, 'info');
return array_count_values($query);
}
/**
* Returns sum of all duplicated queries
*
* @param $modelData
* @return int
* @since 2.0.13
*/
public function sumDuplicateQueries($modelData)
{
$numDuplicates = 0;
$duplicates = ArrayHelper::getColumn($modelData, 'duplicate');
foreach ($duplicates as $duplicate) {
if ($duplicate > 1) {
$numDuplicates++;
}
}
return $numDuplicates;
}
/**
* Returns database query type.
*
* @param string $timing timing procedure string
* @return string query type such as select, insert, delete, etc.
*/
protected function getQueryType($timing)
{
$timing = ltrim($timing);
preg_match('/^([a-zA-z]*)/', $timing, $matches);
return count($matches) ? mb_strtoupper($matches[0], 'utf8') : '';
}
/**
* Check if given queries count is critical according settings.
*
* @param int $count queries count
* @return bool
*/
public function isQueryCountCritical($count)
{
return (($this->criticalQueryThreshold !== null) && ($count > $this->criticalQueryThreshold));
}
/**
* Returns array query types
*
* @return array
* @since 2.0.3
*/
public function getTypes()
{
return array_reduce(
$this->_models,
function ($result, $item) {
$result[$item['type']] = $item['type'];
return $result;
},
[]
);
}
/**
* @inheritdoc
*/
public function isEnabled()
{
try {
$this->getDb();
} catch (InvalidConfigException $exception) {
return false;
}
return true;
}
/**
* @return bool Whether the DB component has support for EXPLAIN queries
* @since 2.0.5
*/
protected function hasExplain()
{
$db = $this->getDb();
if (!($db instanceof \yii\db\Connection)) {
return false;
}
switch ($db->getDriverName()) {
case 'mysql':
case 'sqlite':
case 'pgsql':
case 'cubrid':
return true;
default:
return false;
}
}
/**
* Check if given query type can be explained.
*
* @param string $type query type
* @return bool
*
* @since 2.0.5
*/
public static function canBeExplained($type)
{
return $type !== 'SHOW';
}
/**
* Returns a reference to the DB component associated with the panel
*
* @return \yii\db\Connection
* @since 2.0.5
*/
public function getDb()
{
return Yii::$app->get($this->db);
}
}

View File

@@ -0,0 +1,110 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\panels;
use Yii;
use yii\debug\Panel;
use yii\helpers\VarDumper;
use yii\log\Logger;
use yii\debug\models\search\Log;
/**
* Debugger panel that collects and displays logs.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class LogPanel extends Panel
{
/**
* @var array log messages extracted to array as models, to use with data provider.
*/
private $_models;
/**
* @inheritdoc
*/
public function getName()
{
return 'Logs';
}
/**
* @inheritdoc
*/
public function getSummary()
{
return Yii::$app->view->render('panels/log/summary', ['data' => $this->data, 'panel' => $this]);
}
/**
* @inheritdoc
*/
public function getDetail()
{
$searchModel = new Log();
$dataProvider = $searchModel->search(Yii::$app->request->getQueryParams(), $this->getModels());
return Yii::$app->view->render('panels/log/detail', [
'dataProvider' => $dataProvider,
'panel' => $this,
'searchModel' => $searchModel,
]);
}
/**
* @inheritdoc
*/
public function save()
{
$target = $this->module->logTarget;
$except = [];
if (isset($this->module->panels['router'])) {
$except = $this->module->panels['router']->getCategories();
}
$messages = $target->filterMessages($target->messages, Logger::LEVEL_ERROR | Logger::LEVEL_INFO | Logger::LEVEL_WARNING | Logger::LEVEL_TRACE, [], $except);
foreach ($messages as &$message) {
if (!is_string($message[0])) {
// exceptions may not be serializable if in the call stack somewhere is a Closure
if ($message[0] instanceof \Throwable || $message[0] instanceof \Exception) {
$message[0] = (string) $message[0];
} else {
$message[0] = VarDumper::export($message[0]);
}
}
}
return ['messages' => $messages];
}
/**
* Returns an array of models that represents logs of the current request.
* Can be used with data providers, such as \yii\data\ArrayDataProvider.
*
* @param bool $refresh if need to build models from log messages and refresh them.
* @return array models
*/
protected function getModels($refresh = false)
{
if ($this->_models === null || $refresh) {
$this->_models = [];
foreach ($this->data['messages'] as $message) {
$this->_models[] = [
'message' => $message[0],
'level' => $message[1],
'category' => $message[2],
'time' => $message[3] * 1000, // time in milliseconds
'trace' => $message[4]
];
}
}
return $this->_models;
}
}

View File

@@ -0,0 +1,157 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\panels;
use Yii;
use yii\base\Event;
use yii\debug\models\search\Mail;
use yii\debug\Panel;
use yii\mail\BaseMailer;
use yii\helpers\FileHelper;
use yii\mail\MessageInterface;
/**
* Debugger panel that collects and displays the generated emails.
*
* @property array $messages Messages. This property is read-only.
*
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
class MailPanel extends Panel
{
/**
* @var string path where all emails will be saved. should be an alias.
*/
public $mailPath = '@runtime/debug/mail';
/**
* @var array current request sent messages
*/
private $_messages = [];
/**
* @inheritdoc
*/
public function init()
{
parent::init();
Event::on(BaseMailer::className(), BaseMailer::EVENT_AFTER_SEND, function ($event) {
/* @var $message MessageInterface */
$message = $event->message;
$messageData = [
'isSuccessful' => $event->isSuccessful,
'from' => $this->convertParams($message->getFrom()),
'to' => $this->convertParams($message->getTo()),
'reply' => $this->convertParams($message->getReplyTo()),
'cc' => $this->convertParams($message->getCc()),
'bcc' => $this->convertParams($message->getBcc()),
'subject' => $message->getSubject(),
'charset' => $message->getCharset(),
];
// add more information when message is a SwiftMailer message
if ($message instanceof \yii\swiftmailer\Message) {
/* @var $swiftMessage \Swift_Message */
$swiftMessage = $message->getSwiftMessage();
$body = $swiftMessage->getBody();
if (empty($body)) {
$parts = $swiftMessage->getChildren();
foreach ($parts as $part) {
if (!($part instanceof \Swift_Mime_Attachment)) {
/* @var $part \Swift_Mime_MimePart */
if ($part->getContentType() === 'text/plain') {
$messageData['charset'] = $part->getCharset();
$body = $part->getBody();
break;
}
}
}
}
$messageData['body'] = $body;
$messageData['time'] = $swiftMessage->getDate();
$messageData['headers'] = $swiftMessage->getHeaders();
}
// store message as file
$fileName = $event->sender->generateMessageFileName();
FileHelper::createDirectory(Yii::getAlias($this->mailPath));
file_put_contents(Yii::getAlias($this->mailPath) . '/' . $fileName, $message->toString());
$messageData['file'] = $fileName;
$this->_messages[] = $messageData;
});
}
/**
* @inheritdoc
*/
public function getName()
{
return 'Mail';
}
/**
* @inheritdoc
*/
public function getSummary()
{
return Yii::$app->view->render('panels/mail/summary', ['panel' => $this, 'mailCount' => count($this->data)]);
}
/**
* @inheritdoc
*/
public function getDetail()
{
$searchModel = new Mail();
$dataProvider = $searchModel->search(Yii::$app->request->get(), $this->data);
return Yii::$app->view->render('panels/mail/detail', [
'panel' => $this,
'dataProvider' => $dataProvider,
'searchModel' => $searchModel
]);
}
/**
* @inheritdoc
*/
public function save()
{
return $this->getMessages();
}
/**
* Returns info about messages of current request. Each element is array holding
* message info, such as: time, reply, bc, cc, from, to and other.
* @return array messages
*/
public function getMessages()
{
return $this->_messages;
}
/**
* @param mixed $attr
* @return string
*/
private function convertParams($attr)
{
if (is_array($attr)) {
$attr = implode(', ', array_keys($attr));
}
return $attr;
}
}

View File

@@ -0,0 +1,104 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\panels;
use Yii;
use yii\debug\Panel;
use yii\log\Logger;
use yii\debug\models\search\Profile;
/**
* Debugger panel that collects and displays performance profiling info.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class ProfilingPanel extends Panel
{
/**
* @var array current request profile timings
*/
private $_models;
/**
* @inheritdoc
*/
public function getName()
{
return 'Profiling';
}
/**
* @inheritdoc
*/
public function getSummary()
{
return Yii::$app->view->render('panels/profile/summary', [
'memory' => sprintf('%.3f MB', $this->data['memory'] / 1048576),
'time' => number_format($this->data['time'] * 1000) . ' ms',
'panel' => $this
]);
}
/**
* @inheritdoc
*/
public function getDetail()
{
$searchModel = new Profile();
$dataProvider = $searchModel->search(Yii::$app->request->getQueryParams(), $this->getModels());
return Yii::$app->view->render('panels/profile/detail', [
'panel' => $this,
'dataProvider' => $dataProvider,
'searchModel' => $searchModel,
'memory' => sprintf('%.3f MB', $this->data['memory'] / 1048576),
'time' => number_format($this->data['time'] * 1000) . ' ms',
]);
}
/**
* @inheritdoc
*/
public function save()
{
$target = $this->module->logTarget;
$messages = $target->filterMessages($target->messages, Logger::LEVEL_PROFILE);
return [
'memory' => memory_get_peak_usage(),
'time' => microtime(true) - YII_BEGIN_TIME,
'messages' => $messages,
];
}
/**
* Returns array of profiling models that can be used in a data provider.
* @return array models
*/
protected function getModels()
{
if ($this->_models === null) {
$this->_models = [];
$timings = Yii::getLogger()->calculateTimings(isset($this->data['messages']) ? $this->data['messages'] : []);
foreach ($timings as $seq => $profileTiming) {
$this->_models[] = [
'duration' => $profileTiming['duration'] * 1000, // in milliseconds
'category' => $profileTiming['category'],
'info' => $profileTiming['info'],
'level' => $profileTiming['level'],
'timestamp' => $profileTiming['timestamp'] * 1000, //in milliseconds
'seq' => $seq,
];
}
}
return $this->_models;
}
}

View File

@@ -0,0 +1,148 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\panels;
use Yii;
use yii\base\InlineAction;
use yii\debug\Panel;
/**
* Debugger panel that collects and displays request data.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class RequestPanel extends Panel
{
/**
* @var array list of the PHP predefined variables that are allowed to be displayed in the request panel.
* Note that a variable must be accessible via `$GLOBALS`. Otherwise it won't be displayed.
* @since 2.0.10
*/
public $displayVars = ['_SERVER', '_GET', '_POST', '_COOKIE', '_FILES', '_SESSION'];
/**
* @inheritdoc
*/
public function getName()
{
return 'Request';
}
/**
* @inheritdoc
*/
public function getSummary()
{
return Yii::$app->view->render('panels/request/summary', ['panel' => $this]);
}
/**
* @inheritdoc
*/
public function getDetail()
{
return Yii::$app->view->render('panels/request/detail', ['panel' => $this]);
}
/**
* @inheritdoc
*/
public function save()
{
$headers = Yii::$app->getRequest()->getHeaders();
$requestHeaders = [];
foreach ($headers as $name => $value) {
if (is_array($value) && count($value) == 1) {
$requestHeaders[$name] = current($value);
} else {
$requestHeaders[$name] = $value;
}
}
$responseHeaders = [];
foreach (headers_list() as $header) {
if (($pos = strpos($header, ':')) !== false) {
$name = substr($header, 0, $pos);
$value = trim(substr($header, $pos + 1));
if (isset($responseHeaders[$name])) {
if (!is_array($responseHeaders[$name])) {
$responseHeaders[$name] = [$responseHeaders[$name], $value];
} else {
$responseHeaders[$name][] = $value;
}
} else {
$responseHeaders[$name] = $value;
}
} else {
$responseHeaders[] = $header;
}
}
if (Yii::$app->requestedAction) {
if (Yii::$app->requestedAction instanceof InlineAction) {
$action = get_class(Yii::$app->requestedAction->controller) . '::' . Yii::$app->requestedAction->actionMethod . '()';
} else {
$action = get_class(Yii::$app->requestedAction) . '::run()';
}
} else {
$action = null;
}
$data = [
'flashes' => $this->getFlashes(),
'statusCode' => Yii::$app->getResponse()->getStatusCode(),
'requestHeaders' => $requestHeaders,
'responseHeaders' => $responseHeaders,
'route' => Yii::$app->requestedAction ? Yii::$app->requestedAction->getUniqueId() : Yii::$app->requestedRoute,
'action' => $action,
'actionParams' => Yii::$app->requestedParams,
'general' => [
'method' => Yii::$app->getRequest()->getMethod(),
'isAjax' => Yii::$app->getRequest()->getIsAjax(),
'isPjax' => Yii::$app->getRequest()->getIsPjax(),
'isFlash' => Yii::$app->getRequest()->getIsFlash(),
'isSecureConnection' => Yii::$app->getRequest()->getIsSecureConnection(),
],
'requestBody' => Yii::$app->getRequest()->getRawBody() == '' ? [] : [
'Content Type' => Yii::$app->getRequest()->getContentType(),
'Raw' => Yii::$app->getRequest()->getRawBody(),
'Decoded to Params' => Yii::$app->getRequest()->getBodyParams(),
],
];
foreach ($this->displayVars as $name) {
$data[trim($name, '_')] = empty($GLOBALS[$name]) ? [] : $GLOBALS[$name];
}
return $data;
}
/**
* Getting flash messages without deleting them or touching deletion counters
*
* @return array flash messages (key => message).
*/
protected function getFlashes()
{
/* @var $session \yii\web\Session */
$session = Yii::$app->has('session', true) ? Yii::$app->get('session') : null;
if ($session === null || !$session->getIsActive()) {
return [];
}
$counters = $session->get($session->flashParam, []);
$flashes = [];
foreach (array_keys($counters) as $key) {
if (array_key_exists($key, $_SESSION)) {
$flashes[$key] = $_SESSION[$key];
}
}
return $flashes;
}
}

View File

@@ -0,0 +1,84 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\panels;
use Yii;
use yii\debug\models\Router;
use yii\debug\Panel;
use yii\log\Logger;
/**
* RouterPanel provides a panel which displays information about routing process.
*
* @property array $categories Note that the type of this property differs in getter and setter. See
* [[getCategories()]] and [[setCategories()]] for details.
*
* @author Dmitriy Bashkarev <dmitriy@bashkarev.com>
* @since 2.0.8
*/
class RouterPanel extends Panel
{
/**
* @var array
*/
private $_categories = [
'yii\web\UrlManager::parseRequest',
'yii\web\UrlRule::parseRequest',
'yii\web\CompositeUrlRule::parseRequest',
'yii\rest\UrlRule::parseRequest'
];
/**
* @param string|array $values
*/
public function setCategories($values)
{
if (!is_array($values)) {
$values = [$values];
}
$this->_categories = array_merge($this->_categories, $values);
}
/**
* Listens categories of the messages.
* @return array
*/
public function getCategories()
{
return $this->_categories;
}
/**
* @inheritdoc
*/
public function getName()
{
return 'Router';
}
/**
* @inheritdoc
*/
public function getDetail()
{
return Yii::$app->view->render('panels/router/detail', ['model' => new Router($this->data)]);
}
/**
* @inheritdoc
*/
public function save()
{
$target = $this->module->logTarget;
return [
'messages' => $target::filterMessages($target->messages, Logger::LEVEL_TRACE, $this->_categories)
];
}
}

View File

@@ -0,0 +1,247 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\panels;
use Yii;
use yii\debug\Panel;
use yii\debug\models\timeline\Search;
use yii\debug\models\timeline\Svg;
use yii\base\InvalidConfigException;
/**
* Debugger panel that collects and displays timeline data.
*
* @property array $colors
* @property float $duration This property is read-only.
* @property float $start This property is read-only.
* @property array $svgOptions
*
* @author Dmitriy Bashkarev <dmitriy@bashkarev.com>
* @since 2.0.7
*/
class TimelinePanel extends Panel
{
/**
* @var array Color indicators item profile.
*
* - keys: percentages of time request
* - values: hex color
*/
private $_colors = [
20 => '#1e6823',
10 => '#44a340',
1 => '#8cc665'
];
/**
* @var array log messages extracted to array as models, to use with data provider.
*/
private $_models;
/**
* @var float Start request, timestamp (obtained by microtime(true))
*/
private $_start;
/**
* @var float End request, timestamp (obtained by microtime(true))
*/
private $_end;
/**
* @var float Request duration, milliseconds
*/
private $_duration;
/**
* @var Svg|null
*/
private $_svg;
/**
* @var array
*/
private $_svgOptions = [
'class' => 'yii\debug\models\timeline\Svg'
];
/**
* @var int Used memory in request
*/
private $_memory;
/**
* @inheritdoc
*/
public function init()
{
if (!isset($this->module->panels['profiling'])) {
throw new InvalidConfigException('Unable to determine the profiling panel');
}
parent::init();
}
/**
* @inheritdoc
*/
public function getName()
{
return 'Timeline';
}
/**
* @inheritdoc
*/
public function getDetail()
{
$searchModel = new Search();
$dataProvider = $searchModel->search(Yii::$app->request->getQueryParams(), $this);
return Yii::$app->view->render('panels/timeline/detail', [
'panel' => $this,
'dataProvider' => $dataProvider,
'searchModel' => $searchModel,
]);
}
/**
* @inheritdoc
*/
public function load($data)
{
if (!isset($data['start']) || empty($data['start'])) {
throw new \RuntimeException('Unable to determine request start time');
}
$this->_start = $data['start'] * 1000;
if (!isset($data['end']) || empty($data['end'])) {
throw new \RuntimeException('Unable to determine request end time');
}
$this->_end = $data['end'] * 1000;
if (isset($this->module->panels['profiling']->data['time'])) {
$this->_duration = $this->module->panels['profiling']->data['time'] * 1000;
} else {
$this->_duration = $this->_end - $this->_start;
}
if ($this->_duration <= 0) {
throw new \RuntimeException('Duration cannot be zero');
}
if (!isset($data['memory']) || empty($data['memory'])) {
throw new \RuntimeException('Unable to determine used memory in request');
}
$this->_memory = $data['memory'];
}
/**
* @inheritdoc
*/
public function save()
{
return [
'start' => YII_BEGIN_TIME,
'end' => microtime(true),
'memory' => memory_get_peak_usage(),
];
}
/**
* Sets color indicators.
* key: percentages of time request, value: hex color
* @param array $colors
*/
public function setColors($colors)
{
krsort($colors);
$this->_colors = $colors;
}
/**
* Color indicators item profile,
* key: percentages of time request, value: hex color
* @return array
*/
public function getColors()
{
return $this->_colors;
}
/**
* @param array $options
*/
public function setSvgOptions($options)
{
if ($this->_svg !== null) {
$this->_svg = null;
}
$this->_svgOptions = array_merge($this->_svgOptions, $options);
}
/**
* @return array
*/
public function getSvgOptions()
{
return $this->_svgOptions;
}
/**
* Start request, timestamp (obtained by microtime(true))
* @return float
*/
public function getStart()
{
return $this->_start;
}
/**
* Request duration, milliseconds
* @return float
*/
public function getDuration()
{
return $this->_duration;
}
/**
* Memory peak in request, bytes. (obtained by memory_get_peak_usage())
* @return int
* @since 2.0.8
*/
public function getMemory()
{
return $this->_memory;
}
/**
* @return Svg
* @since 2.0.8
*/
public function getSvg()
{
if ($this->_svg === null) {
$this->_svg = Yii::createObject($this->_svgOptions,[$this]);
}
return $this->_svg;
}
/**
* Returns an array of models that represents logs of the current request.
* Can be used with data providers, such as \yii\data\ArrayDataProvider.
*
* @param bool $refresh if need to build models from log messages and refresh them.
* @return array models
*/
protected function getModels($refresh = false)
{
if ($this->_models === null || $refresh) {
$this->_models = [];
if (isset($this->module->panels['profiling']->data['messages'])) {
$this->_models = Yii::getLogger()->calculateTimings($this->module->panels['profiling']->data['messages']);
}
}
return $this->_models;
}
}

View File

@@ -0,0 +1,332 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\panels;
use Yii;
use yii\base\Controller;
use yii\base\Model;
use yii\base\InvalidConfigException;
use yii\data\ArrayDataProvider;
use yii\data\DataProviderInterface;
use yii\db\ActiveRecord;
use yii\debug\controllers\UserController;
use yii\debug\models\search\UserSearchInterface;
use yii\debug\models\UserSwitch;
use yii\debug\Panel;
use yii\filters\AccessControl;
use yii\filters\AccessRule;
use yii\helpers\ArrayHelper;
use yii\helpers\VarDumper;
use yii\web\IdentityInterface;
use yii\web\User;
/**
* Debugger panel that collects and displays user data.
*
* @property DataProviderInterface $userDataProvider This property is read-only.
* @property Model|UserSearchInterface $usersFilterModel This property is read-only.
*
* @author Daniel Gomez Pan <pana_1990@hotmail.com>
* @since 2.0.8
*/
class UserPanel extends Panel
{
/**
* @var array the rule which defines who allowed to switch user identity.
* Access Control Filter single rule. Ignore: actions, controllers, verbs.
* Settable: allow, roles, ips, matchCallback, denyCallback.
* By default deny for everyone. Recommendation: can allow for administrator
* or developer (if implement) role: ['allow' => true, 'roles' => ['admin']]
* @see http://www.yiiframework.com/doc-2.0/guide-security-authorization.html
* @since 2.0.10
*/
public $ruleUserSwitch = [
'allow' => false,
];
/**
* @var UserSwitch object of switching users
* @since 2.0.10
*/
public $userSwitch;
/**
* @var Model|UserSearchInterface Implements of User model with search method.
* @since 2.0.10
*/
public $filterModel;
/**
* @var array allowed columns for GridView.
* @see http://www.yiiframework.com/doc-2.0/yii-grid-gridview.html#$columns-detail
* @since 2.0.10
*/
public $filterColumns = [];
/**
* @var string|User ID of the user component or a user object
* @since 2.0.13
*/
public $userComponent = 'user';
/**
* @inheritdoc
*/
public function init()
{
if (
!$this->isEnabled()
|| $this->getUser()->isGuest
) {
return;
}
$this->userSwitch = new UserSwitch(['userComponent' => $this->userComponent]);
$this->addAccesRules();
if (!is_object($this->filterModel)
&& class_exists($this->filterModel)
&& in_array('yii\debug\models\search\UserSearchInterface', class_implements($this->filterModel), true)
) {
$this->filterModel = new $this->filterModel;
} elseif ($this->getUser() && $this->getUser()->identityClass) {
if (is_subclass_of($this->getUser()->identityClass, ActiveRecord::className())) {
$this->filterModel = new \yii\debug\models\search\User();
}
}
}
/**
* @return User|null
* @since 2.0.13
*/
public function getUser()
{
/* @var $user User */
return is_string($this->userComponent) ? Yii::$app->get($this->userComponent, false) : $this->userComponent;
}
/**
* Add ACF rule. AccessControl attach to debug module.
* Access rule for main user.
*/
private function addAccesRules()
{
$this->ruleUserSwitch['controllers'] = [$this->module->id . '/user'];
$this->module->attachBehavior(
'access_debug',
[
'class' => AccessControl::className(),
'only' => [$this->module->id . '/user', $this->module->id . '/default'],
'user' => $this->userSwitch->getMainUser(),
'rules' => [
$this->ruleUserSwitch,
],
]
);
}
/**
* Get model for GridView -> FilterModel
* @return Model|UserSearchInterface
*/
public function getUsersFilterModel()
{
return $this->filterModel;
}
/**
* Get model for GridView -> DataProvider
* @return DataProviderInterface
*/
public function getUserDataProvider()
{
return $this->getUsersFilterModel()->search(Yii::$app->request->queryParams);
}
/**
* Check is available search of users
* @return bool
*/
public function canSearchUsers()
{
return (isset($this->filterModel) &&
$this->filterModel instanceof Model &&
$this->filterModel->hasMethod('search')
);
}
/**
* Check can main user switch identity.
* @return bool
*/
public function canSwitchUser()
{
if ($this->getUser()->isGuest) {
return false;
}
$allowSwitchUser = false;
$rule = new AccessRule($this->ruleUserSwitch);
/** @var Controller $userController */
$userController = null;
$controller = $this->module->createController('user');
if (isset($controller[0]) && $controller[0] instanceof UserController) {
$userController = $controller[0];
}
//check by rule
if ($userController) {
$action = $userController->createAction('set-identity');
$user = $this->userSwitch->getMainUser();
$request = Yii::$app->request;
$allowSwitchUser = $rule->allows($action, $user, $request) ?: false;
}
return $allowSwitchUser;
}
/**
* @inheritdoc
*/
public function getName()
{
return 'User';
}
/**
* @inheritdoc
*/
public function getSummary()
{
return Yii::$app->view->render('panels/user/summary', ['panel' => $this]);
}
/**
* @inheritdoc
*/
public function getDetail()
{
return Yii::$app->view->render('panels/user/detail', ['panel' => $this]);
}
/**
* @inheritdoc
*/
public function save()
{
$identity = Yii::$app->user->identity;
if (!isset($identity)) {
return;
}
$rolesProvider = null;
$permissionsProvider = null;
try {
$authManager = Yii::$app->getAuthManager();
if ($authManager instanceof \yii\rbac\ManagerInterface) {
$roles = ArrayHelper::toArray($authManager->getRolesByUser($this->getUser()->id));
foreach ($roles as &$role) {
$role['data'] = $this->dataToString($role['data']);
}
unset($role);
$rolesProvider = new ArrayDataProvider([
'allModels' => $roles,
]);
$permissions = ArrayHelper::toArray($authManager->getPermissionsByUser($this->getUser()->id));
foreach ($permissions as &$permission) {
$permission['data'] = $this->dataToString($permission['data']);
}
unset($permission);
$permissionsProvider = new ArrayDataProvider([
'allModels' => $permissions,
]);
}
} catch (\Exception $e) {
// ignore auth manager misconfiguration
}
$identityData = $this->identityData($identity);
foreach ($identityData as $key => $value) {
$identityData[$key] = VarDumper::dumpAsString($value);
}
// If the identity is a model, let it specify the attribute labels
if ($identity instanceof Model) {
$attributes = [];
foreach (array_keys($identityData) as $attribute) {
$attributes[] = [
'attribute' => $attribute,
'label' => $identity->getAttributeLabel($attribute),
];
}
} else {
// Let the DetailView widget figure the labels out
$attributes = null;
}
return [
'id' => $identity->getId(),
'identity' => $identityData,
'attributes' => $attributes,
'rolesProvider' => $rolesProvider,
'permissionsProvider' => $permissionsProvider,
];
}
/**
* @inheritdoc
*/
public function isEnabled()
{
try {
$this->getUser();
} catch (InvalidConfigException $exception) {
return false;
}
return true;
}
/**
* Converts mixed data to string
*
* @param mixed $data
* @return string
*/
protected function dataToString($data)
{
if (is_string($data)) {
return $data;
}
return VarDumper::export($data);
}
/**
* Returns the array that should be set on [[\yii\widgets\DetailView::model]]
*
* @param IdentityInterface $identity
* @return array
*/
protected function identityData($identity)
{
if ($identity instanceof Model) {
return $identity->getAttributes();
}
return get_object_vars($identity);
}
}