Created
January 28, 2017 06:32
-
-
Save Insolita/8d1e5cdf5d9e48ac81698fc63d2ad07a to your computer and use it in GitHub Desktop.
yii2 profiling helper for console/test environment
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace common\tests; | |
use Codeception\Util\Debug; | |
use yii\console\Application; | |
use yii\db\ActiveQuery; | |
use yii\db\Connection; | |
use yii\helpers\ArrayHelper; | |
use yii\helpers\Console; | |
use yii\helpers\VarDumper; | |
/** | |
* Useful set for performance optimization in console scripts, and test researches | |
* | |
* Trait ProfilingInspectorTrait | |
* | |
* @package common\tests | |
*/ | |
trait ProfilingInspectorTrait | |
{ | |
/** | |
* @param \yii\db\Query|ActiveQuery $query | |
* @param \yii\db\Connection $db | |
*/ | |
protected function showQuery(\yii\db\Query $query, Connection $db) | |
{ | |
$sql = $query->prepare($db->queryBuilder)->createCommand($db)->rawSql; | |
$this->resolvedOutput($sql, __FUNCTION__); | |
} | |
/** | |
* @param \yii\db\Query|ActiveQuery $query | |
* @param \yii\db\Connection $db | |
*/ | |
protected function explainQuery(\yii\db\Query $query, Connection $db) | |
{ | |
$sql = $query->prepare($db->queryBuilder)->createCommand($db)->rawSql; | |
$sql = 'EXPLAIN: ' . implode(PHP_EOL, $db->createCommand('EXPLAIN ' . $sql)->queryColumn('QUERY PLAN')); | |
$this->resolvedOutput($sql, __FUNCTION__); | |
} | |
/** | |
* @param \yii\db\Query|ActiveQuery $query | |
* @param \yii\db\Connection $db | |
*/ | |
protected function analyzeQuery(\yii\db\Query $query, Connection $db) | |
{ | |
$sql = $query->prepare($db->queryBuilder)->createCommand($db)->rawSql; | |
$sql = 'ANALYZE: ' . implode(PHP_EOL, $db->createCommand('EXPLAIN ANALYZE ' . $sql)->queryColumn('QUERY PLAN')); | |
$this->resolvedOutput($sql, __FUNCTION__); | |
} | |
/** | |
* Show execution time for specified callback and time elapsed from app start | |
* Return real function result | |
* | |
* @example | |
* $result=$this->timeIt(function(){ | |
* $data = User::find()->where(['<','id', 100])->all(); | |
* return ArrayHelper::map($data,'id','name'); | |
* }); | |
* | |
* @param \Closure $function | |
* @param string $comment Additional log comment | |
* | |
* @return mixed | |
*/ | |
protected function timeIt(\Closure $function, string $comment = '') | |
{ | |
$mStart = microtime(true); | |
$result = $function(); | |
$mEnd = microtime(true); | |
$this->resolvedOutput( | |
[ | |
'timeDelta' => ($mEnd - $mStart), | |
'elapsedTime'=>\Yii::getLogger()->elapsedTime | |
], | |
__FUNCTION__ . ':' . $comment | |
); | |
return $result; | |
} | |
/** | |
* Show execution time, memory usage, and peek memory check for specified callback | |
* Return real function result | |
* | |
* @example | |
* $result1=$this->inspectIt(function(){ | |
* $data = User::find()->where(['<','id', 100])->all(); | |
* return ArrayHelper::map($data,'id','name'); | |
* },'Object result'); | |
* $result2 = $this->inspectIt(function(){ | |
* $data = User::find()->where(['<','id', 100])->asArray()->all(); | |
* $result = ArrayHelper::map($data,'id','name'); | |
* unset($data); | |
* return $result; | |
* },'As Array result'); | |
* | |
* @param \Closure $function | |
* @param string $comment Additional log comment | |
* | |
* @return mixed | |
*/ | |
protected function inspectIt(\Closure $function, string $comment = '') | |
{ | |
$start = \Yii::getLogger()->elapsedTime; | |
$start_memory = memory_get_usage(); | |
$start_peek = memory_get_peak_usage(); | |
$result = $function(); | |
$end_memory = memory_get_usage(); | |
$end_peek = memory_get_peak_usage(); | |
$end = \Yii::getLogger()->elapsedTime; | |
$this->resolvedOutput( | |
[ | |
'timeDelta' => ($end - $start), | |
'memory' => [ | |
'from' => \Yii::$app->formatter->asSize($start_memory), | |
'to' => \Yii::$app->formatter->asSize($end_memory), | |
'delta' => \Yii::$app->formatter->asSize(($end_memory - $start_memory)), | |
'peekMemoryIncreased' => ($end_peek > $start_peek) ? \Yii::$app->formatter->asSize($start_peek) | |
. ' +' . \Yii::$app->formatter->asSize($end_peek - $start_peek) : 'no', | |
], | |
], | |
__FUNCTION__ . ':' . $comment | |
); | |
return $result; | |
} | |
/** | |
* Show profile log for specified callback | |
* Return real function result | |
* | |
* @example | |
* $result=$this->profileIt(function(){ | |
* $data = User::find()->where(['<','id', 100])->all(); | |
* return ArrayHelper::map($data,'id','name'); | |
* }); | |
* | |
* @param \Closure $function | |
* @param string $comment Additional log comment | |
* | |
* @return mixed | |
*/ | |
protected function profileIt(\Closure $function, string $comment = '') | |
{ | |
$id = uniqid('profile_'); | |
\Yii::beginProfile($id, __FUNCTION__ . ':' . $id); | |
$result = $function(); | |
\Yii::endProfile($id, __FUNCTION__ . ':' . $id); | |
$profile = \Yii::getLogger()->getProfiling([]); | |
$map = ArrayHelper::getColumn($profile, 'info'); | |
$profile = array_slice($profile, array_search($id, $map)); | |
unset($map); | |
$this->resolvedOutput($profile, __FUNCTION__ . ':' . $id . ':' . $comment); | |
return $result; | |
} | |
/** | |
* @param $message | |
*/ | |
protected function resolvedOutput($message, $subj = ''): void | |
{ | |
$divider = '============= ' . $subj . ' ==============='; | |
if (YII_ENV_TEST === true) { | |
Debug::debug($divider); | |
Debug::debug($message); | |
} elseif (\Yii::$app instanceof Application) { | |
Console::output(Console::ansiFormat($divider . PHP_EOL, [Console::FG_BLUE])); | |
Console::output(Console::ansiFormat(VarDumper::export($message) . PHP_EOL, [Console::FG_GREEN])); | |
} else { | |
\Yii::trace($message, get_called_class().':'.$subj); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment