From fe7b7fcd8924d921e464a3e5fad3ee234d752ad1 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Thu, 23 Jun 2022 13:57:47 +0300 Subject: [PATCH 01/26] Future add data source (#1) * add data storage * add data storage * add data storage * fix type hints --- src/LogTarget.php | 154 +----------- src/Module.php | 285 ++++++----------------- src/components/data/CacheDataStorage.php | 161 +++++++++++++ src/components/data/DataStorage.php | 37 +++ src/components/data/FileDataStorage.php | 200 ++++++++++++++++ src/controllers/DefaultController.php | 97 +++----- 6 files changed, 510 insertions(+), 424 deletions(-) create mode 100644 src/components/data/CacheDataStorage.php create mode 100644 src/components/data/DataStorage.php create mode 100644 src/components/data/FileDataStorage.php diff --git a/src/LogTarget.php b/src/LogTarget.php index c213d59bd..9b58680fd 100644 --- a/src/LogTarget.php +++ b/src/LogTarget.php @@ -44,25 +44,17 @@ public function __construct($module, $config = []) /** * Exports log messages to a specific destination. * Child classes must implement this method. - * @throws \yii\base\Exception */ public function export() { - $path = $this->module->dataPath; - FileHelper::createDirectory($path, $this->module->dirMode); - $summary = $this->collectSummary(); - $dataFile = "$path/{$this->tag}.data"; + $data = []; $exceptions = []; + foreach ($this->module->panels as $id => $panel) { try { - $panelData = $panel->save(); - if ($id === 'profiling') { - $summary['peakMemory'] = $panelData['memory']; - $summary['processingTime'] = $panelData['time']; - } - $data[$id] = serialize($panelData); + $data[$id] = serialize($panel->save()); } catch (\Exception $exception) { $exceptions[$id] = new FlattenException($exception); } @@ -70,98 +62,9 @@ public function export() $data['summary'] = $summary; $data['exceptions'] = $exceptions; - file_put_contents($dataFile, serialize($data)); - if ($this->module->fileMode !== null) { - @chmod($dataFile, $this->module->fileMode); - } - - $indexFile = "$path/index.data"; - $this->updateIndexFile($indexFile, $summary); - } - - /** - * @see DefaultController - * @return array - */ - public function loadManifest() - { - $indexFile = $this->module->dataPath . '/index.data'; + $this->module->getDataStorage()->setData($this->tag,$data); - $content = ''; - $fp = @fopen($indexFile, 'r'); - if ($fp !== false) { - @flock($fp, LOCK_SH); - $content = fread($fp, filesize($indexFile)); - @flock($fp, LOCK_UN); - fclose($fp); - } - - if ($content !== '') { - return array_reverse(unserialize($content), true); - } - return []; - } - - /** - * @see DefaultController - * @return array - */ - public function loadTagToPanels($tag) - { - $dataFile = $this->module->dataPath . "/$tag.data"; - $data = unserialize(file_get_contents($dataFile)); - $exceptions = $data['exceptions']; - foreach ($this->module->panels as $id => $panel) { - if (isset($data[$id])) { - $panel->tag = $tag; - $panel->load(unserialize($data[$id])); - } - if (isset($exceptions[$id])) { - $panel->setError($exceptions[$id]); - } - } - - return $data; - } - - /** - * 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) - { - if (!@touch($indexFile) || ($fp = @fopen($indexFile, 'r+')) === false) { - throw new InvalidConfigException("Unable to open debug data index file: $indexFile"); - } - @flock($fp, LOCK_EX); - $manifest = ''; - while (($buffer = fgets($fp)) !== false) { - $manifest .= $buffer; - } - if (!feof($fp) || empty($manifest)) { - // error while reading index data, ignore and create new - $manifest = []; - } else { - $manifest = unserialize($manifest); - } - - $manifest[$this->tag] = $summary; - $this->gc($manifest); - - ftruncate($fp, 0); - rewind($fp); - fwrite($fp, serialize($manifest)); - - @flock($fp, LOCK_UN); - @fclose($fp); - - if ($this->module->fileMode !== null) { - @chmod($indexFile, $this->module->fileMode); - } } /** @@ -171,7 +74,6 @@ private function updateIndexFile($indexFile, $summary) * @param array $messages log messages to be processed. See [[\yii\log\Logger::messages]] for the structure * of each message. * @param bool $final whether this method is called at the end of the current application - * @throws \yii\base\Exception */ public function collect($messages, $final) { @@ -181,54 +83,6 @@ public function collect($messages, $final) } } - /** - * Removes obsolete data files - * @param array $manifest - */ - protected function gc(&$manifest) - { - if (count($manifest) > $this->module->historySize + 10) { - $n = count($manifest) - $this->module->historySize; - foreach (array_keys($manifest) as $tag) { - $file = $this->module->dataPath . "/$tag.data"; - @unlink($file); - if (isset($manifest[$tag]['mailFiles'])) { - foreach ($manifest[$tag]['mailFiles'] as $mailFile) { - @unlink(Yii::getAlias($this->module->panels['mail']->mailPath) . "/$mailFile"); - } - } - unset($manifest[$tag]); - if (--$n <= 0) { - break; - } - } - $this->removeStaleDataFiles($manifest); - } - } - - /** - * Remove staled data files i.e. files that are not in the current index file - * (may happen because of corrupted or rotated index file) - * - * @param array $manifest - * @since 2.0.11 - */ - protected function removeStaleDataFiles($manifest) - { - $storageTags = array_map( - function ($file) { - return pathinfo($file, PATHINFO_FILENAME); - }, - FileHelper::findFiles($this->module->dataPath, ['except' => ['index.data']]) - ); - - $staledTags = array_diff($storageTags, array_keys($manifest)); - - foreach ($staledTags as $tag) { - @unlink($this->module->dataPath . "/$tag.data"); - } - } - /** * Collects summary data of current request. * @return array diff --git a/src/Module.php b/src/Module.php index b804ab029..e59df49c6 100644 --- a/src/Module.php +++ b/src/Module.php @@ -1,8 +1,8 @@ - * @since 2.0 + * @since 2.0 */ class Module extends \yii\base\Module implements BootstrapInterface { @@ -30,12 +32,10 @@ class Module extends \yii\base\Module implements BootstrapInterface /** * @var array the list of IPs that are allowed to access this module. - * Each array element represents a single IP filter which can be either: - * - an IP address (e.g. 1.2.3.4), - * - an address with wildcard (e.g. 192.168.0.*) to represent a network segment - * - a CIDR range (e.g. 172.16.0.0/12) (available since version 2.1.18). - * The default value is `['127.0.0.1', '::1']`, which means the module can only be accessed - * by localhost. + * Each array element represents a single IP filter which can be either an IP address + * or an address with wildcard (e.g. 192.168.0.*) to represent a network segment. + * The default value is `['127.0.0.1', '::1']`, which means the module can only be accessed + * by localhost. */ public $allowedIPs = ['127.0.0.1', '::1']; /** @@ -45,24 +45,14 @@ class Module extends \yii\base\Module implements BootstrapInterface * The default value is `[]`. */ public $allowedHosts = []; - /** - * @var callable A valid PHP callback that returns true if user is allowed to use web shell and false otherwise - * - * The signature is the following: - * - * function (Action|null $action) The action can be null when called from a non action context (like set debug header) - * - * @since 2.1.0 - */ - public $checkAccessCallback; /** * {@inheritdoc} */ public $controllerNamespace = 'yii\debug\controllers'; /** - * @var LogTarget|array|string the logTarget object, or the configuration for creating the logTarget object. + * @var LogTarget */ - public $logTarget = 'yii\debug\LogTarget'; + public $logTarget; /** * @var array|Panel[] list of debug panels. The array keys are the panel IDs, and values are the corresponding * panel class names or configuration arrays. This will be merged with [[corePanels()]]. @@ -76,51 +66,29 @@ class Module extends \yii\base\Module implements BootstrapInterface * @since 2.0.7 */ public $defaultPanel = 'log'; + /** - * @var string the directory storing the debugger data files. This can be specified using a path alias. - */ - public $dataPath = '@runtime/debug'; - /** - * @var int the permission to be set for newly created debugger data files. - * This value will be used by PHP [[chmod()]] function. No umask will be applied. - * If not set, the permission will be determined by the current environment. - * @since 2.0.6 - */ - public $fileMode; - /** - * @var int the permission to be set for newly created directories. - * This value will be used by PHP [[chmod()]] function. No umask will be applied. - * Defaults to 0775, meaning the directory is read-writable by owner and group, - * but read-only for other users. - * @since 2.0.6 - */ - public $dirMode = 0775; - /** - * @var int the maximum number of debug data files to keep. If there are more files generated, - * the oldest ones will be removed. - */ - public $historySize = 50; - /** - * @var int the debug bar default height, as a percentage of the total screen height - * @since 2.1.1 + * @var DataStorage */ - public $defaultHeight = 50; + private $dataStorage; + + public $dataStorageConfig = [ + 'class' => FileDataStorage::class, + ]; + /** * @var bool whether to enable message logging for the requests about debug module actions. * You normally do not want to keep these logs because they may distract you from the logs about your applications. * You may want to enable the debug logs if you want to investigate how the debug module itself works. */ public $enableDebugLogs = false; + /** * @var bool whether to disable IP address restriction warning triggered by checkAccess function * @since 2.0.14 */ public $disableIpRestrictionWarning = false; - /** - * @var bool whether to disable access callback restriction warning triggered by checkAccess function - * @since 2.1.0 - */ - public $disableCallbackRestrictionWarning = false; + /** * @var mixed the string with placeholders to be be substituted or an anonymous function that returns the trace line string. * The placeholders are {file}, {line} and {text} and the string should be as follows: @@ -138,61 +106,13 @@ class Module extends \yii\base\Module implements BootstrapInterface * @since 2.0.7 */ public $traceLine = self::DEFAULT_IDE_TRACELINE; - /** - * @var array used when the virtual, containerized, or remote debug trace paths don't correspond to the developers - * local paths. Acts on the {file} portion for the `$traceLine` property. - * - * The array key is the environment's path, while the value is the local desired path. - * - * It will only map the first matched matched key. - * - * Example: - * - * ```php - * [ - * '/app' => '/home/user/project/app', - * ] - * ``` - * - * Note that this will not change the displayed text, only the link url. - * - * @since 2.1.6 - */ - public $tracePathMappings = []; - /** - * @var string The [[UrlRule]] class to use for rules generated by this module. - * @since 2.1.1 - */ - public $urlRuleClass = 'yii\web\UrlRule'; - /** - * @var string|callable Page title could be a string or a callable function - * - * ```php - * ... - * 'pageTitle' => 'Custom Debug Title', - * ... - * // OR - * 'pageTitle' => function($url) { - * $domain = getDomain($url); - * return $domain . ' debugger'; - * } - * ``` - * - * @since 2.1.1 - */ - public $pageTitle; + /** * @var string Yii logo URL */ private static $_yiiLogo = 'data:image/png;base64,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'; - /** - * @var array routes of AJAX requests to skip from being displayed in toolbar - * @since 2.1.14 - */ - public $skipAjaxRequestUrl = []; - /** * Returns the logo URL to be used in `'; + return ''; } /** * Renders mini-toolbar at the end of page body. * * @param \yii\base\Event $event - * @throws \Throwable */ public function renderToolbar($event) { @@ -398,66 +304,36 @@ public function renderToolbar($event) /* @var $view View */ $view = $event->sender; - echo $view->renderDynamic('return Yii::$app->getModule("' . $this->getUniqueId() . '")->getToolbarHtml();'); + echo $view->renderDynamic('return Yii::$app->getModule("' . $this->id . '")->getToolbarHtml();'); // echo is used in order to support cases where asset manager is not available - echo ''; - echo ''; + echo ''; + echo ''; } /** * Checks if current user is allowed to access the module - * @param \yii\base\Action|null $action the action to be executed. May be `null` when called from - * a non action context + * * @return bool if access is granted */ - protected function checkAccess($action = null) + protected function checkAccess() { - $allowed = false; - $ip = Yii::$app->getRequest()->getUserIP(); foreach ($this->allowedIPs as $filter) { - if ($filter === '*' - || $filter === $ip - || ( - ($pos = strpos($filter, '*')) !== false - && !strncmp($ip, $filter, $pos) - ) - || ( - strpos($filter, '/') !== false - && IpHelper::inRange($ip, $filter) - ) - ) { - $allowed = true; - break; + if ($filter === '*' || $filter === $ip || (($pos = strpos($filter, '*')) !== false && !strncmp($ip, $filter, $pos))) { + return true; } } - if ($allowed === false) { - foreach ($this->allowedHosts as $hostname) { - $filter = gethostbyname($hostname); - if ($filter === $ip) { - $allowed = true; - break; - } + foreach ($this->allowedHosts as $hostname) { + $filter = gethostbyname($hostname); + if ($filter === $ip) { + return true; } } - if ($allowed === false) { - if (!$this->disableIpRestrictionWarning) { - Yii::warning('Access to debugger is denied due to IP address restriction. The requesting IP address is ' . $ip, __METHOD__); - } - - return false; + if (!$this->disableIpRestrictionWarning) { + Yii::warning('Access to debugger is denied due to IP address restriction. The requesting IP address is ' . $ip, __METHOD__); } - - if ($this->checkAccessCallback !== null && call_user_func($this->checkAccessCallback, $action) !== true) { - if (!$this->disableCallbackRestrictionWarning) { - Yii::warning('Access to debugger is denied due to checkAccessCallback.', __METHOD__); - } - - return false; - } - - return true; + return false; } /** @@ -468,7 +344,6 @@ protected function corePanels() return [ 'config' => ['class' => 'yii\debug\panels\ConfigPanel'], 'request' => ['class' => 'yii\debug\panels\RequestPanel'], - 'router' => ['class' => 'yii\debug\panels\RouterPanel'], 'log' => ['class' => 'yii\debug\panels\LogPanel'], 'profiling' => ['class' => 'yii\debug\panels\ProfilingPanel'], 'db' => ['class' => 'yii\debug\panels\DbPanel'], @@ -477,10 +352,19 @@ protected function corePanels() 'mail' => ['class' => 'yii\debug\panels\MailPanel'], 'timeline' => ['class' => 'yii\debug\panels\TimelinePanel'], 'user' => ['class' => 'yii\debug\panels\UserPanel'], - 'dump' => ['class' => 'yii\debug\panels\DumpPanel'], + 'router' => ['class' => 'yii\debug\panels\RouterPanel'], ]; } + /** + * @return DataStorage + */ + public function getDataStorage() + { + return $this->dataStorage; + } + + /** * {@inheritdoc} * @since 2.0.7 @@ -494,21 +378,4 @@ protected function defaultVersion() } return parent::defaultVersion(); } - - /** - * @return string page title to be used in HTML - * @since 2.1.1 - */ - public function htmlTitle() - { - if (is_string($this->pageTitle) && !empty($this->pageTitle)) { - return $this->pageTitle; - } - - if (is_callable($this->pageTitle)) { - return call_user_func($this->pageTitle, Url::base(true)); - } - - return 'Yii Debugger'; - } } diff --git a/src/components/data/CacheDataStorage.php b/src/components/data/CacheDataStorage.php new file mode 100644 index 000000000..5f2f1c0e8 --- /dev/null +++ b/src/components/data/CacheDataStorage.php @@ -0,0 +1,161 @@ +cache = \Yii::$app->get($this->cacheComponent); + } + + /** + * @param string $tag + * + * @return array + */ + public function getData($tag) + { + $data = $this->cache->get($this->cacheDebugDataKey . $tag); + if (empty($data)) { + return []; + } else { + return unserialize($data); + } + } + + /** + * @param string $tag + * @param array $data + * + * @return mixed|void + */ + public function setData($tag, $data) + { + $this->cache->set($this->cacheDebugDataKey . $tag, serialize($data), $this->dataDuration); + $this->updateIndex($tag, $data['summary'] ?: []); + } + + /** + * @param $forceReload + * + * @return array|mixed + */ + public function getDataManifest($forceReload = false) + { + $manifest = $this->cache->get($this->cacheDebugManifestKey); + if (empty($manifest)) { + return []; + } else { + return unserialize($manifest); + } + } + + /** + * @param Module $module + * + * @return mixed|void + */ + public function setModule(Module $module) + { + $this->module = $module; + } + + /** + * @param string $tag + * @param $summary + * + * @return void + */ + private function updateIndex($tag, $summary) + { + $manifest = $this->cache->get($this->cacheDebugManifestKey); + + if (empty($manifest)) { + $manifest = []; + } else { + $manifest = unserialize($manifest); + } + + $manifest[$tag] = $summary; + $this->gc($manifest); + + $this->cache->set($this->cacheDebugManifestKey, serialize($manifest), $this->manifestDuration); + } + + + /** + * Removes obsolete data files + * + * @param array $manifest + */ + protected function gc(&$manifest) + { + if (count($manifest) > $this->historySize + 10) { + $n = count($manifest) - $this->historySize; + foreach (array_keys($manifest) as $tag) { + if (isset($manifest[$tag]['mailFiles'])) { + foreach ($manifest[$tag]['mailFiles'] as $mailFile) { + @unlink(Yii::getAlias($this->module->panels['mail']->mailPath) . "/$mailFile"); + } + } + unset($manifest[$tag]); + if (--$n <= 0) { + break; + } + } + } + } +} diff --git a/src/components/data/DataStorage.php b/src/components/data/DataStorage.php new file mode 100644 index 000000000..a931d01ef --- /dev/null +++ b/src/components/data/DataStorage.php @@ -0,0 +1,37 @@ +dataPath = Yii::getAlias($this->dataPath); + } + + /** + * @param $tag + * + * @return array + */ + public function getData($tag) + { + $dataFile = $this->dataPath . "/$tag.data"; + return unserialize(file_get_contents($dataFile)); + } + + /** + * @param string $tag + * @param array $data + * + * @return void + * @throws InvalidConfigException + * @throws \yii\base\Exception + */ + public function setData($tag, $data) + { + $path = $this->dataPath; + FileHelper::createDirectory($path, $this->dirMode); + $dataFile = "$path/{$tag}.data"; + + file_put_contents($dataFile, serialize($data)); + if ($this->fileMode !== null) { + @chmod($dataFile, $this->fileMode); + } + + $indexFile = "$path/index.data"; + $this->updateIndexFile($indexFile, $tag, $data['summary'] ?: []); + } + + /** + * @param $forceReload + * + * @return array|mixed + */ + public function getDataManifest($forceReload = false) + { + if ($this->_manifest === null || $forceReload) { + if ($forceReload) { + clearstatcache(); + } + $indexFile = $this->dataPath . '/index.data'; + + $content = ''; + $fp = @fopen($indexFile, 'r'); + if ($fp !== false) { + @flock($fp, LOCK_SH); + $content = fread($fp, filesize($indexFile)); + @flock($fp, LOCK_UN); + fclose($fp); + } + + if ($content !== '') { + $this->_manifest = array_reverse(unserialize($content), true); + } else { + $this->_manifest = []; + } + } + + return $this->_manifest; + } + + + /** + * 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, $tag, $summary) + { + touch($indexFile); + if (($fp = @fopen($indexFile, 'r+')) === false) { + throw new InvalidConfigException("Unable to open debug data index file: $indexFile"); + } + @flock($fp, LOCK_EX); + $manifest = ''; + while (($buffer = fgets($fp)) !== false) { + $manifest .= $buffer; + } + if (!feof($fp) || empty($manifest)) { + // error while reading index data, ignore and create new + $manifest = []; + } else { + $manifest = unserialize($manifest); + } + + $manifest[$tag] = $summary; + $this->gc($manifest); + + ftruncate($fp, 0); + rewind($fp); + fwrite($fp, serialize($manifest)); + + @flock($fp, LOCK_UN); + @fclose($fp); + + if ($this->fileMode !== null) { + @chmod($indexFile, $this->fileMode); + } + } + + + /** + * Removes obsolete data files + * + * @param array $manifest + */ + protected function gc(&$manifest) + { + if (count($manifest) > $this->historySize + 10) { + $n = count($manifest) - $this->historySize; + foreach (array_keys($manifest) as $tag) { + $file = $this->dataPath . "/$tag.data"; + @unlink($file); + if (isset($manifest[$tag]['mailFiles'])) { + foreach ($manifest[$tag]['mailFiles'] as $mailFile) { + @unlink(Yii::getAlias($this->module->panels['mail']->mailPath) . "/$mailFile"); + } + } + unset($manifest[$tag]); + if (--$n <= 0) { + break; + } + } + } + } + + + public function setModule(Module $module) + { + $this->module = $module; + } +} diff --git a/src/controllers/DefaultController.php b/src/controllers/DefaultController.php index 2ff46e83e..c13b123ad 100644 --- a/src/controllers/DefaultController.php +++ b/src/controllers/DefaultController.php @@ -1,25 +1,25 @@ - * @since 2.0 + * @since 2.0 */ class DefaultController extends Controller { @@ -36,11 +36,6 @@ class DefaultController extends Controller */ public $summary; - /** - * @var array - */ - private $_manifest; - /** * {@inheritdoc} @@ -57,7 +52,6 @@ public function actions() /** * {@inheritdoc} - * @throws \yii\web\BadRequestHttpException */ public function beforeAction($action) { @@ -65,24 +59,14 @@ public function beforeAction($action) return parent::beforeAction($action); } - /** - * Index action - * - * @return string - * @throws NotFoundHttpException - */ public function actionIndex() { $searchModel = new Debug(); - $dataProvider = $searchModel->search($_GET, $this->getManifest()); + $manifest = $this->module->getDataStorage()->getDataManifest(); + $dataProvider = $searchModel->search($_GET, $manifest); // load latest request - $tags = array_keys($this->getManifest()); - - if (empty($tags)) { - throw new \Exception("No debug data have been collected yet, try browsing the website first."); - } - + $tags = array_keys($manifest); $tag = reset($tags); $this->loadData($tag); @@ -90,21 +74,24 @@ public function actionIndex() 'panels' => $this->module->panels, 'dataProvider' => $dataProvider, 'searchModel' => $searchModel, - 'manifest' => $this->getManifest(), + 'manifest' => $manifest, ]); } /** - * @see \yii\debug\Panel - * @param string|null $tag debug data tag. + * @param string|null $tag debug data tag. * @param string|null $panel debug panel ID. + * * @return mixed response. * @throws NotFoundHttpException if debug data not found. + * @see \yii\debug\Panel */ public function actionView($tag = null, $panel = null) { + $manifest = $this->module->getDataStorage()->getDataManifest(); + if ($tag === null) { - $tags = array_keys($this->getManifest()); + $tags = array_keys($manifest); $tag = reset($tags); } $this->loadData($tag); @@ -121,19 +108,12 @@ public function actionView($tag = null, $panel = null) return $this->render('view', [ 'tag' => $tag, 'summary' => $this->summary, - 'manifest' => $this->getManifest(), + 'manifest' => $manifest, 'panels' => $this->module->panels, 'activePanel' => $activePanel, ]); } - /** - * Toolbar action - * - * @param string $tag - * @return string - * @throws NotFoundHttpException - */ public function actionToolbar($tag) { $this->loadData($tag, 5); @@ -142,17 +122,9 @@ public function actionToolbar($tag) 'tag' => $tag, 'panels' => $this->module->panels, 'position' => 'bottom', - 'defaultHeight' => $this->module->defaultHeight, ]); } - /** - * Download mail action - * - * @param string $file - * @return \yii\console\Response|Response - * @throws NotFoundHttpException - */ public function actionDownloadMail($file) { $filePath = Yii::getAlias($this->module->panels['mail']->mailPath) . '/' . basename($file); @@ -165,24 +137,9 @@ public function actionDownloadMail($file) } /** - * @param bool $forceReload - * @return array - */ - protected function getManifest($forceReload = false) - { - if ($this->_manifest === null || $forceReload) { - if ($forceReload) { - clearstatcache(); - } - $this->_manifest = $this->module->logTarget->loadManifest(); - } - - return $this->_manifest; - } - - /** - * @param string $tag debug data tag. - * @param int $maxRetry maximum numbers of tag retrieval attempts. + * @param string $tag debug data tag. + * @param int $maxRetry maximum numbers of tag retrieval attempts. + * * @throws NotFoundHttpException if specified tag not found. */ public function loadData($tag, $maxRetry = 0) @@ -191,9 +148,19 @@ public function loadData($tag, $maxRetry = 0) // which may be delayed in some environment if xdebug is enabled. // See: https://github.com/yiisoft/yii2/issues/1504 for ($retry = 0; $retry <= $maxRetry; ++$retry) { - $manifest = $this->getManifest($retry > 0); + $manifest = $this->module->getDataStorage()->getDataManifest($retry > 0); if (isset($manifest[$tag])) { - $data = $this->module->logTarget->loadTagToPanels($tag); + $data=$this->module->getDataStorage()->getData($tag); + $exceptions = $data['exceptions']; + foreach ($this->module->panels as $id => $panel) { + if (isset($data[$id])) { + $panel->tag = $tag; + $panel->load(unserialize($data[$id])); + } + if (isset($exceptions[$id])) { + $panel->setError($exceptions[$id]); + } + } $this->summary = $data['summary']; return; @@ -203,4 +170,4 @@ public function loadData($tag, $maxRetry = 0) throw new NotFoundHttpException("Unable to find debug data tagged with '$tag'."); } -} +} \ No newline at end of file From c95f6cc956e46af61d1249d813561116112f1c0e Mon Sep 17 00:00:00 2001 From: "bukharev.evgeny" Date: Thu, 23 Jun 2022 16:20:58 +0300 Subject: [PATCH 02/26] add data storage --- src/LogTarget.php | 143 ++------------- src/Module.php | 48 +++-- src/components/data/CacheDataStorage.php | 162 +++++++++++++++++ src/components/data/DataStorage.php | 37 ++++ src/components/data/FileDataStorage.php | 222 +++++++++++++++++++++++ src/controllers/DefaultController.php | 31 +--- 6 files changed, 477 insertions(+), 166 deletions(-) create mode 100644 src/components/data/CacheDataStorage.php create mode 100644 src/components/data/DataStorage.php create mode 100644 src/components/data/FileDataStorage.php diff --git a/src/LogTarget.php b/src/LogTarget.php index c213d59bd..440d02785 100644 --- a/src/LogTarget.php +++ b/src/LogTarget.php @@ -1,8 +1,8 @@ - * @since 2.0 + * @since 2.0 */ class LogTarget extends Target { @@ -32,7 +32,7 @@ class LogTarget extends Target /** * @param \yii\debug\Module $module - * @param array $config + * @param array $config */ public function __construct($module, $config = []) { @@ -44,17 +44,16 @@ public function __construct($module, $config = []) /** * Exports log messages to a specific destination. * Child classes must implement this method. + * * @throws \yii\base\Exception */ public function export() { - $path = $this->module->dataPath; - FileHelper::createDirectory($path, $this->module->dirMode); - $summary = $this->collectSummary(); - $dataFile = "$path/{$this->tag}.data"; + $data = []; $exceptions = []; + foreach ($this->module->panels as $id => $panel) { try { $panelData = $panel->save(); @@ -70,47 +69,25 @@ public function export() $data['summary'] = $summary; $data['exceptions'] = $exceptions; - file_put_contents($dataFile, serialize($data)); - if ($this->module->fileMode !== null) { - @chmod($dataFile, $this->module->fileMode); - } - - $indexFile = "$path/index.data"; - $this->updateIndexFile($indexFile, $summary); + $this->module->getDataStorage()->setData($this->tag, $data); } /** - * @see DefaultController * @return array + * @see DefaultController */ public function loadManifest() { - $indexFile = $this->module->dataPath . '/index.data'; - - $content = ''; - $fp = @fopen($indexFile, 'r'); - if ($fp !== false) { - @flock($fp, LOCK_SH); - $content = fread($fp, filesize($indexFile)); - @flock($fp, LOCK_UN); - fclose($fp); - } - - if ($content !== '') { - return array_reverse(unserialize($content), true); - } - - return []; + return $this->module->getDataStorage()->getDataManifest(); } /** - * @see DefaultController * @return array + * @see DefaultController */ public function loadTagToPanels($tag) { - $dataFile = $this->module->dataPath . "/$tag.data"; - $data = unserialize(file_get_contents($dataFile)); + $data = $this->module->getDataStorage()->getData($tag); $exceptions = $data['exceptions']; foreach ($this->module->panels as $id => $panel) { if (isset($data[$id])) { @@ -125,52 +102,16 @@ public function loadTagToPanels($tag) return $data; } - /** - * 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) - { - if (!@touch($indexFile) || ($fp = @fopen($indexFile, 'r+')) === false) { - throw new InvalidConfigException("Unable to open debug data index file: $indexFile"); - } - @flock($fp, LOCK_EX); - $manifest = ''; - while (($buffer = fgets($fp)) !== false) { - $manifest .= $buffer; - } - if (!feof($fp) || empty($manifest)) { - // error while reading index data, ignore and create new - $manifest = []; - } else { - $manifest = unserialize($manifest); - } - - $manifest[$this->tag] = $summary; - $this->gc($manifest); - - ftruncate($fp, 0); - rewind($fp); - fwrite($fp, serialize($manifest)); - - @flock($fp, LOCK_UN); - @fclose($fp); - - if ($this->module->fileMode !== null) { - @chmod($indexFile, $this->module->fileMode); - } - } /** * Processes the given log messages. * This method will filter the given messages with [[levels]] and [[categories]]. * And if requested, it will also export the filtering result to specific medium (e.g. email). + * * @param array $messages log messages to be processed. See [[\yii\log\Logger::messages]] for the structure - * of each message. - * @param bool $final whether this method is called at the end of the current application + * of each message. + * @param bool $final whether this method is called at the end of the current application + * * @throws \yii\base\Exception */ public function collect($messages, $final) @@ -181,56 +122,9 @@ public function collect($messages, $final) } } - /** - * Removes obsolete data files - * @param array $manifest - */ - protected function gc(&$manifest) - { - if (count($manifest) > $this->module->historySize + 10) { - $n = count($manifest) - $this->module->historySize; - foreach (array_keys($manifest) as $tag) { - $file = $this->module->dataPath . "/$tag.data"; - @unlink($file); - if (isset($manifest[$tag]['mailFiles'])) { - foreach ($manifest[$tag]['mailFiles'] as $mailFile) { - @unlink(Yii::getAlias($this->module->panels['mail']->mailPath) . "/$mailFile"); - } - } - unset($manifest[$tag]); - if (--$n <= 0) { - break; - } - } - $this->removeStaleDataFiles($manifest); - } - } - - /** - * Remove staled data files i.e. files that are not in the current index file - * (may happen because of corrupted or rotated index file) - * - * @param array $manifest - * @since 2.0.11 - */ - protected function removeStaleDataFiles($manifest) - { - $storageTags = array_map( - function ($file) { - return pathinfo($file, PATHINFO_FILENAME); - }, - FileHelper::findFiles($this->module->dataPath, ['except' => ['index.data']]) - ); - - $staledTags = array_diff($storageTags, array_keys($manifest)); - - foreach ($staledTags as $tag) { - @unlink($this->module->dataPath . "/$tag.data"); - } - } - /** * Collects summary data of current request. + * * @return array */ protected function collectSummary() @@ -244,7 +138,7 @@ protected function collectSummary() $summary = [ 'tag' => $this->tag, 'url' => $request->getAbsoluteUrl(), - 'ajax' => (int) $request->getIsAjax(), + 'ajax' => (int)$request->getIsAjax(), 'method' => $request->getMethod(), 'ip' => $request->getUserIP(), 'time' => $_SERVER['REQUEST_TIME_FLOAT'], @@ -264,6 +158,7 @@ protected function collectSummary() /** * Returns total sql count executed in current request. If database panel is not configured * returns 0. + * * @return int */ protected function getSqlTotalCount() diff --git a/src/Module.php b/src/Module.php index e9728978d..0b2e3a2d2 100644 --- a/src/Module.php +++ b/src/Module.php @@ -10,11 +10,14 @@ use Yii; use yii\base\Application; use yii\base\BootstrapInterface; +use yii\debug\components\data\DataStorage; +use yii\debug\components\data\FileDataStorage; use yii\helpers\Html; use yii\helpers\IpHelper; use yii\helpers\Json; use yii\helpers\Url; use yii\web\ForbiddenHttpException; +use yii\base\InvalidConfigException; use yii\web\Response; use yii\web\View; @@ -76,30 +79,19 @@ class Module extends \yii\base\Module implements BootstrapInterface * @since 2.0.7 */ public $defaultPanel = 'log'; + /** - * @var string the directory storing the debugger data files. This can be specified using a path alias. - */ - public $dataPath = '@runtime/debug'; - /** - * @var int the permission to be set for newly created debugger data files. - * This value will be used by PHP [[chmod()]] function. No umask will be applied. - * If not set, the permission will be determined by the current environment. - * @since 2.0.6 - */ - public $fileMode; - /** - * @var int the permission to be set for newly created directories. - * This value will be used by PHP [[chmod()]] function. No umask will be applied. - * Defaults to 0775, meaning the directory is read-writable by owner and group, - * but read-only for other users. - * @since 2.0.6 + * @var DataStorage */ - public $dirMode = 0775; + private $dataStorage; + /** - * @var int the maximum number of debug data files to keep. If there are more files generated, - * the oldest ones will be removed. + * @var string[] */ - public $historySize = 50; + public $dataStorageConfig = [ + 'class' => FileDataStorage::class, + ]; + /** * @var int the debug bar default height, as a percentage of the total screen height * @since 2.1.1 @@ -225,6 +217,14 @@ public function init() if (Yii::$app instanceof \yii\web\Application) { $this->initPanels(); } + + $dataStorage = Yii::createObject($this->dataStorageConfig + ['module' => $this]); + + if (!$dataStorage instanceof DataStorage) { + throw new InvalidConfigException(); + } + + $this->dataStorage = $dataStorage; } /** @@ -503,4 +503,12 @@ public function htmlTitle() return 'Yii Debugger'; } + + /** + * @return DataStorage + */ + public function getDataStorage() + { + return $this->dataStorage; + } } diff --git a/src/components/data/CacheDataStorage.php b/src/components/data/CacheDataStorage.php new file mode 100644 index 000000000..595881ee1 --- /dev/null +++ b/src/components/data/CacheDataStorage.php @@ -0,0 +1,162 @@ +cache = \Yii::$app->get($this->cacheComponent); + } + + /** + * @param string $tag + * + * @return array + */ + public function getData($tag) + { + $data = $this->cache->get($this->cacheDebugDataKey . $tag); + if (empty($data)) { + return []; + } else { + return unserialize($data); + } + } + + /** + * @param string $tag + * @param array $data + * + * @return mixed|void + */ + public function setData($tag, $data) + { + $this->cache->set($this->cacheDebugDataKey . $tag, serialize($data), $this->dataDuration); + $this->updateIndex($tag, $data['summary'] ?: []); + } + + /** + * @param $forceReload + * + * @return array|mixed + */ + public function getDataManifest($forceReload = false) + { + $manifest = $this->cache->get($this->cacheDebugManifestKey); + if (empty($manifest)) { + return []; + } else { + return unserialize($manifest); + } + } + + /** + * @param Module $module + * + * @return mixed|void + */ + public function setModule(Module $module) + { + $this->module = $module; + } + + /** + * @param string $tag + * @param $summary + * + * @return void + */ + private function updateIndex($tag, $summary) + { + $manifest = $this->cache->get($this->cacheDebugManifestKey); + + if (empty($manifest)) { + $manifest = []; + } else { + $manifest = unserialize($manifest); + } + + $manifest[$tag] = $summary; + $this->gc($manifest); + + $this->cache->set($this->cacheDebugManifestKey, serialize($manifest), $this->manifestDuration); + } + + + /** + * Removes obsolete data files + * + * @param array $manifest + */ + protected function gc(&$manifest) + { + if (count($manifest) > $this->historySize + 10) { + $n = count($manifest) - $this->historySize; + foreach (array_keys($manifest) as $tag) { + if (isset($manifest[$tag]['mailFiles'])) { + foreach ($manifest[$tag]['mailFiles'] as $mailFile) { + @unlink(Yii::getAlias($this->module->panels['mail']->mailPath) . "/$mailFile"); + } + } + unset($manifest[$tag]); + if (--$n <= 0) { + break; + } + } + } + } + +} diff --git a/src/components/data/DataStorage.php b/src/components/data/DataStorage.php new file mode 100644 index 000000000..a931d01ef --- /dev/null +++ b/src/components/data/DataStorage.php @@ -0,0 +1,37 @@ +dataPath = Yii::getAlias($this->dataPath); + } + + /** + * @param $tag + * + * @return array + */ + public function getData($tag) + { + $dataFile = $this->dataPath . "/$tag.data"; + return unserialize(file_get_contents($dataFile)); + } + + /** + * @param string $tag + * @param array $data + * + * @return void + * @throws InvalidConfigException + * @throws \yii\base\Exception + */ + public function setData($tag, $data) + { + $path = $this->dataPath; + FileHelper::createDirectory($path, $this->dirMode); + $dataFile = "$path/{$tag}.data"; + + file_put_contents($dataFile, serialize($data)); + if ($this->fileMode !== null) { + @chmod($dataFile, $this->fileMode); + } + + $indexFile = "$path/index.data"; + $this->updateIndexFile($indexFile, $tag, $data['summary'] ?: []); + } + + /** + * @param $forceReload + * + * @return array|mixed + */ + public function getDataManifest($forceReload = false) + { + if ($this->_manifest === null || $forceReload) { + if ($forceReload) { + clearstatcache(); + } + $indexFile = $this->dataPath . '/index.data'; + + $content = ''; + $fp = @fopen($indexFile, 'r'); + if ($fp !== false) { + @flock($fp, LOCK_SH); + $content = fread($fp, filesize($indexFile)); + @flock($fp, LOCK_UN); + fclose($fp); + } + + if ($content !== '') { + $this->_manifest = array_reverse(unserialize($content), true); + } else { + $this->_manifest = []; + } + } + + return $this->_manifest; + } + + + /** + * 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, $tag, $summary) + { + touch($indexFile); + if (($fp = @fopen($indexFile, 'r+')) === false) { + throw new InvalidConfigException("Unable to open debug data index file: $indexFile"); + } + @flock($fp, LOCK_EX); + $manifest = ''; + while (($buffer = fgets($fp)) !== false) { + $manifest .= $buffer; + } + if (!feof($fp) || empty($manifest)) { + // error while reading index data, ignore and create new + $manifest = []; + } else { + $manifest = unserialize($manifest); + } + + $manifest[$tag] = $summary; + $this->gc($manifest); + + ftruncate($fp, 0); + rewind($fp); + fwrite($fp, serialize($manifest)); + + @flock($fp, LOCK_UN); + @fclose($fp); + + if ($this->fileMode !== null) { + @chmod($indexFile, $this->fileMode); + } + } + + /** + * Removes obsolete data files + * @param array $manifest + */ + protected function gc(&$manifest) + { + if (count($manifest) > $this->historySize + 10) { + $n = count($manifest) - $this->historySize; + foreach (array_keys($manifest) as $tag) { + $file = $this->dataPath . "/$tag.data"; + @unlink($file); + if (isset($manifest[$tag]['mailFiles'])) { + foreach ($manifest[$tag]['mailFiles'] as $mailFile) { + @unlink(Yii::getAlias($this->module->panels['mail']->mailPath) . "/$mailFile"); + } + } + unset($manifest[$tag]); + if (--$n <= 0) { + break; + } + } + $this->removeStaleDataFiles($manifest); + } + } + + /** + * Remove staled data files i.e. files that are not in the current index file + * (may happen because of corrupted or rotated index file) + * + * @param array $manifest + * @since 2.0.11 + */ + protected function removeStaleDataFiles($manifest) + { + $storageTags = array_map( + function ($file) { + return pathinfo($file, PATHINFO_FILENAME); + }, + FileHelper::findFiles($this->dataPath, ['except' => ['index.data']]) + ); + + $staledTags = array_diff($storageTags, array_keys($manifest)); + + foreach ($staledTags as $tag) { + @unlink($this->dataPath . "/$tag.data"); + } + } + + + public function setModule(Module $module) + { + $this->module = $module; + } +} diff --git a/src/controllers/DefaultController.php b/src/controllers/DefaultController.php index 2ff46e83e..24a66aead 100644 --- a/src/controllers/DefaultController.php +++ b/src/controllers/DefaultController.php @@ -73,11 +73,12 @@ public function beforeAction($action) */ public function actionIndex() { + $manifest = $this->module->getDataStorage()->getDataManifest(); $searchModel = new Debug(); - $dataProvider = $searchModel->search($_GET, $this->getManifest()); + $dataProvider = $searchModel->search($_GET, $manifest); // load latest request - $tags = array_keys($this->getManifest()); + $tags = array_keys($manifest); if (empty($tags)) { throw new \Exception("No debug data have been collected yet, try browsing the website first."); @@ -90,7 +91,7 @@ public function actionIndex() 'panels' => $this->module->panels, 'dataProvider' => $dataProvider, 'searchModel' => $searchModel, - 'manifest' => $this->getManifest(), + 'manifest' => $manifest, ]); } @@ -103,8 +104,10 @@ public function actionIndex() */ public function actionView($tag = null, $panel = null) { + $manifest = $this->module->getDataStorage()->getDataManifest(); + if ($tag === null) { - $tags = array_keys($this->getManifest()); + $tags = array_keys($manifest); $tag = reset($tags); } $this->loadData($tag); @@ -121,7 +124,7 @@ public function actionView($tag = null, $panel = null) return $this->render('view', [ 'tag' => $tag, 'summary' => $this->summary, - 'manifest' => $this->getManifest(), + 'manifest' =>$manifest, 'panels' => $this->module->panels, 'activePanel' => $activePanel, ]); @@ -164,22 +167,6 @@ public function actionDownloadMail($file) return Yii::$app->response->sendFile($filePath); } - /** - * @param bool $forceReload - * @return array - */ - protected function getManifest($forceReload = false) - { - if ($this->_manifest === null || $forceReload) { - if ($forceReload) { - clearstatcache(); - } - $this->_manifest = $this->module->logTarget->loadManifest(); - } - - return $this->_manifest; - } - /** * @param string $tag debug data tag. * @param int $maxRetry maximum numbers of tag retrieval attempts. @@ -191,7 +178,7 @@ public function loadData($tag, $maxRetry = 0) // which may be delayed in some environment if xdebug is enabled. // See: https://github.com/yiisoft/yii2/issues/1504 for ($retry = 0; $retry <= $maxRetry; ++$retry) { - $manifest = $this->getManifest($retry > 0); + $manifest = $this->module->getDataStorage()->getDataManifest($retry > 0); if (isset($manifest[$tag])) { $data = $this->module->logTarget->loadTagToPanels($tag); $this->summary = $data['summary']; From d7a8ed180ef3d8893c47064a91805a9ea1a0946b Mon Sep 17 00:00:00 2001 From: "bukharev.evgeny" Date: Thu, 23 Jun 2022 16:31:53 +0300 Subject: [PATCH 03/26] add data storage --- src/Module.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Module.php b/src/Module.php index 0b2e3a2d2..e68924230 100644 --- a/src/Module.php +++ b/src/Module.php @@ -212,7 +212,6 @@ public static function setYiiLogo($logo) public function init() { parent::init(); - $this->dataPath = Yii::getAlias($this->dataPath); if (Yii::$app instanceof \yii\web\Application) { $this->initPanels(); From 593157b4d9c443093a5cf874d62bef4c5f034de2 Mon Sep 17 00:00:00 2001 From: "bukharev.evgeny" Date: Thu, 23 Jun 2022 16:38:31 +0300 Subject: [PATCH 04/26] add data storage --- src/Module.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Module.php b/src/Module.php index e68924230..788ddf8a7 100644 --- a/src/Module.php +++ b/src/Module.php @@ -89,7 +89,7 @@ class Module extends \yii\base\Module implements BootstrapInterface * @var string[] */ public $dataStorageConfig = [ - 'class' => FileDataStorage::class, + 'class' => 'yii\debug\components\data\FileDataStorage', ]; /** From e8a6dfb8cf893ff3cf6363b1a5d8e567b4d41750 Mon Sep 17 00:00:00 2001 From: "bukharev.evgeny" Date: Tue, 5 Jul 2022 14:07:02 +0300 Subject: [PATCH 05/26] codestyle fix --- src/LogTarget.php | 23 ++++++++++------------- src/Module.php | 2 +- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/LogTarget.php b/src/LogTarget.php index 440d02785..28f7ba12f 100644 --- a/src/LogTarget.php +++ b/src/LogTarget.php @@ -1,8 +1,8 @@ - * @since 2.0 + * @since 2.0 */ class LogTarget extends Target { @@ -32,7 +32,7 @@ class LogTarget extends Target /** * @param \yii\debug\Module $module - * @param array $config + * @param array $config */ public function __construct($module, $config = []) { @@ -44,16 +44,17 @@ public function __construct($module, $config = []) /** * Exports log messages to a specific destination. * Child classes must implement this method. - * * @throws \yii\base\Exception */ public function export() { + $path = $this->module->dataPath; + FileHelper::createDirectory($path, $this->module->dirMode); + $summary = $this->collectSummary(); $data = []; $exceptions = []; - foreach ($this->module->panels as $id => $panel) { try { $panelData = $panel->save(); @@ -107,11 +108,9 @@ public function loadTagToPanels($tag) * Processes the given log messages. * This method will filter the given messages with [[levels]] and [[categories]]. * And if requested, it will also export the filtering result to specific medium (e.g. email). - * * @param array $messages log messages to be processed. See [[\yii\log\Logger::messages]] for the structure - * of each message. - * @param bool $final whether this method is called at the end of the current application - * + * of each message. + * @param bool $final whether this method is called at the end of the current application * @throws \yii\base\Exception */ public function collect($messages, $final) @@ -124,7 +123,6 @@ public function collect($messages, $final) /** * Collects summary data of current request. - * * @return array */ protected function collectSummary() @@ -138,7 +136,7 @@ protected function collectSummary() $summary = [ 'tag' => $this->tag, 'url' => $request->getAbsoluteUrl(), - 'ajax' => (int)$request->getIsAjax(), + 'ajax' => (int) $request->getIsAjax(), 'method' => $request->getMethod(), 'ip' => $request->getUserIP(), 'time' => $_SERVER['REQUEST_TIME_FLOAT'], @@ -158,7 +156,6 @@ protected function collectSummary() /** * Returns total sql count executed in current request. If database panel is not configured * returns 0. - * * @return int */ protected function getSqlTotalCount() diff --git a/src/Module.php b/src/Module.php index 788ddf8a7..811c165ce 100644 --- a/src/Module.php +++ b/src/Module.php @@ -63,7 +63,7 @@ class Module extends \yii\base\Module implements BootstrapInterface */ public $controllerNamespace = 'yii\debug\controllers'; /** - * @var LogTarget + * @var LogTarget|array|string the logTarget object, or the configuration for creating the logTarget object. */ public $logTarget = 'yii\debug\LogTarget'; /** From f1480b0d0529470ebdc6f37e29c93fd6e9c519fa Mon Sep 17 00:00:00 2001 From: "bukharev.evgeny" Date: Tue, 5 Jul 2022 14:24:30 +0300 Subject: [PATCH 06/26] restore --- src/LogTarget.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/LogTarget.php b/src/LogTarget.php index 28f7ba12f..c3d2eaa99 100644 --- a/src/LogTarget.php +++ b/src/LogTarget.php @@ -48,9 +48,6 @@ public function __construct($module, $config = []) */ public function export() { - $path = $this->module->dataPath; - FileHelper::createDirectory($path, $this->module->dirMode); - $summary = $this->collectSummary(); $data = []; From 8c2d36025bfbc9da550402a36373697b590585a7 Mon Sep 17 00:00:00 2001 From: "bukharev.evgeny" Date: Fri, 27 Jan 2023 14:47:50 +0300 Subject: [PATCH 07/26] fix --- src/Module.php | 216 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 174 insertions(+), 42 deletions(-) diff --git a/src/Module.php b/src/Module.php index 74c774df0..b0d2ee5db 100644 --- a/src/Module.php +++ b/src/Module.php @@ -32,10 +32,12 @@ class Module extends \yii\base\Module implements BootstrapInterface /** * @var array the list of IPs that are allowed to access this module. - * Each array element represents a single IP filter which can be either an IP address - * or an address with wildcard (e.g. 192.168.0.*) to represent a network segment. - * The default value is `['127.0.0.1', '::1']`, which means the module can only be accessed - * by localhost. + * Each array element represents a single IP filter which can be either: + * - an IP address (e.g. 1.2.3.4), + * - an address with wildcard (e.g. 192.168.0.*) to represent a network segment + * - a CIDR range (e.g. 172.16.0.0/12) (available since version 2.1.18). + * The default value is `['127.0.0.1', '::1']`, which means the module can only be accessed + * by localhost. */ public $allowedIPs = ['127.0.0.1', '::1']; /** @@ -45,14 +47,24 @@ class Module extends \yii\base\Module implements BootstrapInterface * The default value is `[]`. */ public $allowedHosts = []; + /** + * @var callable A valid PHP callback that returns true if user is allowed to use web shell and false otherwise + * + * The signature is the following: + * + * function (Action|null $action) The action can be null when called from a non action context (like set debug header) + * + * @since 2.1.0 + */ + public $checkAccessCallback; /** * {@inheritdoc} */ public $controllerNamespace = 'yii\debug\controllers'; /** - * @var LogTarget + * @var LogTarget|array|string the logTarget object, or the configuration for creating the logTarget object. */ - public $logTarget; + public $logTarget = 'yii\debug\LogTarget'; /** * @var array|Panel[] list of debug panels. The array keys are the panel IDs, and values are the corresponding * panel class names or configuration arrays. This will be merged with [[corePanels()]]. @@ -88,7 +100,11 @@ class Module extends \yii\base\Module implements BootstrapInterface * @since 2.0.14 */ public $disableIpRestrictionWarning = false; - + /** + * @var bool whether to disable access callback restriction warning triggered by checkAccess function + * @since 2.1.0 + */ + public $disableCallbackRestrictionWarning = false; /** * @var mixed the string with placeholders to be be substituted or an anonymous function that returns the trace line string. * The placeholders are {file}, {line} and {text} and the string should be as follows: @@ -106,13 +122,61 @@ class Module extends \yii\base\Module implements BootstrapInterface * @since 2.0.7 */ public $traceLine = self::DEFAULT_IDE_TRACELINE; - + /** + * @var array used when the virtual, containerized, or remote debug trace paths don't correspond to the developers + * local paths. Acts on the {file} portion for the `$traceLine` property. + * + * The array key is the environment's path, while the value is the local desired path. + * + * It will only map the first matched matched key. + * + * Example: + * + * ```php + * [ + * '/app' => '/home/user/project/app', + * ] + * ``` + * + * Note that this will not change the displayed text, only the link url. + * + * @since 2.1.6 + */ + public $tracePathMappings = []; + /** + * @var string The [[UrlRule]] class to use for rules generated by this module. + * @since 2.1.1 + */ + public $urlRuleClass = 'yii\web\UrlRule'; + /** + * @var string|callable Page title could be a string or a callable function + * + * ```php + * ... + * 'pageTitle' => 'Custom Debug Title', + * ... + * // OR + * 'pageTitle' => function($url) { + * $domain = getDomain($url); + * return $domain . ' debugger'; + * } + * ``` + * + * @since 2.1.1 + */ + public $pageTitle; /** * @var string Yii logo URL */ private static $_yiiLogo = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAMAAAANIilAAAAC7lBMVEUAAACl034Cb7HlcjGRyT/H34fyy5PxqlSfzjwQeb5PmtX71HAMdrWOxkDzmU3qcDSPx0HzhUGNxT+/2lX2olDmUy/Q1l+TyD7rgjq21k3ZRzDQ4GGFw0Ghzz6MwOkKdrTA2lTzzMVjo9mhzkCIxUPk1MLynU7qWS33vmbP1rm011Fwqsj123/r44tUltTyq1aCxEOo0EL1tFuCw0Npp9v7xGVHkM8Ddrza0pvC3FboczHmXSvE21h+wkRkpNHvjkS92FPW3avpeDT2t1zX5GefzUD6wGQReLtMltPN417oczPZ0L+62FF+tuJgqtXZUzNzrN3s4Y7n65y72FLwmk7xjESr0kYof8MQe8DY5Gc6jMnN32DoaDLbTiLulUo1hsni45vuwnIigMXC21dqq8vKzaaBt+XU4mUMd7wDdr7xlUrU4a7A2VTD0LbVx5vvpFP/0m9godp/tuTD0LVyrsfZVDUuhMjkPChsrMt3suK92VDd52oEc7un0EKjzj7D21e01EuSyD2fzDvH3Fqu0kcDdL641k+x00rmXy0EdLiayzzynU2XyTzxmUur0ETshD7lZDDvkUbtiUDrgTvqfjrkWS292FPujEKAuObQ4GH3vWH1slr0r1j0pVLulEiPxj7oeDRnptn4zWrM31/1t13A2lb1rFb1qVS72FKHw0CLxD/qdTfnazL4wGPJ3VzwpFLpcjKFveljo9dfn9ZbntUYfcEIdr35w2XyoFH0ok/pfDZ9tONUmNRPltJIj89Ais388IL85Hn82nL80W33uV72tFy611DxlUnujkSCwkGlz0DqeTnocDJ3r99yrN1Xm9RFjc42hsorgsYhgMQPer/81XD5yGbT4mTriD/lbS3laCvjTiluqN5NktAxhMf853v84He/2VTgVCnmVSg8h8sHcrf6633+3nb8zGr2xmR/wEGcyzt3r+T/6n7tm01tqNnfSCnfPyO4zLmFwkDVRDGOweLP1aX55nrZTTOaxdjuY9uiAAAAfHRSTlMABv7+9hAJ/vMyGP2CbV5DOA+NbyYeG/DV0sC/ubaonYN5blZRQT41MSUk/v797+zj49PR0MXEw8PDu6imppqYlpOGhYN+bldWVFJROjAM+fPy8fDw8O7t6+vp5+Lh4N7e3Nvb2NPQ0MW8urm2rqiimJKFg3t5amZTT0k1ewExHwAABPVJREFUSMed1Xc81HEYB/DvhaOUEe29995777333ntv2sopUTQ4F104hRBSl8ohldCwOqfuuEiKaPdfz/P7/u6Syuu+ff727vM8z+8bhDHNB3TrXI38V6p1fvSosLBwgICd1qx/5cqVT8jrl9c1Wlm2qmFdgbWq5X316lXKq5dxu+ouyNWePevo6JjVd6il9T/soUPe3t48tyI0LeqWlpbk5oJ1dXVVKpNCH/e1/NO2rXXy5CEI5Y+6EZomn0tLSlS50OuaFZQUGuojl7vXtii/VQMnp5MQPW/+C6tUXDFnfeTubm4utVv+fud3EPTIUdfXYZVKpQULxTp75sz5h4PK7C4wO8zFCT1XbkxHG/cdZuaLqXV5Afb0xYW2etxsPxfg73htbEUPBhgXDgoKCg30kbu58Pai8/SW+o3t7e0TExPBYzuObkyXFk7SAnYFnBQYyPeePn3R2fnEiZsWPO5y6pQ9JpHXgPlHWlcLxWiTAh/LqX3wAOlNiYTXRzGn8F9I5LUx/052aLWOWVnwgQMfu7u7UQu9t26FhISYcpObHMdwHstxcR2uAc1ZSlgYsJsL7kutRCKT+XeyxWMfxHAeykE7OQGm6ecIOInaF3grmPkEWn8vL3FXIfxEnWMY8FTD5GYjeNwK3pbSCDEsTC30ysCK79/3HQY/MTggICABOZRTbYYHo9WuSiMjvhi/EWf90frGe3q2JmR8Ts65cwEJCVAOGgc3a6bD1vOVRj5wLVwY7U2dvR/vGRy1BB7TsgMH/HKAQzfVZlZEF0sjwHgtLC7GbySjvWCjojYS0vjIEcpBH8WTmwmIPmON4GEChksXF8MnotYX7NuMDGkb0vbaEeQ50E11A1R67SOnUzsjlsjgzvHx8cFRQKUFvQmpd/kaaD+sPoiYrqyfvDY39QPYOMTU1F8shn09g98WSOPi4szbEBuPy8BRY7V9l3L/34VDy2AvsdgXLfTGmZun9yY1PTw8Ll+DwenWI0j52A6awWGJzNQLj0VtenpsbHshWZXpQasTYO6ZJuTPCC3WQjFeix5LKpWap8dqNJohZHgmaA5DtQ35e6wtNnXS4wwojn2jUSimkH2ZtBpxnYp+67ce1pX7xBkF1KrV+S3IHIrxYuNJxbEd2SM4qoDDim/5+THrSD09bmzIn5eRPTiMNmYqLM2PDUMblNabzaE5PwbSZowHPdi0tsTQmKxor1EXFcXEDKnJf6q9xOBMCPvyVQG6aDGZhw80x8ZwK1h5ISzsRwe1Wt2B1MPHPZgYnqa3b1+4gOUKhUl/sP0Z7ITJycmowz5q3oxrfMBvvYBh6O7ZKcnvqY7dZuPXR8hQvOXSJdQc/7hhTB8TBjs6Ivz6pezsbKobmggYbJWOT1ADT8HFGxKW9LwTjRp4CujbTHj007t37kRHhGP5h5Tk5K0MduLce0/vvoyOjoiIuH4ddMoeBrzz2WvUMDrMDvpDFQa89Pkr4KCBo+7OYEdFpqLGcqqbMuDVaZGpqc/1OjycYerKohtpkZFl9ECG4qoihxvA9aN3ZDlXL5GDXR7Vr56BZtlYcAOwnQMdHXRPlmdd2U5kh5gffRHL0GSUXR5gKBeJ0tIiZ1UmLKlqlydygHD1s8EyYYe8PBFMjulVhbClEdy6kohLVTaJGEYW4eBr6MhsY1fi0ggoe7a3a7d84O6J5L8iNOiX3U+uoa/p8UPtoQAAAABJRU5ErkJggg=='; + /** + * @var array routes of AJAX requests to skip from being displayed in toolbar + * @since 2.1.14 + */ + public $skipAjaxRequestUrl = []; + /** * Returns the logo URL to be used in `'; + + if (!empty($this->skipAjaxRequestUrl)) { + foreach ($this->skipAjaxRequestUrl as $key => $route) { + $this->skipAjaxRequestUrl[$key] = Url::to($route); + } + } + return ''; } /** * Renders mini-toolbar at the end of page body. * * @param \yii\base\Event $event + * @throws \Throwable */ public function renderToolbar($event) { @@ -303,36 +389,66 @@ public function renderToolbar($event) /* @var $view View */ $view = $event->sender; - echo $view->renderDynamic('return Yii::$app->getModule("' . $this->id . '")->getToolbarHtml();'); + echo $view->renderDynamic('return Yii::$app->getModule("' . $this->getUniqueId() . '")->getToolbarHtml();'); // echo is used in order to support cases where asset manager is not available - echo ''; - echo ''; + echo ''; + echo ''; } /** * Checks if current user is allowed to access the module - * + * @param \yii\base\Action|null $action the action to be executed. May be `null` when called from + * a non action context * @return bool if access is granted */ - protected function checkAccess() + protected function checkAccess($action = null) { + $allowed = false; + $ip = Yii::$app->getRequest()->getUserIP(); foreach ($this->allowedIPs as $filter) { - if ($filter === '*' || $filter === $ip || (($pos = strpos($filter, '*')) !== false && !strncmp($ip, $filter, $pos))) { - return true; + if ($filter === '*' + || $filter === $ip + || ( + ($pos = strpos($filter, '*')) !== false + && !strncmp($ip, $filter, $pos) + ) + || ( + strpos($filter, '/') !== false + && IpHelper::inRange($ip, $filter) + ) + ) { + $allowed = true; + break; } } - foreach ($this->allowedHosts as $hostname) { - $filter = gethostbyname($hostname); - if ($filter === $ip) { - return true; + if ($allowed === false) { + foreach ($this->allowedHosts as $hostname) { + $filter = gethostbyname($hostname); + if ($filter === $ip) { + $allowed = true; + break; + } } } - if (!$this->disableIpRestrictionWarning) { - Yii::warning('Access to debugger is denied due to IP address restriction. The requesting IP address is ' . $ip, __METHOD__); + if ($allowed === false) { + if (!$this->disableIpRestrictionWarning) { + Yii::warning('Access to debugger is denied due to IP address restriction. The requesting IP address is ' . $ip, __METHOD__); + } + + return false; } - return false; + + if ($this->checkAccessCallback !== null && call_user_func($this->checkAccessCallback, $action) !== true) { + if (!$this->disableCallbackRestrictionWarning) { + Yii::warning('Access to debugger is denied due to checkAccessCallback.', __METHOD__); + } + + return false; + } + + return true; } /** @@ -342,7 +458,6 @@ protected function corePanels() { $corePanels = [ 'config' => ['class' => 'yii\debug\panels\ConfigPanel'], - 'request' => ['class' => 'yii\debug\panels\RequestPanel'], 'log' => ['class' => 'yii\debug\panels\LogPanel'], 'profiling' => ['class' => 'yii\debug\panels\ProfilingPanel'], 'db' => ['class' => 'yii\debug\panels\DbPanel'], @@ -384,4 +499,21 @@ protected function defaultVersion() } return parent::defaultVersion(); } + + /** + * @return string page title to be used in HTML + * @since 2.1.1 + */ + public function htmlTitle() + { + if (is_string($this->pageTitle) && !empty($this->pageTitle)) { + return $this->pageTitle; + } + + if (is_callable($this->pageTitle)) { + return call_user_func($this->pageTitle, Url::base(true)); + } + + return 'Yii Debugger'; + } } From f46438726243e0a54c5a236f370c582b268381db Mon Sep 17 00:00:00 2001 From: "bukharev.evgeny" Date: Fri, 27 Jan 2023 14:51:32 +0300 Subject: [PATCH 08/26] fix --- src/Module.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Module.php b/src/Module.php index b0d2ee5db..8dd860dd3 100644 --- a/src/Module.php +++ b/src/Module.php @@ -88,6 +88,11 @@ class Module extends \yii\base\Module implements BootstrapInterface 'class' => FileDataStorage::class, ]; + /** + * @var int the debug bar default height, as a percentage of the total screen height + * @since 2.1.1 + */ + public $defaultHeight = 50; /** * @var bool whether to enable message logging for the requests about debug module actions. * You normally do not want to keep these logs because they may distract you from the logs about your applications. From 4af6b3b78d56d66dfc3f561642e4f98e5d384881 Mon Sep 17 00:00:00 2001 From: "bukharev.evgeny" Date: Fri, 27 Jan 2023 14:55:49 +0300 Subject: [PATCH 09/26] fix --- src/controllers/DefaultController.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/controllers/DefaultController.php b/src/controllers/DefaultController.php index e0d5af862..5a6a1f140 100644 --- a/src/controllers/DefaultController.php +++ b/src/controllers/DefaultController.php @@ -122,9 +122,17 @@ public function actionToolbar($tag) 'tag' => $tag, 'panels' => $this->module->panels, 'position' => 'bottom', + 'defaultHeight' => $this->module->defaultHeight, ]); } + /** + * Download mail action + * + * @param string $file + * @return \yii\console\Response|Response + * @throws NotFoundHttpException + */ public function actionDownloadMail($file) { $filePath = Yii::getAlias($this->module->panels['mail']->mailPath) . '/' . basename($file); From abbf63b9b89f559c0c0d8d8a40fa03924370c819 Mon Sep 17 00:00:00 2001 From: "bukharev.evgeny" Date: Fri, 27 Jan 2023 15:14:05 +0300 Subject: [PATCH 10/26] fix --- src/controllers/DefaultController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/controllers/DefaultController.php b/src/controllers/DefaultController.php index 5a6a1f140..8daa449f4 100644 --- a/src/controllers/DefaultController.php +++ b/src/controllers/DefaultController.php @@ -16,6 +16,7 @@ /** * Debugger controller provides browsing over available debug logs. * + * * @see \yii\debug\Panel * * @author Qiang Xue From 55164652487d80b738cfefcc90e8c05f09378a5e Mon Sep 17 00:00:00 2001 From: "bukharev.evgeny" Date: Fri, 27 Jan 2023 15:36:30 +0300 Subject: [PATCH 11/26] add yii\helpers\IpHelper --- src/Module.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Module.php b/src/Module.php index 8dd860dd3..4c03c5588 100644 --- a/src/Module.php +++ b/src/Module.php @@ -14,6 +14,7 @@ use yii\debug\components\data\DataStorage; use yii\debug\components\data\FileDataStorage; use yii\helpers\Json; +use yii\helpers\IpHelper; use yii\web\Response; use yii\helpers\Html; use yii\helpers\Url; From ae8afbf1a204fee31299b48e64be3fab7b7e98ae Mon Sep 17 00:00:00 2001 From: "bukharev.evgeny" Date: Fri, 27 Jan 2023 15:38:14 +0300 Subject: [PATCH 12/26] fix syntax error, unexpected 'class' (T_CLASS), expecting identifier (T_STRING) --- src/Module.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Module.php b/src/Module.php index 4c03c5588..77d28764b 100644 --- a/src/Module.php +++ b/src/Module.php @@ -86,7 +86,7 @@ class Module extends \yii\base\Module implements BootstrapInterface private $dataStorage; public $dataStorageConfig = [ - 'class' => FileDataStorage::class, + 'class' => 'yii\debug\components\dataFileDataStorage', ]; /** From 7ecfedc15709dbaae1c60215c793369f960e4e9a Mon Sep 17 00:00:00 2001 From: "bukharev.evgeny" Date: Fri, 27 Jan 2023 15:40:04 +0300 Subject: [PATCH 13/26] fix syntax error, unexpected 'class' (T_CLASS), expecting identifier (T_STRING) --- src/Module.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Module.php b/src/Module.php index 77d28764b..7ff24c520 100644 --- a/src/Module.php +++ b/src/Module.php @@ -86,7 +86,7 @@ class Module extends \yii\base\Module implements BootstrapInterface private $dataStorage; public $dataStorageConfig = [ - 'class' => 'yii\debug\components\dataFileDataStorage', + 'class' => 'yii\debug\components\FileDataStorage', ]; /** From f6c8f5438ed1246e5938f96517d6fdbf57a2a45b Mon Sep 17 00:00:00 2001 From: "bukharev.evgeny" Date: Fri, 27 Jan 2023 15:41:47 +0300 Subject: [PATCH 14/26] fix syntax error, unexpected 'class' (T_CLASS), expecting identifier (T_STRING) --- src/Module.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Module.php b/src/Module.php index 7ff24c520..68f230130 100644 --- a/src/Module.php +++ b/src/Module.php @@ -85,8 +85,12 @@ class Module extends \yii\base\Module implements BootstrapInterface */ private $dataStorage; + /** + * Config options by DataStorage + * @var string[] + */ public $dataStorageConfig = [ - 'class' => 'yii\debug\components\FileDataStorage', + 'class' => 'yii\debug\components\data\FileDataStorage', ]; /** From 36a7605f956f2cd13abc176cc5d50d88e15146be Mon Sep 17 00:00:00 2001 From: "bukharev.evgeny" Date: Fri, 27 Jan 2023 16:02:28 +0300 Subject: [PATCH 15/26] add data storage info to CHANGELOG and README --- CHANGELOG.md | 4 ++-- README.md | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92aae13f3..7db015cdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,10 @@ Yii Framework 2 debug extension Change Log ========================================== -2.1.23 under development +2.2.0 January 27, 2023 ------------------------ -- no changes in this release. +- Enh #489: Adds data storage functionality that allows you to replace file storage with any available storage. 2.1.22 November 18, 2022 diff --git a/README.md b/README.md index 317397981..0bbaaeb46 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,28 @@ You will see a debugger toolbar showing at the bottom of every page of your appl You can click on the toolbar to see more detailed debug information. +Data Storage +----- + +You can save debug data in any storage, for this you need to implement the yii\debug\components\data\DataStorage interface, and configure the module. +Solves the problem of correct operation of the debug panel in case the application is located on several servers. + +```php +return [ + 'bootstrap' => ['debug'], + 'modules' => [ + 'debug' => [ + 'class' => 'yii\debug\Module', + 'dataStorageConfig'=>[ + 'class'=>'yii\debug\components\data\CacheDataStorage', + ], + ], + // ... + ], + ... +]; +``` + Open Files in IDE ----- From 274a9d631fac73afa5d3dd1514d1da6dc47111c7 Mon Sep 17 00:00:00 2001 From: "bukharev.evgeny" Date: Sat, 28 Jan 2023 13:24:50 +0300 Subject: [PATCH 16/26] recover merge bugs --- src/LogTarget.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/LogTarget.php b/src/LogTarget.php index de9a44679..872c8a945 100644 --- a/src/LogTarget.php +++ b/src/LogTarget.php @@ -91,6 +91,8 @@ public function loadTagToPanels($tag) if (isset($data[$id])) { $panel->tag = $tag; $panel->load(unserialize($data[$id])); + } else { + unset($this->module->panels[$id]); } if (isset($exceptions[$id])) { $panel->setError($exceptions[$id]); From a4c177523d1947036b9d1add078fb178ab9a0dc4 Mon Sep 17 00:00:00 2001 From: "bukharev.evgeny" Date: Sat, 28 Jan 2023 13:28:39 +0300 Subject: [PATCH 17/26] codestyle --- src/LogTarget.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/LogTarget.php b/src/LogTarget.php index 872c8a945..a851f5aa4 100644 --- a/src/LogTarget.php +++ b/src/LogTarget.php @@ -98,9 +98,7 @@ public function loadTagToPanels($tag) $panel->setError($exceptions[$id]); } } - $this->module->getDataStorage()->setData($this->tag,$data); - - + $this->module->getDataStorage()->setData($this->tag, $data); } /** From 6ca168fb3afc47ec9f055f3cada424cd2b518695 Mon Sep 17 00:00:00 2001 From: "bukharev.evgeny" Date: Sat, 28 Jan 2023 13:29:00 +0300 Subject: [PATCH 18/26] recover merge bugs --- src/LogTarget.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/LogTarget.php b/src/LogTarget.php index a851f5aa4..9ef0ab159 100644 --- a/src/LogTarget.php +++ b/src/LogTarget.php @@ -99,6 +99,8 @@ public function loadTagToPanels($tag) } } $this->module->getDataStorage()->setData($this->tag, $data); + + return $data; } /** From d259c640b9381415f7c283f799590579f03dbf93 Mon Sep 17 00:00:00 2001 From: "bukharev.evgeny" Date: Sat, 28 Jan 2023 13:42:11 +0300 Subject: [PATCH 19/26] add Instance::ensure --- src/Module.php | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/Module.php b/src/Module.php index 68f230130..1b1124219 100644 --- a/src/Module.php +++ b/src/Module.php @@ -12,7 +12,7 @@ use yii\base\BootstrapInterface; use yii\base\InvalidConfigException; use yii\debug\components\data\DataStorage; -use yii\debug\components\data\FileDataStorage; +use yii\di\Instance; use yii\helpers\Json; use yii\helpers\IpHelper; use yii\web\Response; @@ -215,15 +215,7 @@ public function init() { parent::init(); - - - $dataStorage = Yii::createObject($this->dataStorageConfig + ['module' => $this]); - - if (!$dataStorage instanceof DataStorage) { - throw new InvalidConfigException(); - } - - $this->dataStorage = $dataStorage; + $this->dataStorage = Instance::ensure($this->dataStorageConfig + ['module' => $this],'yii\debug\components\data\DataStorage'); $this->initPanels(); } @@ -495,7 +487,6 @@ public function getDataStorage() return $this->dataStorage; } - /** * {@inheritdoc} * @since 2.0.7 From 6ea0f29a23ca686db59a2ce4598a259101d1a07e Mon Sep 17 00:00:00 2001 From: "bukharev.evgeny" Date: Sat, 28 Jan 2023 13:54:45 +0300 Subject: [PATCH 20/26] refactor if/else --- src/components/data/CacheDataStorage.php | 39 +++++++++++------------- src/components/data/DataStorage.php | 2 +- src/components/data/FileDataStorage.php | 8 +++-- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/components/data/CacheDataStorage.php b/src/components/data/CacheDataStorage.php index 5f2f1c0e8..97e4d1091 100644 --- a/src/components/data/CacheDataStorage.php +++ b/src/components/data/CacheDataStorage.php @@ -1,4 +1,9 @@ cache->get($this->cacheDebugDataKey . $tag); - if (empty($data)) { - return []; - } else { - return unserialize($data); - } + return $this->cache->exists($this->cacheDebugDataKey . $tag) ? unserialize($this->cache->get($this->cacheDebugDataKey . $tag)) : []; } /** @@ -95,12 +97,7 @@ public function setData($tag, $data) */ public function getDataManifest($forceReload = false) { - $manifest = $this->cache->get($this->cacheDebugManifestKey); - if (empty($manifest)) { - return []; - } else { - return unserialize($manifest); - } + return $this->cache->exists($this->cacheDebugManifestKey) ? unserialize($this->cache->get($this->cacheDebugManifestKey)) : []; } /** @@ -108,7 +105,7 @@ public function getDataManifest($forceReload = false) * * @return mixed|void */ - public function setModule(Module $module) + public function setModule($module) { $this->module = $module; } diff --git a/src/components/data/DataStorage.php b/src/components/data/DataStorage.php index a931d01ef..6155b86db 100644 --- a/src/components/data/DataStorage.php +++ b/src/components/data/DataStorage.php @@ -33,5 +33,5 @@ public function getDataManifest($forceReload = false); * * @return mixed */ - public function setModule(Module $module); + public function setModule($module); } diff --git a/src/components/data/FileDataStorage.php b/src/components/data/FileDataStorage.php index 6054e5936..30238cb3a 100644 --- a/src/components/data/FileDataStorage.php +++ b/src/components/data/FileDataStorage.php @@ -214,8 +214,12 @@ function ($file) { } } - - public function setModule(Module $module) + /** + * @param Module $module + * + * @return mixed|void + */ + public function setModule($module) { $this->module = $module; } From 9946fa2187462a6284a13b82f608cfe64fe4edcb Mon Sep 17 00:00:00 2001 From: "bukharev.evgeny" Date: Sat, 28 Jan 2023 14:00:37 +0300 Subject: [PATCH 21/26] add Instance::ensure --- src/components/data/CacheDataStorage.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/data/CacheDataStorage.php b/src/components/data/CacheDataStorage.php index 97e4d1091..fd68d712e 100644 --- a/src/components/data/CacheDataStorage.php +++ b/src/components/data/CacheDataStorage.php @@ -11,6 +11,7 @@ use yii\base\Component; use yii\caching\Cache; use yii\debug\Module; +use yii\di\Instance; /** * CacheDataStorage @@ -65,7 +66,7 @@ public function init() { parent::init(); - $this->cache = \Yii::$app->get($this->cacheComponent); + $this->cache = Instance::ensure($this->cacheComponent,'yii\caching\Cache'); } /** From 61b3ff0b22d3ea84c00382abaa0e681ff6324d22 Mon Sep 17 00:00:00 2001 From: "bukharev.evgeny" Date: Thu, 16 Feb 2023 12:23:31 +0300 Subject: [PATCH 22/26] make CacheDataStorage properties --- src/components/data/CacheDataStorage.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/data/CacheDataStorage.php b/src/components/data/CacheDataStorage.php index fd68d712e..b657fcd1a 100644 --- a/src/components/data/CacheDataStorage.php +++ b/src/components/data/CacheDataStorage.php @@ -52,12 +52,12 @@ class CacheDataStorage extends Component implements DataStorage /** * @var int Manifest cache data ttl */ - private $manifestDuration = 10000; + public $manifestDuration = 10000; /** * @var int Debug cache data ttl */ - private $dataDuration = 3600; + public $dataDuration = 3600; /** * @return void From 309ddf72c645834bd2e41b0c3c6900d9beb504f9 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Mon, 20 Feb 2023 11:12:19 +0300 Subject: [PATCH 23/26] Update CHANGELOG.md --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7db015cdc..d67ffda87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,10 @@ Yii Framework 2 debug extension Change Log ========================================== -2.2.0 January 27, 2023 ------------------------- +2.2.0 under development +----------------------- -- Enh #489: Adds data storage functionality that allows you to replace file storage with any available storage. +- Enh #489: Adds data storage functionality that allows you to replace file storage with any available storage (evgenybukharev) 2.1.22 November 18, 2022 From 72488bc908256123b313a77964b74b011dd5bbee Mon Sep 17 00:00:00 2001 From: "bukharev.evgeny" Date: Thu, 6 Apr 2023 17:55:35 +0300 Subject: [PATCH 24/26] Fix isset data exceptions --- src/controllers/DefaultController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/DefaultController.php b/src/controllers/DefaultController.php index 8daa449f4..b4057b1d0 100644 --- a/src/controllers/DefaultController.php +++ b/src/controllers/DefaultController.php @@ -160,7 +160,7 @@ public function loadData($tag, $maxRetry = 0) $manifest = $this->module->getDataStorage()->getDataManifest($retry > 0); if (isset($manifest[$tag])) { $data=$this->module->getDataStorage()->getData($tag); - $exceptions = $data['exceptions']; + $exceptions = isset($data['exceptions'])?$data['exceptions']:[]; foreach ($this->module->panels as $id => $panel) { if (isset($data[$id])) { $panel->tag = $tag; From cdb69db1588d39eda6441213ec6465dacd29d840 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Tue, 27 Feb 2024 09:52:09 +0300 Subject: [PATCH 25/26] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0345b6353..0a0889b4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ Yii Framework 2 debug extension Change Log ========================================== -2.2.0 under development ------------------------ +2.1.26 under development +------------------------ - Enh #489: Adds data storage functionality that allows you to replace file storage with any available storage (evgenybukharev) - Enh #430: Allow to configure toolbar position via `Module::$toolbarPosition` property (sasha-x) From e7d9eaf6c2dcebf4047c97353f314fec515fa078 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Tue, 27 Feb 2024 09:53:35 +0300 Subject: [PATCH 26/26] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a0889b4d..0345b6353 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ Yii Framework 2 debug extension Change Log ========================================== -2.1.26 under development ------------------------- +2.2.0 under development +----------------------- - Enh #489: Adds data storage functionality that allows you to replace file storage with any available storage (evgenybukharev) - Enh #430: Allow to configure toolbar position via `Module::$toolbarPosition` property (sasha-x)