Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yii2
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
PSDI Army
yii2
Commits
18be2212
Commit
18be2212
authored
Feb 01, 2014
by
Alexander Makarov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactored debug module, added all missing docs
parent
5fbd4ce7
Show whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
302 additions
and
114 deletions
+302
-114
DebugAsset.php
extensions/debug/DebugAsset.php
+2
-0
LogTarget.php
extensions/debug/LogTarget.php
+7
-0
Module.php
extensions/debug/Module.php
+19
-2
Panel.php
extensions/debug/Panel.php
+5
-0
Filter.php
extensions/debug/components/search/Filter.php
+31
-23
Base.php
extensions/debug/components/search/matchers/Base.php
+18
-4
GreaterThan.php
extensions/debug/components/search/matchers/GreaterThan.php
+6
-8
LowerThan.php
extensions/debug/components/search/matchers/LowerThan.php
+6
-8
MatcherInterface.php
...ons/debug/components/search/matchers/MatcherInterface.php
+18
-4
SameAs.php
extensions/debug/components/search/matchers/SameAs.php
+8
-10
DefaultController.php
extensions/debug/controllers/DefaultController.php
+8
-0
Base.php
extensions/debug/models/search/Base.php
+21
-14
Db.php
extensions/debug/models/search/Db.php
+17
-6
Debug.php
extensions/debug/models/search/Debug.php
+18
-6
Log.php
extensions/debug/models/search/Log.php
+16
-5
Profile.php
extensions/debug/models/search/Profile.php
+16
-5
ConfigPanel.php
extensions/debug/panels/ConfigPanel.php
+22
-0
DbPanel.php
extensions/debug/panels/DbPanel.php
+23
-11
LogPanel.php
extensions/debug/panels/LogPanel.php
+16
-5
ProfilingPanel.php
extensions/debug/panels/ProfilingPanel.php
+13
-2
RequestPanel.php
extensions/debug/panels/RequestPanel.php
+12
-1
No files found.
extensions/debug/DebugAsset.php
View file @
18be2212
...
...
@@ -9,6 +9,8 @@ namespace yii\debug;
use
yii\web\AssetBundle
;
/**
* Debugger asset bundle
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
...
...
extensions/debug/LogTarget.php
View file @
18be2212
...
...
@@ -72,6 +72,13 @@ class LogTarget extends Target
$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
)
{
touch
(
$indexFile
);
...
...
extensions/debug/Module.php
View file @
18be2212
...
...
@@ -50,7 +50,9 @@ class Module extends \yii\base\Module
*/
public
$historySize
=
50
;
/**
* @inheritdoc
*/
public
function
init
()
{
parent
::
init
();
...
...
@@ -69,13 +71,16 @@ class Module extends \yii\base\Module
}
}
/**
* @inheritdoc
*/
public
function
beforeAction
(
$action
)
{
Yii
::
$app
->
getView
()
->
off
(
View
::
EVENT_END_BODY
,
[
$this
,
'renderToolbar'
]);
unset
(
Yii
::
$app
->
getLog
()
->
targets
[
'debug'
]);
$this
->
logTarget
=
null
;
if
(
$this
->
checkAccess
(
$action
))
{
if
(
$this
->
checkAccess
())
{
return
parent
::
beforeAction
(
$action
);
}
elseif
(
$action
->
id
===
'toolbar'
)
{
return
false
;
...
...
@@ -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
)
{
if
(
!
$this
->
checkAccess
()
||
Yii
::
$app
->
getRequest
()
->
getIsAjax
())
{
...
...
@@ -99,6 +109,10 @@ class Module extends \yii\base\Module
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
()
{
$ip
=
Yii
::
$app
->
getRequest
()
->
getUserIP
();
...
...
@@ -111,6 +125,9 @@ class Module extends \yii\base\Module
return
false
;
}
/**
* @return array default set of panels
*/
protected
function
corePanels
()
{
return
[
...
...
extensions/debug/Panel.php
View file @
18be2212
...
...
@@ -73,6 +73,11 @@ class Panel extends Component
return
null
;
}
/**
* Loads data into the panel
*
* @param mixed $data
*/
public
function
load
(
$data
)
{
$this
->
data
=
$data
;
...
...
extensions/debug/components/search/Filter.php
View file @
18be2212
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace
yii\debug\components\search
;
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
{
/**
* @var array rules for matching filters in the way: [:fieldName => [rule1, rule2,..]]
*/
protected
$rules
=
[];
/**
* Adds rules for filtering data. Match can be partial or exactly.
* Adds data filtering rule.
*
* @param string $name attribute name
* @param
\yii\debug\components\search\matches\Bas
e $rule
* @param
MatcherInterfac
e $rule
*/
public
function
addMatch
(
$name
,
$rule
)
public
function
addMatch
er
(
$name
,
MatcherInterface
$rule
)
{
if
(
empty
(
$rule
->
value
)
&&
$rule
->
value
!==
0
)
{
return
;
}
if
(
$rule
->
hasValue
())
{
$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
* @return array filtered data
*/
...
...
@@ -36,7 +47,7 @@ class Filter extends Component
$filtered
=
[];
foreach
(
$data
as
$row
)
{
if
(
$this
->
check
Filter
(
$row
))
{
if
(
$this
->
passes
Filter
(
$row
))
{
$filtered
[]
=
$row
;
}
}
...
...
@@ -45,28 +56,25 @@ class Filter extends Component
}
/**
* Check if the given data satisfies filters.
* @param array $row
* Checks if the given data satisfies filters.
*
* @param array $row data
* @return boolean if data passed filtering
*/
p
ublic
function
check
Filter
(
array
$row
)
p
rivate
function
passes
Filter
(
array
$row
)
{
$matched
=
true
;
foreach
(
$row
as
$name
=>
$value
)
{
if
(
isset
(
$this
->
rules
[
$name
]))
{
#check all rules for given attribute
// check all rules for a given attribute
foreach
(
$this
->
rules
[
$name
]
as
$rule
)
{
if
(
!
$rule
->
check
(
$value
))
{
$matched
=
false
;
/** @var MatcherInterface $rule */
if
(
!
$rule
->
match
(
$value
))
{
return
false
;
}
}
}
}
return
$matched
;
return
true
;
}
}
extensions/debug/components/search/matches/Base.php
→
extensions/debug/components/search/matche
r
s/Base.php
View file @
18be2212
...
...
@@ -5,22 +5,36 @@
* @license http://www.yiiframework.com/license/
*/
namespace
yii\debug\components\search\matches
;
namespace
yii\debug\components\search\matche
r
s
;
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>
* @since 2.0
*/
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
;
}
}
extensions/debug/components/search/matche
s/Greater
.php
→
extensions/debug/components/search/matche
rs/GreaterThan
.php
View file @
18be2212
...
...
@@ -5,23 +5,21 @@
* @license http://www.yiiframework.com/license/
*/
namespace
yii\debug\components\search\matches
;
namespace
yii\debug\components\search\matche
r
s
;
/**
* Checks if the given value is greater than the base one.
*
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
class
Greater
extends
Base
class
Greater
Than
extends
Base
{
/**
* Checks if the given value is the same as base one or has partial match with base one.
* @param mixed $value
* @inheritdoc
*/
public
function
check
(
$value
)
public
function
match
(
$value
)
{
return
(
$value
>
$this
->
v
alue
);
return
(
$value
>
$this
->
baseV
alue
);
}
}
extensions/debug/components/search/matche
s/Lower
.php
→
extensions/debug/components/search/matche
rs/LowerThan
.php
View file @
18be2212
...
...
@@ -5,23 +5,21 @@
* @license http://www.yiiframework.com/license/
*/
namespace
yii\debug\components\search\matches
;
namespace
yii\debug\components\search\matche
r
s
;
/**
* Checks if the given value is lower than the base one.
*
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
class
Lower
extends
Base
class
Lower
Than
extends
Base
{
/**
* Checks if the given value is the same as base one or has partial match with base one.
* @param mixed $value
* @inheritdoc
*/
public
function
check
(
$value
)
public
function
match
(
$value
)
{
return
(
$value
<
$this
->
v
alue
);
return
(
$value
<
$this
->
baseV
alue
);
}
}
extensions/debug/components/search/matches/MatcherInterface.php
→
extensions/debug/components/search/matche
r
s/MatcherInterface.php
View file @
18be2212
...
...
@@ -5,21 +5,35 @@
* @license http://www.yiiframework.com/license/
*/
namespace
yii\debug\components\search\matches
;
namespace
yii\debug\components\search\matche
r
s
;
/**
* 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>
* @since 2.0
*/
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
*/
public
function
check
(
$value
);
public
function
setValue
(
$value
);
/**
* Checks if base value is set
*
* @return boolean if base value is set
*/
public
function
hasValue
();
}
extensions/debug/components/search/matche
s/Exact
.php
→
extensions/debug/components/search/matche
rs/SameAs
.php
View file @
18be2212
...
...
@@ -5,32 +5,30 @@
* @license http://www.yiiframework.com/license/
*/
namespace
yii\debug\components\search\matches
;
namespace
yii\debug\components\search\matche
r
s
;
/**
* Checks if the given value is exactly or partially same as the base one.
*
* @author Mark Jebri <mark.github@yandex.ru>
* @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
;
/**
* Checks if the given value is the same as base one or has partial match with base one.
* @param mixed $value
* @inheritdoc
*/
public
function
check
(
$value
)
public
function
match
(
$value
)
{
if
(
!
$this
->
partial
)
{
return
(
mb_strtolower
(
$this
->
v
alue
,
'utf8'
)
==
mb_strtolower
(
$value
,
'utf8'
));
return
(
mb_strtolower
(
$this
->
baseV
alue
,
'utf8'
)
==
mb_strtolower
(
$value
,
'utf8'
));
}
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
);
}
}
}
extensions/debug/controllers/DefaultController.php
View file @
18be2212
...
...
@@ -13,11 +13,16 @@ use yii\web\NotFoundHttpException;
use
yii\debug\models\search\Debug
;
/**
* Debugger controller
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class
DefaultController
extends
Controller
{
/**
* @inheritdoc
*/
public
$layout
=
'main'
;
/**
* @var \yii\debug\Module
...
...
@@ -28,6 +33,9 @@ class DefaultController extends Controller
*/
public
$summary
;
/**
* @inheritdoc
*/
public
function
actions
()
{
$actions
=
[];
...
...
extensions/debug/models/search/Base.php
View file @
18be2212
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace
yii\debug\models\search
;
use
yii\base\Model
;
use
yii\debug\components\search\Filter
;
use
yii\debug\components\search\matches
;
use
yii\debug\components\search\matche
r
s
;
/**
* Base search model
*
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
class
Base
extends
Model
{
/**
* @param Filter $filter
* @param string $attribute
* @param boolean $partial
* Adds filtering condition for a given attribute
*
* @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
;
if
(
mb_strpos
(
$value
,
'>'
)
!==
false
)
{
$value
=
intval
(
str_replace
(
'>'
,
''
,
$value
));
$filter
->
addMatch
(
$attribute
,
new
matches\Greater
([
'value'
=>
$value
]));
$filter
->
addMatch
er
(
$attribute
,
new
matchers\GreaterThan
([
'value'
=>
$value
]));
}
elseif
(
mb_strpos
(
$value
,
'<'
)
!==
false
)
{
$value
=
intval
(
str_replace
(
'<'
,
''
,
$value
));
$filter
->
addMatch
(
$attribute
,
new
matches\Lower
([
'value'
=>
$value
]));
$filter
->
addMatcher
(
$attribute
,
new
matchers\LowerThan
([
'value'
=>
$value
]));
}
else
{
$filter
->
addMatch
(
$attribute
,
new
matches\Exact
([
'value'
=>
$value
,
'partial'
=>
$partial
]));
$filter
->
addMatch
er
(
$attribute
,
new
matchers\SameAs
([
'value'
=>
$value
,
'partial'
=>
$partial
]));
}
}
}
extensions/debug/models/search/Db.php
View file @
18be2212
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace
yii\debug\models\search
;
...
...
@@ -6,13 +11,16 @@ use yii\data\ArrayDataProvider;
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
{
/**
* @var string type
attribut
e input search value
* @var string type
of th
e input search value
*/
public
$type
;
...
...
@@ -21,6 +29,9 @@ class Db extends Base
*/
public
$query
;
/**
* @inheritdoc
*/
public
function
rules
()
{
return
[
...
...
@@ -41,8 +52,9 @@ class Db extends Base
/**
* 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
*/
public
function
search
(
$params
,
$models
)
...
...
@@ -69,5 +81,4 @@ class Db extends Base
return
$dataProvider
;
}
}
extensions/debug/models/search/Debug.php
View file @
18be2212
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace
yii\debug\models\search
;
...
...
@@ -6,7 +11,11 @@ use yii\data\ArrayDataProvider;
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
{
...
...
@@ -51,6 +60,9 @@ class Debug extends Base
*/
public
$criticalCodes
=
[
400
,
404
,
500
];
/**
* @inheritdoc
*/
public
function
rules
()
{
return
[
...
...
@@ -76,8 +88,8 @@ class Debug extends Base
/**
* 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
*/
public
function
search
(
$params
,
$models
)
...
...
@@ -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
* @return bool
* @return bool
ean
*/
public
function
isCodeCritical
(
$code
)
{
return
in_array
(
$code
,
$this
->
criticalCodes
);
}
}
extensions/debug/models/search/Log.php
View file @
18be2212
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace
yii\debug\models\search
;
...
...
@@ -6,11 +11,14 @@ use yii\data\ArrayDataProvider;
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
{
/**
* @var string ip attribute input search value
*/
...
...
@@ -26,6 +34,9 @@ class Log extends Base
*/
public
$message
;
/**
* @inheritdoc
*/
public
function
rules
()
{
return
[
...
...
@@ -47,8 +58,9 @@ class Log extends Base
/**
* 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
*/
public
function
search
(
$params
,
$models
)
...
...
@@ -73,5 +85,4 @@ class Log extends Base
return
$dataProvider
;
}
}
extensions/debug/models/search/Profile.php
View file @
18be2212
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace
yii\debug\models\search
;
...
...
@@ -6,11 +11,14 @@ use yii\data\ArrayDataProvider;
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
{
/**
* @var string method attribute input search value
*/
...
...
@@ -21,6 +29,9 @@ class Profile extends Base
*/
public
$info
;
/**
* @inheritdoc
*/
public
function
rules
()
{
return
[
...
...
@@ -41,8 +52,9 @@ class Profile extends Base
/**
* 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
*/
public
function
search
(
$params
,
$models
)
...
...
@@ -69,5 +81,4 @@ class Profile extends Base
return
$dataProvider
;
}
}
extensions/debug/panels/ConfigPanel.php
View file @
18be2212
...
...
@@ -18,26 +18,45 @@ use yii\debug\Panel;
*/
class
ConfigPanel
extends
Panel
{
/**
* @inheritdoc
*/
public
function
getName
()
{
return
'Configuration'
;
}
/**
* Returns Yii logo ready to use in `<img src="`
*
* @return string base64 representation of the image
*/
public
static
function
getYiiLogo
()
{
return
'data:image/png;base64,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'
;
}
/**
* @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
=
[];
...
...
@@ -47,6 +66,9 @@ class ConfigPanel extends Panel
return
$data
;
}
/**
* @inheritdoc
*/
public
function
save
()
{
return
[
...
...
extensions/debug/panels/DbPanel.php
View file @
18be2212
...
...
@@ -20,7 +20,6 @@ use yii\debug\models\search\Db;
*/
class
DbPanel
extends
Panel
{
/**
* @var array db queries info extracted to array as models, to use with data provider.
*/
...
...
@@ -31,11 +30,17 @@ class DbPanel extends Panel
*/
private
$_timings
;
/**
* @inheritdoc
*/
public
function
getName
()
{
return
'Database'
;
}
/**
* @inheritdoc
*/
public
function
getSummary
()
{
$timings
=
$this
->
calculateTimings
();
...
...
@@ -50,6 +55,9 @@ class DbPanel extends Panel
]);
}
/**
* @inheritdoc
*/
public
function
getDetail
()
{
$searchModel
=
new
Db
();
...
...
@@ -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]
*/
protected
function
calculateTimings
()
...
...
@@ -74,6 +83,9 @@ class DbPanel extends Panel
return
$this
->
_timings
;
}
/**
* @inheritdoc
*/
public
function
save
()
{
$target
=
$this
->
module
->
logTarget
;
...
...
@@ -82,7 +94,8 @@ class DbPanel extends Panel
}
/**
* Returns total queries time.
* Returns total query time.
*
* @param array $timings
* @return integer total time
*/
...
...
@@ -98,8 +111,8 @@ class DbPanel extends Panel
}
/**
* Returns a
rray of models that represents logs of the current request. Can be used with data providers,
*
like
yii\data\ArrayDataProvider.
* Returns a
n 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
()
...
...
@@ -110,7 +123,7 @@ class DbPanel extends Panel
foreach
(
$timings
as
$seq
=>
$dbTiming
)
{
$this
->
_models
[]
=
[
'type'
=>
$this
->
detec
tQueryType
(
$dbTiming
[
'info'
]),
'type'
=>
$this
->
ge
tQueryType
(
$dbTiming
[
'info'
]),
'query'
=>
$dbTiming
[
'info'
],
'duration'
=>
(
$dbTiming
[
'duration'
]
*
1000
),
// in milliseconds
'trace'
=>
$dbTiming
[
'trace'
],
...
...
@@ -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
.
*
First word before space is timing type. If there is no such words, timing will have empty type.
*
Returns databse query type
.
*
* @param string $timing timing procedure string
* @return string query type s
elect|insert|delete|etc
* @return string query type s
uch as select, insert, delete, etc.
*/
protected
function
detec
tQueryType
(
$timing
)
protected
function
ge
tQueryType
(
$timing
)
{
$timing
=
ltrim
(
$timing
);
preg_match
(
'/^([a-zA-z]*)/'
,
$timing
,
$matches
);
return
count
(
$matches
)
?
$matches
[
0
]
:
''
;
}
}
extensions/debug/panels/LogPanel.php
View file @
18be2212
...
...
@@ -20,22 +20,30 @@ use yii\debug\models\search\Log;
*/
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
();
...
...
@@ -48,6 +56,9 @@ class LogPanel extends Panel
]);
}
/**
* @inheritdoc
*/
public
function
save
()
{
$target
=
$this
->
module
->
logTarget
;
...
...
@@ -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,
* like yii\data\ArrayDataProvider.
* @param boolean $refresh if needed to build models from log messages and refresh them.
* Returns an array of models that represents logs of the current request.
* Can be used with data providers, such as \yii\data\ArrayDataProvider.
*
* @param boolean $refresh if need to build models from log messages and refresh them.
* @return array models
*/
protected
function
getModels
(
$refresh
=
false
)
...
...
@@ -78,5 +90,4 @@ class LogPanel extends Panel
}
return
$this
->
_models
;
}
}
extensions/debug/panels/ProfilingPanel.php
View file @
18be2212
...
...
@@ -25,11 +25,17 @@ class ProfilingPanel extends Panel
*/
private
$_models
;
/**
* @inheritdoc
*/
public
function
getName
()
{
return
'Profiling'
;
}
/**
* @inheritdoc
*/
public
function
getSummary
()
{
return
Yii
::
$app
->
view
->
render
(
'panels/profile/summary'
,
[
...
...
@@ -39,6 +45,9 @@ class ProfilingPanel extends Panel
]);
}
/**
* @inheritdoc
*/
public
function
getDetail
()
{
$searchModel
=
new
Profile
();
...
...
@@ -53,6 +62,9 @@ class ProfilingPanel extends Panel
]);
}
/**
* @inheritdoc
*/
public
function
save
()
{
$target
=
$this
->
module
->
logTarget
;
...
...
@@ -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
*/
protected
function
getModels
()
...
...
@@ -87,5 +99,4 @@ class ProfilingPanel extends Panel
}
return
$this
->
_models
;
}
}
extensions/debug/panels/RequestPanel.php
View file @
18be2212
...
...
@@ -19,21 +19,33 @@ use yii\debug\Panel;
*/
class
RequestPanel
extends
Panel
{
/**
* @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
();
...
...
@@ -96,5 +108,4 @@ class RequestPanel extends Panel
'SESSION'
=>
empty
(
$_SESSION
)
?
[]
:
$_SESSION
,
];
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment