Commit e1decbc9 by Smotrov Dmitriy

Merge pull request #2 from yiisoft/master

Merge from yiisoft/master
parents 4efc097e 30ec0afd
...@@ -35,13 +35,6 @@ return array( ...@@ -35,13 +35,6 @@ return array(
), ),
'depends' => array('yii'), 'depends' => array('yii'),
), ),
'yii/debug' => array(
'sourcePath' => __DIR__ . '/assets',
'js' => array(
'yii.debug.js',
),
'depends' => array('yii'),
),
'yii/punycode' => array( 'yii/punycode' => array(
'sourcePath' => __DIR__ . '/assets', 'sourcePath' => __DIR__ . '/assets',
'js' => array( 'js' => array(
...@@ -55,4 +48,17 @@ return array( ...@@ -55,4 +48,17 @@ return array(
), ),
'depends' => array('yii/jquery'), 'depends' => array('yii/jquery'),
), ),
'yii/debug' => array(
'sourcePath' => __DIR__ . '/debug/assets',
'css' => array(
'main.css',
),
'js' => array(
'yii.debug.js',
),
'depends' => array(
'yii',
'yii/bootstrap/responsive',
),
),
); );
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
namespace yii\base; namespace yii\base;
use Yii;
/** /**
* Action is the base class for all controller action classes. * Action is the base class for all controller action classes.
* *
...@@ -75,6 +77,10 @@ class Action extends Component ...@@ -75,6 +77,10 @@ class Action extends Component
throw new InvalidConfigException(get_class($this) . ' must define a "run()" method.'); throw new InvalidConfigException(get_class($this) . ' must define a "run()" method.');
} }
$args = $this->controller->bindActionParams($this, $params); $args = $this->controller->bindActionParams($this, $params);
Yii::info('Running "' . get_class($this) . '::run()" with parameters: ' . var_export($args, true), __METHOD__);
if (Yii::$app->requestedParams === null) {
Yii::$app->requestedParams = $args;
}
return call_user_func_array(array($this, 'run'), $args); return call_user_func_array(array($this, 'run'), $args);
} }
} }
...@@ -78,6 +78,18 @@ abstract class Application extends Module ...@@ -78,6 +78,18 @@ abstract class Application extends Module
* Defaults to 256KB. * Defaults to 256KB.
*/ */
public $memoryReserveSize = 262144; public $memoryReserveSize = 262144;
/**
* @var string the requested route
*/
public $requestedRoute;
/**
* @var Action the requested Action. If null, it means the request cannot be resolved into an action.
*/
public $requestedAction;
/**
* @var array the parameters supplied to the requested action.
*/
public $requestedParams;
/** /**
* @var string Used to reserve memory for fatal error handler. * @var string Used to reserve memory for fatal error handler.
......
...@@ -107,6 +107,10 @@ class Controller extends Component ...@@ -107,6 +107,10 @@ class Controller extends Component
{ {
$action = $this->createAction($id); $action = $this->createAction($id);
if ($action !== null) { if ($action !== null) {
Yii::trace("Route to run: " . $action->getUniqueId(), __METHOD__);
if (Yii::$app->requestedAction === null) {
Yii::$app->requestedAction = $action;
}
$oldAction = $this->action; $oldAction = $this->action;
$this->action = $action; $this->action = $action;
$result = null; $result = null;
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
namespace yii\base; namespace yii\base;
use Yii;
/** /**
* InlineAction represents an action that is defined as a controller method. * InlineAction represents an action that is defined as a controller method.
* *
...@@ -44,6 +46,10 @@ class InlineAction extends Action ...@@ -44,6 +46,10 @@ class InlineAction extends Action
public function runWithParams($params) public function runWithParams($params)
{ {
$args = $this->controller->bindActionParams($this, $params); $args = $this->controller->bindActionParams($this, $params);
Yii::info("Running '" . get_class($this->controller) . '::' . $this->actionMethod . "()' with parameters: " . var_export($args, true), __METHOD__);
if (Yii::$app->requestedParams === null) {
Yii::$app->requestedParams = $args;
}
return call_user_func_array(array($this->controller, $this->actionMethod), $args); return call_user_func_array(array($this->controller, $this->actionMethod), $args);
} }
} }
<?php <?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
return array( return array(
'yii/bootstrap' => array( 'yii/bootstrap' => array(
......
...@@ -92,6 +92,7 @@ class Application extends \yii\base\Application ...@@ -92,6 +92,7 @@ class Application extends \yii\base\Application
public function handleRequest($request) public function handleRequest($request)
{ {
list ($route, $params) = $request->resolve(); list ($route, $params) = $request->resolve();
$this->requestedRoute = $route;
$result = $this->runAction($route, $params); $result = $this->runAction($route, $params);
if ($result instanceof Response) { if ($result instanceof Response) {
return $result; return $result;
......
...@@ -21,13 +21,16 @@ class LogTarget extends Target ...@@ -21,13 +21,16 @@ class LogTarget extends Target
*/ */
public $module; public $module;
public $tag; public $tag;
public $historySize = 20;
/**
* @param \yii\debug\Module $module
* @param array $config
*/
public function __construct($module, $config = array()) public function __construct($module, $config = array())
{ {
parent::__construct($config); parent::__construct($config);
$this->module = $module; $this->module = $module;
$this->tag = date('Ymd-His', microtime(true)); $this->tag = uniqid();
} }
/** /**
...@@ -36,16 +39,32 @@ class LogTarget extends Target ...@@ -36,16 +39,32 @@ class LogTarget extends Target
*/ */
public function export() public function export()
{ {
$path = Yii::$app->getRuntimePath() . '/debug'; $path = $this->module->dataPath;
if (!is_dir($path)) { if (!is_dir($path)) {
mkdir($path); mkdir($path);
} }
$file = "$path/{$this->tag}.log"; $indexFile = "$path/index.json";
if (!is_file($indexFile)) {
$manifest = array();
} else {
$manifest = json_decode(file_get_contents($indexFile), true);
}
$request = Yii::$app->getRequest();
$manifest[$this->tag] = array(
'url' => $request->getAbsoluteUrl(),
'ajax' => $request->getIsAjax(),
'method' => $request->getMethod(),
'ip' => $request->getUserIP(),
'time' => time(),
);
$dataFile = "$path/{$this->tag}.json";
$data = array(); $data = array();
foreach ($this->module->panels as $panel) { foreach ($this->module->panels as $id => $panel) {
$data[$panel->id] = $panel->save(); $data[$id] = $panel->save();
} }
file_put_contents($file, json_encode($data)); file_put_contents($dataFile, json_encode($data));
file_put_contents($indexFile, json_encode($manifest));
} }
/** /**
...@@ -58,36 +77,9 @@ class LogTarget extends Target ...@@ -58,36 +77,9 @@ class LogTarget extends Target
*/ */
public function collect($messages, $final) public function collect($messages, $final)
{ {
$this->messages = array_merge($this->messages, $this->filterMessages($messages)); $this->messages = array_merge($this->messages, $messages);
if ($final) { if ($final) {
$this->export($this->messages); $this->export($this->messages);
$this->gc();
}
}
protected function gc()
{
if (mt_rand(0, 10000) > 100) {
return;
}
$iterator = new \DirectoryIterator(Yii::$app->getRuntimePath() . '/debug');
$files = array();
foreach ($iterator as $file) {
/** @var \DirectoryIterator $file */
if (preg_match('/^[\d\-]+\.log$/', $file->getFileName()) && $file->isFile()) {
$files[] = $file->getPathname();
}
}
sort($files);
if (count($files) > $this->historySize) {
$n = count($files) - $this->historySize;
foreach ($files as $i => $file) {
if ($i < $n) {
unlink($file);
} else {
break;
}
}
} }
} }
} }
...@@ -28,27 +28,38 @@ class Module extends \yii\base\Module ...@@ -28,27 +28,38 @@ class Module extends \yii\base\Module
public $controllerNamespace = 'yii\debug\controllers'; public $controllerNamespace = 'yii\debug\controllers';
/** /**
* @var LogTarget
*/
public $logTarget;
/**
* @var array|Panel[] * @var array|Panel[]
*/ */
public $panels = array(); public $panels = array();
/**
* @var string the directory storing the debugger data files. This can be specified using a path alias.
*/
public $dataPath = '@runtime/debug';
public $historySize = 50;
public function init() public function init()
{ {
parent::init(); parent::init();
$this->dataPath = Yii::getAlias($this->dataPath);
$this->logTarget = Yii::$app->getLog()->targets['debug'] = new LogTarget($this);
Yii::$app->getView()->on(View::EVENT_END_BODY, array($this, 'renderToolbar'));
foreach (array_merge($this->corePanels(), $this->panels) as $id => $config) { foreach (array_merge($this->corePanels(), $this->panels) as $id => $config) {
$config['id'] = $id; $config['module'] = $this;
$this->panels[$id] = Yii::createObject($config); $this->panels[$id] = Yii::createObject($config);
} }
Yii::$app->getLog()->targets['debug'] = new LogTarget($this);
Yii::$app->getView()->on(View::EVENT_END_BODY, array($this, 'renderToolbar'));
} }
public function beforeAction($action) public function beforeAction($action)
{ {
Yii::$app->getView()->off(View::EVENT_END_BODY, array($this, 'renderToolbar')); Yii::$app->getView()->off(View::EVENT_END_BODY, array($this, 'renderToolbar'));
unset(Yii::$app->getLog()->targets['debug']); unset(Yii::$app->getLog()->targets['debug']);
$this->logTarget = null;
$ip = Yii::$app->getRequest()->getUserIP(); $ip = Yii::$app->getRequest()->getUserIP();
foreach ($this->allowedIPs as $filter) { foreach ($this->allowedIPs as $filter) {
...@@ -63,7 +74,7 @@ class Module extends \yii\base\Module ...@@ -63,7 +74,7 @@ class Module extends \yii\base\Module
{ {
/** @var View $view */ /** @var View $view */
$id = 'yii-debug-toolbar'; $id = 'yii-debug-toolbar';
$tag = Yii::$app->getLog()->targets['debug']->tag; $tag = $this->logTarget->tag;
$url = Yii::$app->getUrlManager()->createUrl('debug/default/toolbar', array( $url = Yii::$app->getUrlManager()->createUrl('debug/default/toolbar', array(
'tag' => $tag, 'tag' => $tag,
)); ));
...@@ -88,6 +99,12 @@ class Module extends \yii\base\Module ...@@ -88,6 +99,12 @@ class Module extends \yii\base\Module
'log' => array( 'log' => array(
'class' => 'yii\debug\panels\LogPanel', 'class' => 'yii\debug\panels\LogPanel',
), ),
'profiling' => array(
'class' => 'yii\debug\panels\ProfilingPanel',
),
'db' => array(
'class' => 'yii\debug\panels\DbPanel',
),
); );
} }
} }
...@@ -15,7 +15,10 @@ use yii\base\Component; ...@@ -15,7 +15,10 @@ use yii\base\Component;
*/ */
class Panel extends Component class Panel extends Component
{ {
public $id; /**
* @var Module
*/
public $module;
public $data; public $data;
public function getName() public function getName()
......
#yii-debug-toolbar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
margin: 0;
padding: 0;
z-index: 1000000;
font: 11px Verdana, Arial, sans-serif;
text-align: left;
height: 38px;
border-top: 1px solid #ccc;
background: rgb(237,237,237);
background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2VkZWRlZCIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjUzJSIgc3RvcC1jb2xvcj0iI2Y2ZjZmNiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+);
background: -moz-linear-gradient(top, rgba(237,237,237,1) 0%, rgba(246,246,246,1) 53%, rgba(255,255,255,1) 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(237,237,237,1)), color-stop(53%,rgba(246,246,246,1)), color-stop(100%,rgba(255,255,255,1)));
background: -webkit-linear-gradient(top, rgba(237,237,237,1) 0%,rgba(246,246,246,1) 53%,rgba(255,255,255,1) 100%);
background: -o-linear-gradient(top, rgba(237,237,237,1) 0%,rgba(246,246,246,1) 53%,rgba(255,255,255,1) 100%);
background: -ms-linear-gradient(top, rgba(237,237,237,1) 0%,rgba(246,246,246,1) 53%,rgba(255,255,255,1) 100%);
background: linear-gradient(to bottom, rgba(237,237,237,1) 0%,rgba(246,246,246,1) 53%,rgba(255,255,255,1) 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ededed', endColorstr='#ffffff',GradientType=0 );
}
.yii-debug-toolbar-block {
float: left;
margin: 0;
border-right: 1px solid #e4e4e4;
padding: 4px 8px;
line-height: 32px;
}
.yii-debug-toolbar-block a {
text-decoration: none;
color: black !important;
}
.yii-debug-toolbar-block span {
}
.yii-debug-toolbar-block img {
vertical-align: middle;
}
span.indent {
color: #ccc;
}
...@@ -21,16 +21,22 @@ class DefaultController extends Controller ...@@ -21,16 +21,22 @@ class DefaultController extends Controller
public $module; public $module;
public $layout = 'main'; public $layout = 'main';
public function actionIndex($tag, $panel = null) public function actionIndex($tag = null, $panel = null)
{ {
$this->loadData($tag); if ($tag === null) {
$tags = array_keys($this->getManifest());
$tag = end($tags);
}
$meta = $this->loadData($tag);
if (isset($this->module->panels[$panel])) { if (isset($this->module->panels[$panel])) {
$activePanel = $this->module->panels[$panel]; $activePanel = $this->module->panels[$panel];
} else { } else {
$activePanel = reset($this->module->panels); $activePanel = $this->module->panels['request'];
} }
return $this->render('index', array( return $this->render('index', array(
'tag' => $tag, 'tag' => $tag,
'meta' => $meta,
'manifest' => $this->getManifest(),
'panels' => $this->module->panels, 'panels' => $this->module->panels,
'activePanel' => $activePanel, 'activePanel' => $activePanel,
)); ));
...@@ -45,19 +51,53 @@ class DefaultController extends Controller ...@@ -45,19 +51,53 @@ class DefaultController extends Controller
)); ));
} }
public function actionPhpinfo()
{
phpinfo();
}
private $_manifest;
protected function getManifest()
{
if ($this->_manifest === null) {
$indexFile = $this->module->dataPath . '/index.json';
if (is_file($indexFile)) {
$this->_manifest = json_decode(file_get_contents($indexFile), true);
} else {
$this->_manifest = array();
}
if (count($this->_manifest) > $this->module->historySize) {
$n = count($this->_manifest) - $this->module->historySize;
foreach (array_keys($this->_manifest) as $tag) {
$file = $this->module->dataPath . "/$tag.json";
@unlink($file);
unset($this->_manifest[$tag]);
if (--$n <= 0) {
break;
}
}
file_put_contents($indexFile, json_encode($this->_manifest));
}
}
return $this->_manifest;
}
protected function loadData($tag) protected function loadData($tag)
{ {
$file = Yii::$app->getRuntimePath() . "/debug/$tag.log"; $manifest = $this->getManifest();
if (preg_match('/^[\w\-]+$/', $tag) && is_file($file)) { if (isset($manifest[$tag])) {
$data = json_decode(file_get_contents($file), true); $dataFile = $this->module->dataPath . "/$tag.json";
$data = json_decode(file_get_contents($dataFile), true);
foreach ($this->module->panels as $id => $panel) { foreach ($this->module->panels as $id => $panel) {
if (isset($data[$panel->id])) { if (isset($data[$id])) {
$panel->load($data[$panel->id]); $panel->load($data[$id]);
} else { } else {
// remove the panel since it has not received any data // remove the panel since it has not received any data
unset($this->module->panels[$id]); unset($this->module->panels[$id]);
} }
} }
return $manifest[$tag];
} else { } else {
throw new HttpException(404, "Unable to find debug data tagged with '$tag'."); throw new HttpException(404, "Unable to find debug data tagged with '$tag'.");
} }
......
<?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\debug\Panel;
/**
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class DbPanel extends Panel
{
public function getName()
{
return 'Database';
}
}
...@@ -11,6 +11,7 @@ use Yii; ...@@ -11,6 +11,7 @@ use Yii;
use yii\debug\Panel; use yii\debug\Panel;
use yii\helpers\Html; use yii\helpers\Html;
use yii\log\Logger; use yii\log\Logger;
use yii\log\Target;
/** /**
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
...@@ -25,12 +26,29 @@ class LogPanel extends Panel ...@@ -25,12 +26,29 @@ class LogPanel extends Panel
public function getSummary() public function getSummary()
{ {
$count = count($this->data['messages']); $output = array();
return <<<EOD $errorCount = count(Target::filterMessages($this->data['messages'], Logger::LEVEL_ERROR));
if ($errorCount === 1) {
$output[] = '1 error';
} elseif ($errorCount > 1) {
$output[] = "$errorCount errors";
}
$warningCount = count(Target::filterMessages($this->data['messages'], Logger::LEVEL_WARNING));
if ($warningCount === 1) {
$output[] = '1 warning';
} elseif ($warningCount > 1) {
$output[] = "$warningCount warnings";
}
if (!empty($output)) {
$log = implode(', ', $output);
return <<<EOD
<div class="yii-debug-toolbar-block"> <div class="yii-debug-toolbar-block">
Log messages: $count $log
</div> </div>
EOD; EOD;
} else {
return '';
}
} }
public function getDetail() public function getDetail()
...@@ -40,10 +58,21 @@ EOD; ...@@ -40,10 +58,21 @@ EOD;
$time = date('H:i:s.', $log[3]) . sprintf('%03d', (int)(($log[3] - (int)$log[3]) * 1000)); $time = date('H:i:s.', $log[3]) . sprintf('%03d', (int)(($log[3] - (int)$log[3]) * 1000));
$level = Logger::getLevelName($log[1]); $level = Logger::getLevelName($log[1]);
$message = Html::encode(wordwrap($log[0])); $message = Html::encode(wordwrap($log[0]));
$rows[] = "<tr><td style=\"width: 100px;\">$time</td><td style=\"width: 100px;\">$level</td><td style=\"width: 250px;\">{$log[2]}</td><td>$message</td></tr>"; if ($log[1] == Logger::LEVEL_ERROR) {
$class = ' class="error"';
} elseif ($log[1] == Logger::LEVEL_WARNING) {
$class = ' class="warning"';
} elseif ($log[1] == Logger::LEVEL_INFO) {
$class = ' class="info"';
} else {
$class = '';
}
$rows[] = "<tr$class><td style=\"width: 100px;\">$time</td><td style=\"width: 100px;\">$level</td><td style=\"width: 250px;\">{$log[2]}</td><td>$message</td></tr>";
} }
$rows = implode("\n", $rows); $rows = implode("\n", $rows);
return <<<EOD return <<<EOD
<h1>Log Messages</h1>
<table class="table table-condensed table-bordered table-striped table-hover" style="table-layout: fixed;"> <table class="table table-condensed table-bordered table-striped table-hover" style="table-layout: fixed;">
<thead> <thead>
<tr> <tr>
...@@ -62,8 +91,10 @@ EOD; ...@@ -62,8 +91,10 @@ EOD;
public function save() public function save()
{ {
$target = $this->module->logTarget;
$messages = $target->filterMessages($target->messages, Logger::LEVEL_ERROR | Logger::LEVEL_INFO | Logger::LEVEL_WARNING | Logger::LEVEL_TRACE);
return array( return array(
'messages' => Yii::$app->getLog()->targets['debug']->messages, 'messages' => $messages,
); );
} }
} }
<?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\Html;
use yii\log\Logger;
/**
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class ProfilingPanel extends Panel
{
public function getName()
{
return 'Profiling';
}
public function getDetail()
{
$messages = $this->data['messages'];
$timings = array();
$stack = array();
foreach ($messages as $i => $log) {
list($token, $level, $category, $timestamp) = $log;
$log[4] = $i;
if ($level == Logger::LEVEL_PROFILE_BEGIN) {
$stack[] = $log;
} elseif ($level == Logger::LEVEL_PROFILE_END) {
if (($last = array_pop($stack)) !== null && $last[0] === $token) {
$timings[$last[4]] = array(count($stack), $token, $category, $timestamp - $last[3]);
}
}
}
$now = microtime(true);
while (($last = array_pop($stack)) !== null) {
$delta = $now - $last[3];
$timings[$last[4]] = array(count($stack), $last[0], $last[2], $delta);
}
ksort($timings);
$rows = array();
foreach ($timings as $timing) {
$time = sprintf('%.1f ms', $timing[3] * 1000);
$procedure = str_repeat('<span class="indent">→</span>', $timing[0]) . Html::encode($timing[1]);
$category = Html::encode($timing[2]);
$rows[] = "<tr><td style=\"width: 80px;\">$time</td><td style=\"width: 220px;\">$category</td><td>$procedure</td>";
}
$rows = implode("\n", $rows);
return <<<EOD
<h1>Performance Profiling</h1>
<table class="table table-condensed table-bordered table-striped table-hover" style="table-layout: fixed;">
<thead>
<tr>
<th style="width: 80px;">Time</th>
<th style="width: 220px;">Category</th>
<th>Procedure</th>
</tr>
</thead>
<tbody>
$rows
</tbody>
</table>
EOD;
}
public function save()
{
$target = $this->module->logTarget;
$messages = $target->filterMessages($target->messages, Logger::LEVEL_PROFILE);
return array(
'messages' => $messages,
);
}
}
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
namespace yii\debug\panels; namespace yii\debug\panels;
use Yii;
use yii\base\InlineAction;
use yii\debug\Panel; use yii\debug\Panel;
use yii\helpers\Html; use yii\helpers\Html;
...@@ -23,8 +25,8 @@ class RequestPanel extends Panel ...@@ -23,8 +25,8 @@ class RequestPanel extends Panel
public function getSummary() public function getSummary()
{ {
$memory = sprintf('%.2fMB', $this->data['memory'] / 1048576); $memory = sprintf('%.1f MB', $this->data['memory'] / 1048576);
$time = sprintf('%.3fs', $this->data['time']); $time = number_format($this->data['time'] * 1000) . ' ms';
return <<<EOD return <<<EOD
<div class="yii-debug-toolbar-block"> <div class="yii-debug-toolbar-block">
...@@ -39,37 +41,92 @@ EOD; ...@@ -39,37 +41,92 @@ EOD;
public function getDetail() public function getDetail()
{ {
return "<h3>\$_GET</h3>\n" . $this->renderTable($this->data['GET']) . "\n" $data = array(
. "<h3>\$_POST</h3>\n" . $this->renderTable($this->data['POST']) . "\n" 'Route' => $this->data['route'],
. "<h3>\$_COOKIE</h3>\n" . $this->renderTable($this->data['COOKIE']) . "\n" 'Action' => $this->data['action'],
. "<h3>\$_FILES</h3>\n" . $this->renderTable($this->data['FILES']) . "\n" 'Parameters' => $this->data['actionParams'],
. "<h3>\$_SESSION</h3>\n" . $this->renderTable($this->data['SESSION']) . "\n" );
. "<h3>\$_SERVER</h3>\n" . $this->renderTable($this->data['SERVER']); return "<h1>Request Information</h1>\n"
. $this->renderData('Routing', $data) . "\n"
. $this->renderData('Flashes', $this->data['flashes']) . "\n"
. $this->renderData('$_GET', $this->data['GET']) . "\n"
. $this->renderData('$_POST', $this->data['POST']) . "\n"
. $this->renderData('$_COOKIE', $this->data['COOKIE']) . "\n"
. $this->renderData('$_FILES', $this->data['FILES']) . "\n"
. $this->renderData('$_SESSION', $this->data['SESSION']) . "\n"
. $this->renderData('$_SERVER', $this->data['SERVER']) . "\n"
. $this->renderData('Request Headers', $this->data['requestHeaders']) . "\n"
. $this->renderData('Response Headers', $this->data['responseHeaders']);
} }
public function save() public function save()
{ {
if (function_exists('apache_request_headers')) {
$requestHeaders = apache_request_headers();
} elseif (function_exists('http_get_request_headers')) {
$requestHeaders = http_get_request_headers();
} else {
$requestHeaders = array();
}
$responseHeaders = array();
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] = array($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;
}
/** @var \yii\web\Session $session */
$session = Yii::$app->getComponent('session', false);
return array( return array(
'memory' => memory_get_peak_usage(), 'memory' => memory_get_peak_usage(),
'time' => microtime(true) - YII_BEGIN_TIME, 'time' => microtime(true) - YII_BEGIN_TIME,
'SERVER' => $_SERVER, 'flashes' => $session ? $session->getAllFlashes() : array(),
'GET' => $_GET, 'requestHeaders' => $requestHeaders,
'POST' => $_POST, 'responseHeaders' => $responseHeaders,
'COOKIE' => $_COOKIE, 'route' => Yii::$app->requestedAction->getUniqueId(),
'action' => $action,
'actionParams' => Yii::$app->requestedParams,
'SERVER' => empty($_SERVER) ? array() : $_SERVER,
'GET' => empty($_GET) ? array() : $_GET,
'POST' => empty($_POST) ? array() : $_POST,
'COOKIE' => empty($_COOKIE) ? array() : $_COOKIE,
'FILES' => empty($_FILES) ? array() : $_FILES, 'FILES' => empty($_FILES) ? array() : $_FILES,
'SESSION' => empty($_SESSION) ? array() : $_SESSION, 'SESSION' => empty($_SESSION) ? array() : $_SESSION,
); );
} }
protected function renderTable($values) protected function renderData($caption, $values)
{ {
if (empty($values)) {
return "<h3>$caption</h3>\n<p>Empty.</p>";
}
$rows = array(); $rows = array();
foreach ($values as $name => $value) { foreach ($values as $name => $value) {
$rows[] = '<tr><th style="width: 200px;">' . Html::encode($name) . '</th><td><div style="overflow:auto">' . Html::encode(var_export($value, true)) . '</div></td></tr>'; $rows[] = '<tr><th style="width: 200px;">' . Html::encode($name) . '</th><td><div style="overflow:auto">' . Html::encode(var_export($value, true)) . '</div></td></tr>';
} }
if (!empty($rows)) { $rows = implode("\n", $rows);
$rows = implode("\n", $rows); return <<<EOD
return <<<EOD <h3>$caption</h3>
<table class="table table-condensed table-bordered table-striped table-hover" style="table-layout: fixed;"> <table class="table table-condensed table-bordered table-striped table-hover" style="table-layout: fixed;">
<thead><tr><th style="width: 200px;">Name</th><th>Value</th></tr></thead> <thead><tr><th style="width: 200px;">Name</th><th>Value</th></tr></thead>
<tbody> <tbody>
...@@ -77,8 +134,5 @@ $rows ...@@ -77,8 +134,5 @@ $rows
</tbody> </tbody>
</table> </table>
EOD; EOD;
} else {
return 'Empty.';
}
} }
} }
...@@ -4,16 +4,23 @@ use yii\helpers\Html; ...@@ -4,16 +4,23 @@ use yii\helpers\Html;
/** /**
* @var \yii\base\View $this * @var \yii\base\View $this
* @var array $meta
* @var string $tag * @var string $tag
* @var array $manifest
* @var \yii\debug\Panel[] $panels * @var \yii\debug\Panel[] $panels
* @var \yii\debug\Panel $activePanel * @var \yii\debug\Panel $activePanel
*/ */
$this->registerAssetBundle('yii/bootstrap/dropdown');
$this->title = 'Yii Debugger';
?> ?>
<div class="default-index"> <div class="default-index">
<div class="navbar"> <div class="navbar">
<div class="navbar-inner"> <div class="navbar-inner">
<div class="container"> <div class="container">
<span class="brand">Yii Debugger</span> <?php foreach ($panels as $panel): ?>
<?php echo $panel->getSummary(); ?>
<?php endforeach; ?>
</div> </div>
</div> </div>
</div> </div>
...@@ -21,18 +28,39 @@ use yii\helpers\Html; ...@@ -21,18 +28,39 @@ use yii\helpers\Html;
<div class="container-fluid"> <div class="container-fluid">
<div class="row-fluid"> <div class="row-fluid">
<div class="span2"> <div class="span2">
<div class="well sidebar-nav"> <ul class="nav nav-tabs nav-list nav-stacked">
<ul class="nav nav-list">
<?php <?php
foreach ($panels as $panel) { foreach ($panels as $id => $panel) {
$link = Html::a(Html::encode($panel->getName()), array('debug/default/index', 'tag' => $tag, 'panel' => $panel->id)); $link = Html::a(Html::encode($panel->getName()), array('debug/default/index', 'tag' => $tag, 'panel' => $id));
echo Html::tag('li', $link, array('class' => $panel === $activePanel ? 'active' : null)); echo Html::tag('li', $link, array('class' => $panel === $activePanel ? 'active' : null));
} }
?> ?>
</ul> </ul>
</div><!--/.well -->
</div><!--/span--> </div><!--/span-->
<div class="span10"> <div class="span10">
<div class="meta alert alert-info">
<div class="btn-group">
<button class="btn dropdown-toggle" data-toggle="dropdown">
View others
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<?php foreach ($manifest as $tag2 => $meta2) {
$label = $meta2['method'] . ' ' . $meta2['url'] . ($meta2['ajax'] ? ' (AJAX)' : '')
. ', ' . date('Y/m/d h:i:sa', $meta2['time'])
. ', ' . $meta2['ip'] . ', ' . $tag2;
$url = array('debug/default/index', 'tag' => $tag2);
echo '<li>' . Html::a(Html::encode($label), $url) . '</li>';
} ?>
</ul>
</div>
Debugging:
<?php echo $meta['method']; ?>
<?php echo Html::a(Html::encode($meta['url']), $meta['url']); ?>
<?php echo $meta['ajax'] ? ' (AJAX)' : ''; ?>
at <?php echo date('Y/m/d h:i:sa', $meta['time']); ?>
by <?php echo $meta['ip']; ?>
</div>
<?php echo $activePanel->getDetail(); ?> <?php echo $activePanel->getDetail(); ?>
</div> </div>
</div> </div>
......
...@@ -12,17 +12,42 @@ use yii\helpers\Html; ...@@ -12,17 +12,42 @@ use yii\helpers\Html;
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
background-color: #eee;
border-top: 1px solid #ccc;
margin: 0; margin: 0;
padding: 5px 10px; padding: 0;
z-index: 1000000; z-index: 1000000;
font: 11px Verdana, Arial, sans-serif; font: 11px Verdana, Arial, sans-serif;
text-align: left; text-align: left;
height: 38px;
border-top: 1px solid #ccc;
background: rgb(237,237,237);
background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2VkZWRlZCIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjUzJSIgc3RvcC1jb2xvcj0iI2Y2ZjZmNiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+);
background: -moz-linear-gradient(top, rgba(237,237,237,1) 0%, rgba(246,246,246,1) 53%, rgba(255,255,255,1) 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(237,237,237,1)), color-stop(53%,rgba(246,246,246,1)), color-stop(100%,rgba(255,255,255,1)));
background: -webkit-linear-gradient(top, rgba(237,237,237,1) 0%,rgba(246,246,246,1) 53%,rgba(255,255,255,1) 100%);
background: -o-linear-gradient(top, rgba(237,237,237,1) 0%,rgba(246,246,246,1) 53%,rgba(255,255,255,1) 100%);
background: -ms-linear-gradient(top, rgba(237,237,237,1) 0%,rgba(246,246,246,1) 53%,rgba(255,255,255,1) 100%);
background: linear-gradient(to bottom, rgba(237,237,237,1) 0%,rgba(246,246,246,1) 53%,rgba(255,255,255,1) 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ededed', endColorstr='#ffffff',GradientType=0 );
} }
.yii-debug-toolbar-block { .yii-debug-toolbar-block {
float: left; float: left;
margin: 0 10px; margin: 0;
border-right: 1px solid #e4e4e4;
padding: 4px 8px;
line-height: 32px;
}
.yii-debug-toolbar-block a {
text-decoration: none;
color: black !important;
}
.yii-debug-toolbar-block span {
}
.yii-debug-toolbar-block img {
vertical-align: middle;
} }
</style> </style>
...@@ -30,7 +55,4 @@ use yii\helpers\Html; ...@@ -30,7 +55,4 @@ use yii\helpers\Html;
<?php foreach ($panels as $panel): ?> <?php foreach ($panels as $panel): ?>
<?php echo $panel->getSummary(); ?> <?php echo $panel->getSummary(); ?>
<?php endforeach; ?> <?php endforeach; ?>
<div class="yii-debug-toolbar-block">
<?php echo Html::a('more details', array('index', 'tag' => $tag)); ?>
</div>
</div> </div>
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
*/ */
use yii\helpers\Html; use yii\helpers\Html;
Yii::$app->getView()->registerAssetBundle('yii/bootstrap/responsive'); Yii::$app->getView()->registerAssetBundle('yii/debug');
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
......
...@@ -855,7 +855,7 @@ class Html ...@@ -855,7 +855,7 @@ class Html
* The signature of this callback must be: * The signature of this callback must be:
* *
* ~~~ * ~~~
* function ($index, $item) * function ($item, $index)
* ~~~ * ~~~
* *
* where $index is the array key corresponding to `$item` in `$items`. The callback should return * where $index is the array key corresponding to `$item` in `$items`. The callback should return
...@@ -876,7 +876,7 @@ class Html ...@@ -876,7 +876,7 @@ class Html
$results = array(); $results = array();
foreach ($items as $index => $item) { foreach ($items as $index => $item) {
if ($formatter !== null) { if ($formatter !== null) {
$results[] = call_user_func($formatter, $index, $item); $results[] = call_user_func($formatter, $item, $index);
} else { } else {
$results[] = static::tag('li', $encode ? static::encode($item) : $item, $itemOptions); $results[] = static::tag('li', $encode ? static::encode($item) : $item, $itemOptions);
} }
...@@ -897,7 +897,7 @@ class Html ...@@ -897,7 +897,7 @@ class Html
* The signature of this callback must be: * The signature of this callback must be:
* *
* ~~~ * ~~~
* function ($index, $item) * function ($item, $index)
* ~~~ * ~~~
* *
* where $index is the array key corresponding to `$item` in `$items`. The callback should return * where $index is the array key corresponding to `$item` in `$items`. The callback should return
......
...@@ -161,7 +161,7 @@ class Logger extends Component ...@@ -161,7 +161,7 @@ class Logger extends Component
public function log($message, $level, $category = 'application') public function log($message, $level, $category = 'application')
{ {
$time = microtime(true); $time = microtime(true);
if (YII_DEBUG && YII_TRACE_LEVEL > 0) { if (YII_DEBUG && YII_TRACE_LEVEL > 0 && !($level & self::LEVEL_PROFILE)) {
$traces = debug_backtrace(); $traces = debug_backtrace();
$count = 0; $count = 0;
foreach ($traces as $trace) { foreach ($traces as $trace) {
......
...@@ -90,7 +90,7 @@ abstract class Target extends Component ...@@ -90,7 +90,7 @@ abstract class Target extends Component
*/ */
public function collect($messages, $final) public function collect($messages, $final)
{ {
$this->messages = array_merge($this->messages, $this->filterMessages($messages)); $this->messages = array_merge($this->messages, $this->filterMessages($messages, $this->getLevels(), $this->categories, $this->except));
$count = count($this->messages); $count = count($this->messages);
if ($count > 0 && ($final || $this->exportInterval > 0 && $count >= $this->exportInterval)) { if ($count > 0 && ($final || $this->exportInterval > 0 && $count >= $this->exportInterval)) {
if (($context = $this->getContextMessage()) !== '') { if (($context = $this->getContextMessage()) !== '') {
...@@ -178,22 +178,22 @@ abstract class Target extends Component ...@@ -178,22 +178,22 @@ abstract class Target extends Component
/** /**
* Filters the given messages according to their categories and levels. * Filters the given messages according to their categories and levels.
* @param array $messages messages to be filtered * @param array $messages messages to be filtered
* @param integer $levels the message levels to filter by. This is a bitmap of
* level values. Value 0 means allowing all levels.
* @param array $categories the message categories to filter by. If empty, it means all categories are allowed.
* @param array $except the message categories to exclude. If empty, it means all categories are allowed.
* @return array the filtered messages. * @return array the filtered messages.
* @see filterByCategory
* @see filterByLevel
*/ */
protected function filterMessages($messages) public static function filterMessages($messages, $levels = 0, $categories = array(), $except = array())
{ {
$levels = $this->getLevels();
foreach ($messages as $i => $message) { foreach ($messages as $i => $message) {
if ($levels && !($levels & $message[1])) { if ($levels && !($levels & $message[1])) {
unset($messages[$i]); unset($messages[$i]);
continue; continue;
} }
$matched = empty($this->categories); $matched = empty($categories);
foreach ($this->categories as $category) { foreach ($categories as $category) {
if ($message[2] === $category || substr($category, -1) === '*' && strpos($message[2], rtrim($category, '*')) === 0) { if ($message[2] === $category || substr($category, -1) === '*' && strpos($message[2], rtrim($category, '*')) === 0) {
$matched = true; $matched = true;
break; break;
...@@ -201,7 +201,7 @@ abstract class Target extends Component ...@@ -201,7 +201,7 @@ abstract class Target extends Component
} }
if ($matched) { if ($matched) {
foreach ($this->except as $category) { foreach ($except as $category) {
$prefix = rtrim($category, '*'); $prefix = rtrim($category, '*');
if (strpos($message[2], $prefix) === 0 && ($message[2] === $category || $prefix !== $category)) { if (strpos($message[2], $prefix) === 0 && ($message[2] === $category || $prefix !== $category)) {
$matched = false; $matched = false;
......
...@@ -65,6 +65,8 @@ class Application extends \yii\base\Application ...@@ -65,6 +65,8 @@ class Application extends \yii\base\Application
$params = array_splice($this->catchAll, 1); $params = array_splice($this->catchAll, 1);
} }
try { try {
Yii::trace("Route requested: '$route'", __METHOD__);
$this->requestedRoute = $route;
$result = $this->runAction($route, $params); $result = $this->runAction($route, $params);
if ($result instanceof Response) { if ($result instanceof Response) {
return $result; return $result;
......
...@@ -177,7 +177,7 @@ class CaptchaAction extends Action ...@@ -177,7 +177,7 @@ class CaptchaAction extends Action
$session->open(); $session->open();
$name = $this->getSessionKey() . 'count'; $name = $this->getSessionKey() . 'count';
$session[$name] = $session[$name] + 1; $session[$name] = $session[$name] + 1;
if ($session[$name] > $this->testLimit && $this->testLimit > 0) { if ($valid || $session[$name] > $this->testLimit && $this->testLimit > 0) {
$this->getVerifyCode(true); $this->getVerifyCode(true);
} }
return $valid; return $valid;
......
...@@ -171,6 +171,7 @@ class UrlManager extends Component ...@@ -171,6 +171,7 @@ class UrlManager extends Component
/** @var $rule UrlRule */ /** @var $rule UrlRule */
foreach ($this->rules as $rule) { foreach ($this->rules as $rule) {
if (($result = $rule->parseRequest($this, $request)) !== false) { if (($result = $rule->parseRequest($this, $request)) !== false) {
Yii::info("Request parsed with URL rule: {$rule->name}", __METHOD__);
return $result; return $result;
} }
} }
...@@ -194,12 +195,14 @@ class UrlManager extends Component ...@@ -194,12 +195,14 @@ class UrlManager extends Component
} }
} }
Yii::info('No matching URL rules. Using default URL parsing logic.', __METHOD__);
return array($pathInfo, array()); return array($pathInfo, array());
} else { } else {
$route = $request->get($this->routeVar); $route = $request->get($this->routeVar);
if (is_array($route)) { if (is_array($route)) {
$route = ''; $route = '';
} }
Yii::info('Pretty URL not enabled. Using default URL parsing logic.', __METHOD__);
return array((string)$route, array()); return array((string)$route, array());
} }
} }
......
...@@ -388,7 +388,7 @@ EOD; ...@@ -388,7 +388,7 @@ EOD;
EOD; EOD;
$this->assertEqualsWithoutLE($expected, Html::ul($data, array( $this->assertEqualsWithoutLE($expected, Html::ul($data, array(
'class' => 'test', 'class' => 'test',
'item' => function($index, $item) { 'item' => function($item, $index) {
return "<li class=\"item-$index\">$item</li>"; return "<li class=\"item-$index\">$item</li>";
} }
))); )));
...@@ -418,7 +418,7 @@ EOD; ...@@ -418,7 +418,7 @@ EOD;
EOD; EOD;
$this->assertEqualsWithoutLE($expected, Html::ol($data, array( $this->assertEqualsWithoutLE($expected, Html::ol($data, array(
'class' => 'test', 'class' => 'test',
'item' => function($index, $item) { 'item' => function($item, $index) {
return "<li class=\"item-$index\">$item</li>"; return "<li class=\"item-$index\">$item</li>";
} }
))); )));
......
...@@ -7,6 +7,12 @@ use yiiunit\TestCase; ...@@ -7,6 +7,12 @@ use yiiunit\TestCase;
class UrlManagerTest extends TestCase class UrlManagerTest extends TestCase
{ {
protected function setUp()
{
parent::setUp();
$this->mockApplication();
}
public function testCreateUrl() public function testCreateUrl()
{ {
// default setting with '/' as base url // default setting with '/' as base url
......
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