Initial commit: apps.prometeysev.ru

This commit is contained in:
root committed 2026-10-22 00:22:57 +03:00
commit 96ccabcd8b
6561 files changed
+1270349

No files matched your search

@@ -0,0 +1,317 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*
*/
namespace DebugKit\Cache\Engine;
use Cake\Cache\CacheEngine;
use Cake\Cache\CacheRegistry;
use DebugKit\DebugTimer;
/**
* A spying proxy for cache engines.
*
* Used by the CachePanel to wrap and track metrics related to caching.
*/
class DebugEngine extends CacheEngine
{
/**
* Proxied cache engine config.
*
* @var mixed
*/
protected $_config;
/**
* Proxied engine
*
* @var mixed
*/
protected $_engine;
/**
* Hit/miss metrics.
*
* @var mixed
*/
protected $_metrics = [
'write' => 0,
'delete' => 0,
'read' => 0,
'hit' => 0,
'miss' => 0,
];
/**
* Constructor
*
* @param mixed $config Config data or the proxied adapter.
*/
public function __construct($config)
{
$this->_config = $config;
}
/**
* Initialize the proxied Cache Engine
*
* @param array $config Array of setting for the engine.
* @return bool True, this engine cannot fail to initialize.
*/
public function init(array $config = [])
{
if (is_object($this->_config)) {
$this->_engine = $this->_config;
return true;
}
$registry = new CacheRegistry();
$this->_engine = $registry->load('spies', $this->_config);
unset($registry);
return true;
}
/**
* Get the internal engine
*
* @return \Cake\Cache\CacheEngine
*/
public function engine()
{
return $this->_engine;
}
/**
* Get the metrics for this object.
*
* @return array
*/
public function metrics()
{
return $this->_metrics;
}
/**
* Track a metric.
*
* @param string $metric The metric to increment.
* @return void
*/
protected function _track($metric)
{
$this->_metrics[$metric]++;
}
/**
* {@inheritDoc}
*/
public function write($key, $value)
{
$this->_track('write');
DebugTimer::start('Cache.write ' . $key);
$result = $this->_engine->write($key, $value);
DebugTimer::stop('Cache.write ' . $key);
return $result;
}
/**
* {@inheritDoc}
*/
public function writeMany($data)
{
$this->_track('write');
DebugTimer::start('Cache.writeMany');
$result = $this->_engine->writeMany($data);
DebugTimer::stop('Cache.writeMany');
return $result;
}
/**
* {@inheritDoc}
*/
public function read($key)
{
$this->_track('read');
DebugTimer::start('Cache.read ' . $key);
$result = $this->_engine->read($key);
DebugTimer::stop('Cache.read ' . $key);
$metric = 'hit';
if ($result === false) {
$metric = 'miss';
}
$this->_track($metric);
return $result;
}
/**
* {@inheritDoc}
*/
public function readMany($data)
{
$this->_track('read');
DebugTimer::start('Cache.readMany');
$result = $this->_engine->readMany($data);
DebugTimer::stop('Cache.readMany');
return $result;
}
/**
* {@inheritDoc}
*/
public function increment($key, $offset = 1)
{
$this->_track('write');
DebugTimer::start('Cache.increment ' . $key);
$result = $this->_engine->increment($key, $offset);
DebugTimer::stop('Cache.increment ' . $key);
return $result;
}
/**
* {@inheritDoc}
*/
public function decrement($key, $offset = 1)
{
$this->_track('write');
DebugTimer::start('Cache.decrement ' . $key);
$result = $this->_engine->decrement($key, $offset);
DebugTimer::stop('Cache.decrement ' . $key);
return $result;
}
/**
* {@inheritDoc}
*/
public function delete($key)
{
$this->_track('delete');
DebugTimer::start('Cache.delete ' . $key);
$result = $this->_engine->delete($key);
DebugTimer::stop('Cache.delete ' . $key);
return $result;
}
/**
* {@inheritDoc}
*/
public function deleteMany($data)
{
$this->_track('delete');
DebugTimer::start('Cache.deleteMany');
$result = $this->_engine->deleteMany($data);
DebugTimer::stop('Cache.deleteMany');
return $result;
}
/**
* {@inheritDoc}
*/
public function clear($check)
{
$this->_track('delete');
DebugTimer::start('Cache.clear');
$result = $this->_engine->clear($check);
DebugTimer::stop('Cache.clear');
return $result;
}
/**
* {@inheritDoc}
*/
public function groups()
{
return $this->_engine->groups();
}
/**
* Return the proxied configuration data.
*
* This method uses func_get_args() as not doing so confuses the
* proxied class.
*
* @deprecated 3.6.0 use setConfig()/getConfig() instead.
* @param string $key The key to set/read.
* @param mixed $value The value to set.
* @param bool $merge Whether or not configuration should be merged.
* @return mixed
*/
public function config($key = null, $value = null, $merge = true)
{
return call_user_func_array([$this->_engine, 'config'], func_get_args());
}
/**
* Returns the config.
*
* @param string|null $key The key to get or null for the whole config.
* @param mixed $default The return value when the key does not exist.
* @return mixed Config value being read.
*/
public function getConfig($key = null, $default = null)
{
return $this->_engine->getConfig($key, $default);
}
/**
* Sets the config.
*
* @param string|array $key The key to set, or a complete array of configs.
* @param mixed|null $value The value to set.
* @param bool $merge Whether to recursively merge or overwrite existing config, defaults to true.
* @return $this
* @throws \Cake\Core\Exception\Exception When trying to set a key that is invalid.
*/
public function setConfig($key, $value = null, $merge = true)
{
return $this->_engine->setConfig($key, $value, $merge);
}
/**
* {@inheritDoc}
*/
public function clearGroup($group)
{
$this->_track('delete');
DebugTimer::start('Cache.clearGroup ' . $group);
$result = $this->_engine->clearGroup($group);
DebugTimer::stop('Cache.clearGroup ' . $group);
return $result;
}
/**
* Magic __toString() method to get the CacheEngine's name
*
* @return string Returns the CacheEngine's name
*/
public function __toString()
{
if (!empty($this->_engine)) {
list($ns, $class) = namespaceSplit(get_class($this->_engine));
return str_replace('Engine', '', $class);
}
return $this->_config['className'];
}
}
@@ -0,0 +1,45 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since DebugKit 0.1
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Controller\Component;
use Cake\Controller\Component;
use Cake\Controller\ComponentRegistry;
/**
* Class ToolbarComponent
*
* @since DebugKit 0.1
* @deprecated
*/
class ToolbarComponent extends Component
{
/**
* Constructor
*
* If debug is off the component will be disabled and not do any further time tracking
* or load the toolbar helper.
*
* @param \Cake\Controller\ComponentRegistry $registry The ComponentRegistry
* @param array $settings An array of config
* @throws \RuntimeException
*/
public function __construct(ComponentRegistry $registry, $settings = [])
{
parent::__construct($registry, $settings);
$msg = __d('debug_kit', "{0} is now loaded through plugin bootstrapping. Make sure you have {1} in your application's {2}.", 'DebugKit', '`Plugin::load("DebugKit", ["bootstrap" => true, "routes" => true]);`', 'bootstrap.php');
throw new \RuntimeException($msg);
}
}
@@ -0,0 +1,107 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since DebugKit 3.6.1
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Controller;
use Cake\View\JsonView;
use Composer\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
/**
* Provides utility features need by the toolbar.
*/
class ComposerController extends DebugKitController
{
/**
* {@inheritDoc}
*/
public function initialize()
{
parent::initialize();
$this->loadComponent('RequestHandler');
$this->viewBuilder()->setClassName(JsonView::class);
}
/**
* Check outdated composer dependencies
*
* @return void
* @throws \RuntimeException
*/
public function checkDependencies()
{
$this->request->allowMethod('post');
$input = new ArrayInput([
'command' => 'outdated',
'--no-interaction' => true,
'--direct' => filter_var($this->request->getData('direct'), FILTER_VALIDATE_BOOLEAN),
]);
$output = $this->executeComposerCommand($input);
$dependencies = array_filter(explode("\n", $output->fetch()));
$packages = [];
foreach ($dependencies as $dependency) {
if (strpos($dependency, 'php_network_getaddresses') !== false) {
throw new \RuntimeException(__d('debug_kit', 'You have to be connected to the internet'));
}
if (strpos($dependency, '<highlight>') !== false) {
$packages['semverCompatible'][] = $dependency;
continue;
}
$packages['bcBreaks'][] = $dependency;
}
if (!empty($packages['semverCompatible'])) {
$packages['semverCompatible'] = trim(implode("\n", $packages['semverCompatible']));
}
if (!empty($packages['bcBreaks'])) {
$packages['bcBreaks'] = trim(implode("\n", $packages['bcBreaks']));
}
$this->set([
'_serialize' => ['packages'],
'packages' => $packages,
]);
}
/**
* @param ArrayInput $input An array describing the command input
* @return BufferedOutput Aa Console command buffered result
*/
private function executeComposerCommand(ArrayInput $input)
{
$bin = implode(DIRECTORY_SEPARATOR, [ROOT, 'vendor', 'bin', 'composer']);
putenv('COMPOSER_HOME=' . $bin);
putenv('COMPOSER_CACHE_DIR=' . CACHE);
$dir = getcwd();
chdir(ROOT);
$timeLimit = ini_get('max_execution_time');
set_time_limit(300);
$memoryLimit = ini_get('memory_limit');
ini_set('memory_limit', '512M');
$output = new BufferedOutput();
$application = new Application();
$application->setAutoExit(false);
$application->run($input, $output);
// Restore environment
chdir($dir);
set_time_limit($timeLimit);
ini_set('memory_limit', $memoryLimit);
return $output;
}
}
@@ -0,0 +1,68 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Controller;
use Cake\Event\Event;
/**
* Dashboard and common DebugKit backend.
*/
class DashboardController extends DebugKitController
{
/**
* Before filter handler.
*
* @param \Cake\Event\Event $event The event.
* @return void
*/
public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
$this->viewBuilder()->setLayout('dashboard');
}
/**
* Dashboard.
*
* @return void
* @throws \Cake\Network\Exception\NotFoundException
*/
public function index()
{
$this->loadModel('DebugKit.Requests');
$data = [
'driver' => get_class($this->Requests->getConnection()->getDriver()),
'rows' => $this->Requests->find()->count(),
];
$this->set('connection', $data);
}
/**
* Reset SQLite DB.
*
* @return \Cake\Http\Response
*/
public function reset()
{
$this->request->allowMethod('post');
$this->loadModel('DebugKit.Requests');
$this->Requests->Panels->deleteAll('1=1');
$this->Requests->deleteAll('1=1');
return $this->redirect(['action' => 'index']);
}
}
@@ -0,0 +1,54 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Controller;
use Cake\Controller\Controller;
use Cake\Core\Configure;
use Cake\Event\Event;
use Cake\Http\Exception\NotFoundException;
use Cake\Log\Log;
/**
* DebugKit Controller.
*/
class DebugKitController extends Controller
{
/**
* Before filter handler.
*
* @param \Cake\Event\Event $event The event.
* @return void
* @throws \Cake\Http\Exception\NotFoundException
*/
public function beforeFilter(Event $event)
{
if (!Configure::read('debug')) {
throw new NotFoundException('Not available without debug mode on.');
}
// If CakePHP Authorization\Authorization plugin is enabled,
// ignore it, only if `DebugKit.ignoreAuthorization` is set to true
$authorizationService = $this->getRequest()->getAttribute('authorization');
if ($authorizationService instanceof \Authorization\AuthorizationService) {
if (Configure::read('DebugKit.ignoreAuthorization')) {
$authorizationService->skipAuthorization();
} else {
Log::info(
"Cake Authorization plugin is enabled. If you would like " .
"to force DebugKit to ignore it, set `DebugKit.ignoreAuthorization` " .
" Configure option to true."
);
}
}
}
}
@@ -0,0 +1,265 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since 3.3
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Controller;
use Cake\Collection\CollectionInterface;
use Cake\Core\App;
use Cake\Core\Plugin as CorePlugin;
use Cake\Event\Event;
use Cake\Http\Exception\NotFoundException;
use Cake\Routing\Router;
use Cake\Utility\Inflector;
use DebugKit\Mailer\AbstractResult;
use DebugKit\Mailer\PreviewResult;
use DebugKit\Mailer\SentMailResult;
use PDOException;
use Psr\Http\Message\ResponseInterface;
/**
* Provides access to the MailPreview classes for visually debugging email sending
*
* @property \DebugKit\Model\Table\PanelsTable $Panels
*/
class MailPreviewController extends DebugKitController
{
/**
* Before render handler.
*
* @param \Cake\Event\Event $event The event.
* @return void
*/
public function beforeRender(Event $event)
{
$this->viewBuilder()->setLayout('DebugKit.mailer');
}
/**
* Handles mail-preview/index
*
* @return void
*/
public function index()
{
$this->set('mailPreviews', $this->getMailPreviews()->toArray());
}
/**
* Handles the viewing of an already sent email that was logged in the Mail panel
* for DebugKit
*
* @param string $panelId The Mail panel id where the email data is stored.
* @param string $number The email number as stored in the logs.
* @return \Psr\Http\Message\ResponseInterface|null.
*/
public function sent($panelId, $number)
{
$this->loadModel('DebugKit.Panels');
$panel = $this->Panels->get($panelId);
// @codingStandardsIgnoreStart
$content = @unserialize($panel->content);
// @codingStandardsIgnoreEnd
if (empty($content['emails'][$number])) {
throw new NotFoundException(__d('debug_kit', 'No emails found in this request'));
}
$email = $content['emails'][$number];
$email = new SentMailResult(array_filter($email['headers']), $email['message']);
$partType = $this->request->getQuery('part');
if ($partType) {
return $this->respondWithPart($email, $partType);
}
$this->set('noHeader', true);
$this->set('email', $email);
$this->set('plugin', '');
$this->set('part', $this->findPreferredPart($email, $this->request->getQuery('part')));
$this->viewBuilder()->setTemplate('email');
}
/**
* Handles mail-preview/email
*
* @param string $name The Mailer name
* @param string $method The mailer preview method
* @return \Psr\Http\Message\ResponseInterface|null
*/
public function email($name, $method)
{
// Clear the plugin attribute from the request instance
// Router is holding onto so that we can render mail previews
// in a plugin less request context.
Router::pushRequest($this->request->withParam('plugin', null));
$plugin = $this->request->getQuery('plugin');
$email = $this->findPreview($name, $method, $plugin);
$partType = $this->request->getQuery('part');
$this->viewBuilder()->setLayout(false);
if ($partType) {
return $this->respondWithPart($email, $partType);
}
$humanName = Inflector::humanize(Inflector::underscore($name) . "_$method");
$this->set('title', $humanName);
$this->set('email', $email);
$this->set('plugin', $plugin);
$this->set('part', $this->findPreferredPart($email, $this->request->getQuery('part')));
}
/**
* Returns a response object with the requested part type for the
* email or throws an exception, if no such part exists.
*
* @param \Debugkit\Mailer\AbstractResult $email the email to preview
* @param string $partType The email part to retrieve
* @return \Psr\Http\Message\ResponseInterface
*/
protected function respondWithPart($email, $partType)
{
$part = $this->findPart($email, $partType);
if ($part === false) {
throw new NotFoundException(__d('debug_kit', "Email part ''{0}'' not found in email", $partType));
}
$response = $this->response->withType($partType);
if ($part === 'text') {
$part = '<pre>' . $part . "</pre>";
}
$response = $response->withStringBody($part);
return $response;
}
/**
* Retrieves an array of MailPreview objects
*
* @return \Cake\Core\CollectionInterface
*/
protected function getMailPreviews()
{
return $this->getMailPreviewClasses()->groupBy('plugin');
}
/**
* Returns an array of MailPreview class names for the app and plugins
*
* @return \Cake\Core\CollectionInterface
*/
protected function getMailPreviewClasses()
{
$pluginPaths = collection(CorePlugin::loaded())
->reject(function ($plugin) {
return $plugin === 'DebugKit';
})
->map(function ($plugin) {
return [App::path('Mailer/Preview', $plugin), "$plugin."];
});
$appPaths = [App::path('Mailer/Preview'), ''];
return collection([$appPaths])
->append($pluginPaths)
->unfold(function ($pairs) {
list($paths, $plugin) = $pairs;
foreach ($paths as $path) {
yield $plugin => $path;
}
})
->unfold(function ($path, $plugin) {
foreach (glob($path . "*Preview.php") as $file) {
$base = str_replace(".php", "", basename($file));
$class = App::className($plugin . $base, 'Mailer/Preview');
if ($class) {
yield ['plugin' => trim($plugin, '.'), 'class' => new $class()];
}
}
});
}
/**
* Finds a specified email part
*
* @param \DebugKit\Mailer\AbstractResult $email The result of the email preview
* @param string $partType The name of a part
* @return null|string
*/
protected function findPart(AbstractResult $email, $partType)
{
foreach ($email->getParts() as $part => $content) {
if ($part === $partType) {
return $content;
}
}
return null;
}
/**
* Finds a specified email part or the first part available
*
* @param \DebugKit\Mailer\AbstractResult $email The result of the email preview
* @param string $partType The name of a part
* @return null|string
*/
protected function findPreferredPart(AbstractResult $email, $partType)
{
$parts = $email->getParts();
if (empty($partType) && !empty($parts['html'])) {
return 'html';
}
if (empty($partType)) {
foreach ($email->getParts() as $part => $content) {
return $part;
}
}
return $this->findPart($email, $partType) ?: null;
}
/**
* Returns a matching MailPreview object with name
*
* @param string $previewName The Mailer name
* @param string $emailName The mailer preview method
* @param string|null $plugin The plugin where the mailer preview should be found
* @return \DebugKit\Mailer\PreviewResult The result of the email preview
* @throws \Cake\Http\Exception\NotFoundException
*/
protected function findPreview($previewName, $emailName, $plugin = null)
{
if ($plugin) {
$plugin = "$plugin.";
}
$realClass = App::className($plugin . $previewName, "Mailer/Preview");
if (!$realClass) {
throw new NotFoundException("Mailer preview ${previewName} not found");
}
$mailPreview = new $realClass();
$email = $mailPreview->find($emailName);
if (!$email) {
throw new NotFoundException(__d('debug_kit', "Mailer preview {0}::{1} not found", $previewName, $emailName));
}
return new PreviewResult($mailPreview->$email(), $email);
}
}
@@ -0,0 +1,89 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Controller;
use Cake\Event\Event;
use Cake\Http\Exception\NotFoundException;
/**
* Provides access to panel data.
*
* @property \DebugKit\Model\Table\PanelsTable $Panels
*/
class PanelsController extends DebugKitController
{
/**
* components
*
* @var array
*/
public $components = ['RequestHandler', 'Cookie'];
/**
* Before render handler.
*
* @param \Cake\Event\Event $event The event.
* @return void
*/
public function beforeRender(Event $event)
{
$this->viewBuilder()
->setHelpers([
'Form', 'Html', 'Number', 'Url', 'DebugKit.Toolbar',
'DebugKit.Credentials', 'DebugKit.SimpleGraph',
])
->setLayout('DebugKit.toolbar');
if (!$this->request->is('json')) {
$this->viewBuilder()->setClassName('DebugKit.Ajax');
}
}
/**
* Index method that lets you get requests by panelid.
*
* @param string $requestId Request id
* @return void
* @throws \Cake\Http\Exception\NotFoundException
*/
public function index($requestId = null)
{
$query = $this->Panels->find('byRequest', ['requestId' => $requestId]);
$panels = $query->toArray();
if (empty($panels)) {
throw new NotFoundException();
}
$this->set([
'_serialize' => ['panels'],
'panels' => $panels,
]);
}
/**
* View a panel's data.
*
* @param string $id The id.
* @return void
*/
public function view($id = null)
{
$this->Cookie->configKey('debugKit_sort', 'encryption', false);
$this->set('sort', $this->Cookie->read('debugKit_sort'));
$panel = $this->Panels->get($id);
$this->set('panel', $panel);
// @codingStandardsIgnoreStart
$this->set(@unserialize($panel->content));
// @codingStandardsIgnoreEnd
}
}
@@ -0,0 +1,61 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Controller;
use Cake\Event\Event;
/**
* Provides access to panel data.
*
* @property \DebugKit\Model\Table\RequestsTable $Requests
*/
class RequestsController extends DebugKitController
{
/**
* Before filter handler.
*
* @param \Cake\Event\Event $event The event.
* @return void
*/
public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
$this->response = $this->response->withHeader('Content-Security-Policy', '');
}
/**
* Before render handler.
*
* @param \Cake\Event\Event $event The event.
* @return void
*/
public function beforeRender(Event $event)
{
$this->viewBuilder()
->setLayout('DebugKit.toolbar')
->setClassName('DebugKit.Ajax');
}
/**
* View a request's data.
*
* @param string $id The id.
* @return void
*/
public function view($id = null)
{
$toolbar = $this->Requests->get($id, ['contain' => 'Panels']);
$this->set('toolbar', $toolbar);
}
}
@@ -0,0 +1,55 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Controller;
use Cake\Cache\Cache;
use Cake\Http\Exception\NotFoundException;
/**
* Provides utility features need by the toolbar.
*/
class ToolbarController extends DebugKitController
{
/**
* components
*
* @var array
*/
public $components = ['RequestHandler'];
/**
* View class
*
* @var string
*/
public $viewClass = 'Cake\View\JsonView';
/**
* Clear a named cache.
*
* @return void
* @throws \Cake\Http\Exception\NotFoundException
*/
public function clearCache()
{
$this->request->allowMethod('post');
if (!$this->request->getData('name')) {
throw new NotFoundException(__d('debug_kit', 'Invalid cache engine name.'));
}
$result = Cache::clear(false, $this->request->getData('name'));
$this->set([
'_serialize' => ['success'],
'success' => $result,
]);
}
}
+200
View File
@@ -0,0 +1,200 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since 3.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Database\Log;
use Cake\Database\Log\LoggedQuery;
use Cake\Database\Log\QueryLogger;
use Psr\Log\AbstractLogger as PsrAbstractLogger;
/**
* DebugKit Query logger.
*
* This logger decorates the existing logger if it exists,
* and stores log messages internally so they can be displayed
* or stored for future use.
*/
class DebugLog extends QueryLogger
{
/**
* Logs from the current request.
*
* @var array
*/
protected $_queries = [];
/**
* Decorated logger.
*
* @var \Cake\Database\Log\LoggedQuery
*/
protected $_logger;
/**
* Name of the connection being logged.
*
* @var string
*/
protected $_connectionName;
/**
* Total time (ms) of all queries
*
* @var int
*/
protected $_totalTime = 0;
/**
* Total rows of all queries
*
* @var int
*/
protected $_totalRows = 0;
/**
* Set to true to capture schema reflection queries
* in the SQL log panel.
*
* @var bool
*/
protected $_includeSchema = false;
/**
* Constructor
*
* @param \Cake\Database\Log\QueryLogger $logger The logger to decorate and spy on.
* @param string $name The name of the connection being logged.
* @param bool $includeSchema Whether or not schema reflection should be included.
*/
public function __construct($logger, $name, $includeSchema = false)
{
$this->_logger = $logger;
$this->_connectionName = $name;
$this->_includeSchema = $includeSchema;
}
/**
* Set the schema include flag.
*
* @param bool $value Set
* @return $this
*/
public function setIncludeSchema($value)
{
$this->_includeSchema = $value;
return $this;
}
/**
* Get the connection name.
*
* @return array
*/
public function name()
{
return $this->_connectionName;
}
/**
* Get the stored logs.
*
* @return array
*/
public function queries()
{
return $this->_queries;
}
/**
* Get the total time
*
* @return int
*/
public function totalTime()
{
return $this->_totalTime;
}
/**
* Get the total rows
*
* @return int
*/
public function totalRows()
{
return $this->_totalRows;
}
/**
* Log queries
*
* @param \Cake\Database\Log\LoggedQuery $query The query being logged.
* @return void
*/
public function log(LoggedQuery $query)
{
if ($this->_logger) {
if ($this->_logger instanceof PsrAbstractLogger) {
$this->_logger->log($query, $query->error);
} else {
$this->_logger->log($query);
}
}
if ($this->_includeSchema === false && $this->isSchemaQuery($query)) {
return;
}
if (!empty($query->params)) {
$query->query = $this->_interpolate($query);
}
$this->_totalTime += $query->took;
$this->_totalRows += $query->numRows;
$this->_queries[] = [
'query' => $query->query,
'took' => $query->took,
'rows' => $query->numRows,
];
}
/**
* Sniff SQL statements for things only found in schema reflection.
*
* @param \Cake\Database\Log\LoggedQuery $query The query to check.
* @return bool
*/
protected function isSchemaQuery(LoggedQuery $query)
{
$querystring = $query->query;
return (
// Multiple engines
strpos($querystring, 'FROM information_schema') !== false ||
// Postgres
strpos($querystring, 'FROM pg_catalog') !== false ||
// MySQL
strpos($querystring, 'SHOW TABLE') === 0 ||
strpos($querystring, 'SHOW FULL COLUMNS') === 0 ||
strpos($querystring, 'SHOW INDEXES') === 0 ||
// Sqlite
strpos($querystring, 'FROM sqlite_master') !== false ||
strpos($querystring, 'PRAGMA') === 0 ||
// Sqlserver
strpos($querystring, 'FROM INFORMATION_SCHEMA') !== false ||
strpos($querystring, 'FROM sys.') !== false
);
}
}
+195
View File
@@ -0,0 +1,195 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit;
use Cake\Core\Plugin as CorePlugin;
use Cake\Event\Event;
use Cake\Utility\Hash;
use Composer\Json\JsonFile;
use DebugKit\DebugPanel;
/**
* Contains methods for Providing list of files.
*
*/
class DebugInclude
{
/**
* The list of plugins within the application
*
* @var string[]
*/
protected $_pluginPaths = [];
/**
* The list of Composer packages
*
* @var string[]
*/
protected $_composerPaths = [];
/**
* File Types
*
* @var array
*/
protected $_fileTypes = [
'Auth', 'Cache', 'Collection', 'Config', 'Configure', 'Console', 'Component', 'Controller',
'Behavior', 'Database', 'Datasource', 'Model', 'Template', 'View', 'Utility',
'Network', 'Routing', 'I18n', 'Log', 'Error', 'Event', 'Form', 'Filesystem',
'ORM', 'Filter', 'Validation',
];
/**
* Get a list of plugins on construct for later use
*/
public function __construct()
{
foreach (CorePlugin::loaded() as $plugin) {
$this->_pluginPaths[$plugin] = str_replace('/', DIRECTORY_SEPARATOR, CorePlugin::path($plugin));
}
$lockFile = new JsonFile(ROOT . DIRECTORY_SEPARATOR . 'composer.lock');
if ($lockFile->exists()) {
$lockContent = $lockFile->read();
$vendorDir = ROOT . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR;
$packages = array_merge($lockContent['packages'], $lockContent['packages-dev']);
foreach ($packages as $package) {
$this->_composerPaths[$package['name']] = $vendorDir . str_replace('/', DIRECTORY_SEPARATOR, $package['name']) . DIRECTORY_SEPARATOR;
}
}
}
/**
* Get the possible include paths
*
* @return array
*/
public function includePaths()
{
$paths = explode(PATH_SEPARATOR, get_include_path());
$paths = array_filter($paths, function ($path) {
if ($path === '.' || strlen($path) === 0) {
return false;
}
return true;
});
return array_values($paths);
}
/**
* Check if a path is part of CakePHP
*
* @param string $file File to check
* @return bool
*/
public function isCakeFile($file)
{
return strpos($file, CAKE) === 0;
}
/**
* Check if a path is from APP but not a plugin
*
* @param string $file File to check
* @return bool
*/
public function isAppFile($file)
{
return strpos($file, APP) === 0;
}
/**
* Detect plugin the file belongs to
*
* @param string $file File to check
* @return string|bool plugin name, or false if not plugin
*/
public function getPluginName($file)
{
foreach ($this->_pluginPaths as $plugin => $path) {
if (strpos($file, $path) === 0) {
return $plugin;
}
}
return false;
}
/**
* Detect Composer package the file belongs to
*
* @param string $file File to check
* @return string|bool package name, or false if not Composer package
*/
public function getComposerPackageName($file)
{
foreach ($this->_composerPaths as $package => $path) {
if (strpos($file, $path) === 0) {
return $package;
}
}
return false;
}
/**
* Replace the path with APP, CAKE, ROOT or the plugin name
*
* @param string $file File to check
* @param string $type The file type
* @param string|null $name plugin name or composer package
* @return string Path with replaced prefix
*/
public function niceFileName($file, $type, $name = null)
{
switch ($type) {
case 'app':
return str_replace(APP, 'APP' . DIRECTORY_SEPARATOR, $file);
case 'cake':
return str_replace(CAKE, 'CAKE' . DIRECTORY_SEPARATOR, $file);
case 'root':
return str_replace(ROOT, 'ROOT', $file);
case 'plugin':
return str_replace($this->_pluginPaths[$name], $name . DIRECTORY_SEPARATOR, $file);
case 'vendor':
return str_replace($this->_composerPaths[$name], '', $file);
}
}
/**
* Get the type of file (model, controller etc)
*
* @param string $file File to check.
* @return string
*/
public function getFileType($file)
{
foreach ($this->_fileTypes as $type) {
if (stripos($file, DIRECTORY_SEPARATOR . $type . DIRECTORY_SEPARATOR) !== false) {
return $type;
}
}
return 'other';
}
}
+105
View File
@@ -0,0 +1,105 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since DebugKit 2.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit;
use Cake\Error\Debugger;
/**
* Contains methods for Profiling memory usage.
*
*/
class DebugMemory
{
/**
* An array of recorded memory use points.
*
* @var array
*/
protected static $_points = [];
/**
* Get current memory usage
*
* @return int number of bytes ram currently in use. 0 if memory_get_usage() is not available.
*/
public static function getCurrent()
{
return memory_get_usage();
}
/**
* Get peak memory use
*
* @return int peak memory use (in bytes). Returns 0 if memory_get_peak_usage() is not available
*/
public static function getPeak()
{
return memory_get_peak_usage();
}
/**
* Stores a memory point in the internal tracker.
* Takes a optional message name which can be used to identify the memory point.
* If no message is supplied a debug_backtrace will be done to identify the memory point.
*
* @param string $message Message to identify this memory point.
* @return bool
*/
public static function record($message = null)
{
$memoryUse = self::getCurrent();
if (!$message) {
$trace = debug_backtrace();
$message = Debugger::trimPath($trace[0]['file']) . ' line ' . $trace[0]['line'];
}
if (isset(self::$_points[$message])) {
$originalMessage = $message;
$i = 1;
while (isset(self::$_points[$message])) {
$i++;
$message = $originalMessage . ' #' . $i;
}
}
self::$_points[$message] = $memoryUse;
return true;
}
/**
* Get all the stored memory points
*
* @param bool $clear Whether you want to clear the memory points as well. Defaults to false.
* @return array Array of memory marks stored so far.
*/
public static function getAll($clear = false)
{
$marks = self::$_points;
if ($clear) {
self::$_points = [];
}
return $marks;
}
/**
* Clear out any existing memory points
*
* @return void
*/
public static function clear()
{
self::$_points = [];
}
}
+122
View File
@@ -0,0 +1,122 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since DebugKit 0.1
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit;
use Cake\Event\Event;
use Cake\Event\EventListenerInterface;
use Cake\Utility\Inflector;
/**
* Base class for debug panels.
*
* @since DebugKit 0.1
*/
class DebugPanel implements EventListenerInterface
{
/**
* Defines which plugin this panel is from so the element can be located.
*
* @var string
*/
public $plugin = 'DebugKit';
/**
* The data collected about a given request.
*
* @var array
*/
protected $_data = [];
/**
* Get the title for the panel.
*
* @return string
*/
public function title()
{
list($ns, $name) = namespaceSplit(get_class($this));
$name = substr($name, 0, strlen('Panel') * -1);
return Inflector::humanize(Inflector::underscore($name));
}
/**
* Get the element name for the panel.
*
* @return string
*/
public function elementName()
{
list($ns, $name) = namespaceSplit(get_class($this));
if ($this->plugin) {
return $this->plugin . '.' . Inflector::underscore($name);
}
return Inflector::underscore($name);
}
/**
* Get the data a panel has collected.
*
* @return array
*/
public function data()
{
return $this->_data;
}
/**
* Get the summary data for a panel.
*
* This data is displayed in the toolbar even when the panel is collapsed.
*
* @return string
*/
public function summary()
{
return '';
}
/**
* Initialize hook method.
*
* @return void
*/
public function initialize()
{
}
/**
* Shutdown callback
*
* @param \Cake\Event\Event $event The event.
* @return void
*/
public function shutdown(Event $event)
{
}
/**
* Get the events this panels supports.
*
* @return array
*/
public function implementedEvents()
{
return [
'Controller.shutdown' => 'shutdown',
];
}
}
+188
View File
@@ -0,0 +1,188 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since DebugKit 3.11.4
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit;
use Cake\Core\Configure;
use Cake\Database\Query;
use Cake\Error\Debugger;
use SqlFormatter;
/**
* Contains methods for dumping well formatted SQL queries.
*/
class DebugSql
{
/**
* Template used for HTML output.
*
* @var string
*/
private static $templateHtml = <<<HTML
<div class="cake-debug-output">
%s
<pre class="cake-debug">
%s
</pre>
</div>
HTML;
/**
* Template used for CLI and text output.
*
* @var string
*/
private static $templateText = <<<TEXT
%s
########## DEBUG ##########
%s
###########################
TEXT;
/**
* Prints out the SQL statements generated by a Query object.
*
* This function returns the same variable that was passed.
* Only runs if debug mode is enabled.
*
* @param Query $query Query to show SQL statements for.
* @param bool $showValues Renders the SQL statement with bound variables.
* @param bool|null $showHtml If set to true, the method prints the debug
* data in a browser-friendly way.
* @param int $stackDepth Provides a hint as to which file in the call stack to reference.
* @return Query
*/
public static function sql(Query $query, $showValues = true, $showHtml = null, $stackDepth = 0)
{
if (!Configure::read('debug')) {
return $query;
}
$sql = (string)$query;
if ($showValues) {
$sql = method_exists($query, 'getValueBinder')
? static::interpolate($sql, $query->getValueBinder()->bindings())
: static::interpolate($sql, $query->valueBinder()->bindings());
}
$trace = Debugger::trace(['start' => 1, 'depth' => $stackDepth + 2, 'format' => 'array']);
$file = isset($trace[$stackDepth]) ? $trace[$stackDepth]['file'] : 'n/a';
$line = isset($trace[$stackDepth]) ? $trace[$stackDepth]['line'] : 0;
$lineInfo = '';
if ($file) {
$search = [];
if (defined('ROOT')) {
$search = [ROOT];
}
if (defined('CAKE_CORE_INCLUDE_PATH')) {
array_unshift($search, CAKE_CORE_INCLUDE_PATH);
}
$file = str_replace($search, '', $file);
}
$template = static::$templateHtml;
$sqlHighlight = true;
if ((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') || $showHtml === false) {
$template = static::$templateText;
$sqlHighlight = false;
if ($file && $line) {
$lineInfo = sprintf('%s (line %s)', $file, $line);
}
}
if ($showHtml === null && $template !== static::$templateText) {
$showHtml = true;
}
$var = $showHtml ? SqlFormatter::format($sql, $sqlHighlight) : $sql;
$var = str_replace(
'<span >:</span> <span style="color: #333;">',
'<span >:</span><span style="color: #333;">',
$var
);
if ($showHtml) {
$template = static::$templateHtml;
if ($file && $line) {
$lineInfo = sprintf('<span><strong>%s</strong> (line <strong>%s</strong>)</span>', $file, $line);
}
}
printf($template, $lineInfo, $var);
return $query;
}
/**
* Prints out the SQL statements generated by a Query object and dies.
*
* Only runs if debug mode is enabled.
* It will otherwise just continue code execution and ignore this function.
*
* @param Query $query Query to show SQL statements for.
* @param bool $showValues Renders the SQL statement with bound variables.
* @param bool|null $showHtml If set to true, the method prints the debug
* data in a browser-friendly way.
* @param int $stackDepth Provides a hint as to which file in the call stack to reference.
* @return void
*/
public static function sqld(Query $query, $showValues = true, $showHtml = null, $stackDepth = 1)
{
static::sql($query, $showValues, $showHtml, $stackDepth);
die(1);
}
/**
* Helper function used to replace query placeholders by the real
* params used to execute the query.
*
* @param string $sql The SQL statement
* @param array $bindings The Query bindings
* @return string
*/
private static function interpolate($sql, array $bindings)
{
$params = array_map(function ($binding) {
$p = $binding['value'];
if ($p === null) {
return 'NULL';
}
if (is_bool($p)) {
return $p ? '1' : '0';
}
if (is_string($p)) {
$replacements = [
'$' => '\\$',
'\\' => '\\\\\\\\',
"'" => "''",
];
$p = strtr($p, $replacements);
return "'$p'";
}
return $p;
}, $bindings);
$keys = [];
$limit = is_int(key($params)) ? 1 : -1;
foreach ($params as $key => $param) {
$keys[] = is_string($key) ? "/$key\b/" : '/[?]/';
}
return preg_replace($keys, $params, $sql, $limit);
}
}
+216
View File
@@ -0,0 +1,216 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since DebugKit 0.1
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit;
use Cake\Error\Debugger;
/**
* Contains methods for Profiling and creating timers.
*
*/
class DebugTimer
{
/**
* Internal timers array
*
* @var array
*/
protected static $_timers = [];
/**
* Start an benchmarking timer.
*
* @param string $name The name of the timer to start.
* @param string $message A message for your timer
* @return bool Always true
*/
public static function start($name = null, $message = null)
{
$start = microtime(true);
if (!$name) {
$named = false;
$calledFrom = debug_backtrace();
$name = Debugger::trimPath($calledFrom[0]['file']) . ' line ' . $calledFrom[0]['line'];
} else {
$named = true;
}
if (!$message) {
$message = $name;
}
$_name = $name;
$i = 1;
while (isset(self::$_timers[$name])) {
$i++;
$name = $_name . ' #' . $i;
}
if ($i > 1) {
$message .= ' #' . $i;
}
self::$_timers[$name] = [
'start' => $start,
'message' => $message,
'named' => $named,
];
return true;
}
/**
* Stop a benchmarking timer.
*
* $name should be the same as the $name used in startTimer().
*
* @param string $name The name of the timer to end.
* @return bool true if timer was ended, false if timer was not started.
*/
public static function stop($name = null)
{
$end = microtime(true);
if (!$name) {
$names = array_reverse(array_keys(self::$_timers));
foreach ($names as $name) {
if (!empty(self::$_timers[$name]['end'])) {
continue;
}
if (empty(self::$_timers[$name]['named'])) {
break;
}
}
} else {
$i = 1;
$_name = $name;
while (isset(self::$_timers[$name])) {
if (empty(self::$_timers[$name]['end'])) {
break;
}
$i++;
$name = $_name . ' #' . $i;
}
}
if (!isset(self::$_timers[$name])) {
return false;
}
self::$_timers[$name]['end'] = $end;
return true;
}
/**
* Get all timers that have been started and stopped.
* Calculates elapsed time for each timer. If clear is true, will delete existing timers
*
* @param bool $clear false
* @return array
*/
public static function getAll($clear = false)
{
$start = self::requestStartTime();
$now = microtime(true);
$times = [];
if (!empty(self::$_timers)) {
$firstTimer = reset(self::$_timers);
$_end = $firstTimer['start'];
} else {
$_end = $now;
}
$times['Core Processing (Derived from $_SERVER["REQUEST_TIME"])'] = [
'message' => __d('debug_kit', 'Core Processing (Derived from $_SERVER["REQUEST_TIME"])'),
'start' => 0,
'end' => $_end - $start,
'time' => round($_end - $start, 6),
'named' => null,
];
foreach (self::$_timers as $name => $timer) {
if (!isset($timer['end'])) {
$timer['end'] = $now;
}
$times[$name] = array_merge($timer, [
'start' => $timer['start'] - $start,
'end' => $timer['end'] - $start,
'time' => self::elapsedTime($name),
]);
}
if ($clear) {
self::$_timers = [];
}
return $times;
}
/**
* Clear all existing timers
*
* @return bool true
*/
public static function clear()
{
self::$_timers = [];
return true;
}
/**
* Get the difference in time between the timer start and timer end.
*
* @param string $name the name of the timer you want elapsed time for.
* @param int $precision the number of decimal places to return, defaults to 5.
* @return float number of seconds elapsed for timer name, 0 on missing key
*/
public static function elapsedTime($name = 'default', $precision = 5)
{
if (!isset(self::$_timers[$name]['start']) || !isset(self::$_timers[$name]['end'])) {
return 0;
}
return round(self::$_timers[$name]['end'] - self::$_timers[$name]['start'], $precision);
}
/**
* Get the total execution time until this point
*
* @return float elapsed time in seconds since script start.
*/
public static function requestTime()
{
$start = self::requestStartTime();
$now = microtime(true);
return ($now - $start);
}
/**
* get the time the current request started.
*
* @return float time of request start
*/
public static function requestStartTime()
{
if (defined('TIME_START')) {
$startTime = TIME_START;
} elseif (isset($GLOBALS['TIME_START'])) {
$startTime = $GLOBALS['TIME_START'];
} else {
$startTime = env('REQUEST_TIME');
}
return $startTime;
}
}
+138
View File
@@ -0,0 +1,138 @@
# LANGUAGE translation of Debug Kit Application
# Copyright 2008 Andy Dawson <andydawson76@yahoo.co.uk>
# No version information was available in the source files.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: debug_kit-\n"
"POT-Creation-Date: 2009-05-27 09:47+0200\n"
"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\n"
"Last-Translator: Andy Dawson <andydawson76@yahoo.co.uk>\n"
"Language-Team:\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Basepath: ../../../\n"
#: controllers/components/toolbar.php:91
msgid "Component initialization and startup"
msgstr ""
#: controllers/components/toolbar.php:140
msgid "Controller Action"
msgstr ""
#: controllers/components/toolbar.php:167
msgid "Render Controller Action"
msgstr ""
#: controllers/components/toolbar.php:231
msgid "Could not load DebugToolbar panel %s"
msgstr ""
#: views/elements/debug_toolbar.ctp:25
msgid "There are no active panels. You must enable a panel to see its output."
msgstr ""
#: views/elements/history_panel.ctp:21
msgid "Request History"
msgstr ""
#: views/elements/history_panel.ctp:23
msgid "No previous requests logged."
msgstr ""
#: views/elements/history_panel.ctp:25
msgid "previous requests available"
msgstr ""
#: views/elements/history_panel.ctp:27
msgid "Restore to current request"
msgstr ""
#: views/elements/log_panel.ctp:21
msgid "Logs"
msgstr ""
#: views/elements/log_panel.ctp:28
msgid "Time"
msgstr ""
#: views/elements/log_panel.ctp:28
#: views/elements/timer_panel.ctp:54
msgid "Message"
msgstr ""
#: views/elements/log_panel.ctp:37
msgid "There were no log entries made this request"
msgstr ""
#: views/elements/request_panel.ctp:21
msgid "Request"
msgstr ""
#: views/elements/request_panel.ctp:35
msgid "Current Route"
msgstr ""
#: views/elements/session_panel.ctp:21
msgid "Session"
msgstr ""
#: views/elements/sql_log_panel.ctp:21
msgid "Sql Logs"
msgstr ""
#: views/elements/sql_log_panel.ctp:31
msgid "toggle (%s) query explains for %s"
msgstr ""
#: views/elements/sql_log_panel.ctp:39
msgid "No slow queries!, or your database does not support EXPLAIN"
msgstr ""
#: views/elements/sql_log_panel.ctp:44
msgid "No active database connections"
msgstr ""
#: views/elements/timer_panel.ctp:33
msgid "Memory"
msgstr ""
#: views/elements/timer_panel.ctp:35
msgid "Current Memory Use"
msgstr ""
#: views/elements/timer_panel.ctp:39
msgid "Peak Memory Use"
msgstr ""
#: views/elements/timer_panel.ctp:43
msgid "Timers"
msgstr ""
#: views/elements/timer_panel.ctp:45
msgid "%s (ms)"
msgstr ""
#: views/elements/timer_panel.ctp:46
msgid "Total Request Time:"
msgstr ""
#: views/elements/timer_panel.ctp:54
msgid "Time in ms"
msgstr ""
#: views/elements/timer_panel.ctp:54
msgid "Graph"
msgstr ""
#: views/elements/variables_panel.ctp:21
msgid "View Variables"
msgstr ""
#: views/helpers/simple_graph.php:79
msgid "Starting %sms into the request, taking %sms"
msgstr ""
@@ -0,0 +1,135 @@
# LANGUAGE translation of CakePHP Application
# Copyright YEAR NAME <EMAIL@ADDRESS>
# No version information was available in the source files.
#
#, fuzzy
msgid ""
msgstr "Project-Id-Version: PROJECT VERSION\n"
"POT-Creation-Date: 2009-05-27 09:47+0200\n"
"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\n"
"Last-Translator: NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <EMAIL@ADDRESS>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
#: controllers/components/toolbar.php:91
msgid "Component initialization and startup"
msgstr ""
#: controllers/components/toolbar.php:140
msgid "Controller Action"
msgstr ""
#: controllers/components/toolbar.php:167
msgid "Render Controller Action"
msgstr ""
#: controllers/components/toolbar.php:231
msgid "Could not load DebugToolbar panel %s"
msgstr ""
#: views/elements/debug_toolbar.ctp:25
msgid "There are no active panels. You must enable a panel to see its output."
msgstr ""
#: views/elements/history_panel.ctp:21
msgid "Request History"
msgstr ""
#: views/elements/history_panel.ctp:23
msgid "No previous requests logged."
msgstr ""
#: views/elements/history_panel.ctp:25
msgid "previous requests available"
msgstr ""
#: views/elements/history_panel.ctp:27
msgid "Restore to current request"
msgstr ""
#: views/elements/log_panel.ctp:21
msgid "Logs"
msgstr ""
#: views/elements/log_panel.ctp:28
msgid "Time"
msgstr ""
#: views/elements/log_panel.ctp:28 views/elements/timer_panel.ctp:54
msgid "Message"
msgstr ""
#: views/elements/log_panel.ctp:37
msgid "There were no log entries made this request"
msgstr ""
#: views/elements/request_panel.ctp:21
msgid "Request"
msgstr ""
#: views/elements/request_panel.ctp:35
msgid "Current Route"
msgstr ""
#: views/elements/session_panel.ctp:21
msgid "Session"
msgstr ""
#: views/elements/sql_log_panel.ctp:21
msgid "Sql Logs"
msgstr ""
#: views/elements/sql_log_panel.ctp:31
msgid "toggle (%s) query explains for %s"
msgstr ""
#: views/elements/sql_log_panel.ctp:39
msgid "No slow queries!, or your database does not support EXPLAIN"
msgstr ""
#: views/elements/sql_log_panel.ctp:44
msgid "No active database connections"
msgstr ""
#: views/elements/timer_panel.ctp:33
msgid "Memory"
msgstr ""
#: views/elements/timer_panel.ctp:35
msgid "Current Memory Use"
msgstr ""
#: views/elements/timer_panel.ctp:39
msgid "Peak Memory Use"
msgstr ""
#: views/elements/timer_panel.ctp:43
msgid "Timers"
msgstr ""
#: views/elements/timer_panel.ctp:45
msgid "%s (ms)"
msgstr ""
#: views/elements/timer_panel.ctp:46
msgid "Total Request Time:"
msgstr ""
#: views/elements/timer_panel.ctp:54
msgid "Time in ms"
msgstr ""
#: views/elements/timer_panel.ctp:54
msgid "Graph"
msgstr ""
#: views/elements/variables_panel.ctp:21
msgid "View Variables"
msgstr ""
#: views/helpers/simple_graph.php:79
msgid "Starting %sms into the request, taking %sms"
msgstr ""
@@ -0,0 +1,135 @@
# LANGUAGE translation of CakePHP Application
# Copyright YEAR NAME <EMAIL@ADDRESS>
# No version information was available in the source files.
#
#, fuzzy
msgid ""
msgstr "Project-Id-Version: PROJECT VERSION\n"
"POT-Creation-Date: 2013-06-04 12:00+0200\n"
"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\n"
"Last-Translator: cake17 <gvmail76-inscription@yahoo.fr>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n>1;\n"
#: controllers/components/toolbar.php:91
msgid "Component initialization and startup"
msgstr "Initialisation du component et démarrage"
#: controllers/components/toolbar.php:140
msgid "Controller Action"
msgstr "Action du Controller"
#: controllers/components/toolbar.php:167
msgid "Render Controller Action"
msgstr "Rendu de l'Action du Controller"
#: controllers/components/toolbar.php:231
msgid "Could not load DebugToolbar panel %s"
msgstr "Ne peut charger le panel %s de DebugToolbar"
#: views/elements/debug_toolbar.ctp:25
msgid "There are no active panels. You must enable a panel to see its output."
msgstr "Il n'y a pas de panels actifs. Vous devez activer le panel pour voir sa sortie."
#: views/elements/history_panel.ctp:21
msgid "Request History"
msgstr "Historique des Requêtes"
#: views/elements/history_panel.ctp:23
msgid "No previous requests logged."
msgstr "Pas de demandes antérieures enregistrées."
#: views/elements/history_panel.ctp:25
msgid "previous requests available"
msgstr "Des requêtes antérieures sont disponibles"
#: views/elements/history_panel.ctp:27
msgid "Restore to current request"
msgstr "Restaure la requête actuelle"
#: views/elements/log_panel.ctp:21
msgid "Logs"
msgstr "Logs"
#: views/elements/log_panel.ctp:28
msgid "Time"
msgstr "Date"
#: views/elements/log_panel.ctp:28 views/elements/timer_panel.ctp:54
msgid "Message"
msgstr "Message"
#: views/elements/log_panel.ctp:37
msgid "There were no log entries made this request"
msgstr "Il n'y avait pas d'entrées de log faîtes pour cette requête"
#: views/elements/request_panel.ctp:21
msgid "Request"
msgstr "Requête"
#: views/elements/request_panel.ctp:35
msgid "Current Route"
msgstr "Route actuelle"
#: views/elements/session_panel.ctp:21
msgid "Session"
msgstr "Session"
#: views/elements/sql_log_panel.ctp:21
msgid "Sql Logs"
msgstr "Logs Sql"
#: views/elements/sql_log_panel.ctp:31
msgid "toggle (%s) query explains for %s"
msgstr "bascule (%s) requêtes expliquées pour %s"
#: views/elements/sql_log_panel.ctp:39
msgid "No slow queries!, or your database does not support EXPLAIN"
msgstr "Pas de requêtes lentes!, ou votre base de données ne supporte pas EXPLAIN"
#: views/elements/sql_log_panel.ctp:44
msgid "No active database connections"
msgstr "Pas de connections actives de la base de données"
#: views/elements/timer_panel.ctp:33
msgid "Memory"
msgstr "Mémoire"
#: views/elements/timer_panel.ctp:35
msgid "Current Memory Use"
msgstr "Utilisation de la Mémoire Actuelle"
#: views/elements/timer_panel.ctp:39
msgid "Peak Memory Use"
msgstr "Utilisation de la Mémoire au niveau maximum"
#: views/elements/timer_panel.ctp:43
msgid "Timers"
msgstr "Timers"
#: views/elements/timer_panel.ctp:45
msgid "%s (ms)"
msgstr "%s (ms)"
#: views/elements/timer_panel.ctp:46
msgid "Total Request Time:"
msgstr "Total du Temps de Requête"
#: views/elements/timer_panel.ctp:54
msgid "Time in ms"
msgstr "Temps en ms"
#: views/elements/timer_panel.ctp:54
msgid "Graph"
msgstr "Graph"
#: views/elements/variables_panel.ctp:21
msgid "View Variables"
msgstr "Variables du View"
#: views/helpers/simple_graph.php:79
msgid "Starting %sms into the request, taking %sms"
msgstr "Début en %sms de la requête, et prend %sms"
@@ -0,0 +1,136 @@
# LANGUAGE translation of Debug Kit Application
# Copyright 2008 Andy Dawson <andydawson76@yahoo.co.uk>
# No version information was available in the source files.
#
msgid ""
msgstr ""
"Project-Id-Version: debug_kit-\n"
"POT-Creation-Date: 2009-05-27 09:47+0200\n"
"PO-Revision-Date: 2009-05-27 09:47+0200\n"
"Last-Translator: Automatically generated\n"
"Language-Team:none\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Basepath: ../../../\n"
"Language: lim\n"
#: controllers/components/toolbar.php:91
msgid "Component initialization and startup"
msgstr ""
#: controllers/components/toolbar.php:140
msgid "Controller Action"
msgstr ""
#: controllers/components/toolbar.php:167
msgid "Render Controller Action"
msgstr ""
#: controllers/components/toolbar.php:231
msgid "Could not load DebugToolbar panel %s"
msgstr ""
#: views/elements/debug_toolbar.ctp:25
msgid "There are no active panels. You must enable a panel to see its output."
msgstr ""
#: views/elements/history_panel.ctp:21
msgid "Request History"
msgstr ""
#: views/elements/history_panel.ctp:23
msgid "No previous requests logged."
msgstr ""
#: views/elements/history_panel.ctp:25
msgid "previous requests available"
msgstr ""
#: views/elements/history_panel.ctp:27
msgid "Restore to current request"
msgstr ""
#: views/elements/log_panel.ctp:21
msgid "Logs"
msgstr ""
#: views/elements/log_panel.ctp:28
msgid "Time"
msgstr ""
#: views/elements/log_panel.ctp:28 views/elements/timer_panel.ctp:54
msgid "Message"
msgstr ""
#: views/elements/log_panel.ctp:37
msgid "There were no log entries made this request"
msgstr ""
#: views/elements/request_panel.ctp:21
msgid "Request"
msgstr ""
#: views/elements/request_panel.ctp:35
msgid "Current Route"
msgstr ""
#: views/elements/session_panel.ctp:21
msgid "Session"
msgstr ""
#: views/elements/sql_log_panel.ctp:21
msgid "Sql Logs"
msgstr ""
#: views/elements/sql_log_panel.ctp:31
msgid "toggle (%s) query explains for %s"
msgstr ""
#: views/elements/sql_log_panel.ctp:39
msgid "No slow queries!, or your database does not support EXPLAIN"
msgstr ""
#: views/elements/sql_log_panel.ctp:44
msgid "No active database connections"
msgstr ""
#: views/elements/timer_panel.ctp:33
msgid "Memory"
msgstr ""
#: views/elements/timer_panel.ctp:35
msgid "Current Memory Use"
msgstr ""
#: views/elements/timer_panel.ctp:39
msgid "Peak Memory Use"
msgstr ""
#: views/elements/timer_panel.ctp:43
msgid "Timers"
msgstr ""
#: views/elements/timer_panel.ctp:45
msgid "%s (ms)"
msgstr ""
#: views/elements/timer_panel.ctp:46
msgid "Total Request Time:"
msgstr ""
#: views/elements/timer_panel.ctp:54
msgid "Time in ms"
msgstr ""
#: views/elements/timer_panel.ctp:54
msgid "Graph"
msgstr ""
#: views/elements/variables_panel.ctp:21
msgid "View Variables"
msgstr ""
#: views/helpers/simple_graph.php:79
msgid "Starting %sms into the request, taking %sms"
msgstr ""
@@ -0,0 +1,141 @@
# LANGUAGE translation of Debug Kit Application
# Copyright 2008 Andy Dawson <andydawson76@yahoo.co.uk>
# No version information was available in the source files.
#
msgid ""
msgstr ""
"Project-Id-Version: debug_kit-\n"
"POT-Creation-Date: 2009-05-27 09:47+0200\n"
"PO-Revision-Date: 2014-07-17 17:04+0200\n"
"Last-Translator: Marlin Cremers <m.cremers@cvo-technologies.com>\n"
"Language-Team: \n"
"Language: nld\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 1.9\n"
"X-Poedit-Basepath: ../../../\n"
#: controllers/components/toolbar.php:91
msgid "Component initialization and startup"
msgstr "Component initializatie en opstarten"
#: controllers/components/toolbar.php:140
msgid "Controller Action"
msgstr "Controller Actie"
#: controllers/components/toolbar.php:167
msgid "Render Controller Action"
msgstr "Produceer Controller Actie"
#: controllers/components/toolbar.php:231
msgid "Could not load DebugToolbar panel %s"
msgstr "Kon DebugToolbar paneel %s niet laden"
#: views/elements/debug_toolbar.ctp:25
msgid "There are no active panels. You must enable a panel to see its output."
msgstr ""
"Er zijn geen actieve panelen. Je moet een paneel aanzetten om de uitvoer te "
"zien."
#: views/elements/history_panel.ctp:21
msgid "Request History"
msgstr "Aanvraag Geschiedenis"
#: views/elements/history_panel.ctp:23
msgid "No previous requests logged."
msgstr "Geen vorige aanvragen gelogged."
#: views/elements/history_panel.ctp:25
msgid "previous requests available"
msgstr "vorige aanvraag beschikbaar"
#: views/elements/history_panel.ctp:27
msgid "Restore to current request"
msgstr "Herstel naar actuele aanvraag"
#: views/elements/log_panel.ctp:21
msgid "Logs"
msgstr "Logs"
#: views/elements/log_panel.ctp:28
msgid "Time"
msgstr "Tijd"
#: views/elements/log_panel.ctp:28
#: views/elements/timer_panel.ctp:54
msgid "Message"
msgstr "Bericht"
#: views/elements/log_panel.ctp:37
msgid "There were no log entries made this request"
msgstr "Er zijn geen log vermeldingen gemaakt in deze aanvraag"
#: views/elements/request_panel.ctp:21
msgid "Request"
msgstr "Aanvraag"
#: views/elements/request_panel.ctp:35
msgid "Current Route"
msgstr "Huide Route"
#: views/elements/session_panel.ctp:21
msgid "Session"
msgstr "Sessie"
#: views/elements/sql_log_panel.ctp:21
msgid "Sql Logs"
msgstr "Sql Logs"
#: views/elements/sql_log_panel.ctp:31
msgid "toggle (%s) query explains for %s"
msgstr "schakel (%s) aanvraag uitleg voor %s"
#: views/elements/sql_log_panel.ctp:39
msgid "No slow queries!, or your database does not support EXPLAIN"
msgstr "Geen langzame vragen!, of je database ondersteund geen EXPLAIN"
#: views/elements/sql_log_panel.ctp:44
msgid "No active database connections"
msgstr "Geen actieve database verbindingen"
#: views/elements/timer_panel.ctp:33
msgid "Memory"
msgstr "Geheugen"
#: views/elements/timer_panel.ctp:35
msgid "Current Memory Use"
msgstr "Actueel Geheugen Gebruik"
#: views/elements/timer_panel.ctp:39
msgid "Peak Memory Use"
msgstr "Hoogste Geheugen Gebruik"
#: views/elements/timer_panel.ctp:43
msgid "Timers"
msgstr "Timers"
#: views/elements/timer_panel.ctp:45
msgid "%s (ms)"
msgstr "%s (ms)"
#: views/elements/timer_panel.ctp:46
msgid "Total Request Time:"
msgstr "Totale Aanvraag Tijd:"
#: views/elements/timer_panel.ctp:54
msgid "Time in ms"
msgstr "Tijd in ms"
#: views/elements/timer_panel.ctp:54
msgid "Graph"
msgstr "Diagram"
#: views/elements/variables_panel.ctp:21
msgid "View Variables"
msgstr "View Variabelen"
#: views/helpers/simple_graph.php:79
msgid "Starting %sms into the request, taking %sms"
msgstr "Starten %sms in de aanvraag, neemt %sms"
@@ -0,0 +1,135 @@
# LANGUAGE translation of CakePHP Application
# Copyright YEAR NAME <EMAIL@ADDRESS>
# No version information was available in the source files.
#
#, fuzzy
msgid ""
msgstr "Project-Id-Version: PROJECT VERSION\n"
"POT-Creation-Date: 2014-01-23 08:29+0200\n"
"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\n"
"Last-Translator: Lincoln Brito <lincoln.sbrito@gmail.com>\n"
"Language-Team: LANGUAGE <EMAIL@ADDRESS>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n>1;\n"
#: controllers/components/toolbar.php:91
msgid "Component initialization and startup"
msgstr "Inicialização de componente"
#: controllers/components/toolbar.php:140
msgid "Controller Action"
msgstr "Action do Controller"
#: controllers/components/toolbar.php:167
msgid "Render Controller Action"
msgstr "Renderizar Action do Controller"
#: controllers/components/toolbar.php:231
msgid "Could not load DebugToolbar panel %s"
msgstr "Não foi possível carregar o painel %s do DebugToolbar"
#: views/elements/debug_toolbar.ctp:25
msgid "There are no active panels. You must enable a panel to see its output."
msgstr "Não existem painéis ativos. Você deve habilitar um painel para visualizar sua saída."
#: views/elements/history_panel.ctp:21
msgid "Request History"
msgstr "Histórico de requisições"
#: views/elements/history_panel.ctp:23
msgid "No previous requests logged."
msgstr "Sem requisições anteriores logadas."
#: views/elements/history_panel.ctp:25
msgid "previous requests available"
msgstr "requisições anteriores disponíveis"
#: views/elements/history_panel.ctp:27
msgid "Restore to current request"
msgstr "Restaurar para requisição atual"
#: views/elements/log_panel.ctp:21
msgid "Logs"
msgstr "Logs"
#: views/elements/log_panel.ctp:28
msgid "Time"
msgstr "Tempo"
#: views/elements/log_panel.ctp:28 views/elements/timer_panel.ctp:54
msgid "Message"
msgstr "Mensagem"
#: views/elements/log_panel.ctp:37
msgid "There were no log entries made this request"
msgstr "Não houveram entradas no log para essa requisição "
#: views/elements/request_panel.ctp:21
msgid "Request"
msgstr "Requisição"
#: views/elements/request_panel.ctp:35
msgid "Current Route"
msgstr "Rota Atual"
#: views/elements/session_panel.ctp:21
msgid "Session"
msgstr "Sessão"
#: views/elements/sql_log_panel.ctp:21
msgid "Sql Logs"
msgstr "Logs Sql"
#: views/elements/sql_log_panel.ctp:31
msgid "toggle (%s) query explains for %s"
msgstr "alternar (%s) query explains por %s"
#: views/elements/sql_log_panel.ctp:39
msgid "No slow queries!, or your database does not support EXPLAIN"
msgstr "Sem consultas lentas!, ou o seu banco de dados não suporta EXPLAIN"
#: views/elements/sql_log_panel.ctp:44
msgid "No active database connections"
msgstr "Sem conexões ativas de banco de dados"
#: views/elements/timer_panel.ctp:33
msgid "Memory"
msgstr "Memória"
#: views/elements/timer_panel.ctp:35
msgid "Current Memory Use"
msgstr "Uso atual de memória"
#: views/elements/timer_panel.ctp:39
msgid "Peak Memory Use"
msgstr "Pico de uso de memória"
#: views/elements/timer_panel.ctp:43
msgid "Timers"
msgstr "Tempos"
#: views/elements/timer_panel.ctp:45
msgid "%s (ms)"
msgstr "%s (ms)"
#: views/elements/timer_panel.ctp:46
msgid "Total Request Time:"
msgstr "Tempo Total de Requisição"
#: views/elements/timer_panel.ctp:54
msgid "Time in ms"
msgstr "Tempo em ms"
#: views/elements/timer_panel.ctp:54
msgid "Graph"
msgstr "Gráfico"
#: views/elements/variables_panel.ctp:21
msgid "View Variables"
msgstr "Visualizar Variáveis"
#: views/helpers/simple_graph.php:79
msgid "Starting %sms into the request, taking %sms"
msgstr "Começando %sms na requisição, levando %sms"
@@ -0,0 +1,135 @@
# LANGUAGE translation of CakePHP Application
# Copyright YEAR NAME <EMAIL@ADDRESS>
# No version information was available in the source files.
#
#, fuzzy
msgid ""
msgstr "Project-Id-Version: PROJECT VERSION\n"
"POT-Creation-Date: 2009-05-27 09:47+0200\n"
"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\n"
"Last-Translator: NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <EMAIL@ADDRESS>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
#: controllers/components/toolbar.php:91
msgid "Component initialization and startup"
msgstr ""
#: controllers/components/toolbar.php:140
msgid "Controller Action"
msgstr ""
#: controllers/components/toolbar.php:167
msgid "Render Controller Action"
msgstr ""
#: controllers/components/toolbar.php:231
msgid "Could not load DebugToolbar panel %s"
msgstr ""
#: views/elements/debug_toolbar.ctp:25
msgid "There are no active panels. You must enable a panel to see its output."
msgstr ""
#: views/elements/history_panel.ctp:21
msgid "Request History"
msgstr ""
#: views/elements/history_panel.ctp:23
msgid "No previous requests logged."
msgstr ""
#: views/elements/history_panel.ctp:25
msgid "previous requests available"
msgstr ""
#: views/elements/history_panel.ctp:27
msgid "Restore to current request"
msgstr ""
#: views/elements/log_panel.ctp:21
msgid "Logs"
msgstr ""
#: views/elements/log_panel.ctp:28
msgid "Time"
msgstr ""
#: views/elements/log_panel.ctp:28 views/elements/timer_panel.ctp:54
msgid "Message"
msgstr ""
#: views/elements/log_panel.ctp:37
msgid "There were no log entries made this request"
msgstr ""
#: views/elements/request_panel.ctp:21
msgid "Request"
msgstr ""
#: views/elements/request_panel.ctp:35
msgid "Current Route"
msgstr ""
#: views/elements/session_panel.ctp:21
msgid "Session"
msgstr ""
#: views/elements/sql_log_panel.ctp:21
msgid "Sql Logs"
msgstr ""
#: views/elements/sql_log_panel.ctp:31
msgid "toggle (%s) query explains for %s"
msgstr ""
#: views/elements/sql_log_panel.ctp:39
msgid "No slow queries!, or your database does not support EXPLAIN"
msgstr ""
#: views/elements/sql_log_panel.ctp:44
msgid "No active database connections"
msgstr ""
#: views/elements/timer_panel.ctp:33
msgid "Memory"
msgstr ""
#: views/elements/timer_panel.ctp:35
msgid "Current Memory Use"
msgstr ""
#: views/elements/timer_panel.ctp:39
msgid "Peak Memory Use"
msgstr ""
#: views/elements/timer_panel.ctp:43
msgid "Timers"
msgstr ""
#: views/elements/timer_panel.ctp:45
msgid "%s (ms)"
msgstr ""
#: views/elements/timer_panel.ctp:46
msgid "Total Request Time:"
msgstr ""
#: views/elements/timer_panel.ctp:54
msgid "Time in ms"
msgstr ""
#: views/elements/timer_panel.ctp:54
msgid "Graph"
msgstr ""
#: views/elements/variables_panel.ctp:21
msgid "View Variables"
msgstr ""
#: views/helpers/simple_graph.php:79
msgid "Starting %sms into the request, taking %sms"
msgstr ""
+78
View File
@@ -0,0 +1,78 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Log\Engine;
use Cake\Log\Engine\BaseLog;
/**
* A CakeLog listener which saves having to munge files or other configured loggers.
*
*/
class DebugKitLog extends BaseLog
{
/**
* logs
*
* @var array
*/
protected $_logs = [];
/**
* Captures log messages in memory
*
* @param string $type The type of message being logged.
* @param string $message The message being logged.
* @param array $context Additional context data
* @return void
*/
public function log($type, $message, array $context = [])
{
if (!isset($this->_logs[$type])) {
$this->_logs[$type] = [];
}
$this->_logs[$type][] = [date('Y-m-d H:i:s'), $this->_format($message)];
}
/**
* Get the logs.
*
* @return array
*/
public function all()
{
return $this->_logs;
}
/**
* Get the number of log entries.
*
* @return int
*/
public function count()
{
return array_reduce($this->_logs, function ($sum, $v) {
return $sum + count($v);
}, 0);
}
/**
* Check if there are no logs.
*
* @return bool
*/
public function noLogs()
{
return empty($this->_logs);
}
}
+54
View File
@@ -0,0 +1,54 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Mailer;
/**
* A simple structure for representing the results of a sent email
*/
abstract class AbstractResult
{
/**
* The list of headers included in the email
*
* @var array
*/
protected $headers = [];
/**
* The rendered parts of the email (for example text and html)
*
* @var array
*/
protected $parts = [];
/**
* Returns the list of headers included in th email
*
* @return array
*/
public function getHeaders()
{
return $this->headers;
}
/**
* Returns the rendered parts in th email
*
* @return array
*/
public function getParts()
{
return $this->parts;
}
}
+102
View File
@@ -0,0 +1,102 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Mailer;
use Cake\Datasource\ModelAwareTrait;
use Cake\Mailer\MailerAwareTrait;
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;
/**
* Base class for Mailer Previews.
*/
class MailPreview
{
use MailerAwareTrait;
use ModelAwareTrait;
/**
* Returns the name of an email if it is valid
*
* @param string $email Email name
* @return null|string
*/
public function find($email)
{
if ($this->validEmail($email)) {
return $email;
}
return null;
}
/**
* Returns a list of valid emails
*
* @return array
*/
public function getEmails()
{
$emails = [];
foreach (get_class_methods($this) as $methodName) {
if (!$this->validEmail($methodName)) {
continue;
}
$emails[] = $methodName;
}
return $emails;
}
/**
* Returns the name of this preview
*
* @return string
*/
public function name()
{
$classname = get_class($this);
$pos = strrpos($classname, '\\');
return substr($classname, $pos + 1);
}
/**
* Returns whether or not a specified email is valid
* for this MailPreview instance
*
* @param string $email Name of email
* @return bool
*/
protected function validEmail($email)
{
if (empty($email)) {
return false;
}
$baseClass = new ReflectionClass(get_class());
if ($baseClass->hasMethod($email)) {
return false;
}
try {
$method = new ReflectionMethod($this, $email);
} catch (ReflectionException $e) {
return false;
}
return $method->isPublic();
}
}
+63
View File
@@ -0,0 +1,63 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Mailer;
use Cake\Mailer\Mailer;
use ReflectionClass;
/**
* Represents the result of a preview for a given mailer
*/
class PreviewResult extends AbstractResult
{
/**
* Processes the mailer to extract the headers and parts
*
* @param Mailer $mailer The mailer instance to execute and extract the email data from
* @param string $method The method to execute in the mailer
*/
public function __construct(Mailer $mailer, $method)
{
$this->processMailer(clone $mailer, $method);
$mailer->reset();
}
/**
* Executes the mailer and extracts the relevant information from the generated email
*
* @param Mailer $mailer The mailer instance to execute and extract the email data from
* @param string $method The method to execute in the mailer
* @return void
*/
protected function processMailer(Mailer $mailer, $method)
{
$reflection = new ReflectionClass($mailer);
$prop = $reflection->getProperty('_email');
$prop->setAccessible(true);
$email = $prop->getValue($mailer);
if (!$email->viewBuilder()->getTemplate()) {
$email->viewBuilder()->setTemplate($method);
}
$render = (new ReflectionClass($email))
->getMethod('_renderTemplates')
->getClosure($email);
$this->parts = $render('');
$extra = ['from', 'sender', 'replyTo', 'readReceipt', 'returnPath', 'to', 'cc', 'subject'];
$this->headers = array_filter($email->getHeaders($extra));
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Mailer;
use Cake\Mailer\Mailer;
use ReflectionClass;
/**
* Represents the result of an already sent email
*
*/
class SentMailResult extends AbstractResult
{
/**
* Processes the mailer to extract the headers and parts
*
* @param array $headers The headers included in the email
* @param array $parts The rendered parts in the email
*/
public function __construct(array $headers, array $parts)
{
$this->headers = $headers;
$this->parts = $parts;
}
}
@@ -0,0 +1,141 @@
<?php
namespace DebugKit\Mailer\Transport;
use Cake\Core\App;
use Cake\Mailer\AbstractTransport;
use Cake\Mailer\Email;
/**
* Debug Transport class, useful for emulating the email sending process and inspecting
* the resulting email message before actually sending it during development
*/
class DebugKitTransport extends AbstractTransport
{
/**
* The transport object this class is decorating
*
* @var AbstractTransport
*/
protected $originalTransport;
/**
* A reference to the object were emails will be pushed to
* for logging.
*
* @var \ArrayObject
*/
protected $emailLog;
/**
* Constructor
*
* @param array $config Configuration options.
* @param AbstractTransport|null $originalTransport The transport that is to be decorated
*/
public function __construct($config = [], AbstractTransport $originalTransport = null)
{
$this->emailLog = $config['debugKitLog'];
if ($originalTransport !== null) {
$this->originalTransport = $originalTransport;
return;
}
$className = false;
if (!empty($config['originalClassName'])) {
$className = App::className(
$config['originalClassName'],
'Mailer/Transport',
'Transport'
);
}
if ($className) {
unset($config['originalClassName'], $config['debugKitLog']);
$this->originalTransport = new $className($config);
}
}
/**
* Send mail
*
* @param \Cake\Mailer\Email $email Cake Email
* @return array
*/
public function send(Email $email)
{
$headers = $email->getHeaders(['from', 'sender', 'replyTo', 'readReceipt', 'returnPath', 'to', 'cc']);
$parts = [
'text' => $email->message(Email::MESSAGE_TEXT),
'html' => $email->message(Email::MESSAGE_HTML),
];
$headers['Subject'] = $email->getOriginalSubject();
$result = ['headers' => $headers, 'message' => $parts];
$this->emailLog[] = $result;
if ($this->originalTransport !== null) {
return $this->originalTransport->send($email);
}
return $result;
}
/**
* Proxy unknown methods to the wrapped object
*
* @param string $method The method to call
* @param array $args The args to call $method with.
* @return mixed
*/
public function __call($method, array $args)
{
return call_user_func_array([$this->originalTransport, $method], $args);
}
/**
* Proxy property reads to the wrapped object
*
* @param string $name The property to read.
* @return mixed
*/
public function __get($name)
{
return $this->originalTransport->{$name};
}
/**
* Proxy property changes to the wrapped object
*
* @param string $name The property to read.
* @param mixed $value The property value.
* @return mixed
*/
public function __set($name, $value)
{
return $this->originalTransport->{$name} = $value;
}
/**
* Proxy property changes to the wrapped object
*
* @param string $name The property to read.
* @return bool
*/
public function __isset($name)
{
return isset($this->originalTransport->{$name});
}
/**
* Proxy property changes to the wrapped object
*
* @param string $name The property to delete.
* @return void
*/
public function __unset($name)
{
unset($this->originalTransport->{$name});
}
}
@@ -0,0 +1,61 @@
<?php
/**
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Middleware;
use Cake\Core\Configure;
use Cake\Event\EventManager;
use DebugKit\ToolbarService;
/**
* PSR-7 Middleware that enables DebugKit for the layers below.
*/
class DebugKitMiddleware
{
/**
* @var \DebugKit\ToolbarService
*/
protected $service;
/**
* Constructor
*
* @param DebugKit\ToolbarService $service The configured service, or null.
*/
public function __construct(ToolbarService $service = null)
{
$service = $service ?: new ToolbarService(EventManager::instance(), (array)Configure::read('DebugKit'));
$this->service = $service;
}
/**
* Invoke the middleware.
*
* DebugKit will augment the response and add the toolbar if possible.
*
* @param \Psr\Http\Message\ServerRequestInterface $request The request.
* @param \Psr\Http\Message\ResponseInterface $response The response.
* @param callable $next Callback to invoke the next middleware.
* @return \Psr\Http\Message\ResponseInterface A response
*/
public function __invoke($request, $response, $next)
{
$this->service->loadPanels();
$this->service->initializePanels();
$response = $next($request, $response);
$row = $this->service->saveData($request, $response);
if (!$row) {
return $response;
}
return $this->service->injectScripts($row, $response);
}
}
@@ -0,0 +1,68 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since DebugKit 1.3
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Model\Behavior;
use Cake\Event\Event;
use Cake\ORM\Behavior;
use DebugKit\DebugTimer;
/**
* Class TimedBehavior
*/
class TimedBehavior extends Behavior
{
/**
* beforeFind, starts a timer for a find operation.
*
* @param \Cake\Event\Event $event The beforeFind event
* @param \Cake\ORM\Query $query Query
* @return \Cake\ORM\Query
*/
public function beforeFind(Event $event, $query)
{
$alias = $event->getSubject()->getAlias();
DebugTimer::start($alias . '_find', $alias . '->find()');
return $query->formatResults(function ($results) use ($alias) {
DebugTimer::stop($alias . '_find');
return $results;
});
}
/**
* beforeSave, starts a time before a save is initiated.
*
* @param \Cake\Event\Event $event The beforeSave event
* @return void
*/
public function beforeSave(Event $event)
{
$alias = $event->getSubject()->getAlias();
DebugTimer::start($alias . '_save', $alias . '->save()');
}
/**
* afterSave, stop the timer started from a save.
*
* @param \Cake\Event\Event $event The afterSave event
* @return void
*/
public function afterSave(Event $event)
{
$alias = $event->getSubject()->getAlias();
DebugTimer::stop($alias . '_save');
}
}
+52
View File
@@ -0,0 +1,52 @@
<?php
/**
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Model\Entity;
use Cake\ORM\Entity;
/**
* Panel entity class.
*
* @property int $id
* @property int $request_id
* @property string $title
* @property string $element
* @property string $content
*/
class Panel extends Entity
{
/**
* Some fields should not be in JSON/array exports.
*
* @var array
*/
protected $_hidden = ['content'];
/**
* Read the stream contents.
*
* Over certain sizes PDO will return file handles.
* For backwards compatibility and consistency we smooth over that difference here.
*
* @param mixed $content Content
* @return string
*/
protected function _getContent($content)
{
if (is_resource($content)) {
return stream_get_contents($content);
}
return $content;
}
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace DebugKit\Model\Entity;
use Cake\ORM\Entity;
/**
* Request entity class.
*
* @property int $id
* @property array $panels
*/
class Request extends Entity
{
}
@@ -0,0 +1,77 @@
<?php
/**
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Model\Table;
use Cake\Core\App;
use PDOException;
/**
* A set of methods for building a database table when it is missing.
*
* Because the debugkit doesn't come with a pre-built SQLite database,
* we'll need to make it as we need it.
*
* This trait lets us dump fixture schema into a given database at runtime.
*/
trait LazyTableTrait
{
/**
* Ensures the tables for the given fixtures exist in the schema.
*
* If the tables do not exist, they will be created on the current model's connection.
*
* @param array $fixtures The fixture names to check and/or insert.
* @return void
* @throws \RuntimeException When fixtures are missing/unknown/fail.
*/
public function ensureTables(array $fixtures)
{
/** @var \Cake\Database\Connection $connection */
$connection = $this->getConnection();
$schema = $connection->getSchemaCollection();
try {
$existing = $schema->listTables();
} catch (PDOException $e) {
// Handle errors when SQLite blows up if the schema has changed.
if (strpos($e->getMessage(), 'schema has changed') !== false) {
$existing = $schema->listTables();
} else {
throw $e;
}
}
try {
foreach ($fixtures as $name) {
$class = App::className($name, 'Test/Fixture', 'Fixture');
if ($class === false) {
throw new \RuntimeException("Unknown fixture '$name'.");
}
/** @var \Cake\Datasource\FixtureInterface $fixture */
$fixture = new $class($connection->configName());
if (in_array($fixture->table, $existing)) {
continue;
}
$fixture->create($connection);
}
} catch (PDOException $e) {
if (strpos($e->getMessage(), 'unable to open')) {
throw new RuntimeException(
'Could not create a SQLite database. ' .
'Ensure that your webserver has write access to the database file and folder it is in.'
);
}
throw $e;
}
}
}
@@ -0,0 +1,74 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Model\Table;
use Cake\ORM\Query;
use Cake\ORM\Table;
use DebugKit\Model\Entity\Panel;
/**
* The panels table collects the information for each panel on
* each request.
*
* @method Panel get($primaryKey, $options = [])
* @method Panel newEntity($data = null, array $options = [])
* @method Panel[] newEntities(array $data, array $options = [])
* @method Panel save(\Cake\Datasource\EntityInterface $entity, $options = [])
* @method Panel patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = [])
* @method Panel[] patchEntities($entities, array $data, array $options = [])
* @method Panel findOrCreate($search, callable $callback = null)
*/
class PanelsTable extends Table
{
use LazyTableTrait;
/**
* initialize method
*
* @param array $config Config data.
* @return void
*/
public function initialize(array $config)
{
$this->belongsTo('DebugKit.Requests');
$this->ensureTables(['DebugKit.Requests', 'DebugKit.Panels']);
}
/**
* Find panels by requestid
*
* @param \Cake\ORM\Query $query The query
* @param array $options The options to use.
* @return \Cake\ORM\Query The query.
* @throws \RuntimeException
*/
public function findByRequest(Query $query, array $options)
{
if (empty($options['requestId'])) {
throw new \RuntimeException(__d('debug_kit', 'Missing request id in {0}.', 'findByRequest()'));
}
return $query->where(['Panels.request_id' => $options['requestId']])
->order(['Panels.title' => 'ASC']);
}
/**
* DebugKit tables are special.
*
* @return string
*/
public static function defaultConnectionName()
{
return 'debug_kit';
}
}
@@ -0,0 +1,128 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Model\Table;
use Cake\Core\Configure;
use Cake\Database\Driver\Sqlite;
use Cake\ORM\Query;
use Cake\ORM\Table;
use DebugKit\Model\Entity\Request;
/**
* The requests table tracks basic information about each request.
*
* @method Request get($primaryKey, $options = [])
* @method Request newEntity($data = null, array $options = [])
* @method Request[] newEntities(array $data, array $options = [])
* @method Request save(\Cake\Datasource\EntityInterface $entity, $options = [])
* @method Request patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = [])
* @method Request[] patchEntities($entities, array $data, array $options = [])
* @method Request findOrCreate($search, callable $callback = null)
*/
class RequestsTable extends Table
{
use LazyTableTrait;
/**
* initialize method
*
* @param array $config Config data.
* @return void
*/
public function initialize(array $config)
{
$this->hasMany('DebugKit.Panels', [
'sort' => ['Panels.title' => 'ASC'],
]);
$this->addBehavior('Timestamp', [
'events' => [
'Model.beforeSave' => ['requested_at' => 'new'],
],
]);
$this->ensureTables(['DebugKit.Requests', 'DebugKit.Panels']);
}
/**
* DebugKit tables are special.
*
* @return string
*/
public static function defaultConnectionName()
{
return 'debug_kit';
}
/**
* Finder method to get recent requests as a simple array
*
* @param \Cake\ORM\Query $query The query
* @param array $options The options
* @return Query The query.
*/
public function findRecent(Query $query, array $options)
{
return $query->order(['Requests.requested_at' => 'DESC'])
->limit(10);
}
/**
* Check if garbage collection should be run
*
* @return bool
*/
protected function shouldGc()
{
return time() % 100 === 0;
}
/**
* Garbage collect old request data.
*
* Delete request data that is older than latest 20 requests.
* You can use the `DebugKit.requestCount` config to change this limit.
* This method will only trigger periodically.
*
* @return void
*/
public function gc()
{
if (!$this->shouldGc()) {
return;
}
$noPurge = $this->find()
->select(['id'])
->enableHydration(false)
->order(['requested_at' => 'desc'])
->limit(Configure::read('DebugKit.requestCount') ?: 20)
->extract('id')
->toArray();
$query = $this->Panels->query()
->delete()
->where(['request_id NOT IN' => $noPurge]);
$statement = $query->execute();
$statement->closeCursor();
$query = $this->query()
->delete()
->where(['id NOT IN' => $noPurge]);
$statement = $query->execute();
$statement->closeCursor();
$conn = $this->getConnection();
if ($conn->getDriver() instanceof Sqlite) {
$conn->execute('VACUUM;');
}
}
}
+71
View File
@@ -0,0 +1,71 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*
*/
namespace DebugKit\Panel;
use Cake\Cache\Cache;
use DebugKit\Cache\Engine\DebugEngine;
use DebugKit\DebugPanel;
/**
* A panel for spying on cache engines.
*/
class CachePanel extends DebugPanel
{
/**
* The cache spy instances used.
*
* @var DebugEngine[]
*/
protected $_instances = [];
/**
* Initialize - install cache spies.
*
* @return void
*/
public function initialize()
{
foreach (Cache::configured() as $name) {
$config = Cache::getConfig($name);
if (isset($config['className']) && $config['className'] instanceof DebugEngine) {
$instance = $config['className'];
} elseif (isset($config['className'])) {
Cache::drop($name);
$instance = new DebugEngine($config);
Cache::setConfig($name, $instance);
}
if (isset($instance)) {
$this->_instances[$name] = $instance;
}
}
}
/**
* Get the data for this panel
*
* @return array
*/
public function data()
{
$metrics = [];
foreach ($this->_instances as $name => $instance) {
$metrics[$name] = $instance->metrics();
}
return [
'metrics' => $metrics,
];
}
}
+170
View File
@@ -0,0 +1,170 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Panel;
use Cake\Event\Event;
use Cake\Utility\Hash;
use Composer\Json\JsonFile;
use DebugKit\DebugInclude;
use DebugKit\DebugPanel;
/**
* Provides a list of deprecated methods for the current request
*
*/
class DeprecationsPanel extends DebugPanel
{
/**
* The list of depreated errors.
*
* @var array
*/
protected static $deprecatedErrors = [];
/**
* instance of DebugInclude
*
* @var \DebugKit\DebugInclude
*/
protected $_debug;
/**
* construct
*/
public function __construct()
{
$this->_debug = new DebugInclude();
}
/**
* Get a list of files that were deprecated and split them out into the various parts of the app
*
* @return array
*/
protected function _prepare()
{
$errors = static::$deprecatedErrors;
$return = ['cake' => [], 'app' => [], 'plugins' => [], 'vendor' => [], 'other' => []];
foreach ($errors as $error) {
$file = $error['file'];
$line = $error['line'];
if (isset($error['context']['frame'])) {
$file = $error['context']['frame']['file'];
$line = $error['context']['frame']['line'];
}
$errorData = [
'file' => $file,
'line' => $line,
'message' => $error['message'],
];
$pluginName = $this->_debug->getPluginName($file);
if ($pluginName) {
$errorData['niceFile'] = $this->_debug->niceFileName($file, 'plugin', $pluginName);
$return['plugins'][$pluginName][] = $errorData;
} elseif ($this->_debug->isAppFile($file)) {
$errorData['niceFile'] = $this->_debug->niceFileName($file, 'app');
$return['app'][] = $errorData;
} elseif ($this->_debug->isCakeFile($file)) {
$errorData['niceFile'] = $this->_debug->niceFileName($file, 'cake');
$return['cake'][] = $errorData;
} else {
$vendorName = $this->_debug->getComposerPackageName($file);
if ($vendorName) {
$errorData['niceFile'] = $this->_debug->niceFileName($file, 'vendor', $vendorName);
$return['vendor'][$vendorName][] = $errorData;
} else {
$errorData['niceFile'] = $this->_debug->niceFileName($file, 'root');
$return['other'][] = $errorData;
}
}
}
ksort($return['app']);
ksort($return['cake']);
ksort($return['plugins']);
ksort($return['vendor']);
foreach ($return['plugins'] as &$plugin) {
ksort($plugin);
}
return $return;
}
/**
* Add a error
*
* @param array $error The deprecated error
* @return void
*/
public static function addDeprecatedError($error)
{
static::$deprecatedErrors[] = $error;
}
/**
* Reset the tracked errors.
*
* @return void
*/
public static function clearDeprecatedErrors()
{
static::$deprecatedErrors = [];
}
/**
* Get the number of files deprecated in this request.
*
* @return string
*/
public function summary()
{
$data = $this->_data;
if (empty($data)) {
$data = $this->_prepare();
}
return array_reduce($data, function ($carry, $item) {
if (empty($item)) {
return $carry;
}
// app, cake, or other groups
if (Hash::dimensions($item) == 2) {
return $carry + count($item);
}
// plugin and vendor groups
foreach ($item as $group) {
$carry += count($group);
}
return $carry;
}, 0);
}
/**
* Shutdown callback
*
* @param \Cake\Event\Event $event Event
* @return void
*/
public function shutdown(Event $event)
{
$this->_data = $this->_prepare();
}
}
+97
View File
@@ -0,0 +1,97 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*
*/
namespace DebugKit\Panel;
use Cake\Controller\Controller;
use Cake\Core\Configure;
use Cake\Event\Event;
use DebugKit\DebugPanel;
/**
* Provides information about your PHP and CakePHP environment to assist with debugging.
*
*/
class EnvironmentPanel extends DebugPanel
{
/**
* Get necessary data about environment to pass back to controller
*
* @return array
*/
protected function _prepare()
{
$return = [];
// PHP Data
$phpVer = phpversion();
$return['php'] = array_merge(
['PHP_VERSION' => $phpVer],
$_SERVER
);
unset($return['php']['argv']);
// ini Data
$return['ini'] = [
'intl.default_locale' => ini_get('intl.default_locale'),
'memory_limit' => ini_get('memory_limit'),
'error_reporting' => ini_get('error_reporting'),
'upload_max_filesize' => ini_get('upload_max_filesize'),
'post_max_size' => ini_get('post_max_size'),
];
// CakePHP Data
$return['cake'] = [
'APP' => APP,
'APP_DIR' => APP_DIR,
'CACHE' => CACHE,
'CAKE' => CAKE,
'CAKE_CORE_INCLUDE_PATH' => CAKE_CORE_INCLUDE_PATH,
'CONFIG' => CONFIG,
'CORE_PATH' => CORE_PATH,
'CAKE_VERSION' => Configure::version(),
'DS' => DS,
'LOGS' => LOGS,
'ROOT' => ROOT,
'TESTS' => TESTS,
'TMP' => TMP,
'WWW_ROOT' => WWW_ROOT,
];
$hiddenCakeConstants = array_fill_keys(
[
'TIME_START', 'SECOND', 'MINUTE', 'HOUR', 'DAY', 'WEEK', 'MONTH', 'YEAR',
],
''
);
$var = get_defined_constants(true);
$return['app'] = array_diff_key($var['user'], $return['cake'], $hiddenCakeConstants);
if (isset($var['hidef'])) {
$return['hidef'] = $var['hidef'];
}
return $return;
}
/**
* Shutdown callback
*
* @param \Cake\Event\Event $event Event
* @return void
*/
public function shutdown(Event $event)
{
$this->_data = $this->_prepare();
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Panel;
use Cake\ORM\TableRegistry;
use DebugKit\DebugPanel;
/**
* Provides debug information on previous requests.
*
*/
class HistoryPanel extends DebugPanel
{
/**
* Get the data for the panel.
*
* @return array
*/
public function data()
{
$table = TableRegistry::get('DebugKit.Requests');
$recent = $table->find('recent');
return [
'requests' => $recent->toArray(),
];
}
/**
* Gets the initial text for the history summary
*
* @return string
*/
public function summary()
{
return '0 xhr';
}
}
+116
View File
@@ -0,0 +1,116 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Panel;
use Cake\Event\Event;
use Cake\Utility\Hash;
use Composer\Json\JsonFile;
use DebugKit\DebugInclude;
use DebugKit\DebugPanel;
/**
* Provides a list of included files for the current request
*
*/
class IncludePanel extends DebugPanel
{
/**
* instance of DebugInclude
*
* @var \DebugKit\DebugInclude
*/
protected $_debug;
/**
* construct
*/
public function __construct()
{
$this->_debug = new DebugInclude();
}
/**
* Get a list of files that were included and split them out into the various parts of the app
*
* @return array
*/
protected function _prepare()
{
$return = ['cake' => [], 'app' => [], 'plugins' => [], 'vendor' => [], 'other' => []];
foreach (get_included_files() as $file) {
$pluginName = $this->_debug->getPluginName($file);
if ($pluginName) {
$return['plugins'][$pluginName][$this->_debug->getFileType($file)][] = $this->_debug->niceFileName($file, 'plugin', $pluginName);
} elseif ($this->_debug->isAppFile($file)) {
$return['app'][$this->_debug->getFileType($file)][] = $this->_debug->niceFileName($file, 'app');
} elseif ($this->_debug->isCakeFile($file)) {
$return['cake'][$this->_debug->getFileType($file)][] = $this->_debug->niceFileName($file, 'cake');
} else {
$vendorName = $this->_debug->getComposerPackageName($file);
if ($vendorName) {
$return['vendor'][$vendorName][] = $this->_debug->niceFileName($file, 'vendor', $vendorName);
} else {
$return['other'][] = $this->_debug->niceFileName($file, 'root');
}
}
}
$return['paths'] = $this->_debug->includePaths();
ksort($return['app']);
ksort($return['cake']);
ksort($return['plugins']);
ksort($return['vendor']);
foreach ($return['plugins'] as &$plugin) {
ksort($plugin);
}
return $return;
}
/**
* Get the number of files included in this request.
*
* @return string
*/
public function summary()
{
$data = $this->_data;
if (empty($data)) {
$data = $this->_prepare();
}
unset($data['paths']);
$data = array_filter($data, function ($v, $k) {
return !empty($v);
}, ARRAY_FILTER_USE_BOTH);
return count(Hash::flatten($data));
}
/**
* Shutdown callback
*
* @param \Cake\Event\Event $event Event
* @return void
*/
public function shutdown(Event $event)
{
$this->_data = $this->_prepare();
}
}
+66
View File
@@ -0,0 +1,66 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Panel;
use Cake\Log\Log;
use DebugKit\DebugPanel;
/**
* Log Panel - Reads log entries made this request.
*
*/
class LogPanel extends DebugPanel
{
/**
* Initialize hook - sets up the log listener.
*
* @return void
*/
public function initialize()
{
if (Log::getConfig('debug_kit_log_panel')) {
return;
}
Log::setConfig('debug_kit_log_panel', [
'engine' => 'DebugKit.DebugKit',
]);
}
/**
* Get the panel data
*
* @return array
*/
public function data()
{
return [
'logger' => Log::engine('debug_kit_log_panel'),
];
}
/**
* Get the summary data.
*
* @return string
*/
public function summary()
{
$logger = Log::engine('debug_kit_log_panel');
if (!$logger) {
return '0';
}
return (string)$logger->count();
}
}
+93
View File
@@ -0,0 +1,93 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Panel;
use ArrayObject;
use Cake\Core\App;
use Cake\Mailer\TransportFactory;
use DebugKit\DebugPanel;
use DebugKit\Mailer\Transport\DebugKitTransport;
use ReflectionClass;
/**
* Provides debug information on the Emails sent during the request
*/
class MailPanel extends DebugPanel
{
/**
* The list of emails produced during the request
*
* @var ArrayObject
*/
protected $emailLog;
/**
* Initialize hook - configures the email transport.
*
* @return void
*/
public function initialize()
{
$reflection = new ReflectionClass(TransportFactory::class);
$property = $reflection->getProperty('_config');
$property->setAccessible(true);
$configs = $property->getValue();
$log = $this->emailLog = new ArrayObject();
foreach ($configs as $name => &$transport) {
if (is_object($transport)) {
$configs[$name] = new DebugKitTransport(['debugKitLog' => $log], $transport);
continue;
}
$className = App::className($transport['className'], 'Mailer/Transport', 'Transport');
if (!$className) {
continue;
}
$transport['originalClassName'] = $transport['className'];
$transport['className'] = 'DebugKit.DebugKit';
$transport['debugKitLog'] = $log;
}
$property->setValue($configs);
}
/**
* Get the data this panel wants to store.
*
* @return array
*/
public function data()
{
return [
'emails' => isset($this->emailLog) ? $this->emailLog->getArrayCopy() : [],
];
}
/**
* Get summary data from the queries run.
*
* @return string
*/
public function summary()
{
if (empty($this->emailLog)) {
return '';
}
return (string)count($this->emailLog);
}
}
+46
View File
@@ -0,0 +1,46 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since DebugKit 3.5.2
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Panel;
use Composer\Json\JsonFile;
use DebugKit\DebugPanel;
/**
* Packages Panel - Reads all installed packages in the project.
*
*/
class PackagesPanel extends DebugPanel
{
/**
* Get the panel data
*
* @return array
*/
public function data()
{
$packages = $devPackages = [];
$lockFile = new JsonFile(ROOT . DIRECTORY_SEPARATOR . 'composer.lock');
if ($lockFile->exists()) {
$lockContent = $lockFile->read();
$packages = $lockContent['packages'];
$devPackages = $lockContent['packages-dev'];
}
return [
'packages' => $packages,
'devPackages' => $devPackages,
];
}
}
+83
View File
@@ -0,0 +1,83 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Panel;
use Cake\Core\App;
use Cake\Core\ObjectRegistry;
use Cake\Event\EventDispatcherTrait;
use Cake\Event\EventManager;
/**
* Registry object for panels.
*/
class PanelRegistry extends ObjectRegistry
{
use EventDispatcherTrait;
/**
* Constructor
*
* @param \Cake\Event\EventManager $events Event Manager that panels should bind to.
* Typically this is the global manager.
*/
public function __construct(EventManager $events)
{
$this->setEventManager($events);
}
/**
* Resolve a panel class name.
*
* Part of the template method for Cake\Utility\ObjectRegistry::load()
*
* @param string $class Partial class name to resolve.
* @return string|false Either the correct class name or false.
*/
protected function _resolveClassName($class)
{
return App::className($class, 'Panel', 'Panel');
}
/**
* Throws an exception when a component is missing.
*
* Part of the template method for Cake\Utility\ObjectRegistry::load()
*
* @param string $class The classname that is missing.
* @param string $plugin The plugin the component is missing in.
* @return void
* @throws \RuntimeException
*/
protected function _throwMissingClassError($class, $plugin)
{
throw new \RuntimeException(__d('debug_kit', "Unable to find ''{0}'' panel.", $class));
}
/**
* Create the panels instance.
*
* Part of the template method for Cake\Utility\ObjectRegistry::load()
*
* @param string $class The classname to create.
* @param string $alias The alias of the panel.
* @param array $config An array of config to use for the panel.
* @return \DebugKit\DebugPanel The constructed panel class.
*/
protected function _create($class, $alias, $config)
{
$instance = new $class($this, $config);
$this->getEventManager()->on($instance);
return $instance;
}
}
+46
View File
@@ -0,0 +1,46 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Panel;
use Cake\Controller\Controller;
use Cake\Event\Event;
use DebugKit\DebugPanel;
/**
* Provides debug information on the Current request params.
*
*/
class RequestPanel extends DebugPanel
{
/**
* Data collection callback.
*
* @param \Cake\Event\Event $event The shutdown event.
* @return void
*/
public function shutdown(Event $event)
{
/* @var \Cake\Controller\Controller $controller */
$controller = $event->getSubject();
$request = $controller->request;
$this->_data = [
'params' => $request->getAttribute("params"),
'query' => $request->getQueryParams(),
'data' => $request->getData(),
'cookie' => $request->getCookieParams(),
'get' => $_GET,
'matchedRoute' => $request->getParam('_matchedRoute'),
'headers' => ['response' => headers_sent($file, $line), 'file' => $file, 'line' => $line],
];
}
}
+61
View File
@@ -0,0 +1,61 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*
*/
namespace DebugKit\Panel;
use Cake\Core\Configure;
use Cake\Event\Event;
use Cake\Routing\Router;
use DebugKit\DebugPanel;
/**
* A panel to get the list of connected routes for the application
*/
class RoutesPanel extends DebugPanel
{
/**
* Get summary data for the routes panel.
*
* @return string
*/
public function summary()
{
$appClass = Configure::read('App.namespace') . '\Application';
if (class_exists($appClass, false) && !Router::$initialized) {
return '0';
}
$routes = array_filter(Router::routes(), function ($route) {
return (!isset($routes->defaults['plugin'])) || $route->defaults['plugin'] !== 'DebugKit';
});
return (string)count($routes);
}
/**
* Data collection callback.
*
* @param \Cake\Event\Event $event The shutdown event.
* @return void
*/
public function shutdown(Event $event)
{
$controller = $event->getSubject();
/* @var \Cake\Http\ServerRequest $request */
$request = $controller ? $controller->request : null;
$this->_data = [
'matchedRoute' => $request ? $request->getParam('_matchedRoute') : null,
];
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Panel;
use Cake\Event\Event;
use Cake\Http\ServerRequest as Request;
use DebugKit\DebugPanel;
/**
* Provides debug information on the Session contents.
*/
class SessionPanel extends DebugPanel
{
/**
* shutdown callback
*
* @param \Cake\Event\Event $event The event
* @return void
*/
public function shutdown(Event $event)
{
/* @var Request $request */
$request = $event->getSubject()->request;
if ($request) {
$this->_data = ['content' => $request->getSession()->read()];
}
}
}
+111
View File
@@ -0,0 +1,111 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Panel;
use Cake\Core\Configure;
use Cake\Datasource\ConnectionManager;
use Cake\ORM\Table;
use Cake\ORM\TableRegistry;
use DebugKit\Database\Log\DebugLog;
use DebugKit\DebugPanel;
/**
* Provides debug information on the SQL logs and provides links to an ajax explain interface.
*
*/
class SqlLogPanel extends DebugPanel
{
/**
* Loggers connected
*
* @var array
*/
protected $_loggers = [];
/**
* Initialize hook - configures logger.
*
* This will unfortunately build all the connections, but they
* won't connect until used.
*
* @return void
*/
public function initialize()
{
$configs = ConnectionManager::configured();
$includeSchemaReflection = (bool)Configure::read('DebugKit.includeSchemaReflection');
foreach ($configs as $name) {
$connection = ConnectionManager::get($name);
if ($connection->configName() === 'debug_kit') {
continue;
}
$logger = null;
if ($connection->isQueryLoggingEnabled()) {
$logger = $connection->getLogger();
}
if ($logger instanceof DebugLog) {
$logger->setIncludeSchema($includeSchemaReflection);
$this->_loggers[] = $logger;
continue;
}
$logger = new DebugLog($logger, $name, $includeSchemaReflection);
$connection->enableQueryLogging(true);
if (method_exists($connection, 'setLogger')) {
$connection->setLogger($logger);
} else {
$connection->logger($logger);
}
$this->_loggers[] = $logger;
}
}
/**
* Get the data this panel wants to store.
*
* @return array
*/
public function data()
{
return [
'tables' => array_map(function (Table $table) {
return $table->getAlias();
}, TableRegistry::getTableLocator()->genericInstances()),
'loggers' => $this->_loggers,
];
}
/**
* Get summary data from the queries run.
*
* @return string
*/
public function summary()
{
$count = $time = 0;
foreach ($this->_loggers as $logger) {
$count += count($logger->queries());
$time += $logger->totalTime();
}
if (!$count) {
return '0';
}
return "$count / $time ms";
}
}
+129
View File
@@ -0,0 +1,129 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Panel;
use Cake\I18n\Number;
use DebugKit\DebugMemory;
use DebugKit\DebugPanel;
use DebugKit\DebugTimer;
/**
* Provides debug information on all timers used in a request.
*/
class TimerPanel extends DebugPanel
{
/**
* Return an array of events to listen to.
*
* @return array
*/
public function implementedEvents()
{
$before = function ($name) {
return function () use ($name) {
DebugTimer::start($name, __d('debug_kit', $name));
};
};
$after = function ($name) {
return function () use ($name) {
DebugTimer::stop($name);
};
};
$both = function ($name) use ($before, $after) {
return [
['priority' => 0, 'callable' => $before('Event: ' . $name)],
['priority' => 999, 'callable' => $after('Event: ' . $name)],
];
};
return [
'Controller.initialize' => [
['priority' => 0, 'callable' => function () {
DebugMemory::record(__d('debug_kit', 'Controller initialization'));
}],
['priority' => 0, 'callable' => $before('Event: Controller.initialize')],
['priority' => 999, 'callable' => $after('Event: Controller.initialize')],
],
'Controller.startup' => [
['priority' => 0, 'callable' => $before('Event: Controller.startup')],
['priority' => 999, 'callable' => $after('Event: Controller.startup')],
['priority' => 999, 'callable' => function () {
DebugMemory::record(__d('debug_kit', 'Controller action start'));
DebugTimer::start(__d('debug_kit', 'Controller action'));
}],
],
'Controller.beforeRender' => [
['priority' => 0, 'callable' => function () {
DebugTimer::stop(__d('debug_kit', 'Controller action'));
}],
['priority' => 0, 'callable' => $before('Event: Controller.beforeRender')],
['priority' => 999, 'callable' => $after('Event: Controller.beforeRender')],
['priority' => 999, 'callable' => function () {
DebugMemory::record(__d('debug_kit', 'View Render start'));
DebugTimer::start(__d('debug_kit', 'View Render start'));
}],
],
'View.beforeRender' => $both('View.beforeRender'),
'View.afterRender' => $both('View.afterRender'),
'View.beforeLayout' => $both('View.beforeLayout'),
'View.afterLayout' => $both('View.afterLayout'),
'View.beforeRenderFile' => [
['priority' => 0, 'callable' => function ($event, $filename) {
DebugTimer::start(__d('debug_kit', 'Render {0}', $filename));
}],
],
'View.afterRenderFile' => [
['priority' => 0, 'callable' => function ($event, $filename) {
DebugTimer::stop(__d('debug_kit', 'Render {0}', $filename));
}],
],
'Controller.shutdown' => [
['priority' => 0, 'callable' => $before('Event: Controller.shutdown')],
['priority' => 0, 'callable' => function () {
DebugTimer::stop(__d('debug_kit', 'View Render start'));
DebugMemory::record(__d('debug_kit', 'Controller shutdown'));
}],
['priority' => 999, 'callable' => $after('Event: Controller.shutdown')],
],
];
}
/**
* Get the data for the panel.
*
* @return array
*/
public function data()
{
return [
'requestTime' => DebugTimer::requestTime(),
'timers' => DebugTimer::getAll(),
'memory' => DebugMemory::getAll(),
'peakMemory' => DebugMemory::getPeak(),
];
}
/**
* Get the summary for the panel.
*
* @return string
*/
public function summary()
{
$time = Number::precision(DebugTimer::requestTime(), 2) . ' s';
$memory = Number::toReadableSize(DebugMemory::getPeak());
return "$time / $memory";
}
}
+195
View File
@@ -0,0 +1,195 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Panel;
use Cake\Collection\Collection;
use Cake\Datasource\EntityInterface;
use Cake\Event\Event;
use Cake\Form\Form;
use Cake\ORM\Query;
use Cake\ORM\ResultSet;
use Cake\Utility\Hash;
use Closure;
use DebugKit\DebugPanel;
use Exception;
use InvalidArgumentException;
use PDO;
use RuntimeException;
use SimpleXMLElement;
/**
* Provides debug information on the View variables.
*
*/
class VariablesPanel extends DebugPanel
{
/**
* Extracts nested validation errors
*
* @param EntityInterface $entity Entity to extract
*
* @return array
*/
protected function _getErrors(EntityInterface $entity)
{
$errors = $entity->errors();
foreach ($entity->visibleProperties() as $property) {
$v = $entity[$property];
if ($v instanceof EntityInterface) {
$errors[$property] = $this->_getErrors($v);
} elseif (is_array($v)) {
foreach ($v as $key => $varValue) {
if ($varValue instanceof EntityInterface) {
$errors[$property][$key] = $this->_getErrors($varValue);
}
}
}
}
return Hash::filter($errors);
}
/**
* Safely retrieves debug information from an object
* and applies a callback.
*
* @param callable $walker The walker to apply on the debug info array.
* @param object $item The item whose debug info to retrieve.
*
* @return array|string
*/
protected function _walkDebugInfo(callable $walker, $item)
{
try {
$info = $item->__debugInfo();
} catch (\Exception $exception) {
return __d('debug_kit', 'Could not retrieve debug info - {0}. Error: {1} in {2}, line {3}', get_class($item), $exception->getMessage(), $exception->getFile(), $exception->getLine());
}
return array_map($walker, $info);
}
/**
* Shutdown event
*
* @param \Cake\Event\Event $event The event
* @return void
*/
public function shutdown(Event $event)
{
$controller = $event->getSubject();
$errors = [];
$walker = function (&$item) use (&$walker) {
if (
$item instanceof Collection ||
$item instanceof Query ||
$item instanceof ResultSet
) {
try {
$item = $item->toArray();
} catch (\Cake\Database\Exception $e) {
//Likely issue is unbuffered query; fall back to __debugInfo
$item = $this->_walkDebugInfo($walker, $item);
} catch (RuntimeException $e) {
// Likely a non-select query.
$item = $this->_walkDebugInfo($walker, $item);
} catch (InvalidArgumentException $e) {
$item = $this->_walkDebugInfo($walker, $item);
}
} elseif (
$item instanceof Closure ||
$item instanceof PDO ||
$item instanceof SimpleXMLElement
) {
$item = 'Unserializable object - ' . get_class($item);
} elseif ($item instanceof Exception) {
$item = sprintf(
'Unserializable object - %s. Error: %s in %s, line %s',
get_class($item),
$item->getMessage(),
$item->getFile(),
$item->getLine()
);
} elseif (is_object($item)) {
if (method_exists($item, '__debugInfo')) {
// Convert objects into using __debugInfo.
$item = $this->_walkDebugInfo($walker, $item);
} else {
$item = $this->trySerialize($item);
}
} elseif (is_resource($item)) {
$item = sprintf('[%s] %s', get_resource_type($item), $item);
}
return $this->trySerialize($item);
};
// Copy so viewVars is not mutated.
$vars = $controller->viewVars;
array_walk_recursive($vars, $walker);
foreach ($vars as $k => $v) {
// Get the validation errors for Entity
if ($v instanceof EntityInterface) {
$errors[$k] = $this->_getErrors($v);
} elseif ($v instanceof Form) {
$formError = $v->errors();
if (!empty($formError)) {
$errors[$k] = $formError;
}
}
}
$this->_data = [
'content' => $vars,
'errors' => $errors,
];
}
/**
* Try to serialize an item, provide an error message if not possible
*
* @param mixed $item Item to check
* @return mixed The $item if it is serializable, error message if not
*/
protected function trySerialize($item)
{
try {
serialize($item);
return $item;
} catch (\Exception $e) {
if (is_object($item)) {
return __d('debug_kit', 'Unserializable object - {0}. Error: {1} in {2}, line {3}', get_class($item), $e->getMessage(), $e->getFile(), $e->getLine());
}
return __d('debug_kit', 'Unserializable Error: {1} in {2}, line {3}', $e->getMessage(), $e->getFile(), $e->getLine());
}
}
/**
* Get summary data for the variables panel.
*
* @return string
*/
public function summary()
{
if (!isset($this->_data['content'])) {
return '0';
}
return (string)count($this->_data['content']);
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since DebugKit 3.15.0
* @license https://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit;
use Cake\Core\BasePlugin;
/**
* Plugin class for CakePHP 3.6.0 plugin collection.
*/
class Plugin extends BasePlugin
{
}
@@ -0,0 +1,146 @@
<?php
/**
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Routing\Filter;
use Cake\Event\Event;
use Cake\Event\EventManager;
use Cake\Http\Response;
use Cake\Http\ServerRequest as Request;
use Cake\Routing\DispatcherFilter;
use DebugKit\ToolbarService;
/**
* Toolbar injector filter.
*
* This class loads all the panels into the registry
* and binds the correct events into the provided event
* manager
*
* @deprecated Dispatch filters are deprecated. Long term this filter
* will be removed and replaced with middleware.
*/
class DebugBarFilter extends DispatcherFilter
{
/**
* @var \DebugKit\ToolbarService
*/
protected $service;
/**
* Constructor
*
* @param \Cake\Event\EventManager $events The event manager to use.
* @param array $config The configuration data for DebugKit.
*/
public function __construct(EventManager $events, array $config)
{
parent::__construct($config);
$this->service = new ToolbarService($events, $config);
}
/**
* Event bindings
*
* @return array
*/
public function implementedEvents()
{
return [
'Dispatcher.beforeDispatch' => [
'callable' => 'beforeDispatch',
'priority' => 0,
],
'Dispatcher.afterDispatch' => [
'callable' => 'afterDispatch',
'priority' => 9999,
],
];
}
/**
* Check whether or not debug kit is enabled.
*
* @return bool
*/
public function isEnabled()
{
return $this->service->isEnabled();
}
/**
* Get the list of loaded panels
*
* @return array
*/
public function loadedPanels()
{
return $this->service->registry()->loaded();
}
/**
* Get the list of loaded panels
*
* @param string $name The name of the panel you want to get.
* @return \DebugKit\DebugPanel|null The panel or null.
*/
public function panel($name)
{
$registry = $this->service->registry();
return $registry->{$name};
}
/**
* Do the required setup work.
*
* - Build panels.
* - Connect events
*
* @return void
*/
public function setup()
{
$this->service->loadPanels();
}
/**
* Call the initialize method onl all the loaded panels.
*
* @param \Cake\Event\Event $event The beforeDispatch event.
* @return void
*/
public function beforeDispatch(Event $event)
{
$this->service->initializePanels();
}
/**
* Save the toolbar data.
*
* @param \Cake\Event\Event $event The afterDispatch event.
* @return \Psr\Http\Message\ResponseInterface|void Modified response or null
*/
public function afterDispatch(Event $event)
{
/* @var Request $request */
$request = $event->getData('request');
/* @var Response $response */
$response = $event->getData('response');
$row = $this->service->saveData($request, $response);
if (!$row) {
return;
}
return $this->service->injectScripts($row, $response);
}
}
+173
View File
@@ -0,0 +1,173 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since DebugKit 1.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Shell;
use Cake\Console\Shell;
use Cake\Utility\Text;
/**
* Benchmark Shell Class
*
* Provides basic benchmarking of application requests
* functionally similar to Apache AB
*
* @since DebugKit 1.0
*/
class BenchmarkShell extends Shell
{
/**
* Main execution of shell
*
* @return void
*/
public function main()
{
$url = $this->args[0];
$defaults = ['t' => 100, 'n' => 10];
$options = array_merge($defaults, $this->params);
$times = [];
$this->out(Text::insert(__d('debug_kit', '-> Testing :url'), compact('url')));
$this->out("");
for ($i = 0; $i < $options['n']; $i++) {
if (floor($options['t'] - array_sum($times)) <= 0 || $options['n'] <= 1) {
break;
}
$start = microtime(true);
file_get_contents($url);
$stop = microtime(true);
$times[] = $stop - $start;
}
$this->_results($times);
}
/**
* Prints calculated results
*
* @param array $times Array of time values
* @return void
*/
protected function _results($times)
{
$duration = array_sum($times);
$requests = count($times);
$this->out(Text::insert(__d('debug_kit', 'Total Requests made: :requests'), compact('requests')));
$this->out(Text::insert(__d('debug_kit', 'Total Time elapsed: :duration (seconds)'), compact('duration')));
$this->out("");
$this->out(Text::insert(__d('debug_kit', 'Requests/Second: :rps req/sec'), [
'rps' => round($requests / $duration, 3),
]));
$this->out(Text::insert(__d('debug_kit', 'Average request time: :average-time seconds'), [
'average-time' => round($duration / $requests, 3),
]));
$this->out(Text::insert(__d('debug_kit', 'Standard deviation of average request time: :std-dev'), [
'std-dev' => round($this->_deviation($times, true), 3),
]));
$this->out(Text::insert(__d('debug_kit', 'Longest/shortest request: :longest sec/:shortest sec'), [
'longest' => round(max($times), 3),
'shortest' => round(min($times), 3),
]));
$this->out("");
}
/**
* One-pass, numerically stable calculation of population variance.
*
* Donald E. Knuth (1998).
* The Art of Computer Programming, volume 2: Seminumerical Algorithms, 3rd edn.,
* p. 232. Boston: Addison-Wesley.
*
* @param array $times Array of values
* @param bool $sample If true, calculates an unbiased estimate of the population
* variance from a finite sample.
* @return float Variance
*/
protected function _variance($times, $sample = true)
{
$n = $mean = $M2 = 0;
foreach ($times as $time) {
$n += 1;
$delta = $time - $mean;
$mean = $mean + $delta / $n;
$M2 = $M2 + $delta * ($time - $mean);
}
if ($sample) {
$n -= 1;
}
return $M2 / $n;
}
/**
* Calculate the standard deviation.
*
* @param array $times Array of values
* @param bool $sample ''
* @return float Standard deviation
*/
protected function _deviation($times, $sample = true)
{
return sqrt($this->_variance($times, $sample));
}
/**
* Get option parser.
*
* @return \Cake\Console\ConsoleOptionParser
*/
public function getOptionParser()
{
$parser = parent::getOptionParser();
$parser->description(__d(
'debug_kit',
'Allows you to obtain some rough benchmarking statistics' .
'about a fully qualified URL.'
))
->addArgument('url', [
'help' => __d('debug_kit', 'The URL to request.'),
'required' => true,
])
->addOption('n', [
'default' => 10,
'help' => __d('debug_kit', 'Number of iterations to perform.'),
])
->addOption('t', [
'default' => 100,
'help' => __d(
'debug_kit',
'Maximum total time for all iterations, in seconds.' .
'If a single iteration takes more than the timeout, only one request will be made'
),
])
->epilog(__d(
'debug_kit',
'Example Use: `cake benchmark --n 10 --t 100 http://localhost/testsite`. ' .
'<info>Note:</info> this benchmark does not include browser render times.'
));
return $parser;
}
}
+101
View File
@@ -0,0 +1,101 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since DebugKit 1.3
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\Shell;
use Cake\Console\Shell;
use Cake\Filesystem\Folder;
/**
* Whitespace shell. Helps find and trim whitespace from files.
*
* Based on jperras' shell found at http://bin.cakephp.org/view/626544881
*
* @since DebugKit 1.3
*/
class WhitespaceShell extends Shell
{
/**
* Will check files for whitespace and notify you
* of any files containing leading or trailing whitespace.
*
* @return void
*/
public function main()
{
$path = APP;
if (!empty($this->params['path']) && strpos($this->params['path'], '/') === 0) {
$path = $this->params['path'];
} elseif (!empty($this->params['path'])) {
$path .= $this->params['path'];
}
$folder = new Folder($path);
$r = $folder->findRecursive('.*\.php');
$this->out("Checking *.php in " . $path);
foreach ($r as $file) {
$c = file_get_contents($file);
if (preg_match('/^[\n\r|\n\r|\n|\r|\s]+\<\?php/', $c)) {
$this->out('!!!contains leading whitespaces: ' . $this->shortPath($file));
}
if (preg_match('/\?\>[\n\r|\n\r|\n|\r|\s]+$/', $c)) {
$this->out('!!!contains trailing whitespaces: ' . $this->shortPath($file));
}
}
}
/**
* Much like main() except files are modified. Be sure to have
* backups or use version control.
*
* @return void
*/
public function trim()
{
$path = APP;
if (!empty($this->params['path']) && strpos($this->params['path'], '/') === 0) {
$path = $this->params['path'];
} elseif (!empty($this->params['path'])) {
$path .= $this->params['path'];
}
$folder = new Folder($path);
$r = $folder->findRecursive('.*\.php');
$this->out("Checking *.php in " . $path);
foreach ($r as $file) {
$c = file_get_contents($file);
if (preg_match('/^[\n\r|\n\r|\n|\r|\s]+\<\?php/', $c) || preg_match('/\?\>[\n\r|\n\r|\n|\r|\s]+$/', $c)) {
$this->out('trimming' . $this->shortPath($file));
$c = preg_replace('/^[\n\r|\n\r|\n|\r|\s]+\<\?php/', '<?php', $c);
$c = preg_replace('/\?\>[\n\r|\n\r|\n|\r|\s]+$/', '?>', $c);
file_put_contents($file, $c);
}
}
}
/**
* get the option parser
*
* @return \Cake\Console\ConsoleOptionParser
*/
public function getOptionParser()
{
$parser = parent::getOptionParser();
return $parser->addOption('path', [
'short' => 'p',
'help' => __d('debug_kit', 'Absolute path or relative to {0}.', 'APP'),
]);
}
}
@@ -0,0 +1,26 @@
<?php
/**
* @var \App\View\AppView $this
*/
?>
<h1><?= __d('debug_kit', 'Debug Kit Dashboard') ?></h1>
<h2><?= __d('debug_kit', 'Database') ?></h2>
<ul>
<li><?= __d('debug_kit', 'Driver') ?>: <?= h($connection['driver']); ?></li>
<?php if (isset($connection['rows'])): ?>
<li><?= __d('debug_kit', 'Requests') ?>: <?= $this->Number->format($connection['rows']) ?></li>
<?php endif; ?>
</ul>
<?php if (!empty($connection['rows'])): ?>
<?= $this->Form->postLink(
__d('debug_kit', 'Reset database'),
['_method' => 'POST', 'action' => 'reset'],
['confirm' => 'Are you sure?']
); ?>
<?php endif; ?>
<h3>Actions</h3>
<ul>
<li><?= $this->Html->link(__d('debug_kit', 'Mail Preview'), ['controller' => 'MailPreview']); ?></li>
</ul>
@@ -0,0 +1,80 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since DebugKit 0.1
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
* @var \DebugKit\View\AjaxView $this
* @var array $metrics
*/
?>
<?php if (empty($metrics)): ?>
<p class="info"><?= __d('debug_kit', 'There were no cache operations this request.') ?></p>
<?php else: ?>
<?php foreach ($metrics as $name => $counters): ?>
<section class="section-tile">
<h3><?= __d('debug_kit', '{0} Metrics', h($name)) ?> </h3>
<button class="btn-primary clear-cache" data-name="<?= h($name) ?>"><?= __d('debug_kit', 'Clear {0} cache', h($name)) ?></button>
<span class="inline-message"></span>
<table cellspacing="0" cellpadding="0" class="debug-table">
<thead>
<tr>
<th><?= __d('debug_kit', 'Metric') ?></th>
<th><?= __d('debug_kit', 'Total') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($counters as $key => $val): ?>
<tr>
<td><?= h($key) ?></td>
<td class="right-text"><?= $val ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</section>
<?php endforeach; ?>
<?php endif; ?>
<script>
$(document).ready(function() {
var baseUrl = '<?= $this->Url->build([
'plugin' => 'DebugKit',
'controller' => 'Toolbar',
'action' => 'clearCache'
]); ?>';
function showMessage(el, text) {
el.show().text(text).fadeOut(2000);
}
$('.clear-cache').on('click', function(e) {
var el = $(this);
var name = el.data('name');
var messageEl = el.parent().find('.inline-message');
var xhr = $.ajax({
headers: {'X-CSRF-TOKEN': '<?= $this->request->getParam('_csrfToken') ?>'},
url: baseUrl,
data: {name: name},
dataType: 'json',
type: 'POST'
});
xhr.done(function(response) {
showMessage(messageEl, name + ' ' + '<?= __d('debug_kit', 'cache cleared.') ?>');
}).error(function(response) {
showMessage(messageEl, name + ' ' + '<?= __d('debug_kit', 'cache could not be cleared.') ?>');
});
e.preventDefault();
});
});
</script>
@@ -0,0 +1,63 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
* @var \DebugKit\View\AjaxView $this
* @var array $paths
* @var array $app
* @var array $cake
* @var array $plugins
* @var array $vendor
* @var array $other
*/
$printer = function ($section, $data) {
?>
<h3><?= h(ucfirst($section)) ?> </h3>
<ul class="list deprecation-list">
<?php foreach ($data as $message): ?>
<li>
<strong><?= h($message['niceFile']) ?>:<?= h($message['line']) ?></strong>
<br>
<?= h($message['message']) ?>
</li>
<?php endforeach; ?>
</ul>
<?php
};
?>
<?php
if (count($app)):
$printer('app', $app);
endif;
if (count($plugins)):
foreach ($plugins as $plugin => $pluginData):
$printer($plugin, $pluginData);
endforeach;
endif;
if (count($cake)):
$printer('cake', $cake);
endif;
if (count($vendor)):
foreach ($vendor as $vendorSection => $vendorData):
$printer($vendorSection, $vendorData);
endforeach;
endif;
if (count($other)):
$printer('other', $other);
endif;
@@ -0,0 +1,153 @@
<?php
/**
* Environment Panel Element
*
* Shows information about the current app environment
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
use Cake\Error\Debugger;
/**
* @var \DebugKit\View\AjaxView $this
* @var array $app
* @var array $cake
* @var array $php
* @var array $hidef
*/
?>
<h2><?= __d('debug_kit', 'Application Constants') ?></h2>
<?php if (!empty($app)): ?>
<table cellspacing="0" cellpadding="0" class="debug-table">
<thead>
<tr>
<th><?= __d('debug_kit', 'Constant') ?></th>
<th><?= __d('debug_kit', 'Value') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($app as $key => $val): ?>
<tr>
<td><?= h($key) ?></td>
<td><?= h(Debugger::exportVar($val)) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else: ?>
<div class="warning">
<?= __d('debug_kit', 'No application environment available.'); ?>
</div>
<?php endif; ?>
<h2><?= __d('debug_kit', 'CakePHP Constants') ?></h2>
<?php if (!empty($cake)): ?>
<table cellspacing="0" cellpadding="0" class="debug-table">
<thead>
<tr>
<th><?= __d('debug_kit', 'Constant') ?></th>
<th><?= __d('debug_kit', 'Value') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($cake as $key => $val): ?>
<tr>
<td><?= h($key) ?></td>
<td><?= h(Debugger::exportVar($val)) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else: ?>
<div class="warning">
<?= __d('debug_kit', 'CakePHP environment unavailable.'); ?>
</div>
<?php endif; ?>
<h2><?= __d('debug_kit', 'INI Environment') ?></h2>
<?php if (!empty($ini)): ?>
<table cellspacing="0" cellpadding="0" class="debug-table">
<thead>
<tr>
<th>INI Variable</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<?php foreach ($ini as $key => $val): ?>
<tr>
<td><?= h($key) ?></td>
<td><?= $this->Credentials->filter($val) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else: ?>
<div class="warning">
<?= __d('debug_kit', 'ini environment unavailable.'); ?>
</div>
<?php endif; ?>
<h2><?= __d('debug_kit', 'PHP Environment') ?></h2>
<?php if (!empty($php)): ?>
<table cellspacing="0" cellpadding="0" class="debug-table">
<thead>
<tr>
<th><?= __d('debug_kit', 'Environment Variable') ?></th>
<th><?= __d('debug_kit', 'Value') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($php as $key => $val): ?>
<tr>
<td><?= h($key) ?></td>
<td><?= $this->Credentials->filter($val) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else: ?>
<div class="warning">
<?= __d('debug_kit', 'PHP environment unavailable.'); ?>
</div>
<?php endif; ?>
<?php if (isset($hidef)): ?>
<h2><?= __d('debug_kit', 'Hidef Environment') ?></h2>
<?php if (!empty($hidef)): ?>
<table cellspacing="0" cellpadding="0" class="debug-table">
<thead>
<tr>
<th><?= __d('debug_kit', 'Constant') ?></th>
<th><?= __d('debug_kit', 'Value') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($hidef as $key => $val): ?>
<tr>
<td><?= h($key) ?></td>
<td><?= h(Debugger::exportVar($val)) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else: ?>
<div class="warning">
<?= __d('debug_kit', 'No Hidef environment available.'); ?>
</div>
<?php endif; ?>
<?php endif; ?>
@@ -0,0 +1,140 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since DebugKit 1.1
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
* @var \DebugKit\View\AjaxView $this
* @var \DebugKit\Model\Entity\Panel $panel
* @var array $requests
*/
?>
<div id="request-history">
<?php if (empty($requests)): ?>
<p class="warning"><?= __d('debug_kit', 'No previous requests logged.') ?></p>
<?php else: ?>
<p><?= count($requests) ?> <?= __d('debug_kit', 'previous requests available') ?></p>
<ul class="history-list">
<li>
<?= $this->Html->link(
__d('debug_kit', 'Back to current request'),
['plugin' => 'DebugKit', 'controller' => 'Panels', 'action' => 'index', $panel->request_id],
['class' => 'history-link', 'data-request' => $panel->request_id]
) ?>
</li>
<?php foreach ($requests as $request): ?>
<?php $url = ['plugin' => 'DebugKit', 'controller' => 'Panels', 'action' => 'index', $request->id] ?>
<li>
<a class="history-link" data-request="<?= $request->id ?>" href="<?= $this->Url->build($url) ?>">
<span class="history-time"><?= h($request->requested_at) ?></span>
<span class="history-bubble"><?= h($request->method) ?></span>
<span class="history-bubble"><?= h($request->status_code) ?></span>
<span class="history-bubble"><?= h($request->content_type) ?></span>
<span class="history-url"><?= h($request->url) ?></span>
</a>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>
<script type="text/html" id="list-template">
<ul class="history-list">
<li>
<?= $this->Html->link(
__d('debug_kit', 'Back to current request'),
['plugin' => 'DebugKit', 'controller' => 'Panels', 'action' => 'index', $panel->request_id],
['class' => 'history-link', 'data-request' => $panel->request_id]
) ?>
</li>
</ul>
</script>
<script type="text/html" id="list-item-template">
<li>
<?php $url = ['plugin' => 'DebugKit', 'controller' => 'Panels', 'action' => 'index'] ?>
<a class="history-link" data-request="{id}" href="<?= $this->Url->build($url) ?>/{id}">
<span class="history-time">{time}</span>
<span class="history-bubble xhr">XHR</span>
<span class="history-bubble">{method}</span>
<span class="history-bubble">{status}</span>
<span class="history-bubble">{type}</span>
<span class="history-url">{url}</span>
</a>
</li>
</script>
<script>
$(document).ready(function() {
var panelButtons = $('.panel');
var thisPanel = '<?= h($panel->id) ?>';
var toolbar = window.toolbar;
if (!$('#request-history > ul').length) {
$('#request-history').html($('#list-template').html());
}
var listItem = $('#list-item-template').html();
for (var i = 0; i < toolbar.ajaxRequests.length; i++) {
var params = {
id: toolbar.ajaxRequests[i].requestId,
time: (new Date(toolbar.ajaxRequests[i].date)).toLocaleString(),
method: toolbar.ajaxRequests[i].method,
status: toolbar.ajaxRequests[i].status,
url: toolbar.ajaxRequests[i].url,
type: toolbar.ajaxRequests[i].type
};
var content = listItem.replace(/{([^{}]*)}/g, function(a, b) {
var r = params[b];
return typeof r === 'string' || typeof r === 'number' ? r : a;
});
$('ul.history-list li:first').after(content);
}
var buttons = $('.history-link');
// Highlight the active request.
buttons.filter('[data-request=' + window.toolbar.currentRequest + ']').addClass('active');
buttons.on('click', function(e) {
var el = $(this);
e.preventDefault();
buttons.removeClass('active');
el.addClass('active');
window.toolbar.currentRequest = el.data('request');
$.getJSON(el.attr('href'), function(response) {
if (response.panels[0].request_id == window.toolbar.originalRequest) {
$('#panel-content-container').removeClass('history-mode');
$('#toolbar').removeClass('history-mode');
} else {
$('#panel-content-container').addClass('history-mode');
$('#toolbar').addClass('history-mode');
}
for (var i = 0, len = response.panels.length; i < len; i++) {
var panel = response.panels[i];
// Offset by two for scroll buttons
var button = panelButtons.eq(i + 2);
var summary = button.find('.panel-summary');
// Don't overwrite the history panel.
if (button.data('id') == thisPanel) {
continue;
}
button.attr('data-id', panel.id);
summary.text(panel.summary);
}
});
});
});
</script>
@@ -0,0 +1,34 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since DebugKit 2.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
* @var \DebugKit\View\AjaxView $this
* @var array $paths
* @var array $app
* @var array $cake
* @var array $plugins
* @var array $vendor
* @var array $other
*/
// Backwards compat for old DebugKit data.
if (!isset($cake) && isset($core)) {
$cake = $core;
}
?>
<h4><?= __d('debug_kit', 'Include Paths') ?></h4>
<?= $this->Toolbar->makeNeatArray($paths) ?>
<h4><?= __d('debug_kit', 'Included Files') ?></h4>
<?= $this->Toolbar->makeNeatArray(compact('app', 'cake', 'plugins', 'vendor', 'other')) ?>
@@ -0,0 +1,42 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since DebugKit 0.1
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
* @var \DebugKit\View\AjaxView $this
* @var \DebugKit\Log\Engine\DebugKitLog $logger
*/
?>
<?php if ($logger->noLogs()): ?>
<p class="info"><?= __d('debug_kit', 'There were no log entries made this request') ?></p>
<?php else: ?>
<?php foreach ($logger->all() as $logName => $logs): ?>
<h3><?= __d('debug_kit', '{0} Messages', h(ucfirst($logName))) ?> </h3>
<table cellspacing="0" cellpadding="0" class="debug-table">
<thead>
<tr>
<th><?= __d('debug_kit', 'Time') ?></th>
<th><?= __d('debug_kit', 'Message') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($logs as $log): ?>
<tr>
<td width="200" class="code"><?= $log[0] ?></td>
<td><?= h($log[1]) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endforeach; ?>
<?php endif; ?>
@@ -0,0 +1,49 @@
<p class="info">
<?= __d('debug_kit',
'Why not test your emails interactively instead? Go to the {0}',
$this->Html->link(__d('debug_kit', 'Email previews page'), ['controller' => 'MailPreview', 'action' => 'index'], ['target' => '_blank'])) ?>
</p>
<?php
if (empty($emails)) {
echo "<p>" . __d('debug_kit', 'No emails were sent during this request') . "</p>";
return;
}
$url = $this->Url->build(['controller' => 'MailPreview', 'action' => 'sent', 'panel' => $panel->id, 'id' => 0]);
?>
<div style="display:flex">
<div style="width:300px;">
<table class="debug-table">
<tr>
<th><?= __d('debug_kit', 'Subject') ?></th>
</tr>
<?php foreach ($emails as $k => $email) : ?>
<tr onclick="loadSentEmail(this, <?= $k ?>)" class="<?= $k == 0 ? 'highlighted' : '' ?>">
<td style="cursor:pointer;padding:20px 10px;line-height:20px">
<?= "\u{2709}\u{FE0F}" ?>
<?= !empty($email['headers']['Subject']) ?
h($this->Text->truncate($email['headers']['Subject'])) :
'(No Subject)'
?>
</td>
</tr>
<?php endforeach; ?>
</table>
</div>
<iframe seamless
name="sent-email"
src="<?= h($url) ?>"
style="height:calc(100vh - 128px);flex:1;margin-left:20px;padding-left:10px;border-left:1px solid #ccc"
>
</iframe>
</div>
<script>
function loadSentEmail(elem, index) {
var iframe = document.getElementsByName('sent-email')[0];
var current = iframe.contentWindow.location.href;
newLocation = current.replace(/\/\d+$/, '/' + index);
iframe.contentWindow.location.href = newLocation;
$(elem).siblings().removeClass('highlighted');
elem.className = 'highlighted';
}
</script>
@@ -0,0 +1,140 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since DebugKit 3.5.2
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
* @var \DebugKit\View\AjaxView $this
* @var array $packages
*/
?>
<?php if (empty($packages) && empty($devPackages)): ?>
<div class="warning">
<?= __d('debug_kit', '{0} not found', "'composer.lock'"); ?>
</div>
<?php else: ?>
<div class="check-update">
<button class="btn-primary"><?= __d('debug_kit', 'Check for Updates') ?></button>
<label><input type="checkbox" class="direct-dependency"><?= __d('debug_kit', 'Direct dependencies only') ?></label>
</div>
<div class="terminal"></div>
<?php if (!empty($packages)): ?>
<section class="section-tile">
<h3><?= __d('debug_kit', 'Requirements ({0})', count($packages)) ?> </h3>
<table cellspacing="0" cellpadding="0" class="debug-table">
<thead>
<tr>
<th><?= __d('debug_kit', 'Name') ?></th>
<th><?= __d('debug_kit', 'Version') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($packages as $package): ?>
<?php extract($package); ?>
<tr>
<td title="<?= h($description) ?>">
<a href="https://packagist.org/packages/<?= h($name) ?>" title="<?= h($description) ?>" target="_blank" class="package-link"><?= h($name) ?></a>
</td>
<td>
<span class="package-version"><span class="panel-summary"><?= h($version) ?></span></span>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</section>
<?php endif; ?>
<?php if (!empty($devPackages)): ?>
<section class="section-tile">
<h3><?= __d('debug_kit', 'Dev Requirements ({0})', count($devPackages)) ?> </h3>
<table cellspacing="0" cellpadding="0" class="debug-table">
<thead>
<tr>
<th><?= __d('debug_kit', 'Name') ?></th>
<th><?= __d('debug_kit', 'Version') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($devPackages as $package): ?>
<?php extract($package); ?>
<tr>
<td title="<?= h($description) ?>">
<a href="https://packagist.org/packages/<?= h($name) ?>" title="<?= h($description) ?>" target="_blank" class="package-link"><?= h($name) ?></a>
</td>
<td>
<span class="package-version"><span class="panel-summary"><?= h($version) ?></span></span>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</section>
<?php endif; ?>
<?php endif; ?>
<script>
$(document).ready(function() {
var baseUrl = '<?= $this->Url->build([
'plugin' => 'DebugKit',
'controller' => 'Composer',
'action' => 'checkDependencies'
]); ?>';
var terminal = $('.terminal');
function showMessage(el, html) {
el.show().html(html);
}
function buildLoader() {
return '<div class="loading"><?= __d('debug_kit', 'Loading') . ' ' . $this->Html->image('DebugKit./img/cake.icon.png', ['class' => 'indicator']) ?></div>';
}
function buildSuccessfulMessage(response) {
var html = '';
if (response.packages.bcBreaks === undefined && response.packages.semverCompatible === undefined) {
return '<pre class="success-message">All dependencies are up to date</pre>';
}
if (response.packages.bcBreaks !== undefined) {
html += '<h4 class="section-header">Update with potential BC break</h4>';
html += '<pre>' + response.packages.bcBreaks + '</pre>';
}
if (response.packages.semverCompatible !== undefined) {
html += '<h4 class="section-header">Update semver compatible</h4>';
html += '<pre>' + response.packages.semverCompatible + '</pre>';
}
return html;
}
function buildErrorMessage(response) {
return '<pre class="warning-message">' + JSON.parse(response.responseText).message + '</pre>';
}
$('.check-update button').on('click', function(e) {
showMessage(terminal, buildLoader());
var direct = $('.direct-dependency')[0].checked;
var xhr = $.ajax({
headers: {'X-CSRF-TOKEN': '<?= $this->request->getParam('_csrfToken') ?>'},
url: baseUrl,
data: {direct: direct},
dataType: 'json',
type: 'POST'
});
xhr.done(function(response) {
showMessage(terminal, buildSuccessfulMessage(response));
}).error(function(response) {
showMessage(terminal, buildErrorMessage(response));
});
e.preventDefault();
});
});
</script>
@@ -0,0 +1,24 @@
<header>
<table>
<tr>
<td>View As</td>
<td>
<?php $partNames = array_keys($email->getParts()) ?>
<?= $this->Form->control('part', [
'type' => 'select',
'label' => false,
'value' => $this->request->getQuery('part') ?: 'html',
'onChange' => 'formatChanged(this);',
'options' => array_combine($partNames, $partNames),
]);
?>
</td>
</tr>
<?php foreach ($email->getHeaders() as $name => $header) :?>
<tr>
<td><?= h($name) ?></td>
<td><?= h($header) ?></td>
</tr>
<?php endforeach ?>
</table>
</header>
@@ -0,0 +1,65 @@
<?php
/**
* Request Panel Element
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since DebugKit 0.1
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
* @var \DebugKit\View\AjaxView $this
* @var array $headers
* @var array $params
* @var array $data
* @var array $query
* @var array $cookie
* @var string $matchedRoute
*/
?>
<?php if (!empty($headers) && $headers['response']): ?>
<h4><?= __d('debug_kit', 'Warning') ?></h4>
<?= '<p class="warning">' . __d('debug_kit', 'Headers already sent at file {0} and line {1}.', [$headers['file'], $headers['line']]) . '</p>' ?>
<?php endif; ?>
<h4><?= __d('debug_kit', 'Routing Params') ?></h4>
<?= $this->Toolbar->makeNeatArray($params) ?>
<h4><?= __d('debug_kit', 'Post data') ?></h4>
<?php
if (empty($data)):
echo '<p class="info">' . __d('debug_kit', 'No post data.') . '</p>';
else:
echo $this->Toolbar->makeNeatArray($data);
endif;
?>
<h4>Query string</h4>
<?php
if (empty($query)):
echo '<p class="info">' . __d('debug_kit', 'No querystring data.') . '</p>';
else:
echo $this->Toolbar->makeNeatArray($query);
endif;
?>
<h4>Cookie</h4>
<?php if (isset($cookie)): ?>
<?= $this->Toolbar->makeNeatArray($cookie) ?>
<?php else: ?>
<p class="info"><?= __d('debug_kit', 'No Cookie data.') ?></p>
<?php endif; ?>
<?php if (!empty($matchedRoute)): ?>
<h4><?= __d('debug_kit', 'Matched Route') ?></h4>
<p><?= $this->Toolbar->makeNeatArray(['template' => $matchedRoute]) ?></p>
<?php endif; ?>
@@ -0,0 +1,48 @@
<?php
/**
* @var \Cake\Routing\Route\Route[] $routes
* @var string $matchedRoute
*/
use Cake\Utility\Hash;
$routes = Cake\Routing\Router::routes();
?>
<button type="button" class="btn-primary" id="toggle-debugkit-routes">
<?= __d('debug_kit', 'Toggle debugkit internal routes') ?>
</button>
<table cellspacing="0" cellpadding="0" class="debug-table">
<thead>
<tr>
<th><?= __d('debug_kit', 'Route name') ?></th>
<th class="left"><?= __d('debug_kit', 'URI template') ?></th>
<th class="left"><?= __d('debug_kit', 'Defaults') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($routes as $route): ?>
<?php
$class = '';
if ($matchedRoute === $route->template):
$class = 'highlighted';
elseif ($route->defaults['plugin'] === 'DebugKit'):
$class = 'debugkit-route hidden';
endif;
?>
<tr class="<?= $class ?>">
<td><?= h(Hash::get($route->options, '_name', $route->getName())) ?></td>
<td class="left"><?= h($route->template) ?></td>
<td class="left"><pre><?= json_encode($route->defaults, JSON_PRETTY_PRINT) ?></pre></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<script>
$(document).ready(function() {
$('#toggle-debugkit-routes').on('click', function (event) {
event.preventDefault();
var routes = $('.debugkit-route');
routes.toggleClass('hidden');
});
});
</script>
@@ -0,0 +1,20 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since DebugKit 1.1
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
* @var \DebugKit\View\AjaxView $this
* @var array $content
*/
?>
<?= $this->Toolbar->makeNeatArray($content);
@@ -0,0 +1,92 @@
<?php
/**
* SQL Log Panel Element
*
* PHP 5
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since DebugKit 0.1
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
* @var \DebugKit\View\AjaxView $this
* @var array $tables
* @var \DebugKit\Database\Log\DebugLog[] $loggers
*/
$noOutput = true;
// Configure sqlformatter colours.
SqlFormatter::$quote_attributes = 'style="color: #004d40;"';
SqlFormatter::$backtick_quote_attributes = 'style="color: #26a69a;"';
SqlFormatter::$number_attributes = 'style="color: #ec407a;"';
SqlFormatter::$word_attributes = 'style="color: #9c27b0;"';
SqlFormatter::$pre_attributes = 'style="color: #222; background-color: transparent;"';
?>
<?php if (!empty($tables)): ?>
<h4><?= __d('debug_kit', 'Generated Models') ?></h4>
<p class="warning"><?= __d('debug_kit', 'The following Table objects used {0} instead of a concrete class:', '<code>Cake\ORM\Table</code>') ?></p>
<ul class="list">
<?php foreach ($tables as $table): ?>
<li><?= h($table) ?></li>
<?php endforeach ?>
</ul>
<hr />
<?php endif; ?>
<?php if (!empty($loggers)): ?>
<?php foreach ($loggers as $logger): ?>
<?php
$queries = $logger->queries();
if (empty($queries)):
continue;
endif;
$noOutput = false;
?>
<div class="sql-log-panel-query-log">
<h4><?= h($logger->name()) ?></h4>
<h5>
<?= __d(
'debug_kit',
'Total Time: {0} ms &mdash; Total Queries: {1} &mdash; Total Rows: {2}',
$logger->totalTime(),
count($queries),
$logger->totalRows()
);
?>
</h5>
<table cellspacing="0" cellpadding="0">
<thead>
<tr>
<th><?= __d('debug_kit', 'Query') ?></th>
<th><?= __d('debug_kit', 'Rows') ?></th>
<th><?= __d('debug_kit', 'Took (ms)') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($queries as $query): ?>
<tr>
<td><?= SqlFormatter::format($query['query']) ?></td>
<td><?= h($query['rows']) ?></td>
<td><?= h($query['took']) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endforeach; ?>
<?php endif; ?>
<?php if ($noOutput): ?>
<div class="warning"><?= __d('debug_kit', 'No active database connections') ?></div>
<?php endif ?>
@@ -0,0 +1,101 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since DebugKit 0.1
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
* @var \DebugKit\View\AjaxView $this
* @var float $requestTime
* @var int $peakMemory
* @var array $memory
*/
?>
<section>
<h3><?= __d('debug_kit', 'Memory') ?></h3>
<div class="peak-mem-use">
<strong><?= __d('debug_kit', 'Peak Memory Use:') ?></strong>
<?= $this->Number->toReadableSize($peakMemory) ?>
</div>
<table cellspacing="0" cellpadding="0">
<thead>
<tr>
<th><?= __d('debug_kit', 'Message') ?></th>
<th><?= __d('debug_kit', 'Memory Use') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($memory as $key => $value): ?>
<tr>
<td><?= h($key) ?></td>
<td class="right-text"><?= $this->Number->toReadableSize($value) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</section>
<section>
<h3><?= __d('debug_kit', 'Timers') ?></h3>
<div class="request-time">
<strong><?= __d('debug_kit', 'Total Request Time:') ?></strong>
<?= $this->Number->precision($requestTime * 1000, 0) ?> ms
</div>
<table cellspacing="0" cellpadding="0">
<thead>
<tr>
<th><?= __d('debug_kit', 'Event') ?></th>
<th><?= __d('debug_kit', 'Time in ms') ?></th>
<th><?= __d('debug_kit', 'Timeline') ?></th>
</tr>
</thead>
<tbody>
<?php
$rows = [];
$end = end($timers);
$maxTime = $end['end'];
$i = 0;
$values = array_values($timers);
foreach ($timers as $timerName => $timeInfo):
$indent = 0;
for ($j = 0; $j < $i; $j++):
if (($values[$j]['end'] > $timeInfo['start']) && ($values[$j]['end']) > ($timeInfo['end'])):
$indent++;
endif;
endfor;
$indent = str_repeat("\xC2\xA0\xC2\xA0", $indent);
?>
<tr>
<td>
<?= h($indent . $timeInfo['message']) ?>
</td>
<td class="right-text"><?= $this->Number->precision($timeInfo['time'] * 1000, 2) ?></td>
<td>
<?= $this->SimpleGraph->bar(
$timeInfo['time'] * 1000,
$timeInfo['start'] * 1000,
[
'max' => $maxTime * 1000,
'requestTime' => $requestTime * 1000,
]
) ?>
</td>
<?php
$i++;
endforeach;
?>
</tbody>
</table>
</section>
@@ -0,0 +1,36 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since DebugKit 0.1
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
* @var \DebugKit\View\AjaxView $this
* @var string $error
* @var bool $sort
* @var array $content
* @var array $errors
*/
if (isset($error)):
printf('<p class="warning">%s</p>', $error);
endif;
if (!empty($content)):
printf('<label class="toggle-checkbox"><input type="checkbox" class="neat-array-sort"%s>%s</label>', $sort ? ' checked="checked"' : '', __d('debug_kit', 'Sort variables by name'));
$this->Toolbar->setSort($sort);
echo $this->Toolbar->makeNeatArray($content);
endif;
if (!empty($errors)):
echo '<h4>' . __d('debug_kit', 'Validation errors') . '</h4>';
echo $this->Toolbar->makeNeatArray($errors);
endif;
@@ -0,0 +1,13 @@
<?php $this->extend('toolbar') ?>
<div style="height:calc(100vh);overflow-y:scroll">
<?php if (empty($noHeader)) : ?>
<h2 class="panel-title">
<?= isset($title) ? h($title) : __d('debug_kit', 'DebugKit Dashboard') ?>
</h2>
<?php endif ?>
<div class="panel-content">
<?= $this->fetch('content'); ?>
</div>
</div>
+13
View File
@@ -0,0 +1,13 @@
<?php $this->extend('toolbar') ?>
<div style="height:calc(100vh);overflow-y:scroll">
<?php if (empty($noHeader)) : ?>
<h2 class="panel-title">
<?= isset($title) ? h($title) : __d('debug_kit', 'Mailer Previews') . " \u{1f4ee}" ?>
</h2>
<?php endif ?>
<div class="panel-content">
<?= $this->fetch('content'); ?>
</div>
</div>
@@ -0,0 +1,6 @@
<?php
/**
* @var \DebugKit\View\AjaxView $this
*/
echo $this->fetch('content');
@@ -0,0 +1,22 @@
<?php
/**
* @var \DebugKit\View\AjaxView $this
*/
?>
<!DOCTYPE html>
<html>
<head>
<title><?= isset($title) ? h($title) : "Debug Kit Toolbar" ?></title>
<?= $this->Html->css('DebugKit./css/reset') ?>
<?= $this->Html->css('DebugKit./css/toolbar') ?>
</head>
<body>
<?= $this->fetch('content') ?>
<div id="loader">
<?= $this->Html->image('DebugKit./img/cake.icon.png', ['class' => 'indicator'])?>
</div>
</body>
<?= $this->Html->script('DebugKit./js/jquery') ?>
<?= $this->Html->script('DebugKit./js/toolbar-app') ?>
<?= $this->fetch('script') ?>
</html>
@@ -0,0 +1,33 @@
<style type="text/css">
html, body {
height: 100%;
}
body {
margin: 0;
}
iframe.messageBody {
border: 0;
height: calc(100vh);
width: 100%;
}
</style>
<?php if (!$this->request->getQuery('part')) : ?>
<?= $this->element('preview_header'); ?>
<?php endif; ?>
<?php if (!empty($part)) : ?>
<iframe seamless name="messageBody" class="messageBody" src="?part=<?= h($part); ?>&plugin=<?= h($plugin); ?>"></iframe>
<?php else : ?>
<p><?= __d('debug_kit', 'You are trying to preview an email that does not have any content.') ?></p>
<?php endif; ?>
<script>
function formatChanged(form) {
var part_name = form.options[form.selectedIndex].value
var iframe = document.getElementsByName('messageBody')[0];
iframe.contentWindow.location.replace('?part=' + part_name + '&plugin=<?= h($plugin); ?>');
}
</script>
@@ -0,0 +1,90 @@
<?php foreach ($mailPreviews as $plugin => $previews) : ?>
<h3><?= $plugin ?></h3>
<?php foreach ($previews as $preview) : ?>
<?php $mailPreview = $preview['class'] ?>
<h4><?= "\u{2709}\u{FE0F}" ?> <?= h($mailPreview->name()) ?></h4>
<table cellpadding="0" cellspacing="0">
<tbody>
<?php foreach ($mailPreview->getEmails() as $email) : ?>
<tr>
<td>
<?php
echo $this->Html->link($email, [
'controller' => 'MailPreview',
'action' => 'email',
'?' => ['plugin' => $plugin],
$mailPreview->name(),
$email,
]);
?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endforeach; ?>
<?php endforeach; ?>
<div style="font-weight:300;margin-top:100px">
<h3><?= __d('debug_kit', 'How to use this feature?') ?></h3>
<p><?= __d('debug_kit', 'Testing emails can be very time consuming') ?> &#8987;</p>
<p> <?= __d('debug_kit', 'Specially when you need to click a bunch of times on an interface to trigger them.') ?></p>
<p><?= __d('debug_kit', "Wouldn't it be better to just change the templates and refresh the browser to see the result?") ?></p>
<p><?= __d('debug_kit', 'Just the way you work on the web interface!') ?> &#127939;</p>
<h4><?= __d('debug_kit', 'Example') ?></h4>
<p><?= __d('debug_kit', "{0} integrates with CakePHP’s {1} class. Here's an example of such a mailer:", 'MailPreview', 'Mailer') ?></p>
<pre style="background-color:#f8f8f8;font-familiy:Monaco,sans-serif;overflow:scroll;margin:10px 0;line-height:25px;">
<?php
$code = '
<?php
namespace App\Mailer;
use Cake\Mailer\Mailer;
class UserMailer extends Mailer
{
public function welcome($user)
{
return $this // ' . __d('debug_kit', 'Returning the chain is a good idea') . ' :)
->to($user->email)
->subject(sprintf("Welcome %s", $user->name))
->template("welcome_mail") // ' . __d('debug_kit', 'By default template with same name as method name is used.') . '
->layout("custom")
->set(["user" => $user]);
}
}';
highlight_string($code);
?>
</pre>
<p><?= __d('debug_kit', 'Now you create a {0} class where you can pass some dummy values.', 'MailPreview') ?></p>
<pre style="background-color:#f8f8f8;font-familiy:Monaco,sans-serif;overflow:scroll;margin:10px 0;line-height:25px;">
<?php
$code = '
<?php
// Create the file src/Mailer/Preview/UserMailPreview.php
namespace App\Mailer\Preview;
use DebugKit\Mailer\MailPreview;
class UserMailPreview extends MailPreview
{
public function welcome()
{
$this->loadModel("Users");
$user = $this->Users->find()->first();
return $this->getMailer("User")
->welcome($user)
->set(["activationToken" => "dummy-token"]);
}
}';
highlight_string($code);
?>
</pre>
<p><?= __d('debug_kit', 'Note that the function MUST return the UserMailer object at the end.') ?></p>
<p><?= __d('debug_kit', 'Since {0} have a fluent interface, you just need to return the result of the chain of calls.', 'Mailers') ?></p>
<p style="margin:20px 0"><?= __d('debug_kit', "That's it, now refresh this page!") ?> &#128579;</p>
</div>
+10
View File
@@ -0,0 +1,10 @@
<?php
/**
* @var \DebugKit\View\AjaxView $this
* @var \DebugKit\Model\Entity\Panel $panel
*/
?>
<h2 class="panel-title"><?= h($panel->title) ?></h2>
<div class="panel-content">
<?= $this->element($panel->element) ?>
</div>
+57
View File
@@ -0,0 +1,57 @@
<?php
/**
* @var \DebugKit\View\AjaxView $this
* @var \DebugKit\Model\Entity\Request $toolbar
*/
use Cake\Routing\Router;
use Cake\Core\Configure;
?>
<div id="panel-content-container">
<span id="panel-close" class="button-close">&times;</span>
<div id="panel-content">
<!-- content here -->
</div>
</div>
<ul id="toolbar" class="toolbar">
<li class="panel-button-left panel hidden">
<span class="panel-button">
&#x3008;
</span>
</li>
<li class="panel-button-right panel hidden">
<span class="panel-button">
&#x3009;
</span>
</li>
<li class="toolbar-inner">
<ul class="toolbar-inner">
<?php foreach ($toolbar->panels as $panel): ?>
<li class="panel hidden" data-id="<?= $panel->id ?>">
<span class="panel-button">
<?= h($panel->title) ?>
</span>
<?php if (strlen($panel->summary)): ?>
<span class="panel-summary">
<?= h($panel->summary) ?>
</span>
<?php endif ?>
</li>
<?php endforeach; ?>
</ul>
</li>
<li id="panel-button">
<?= $this->Html->image('DebugKit./img/cake.icon.png', [
'alt' => 'Debug Kit', 'title' => 'CakePHP ' . Configure::version() . ' Debug Kit'
]) ?>
</li>
</ul>
<?php $this->Html->script('DebugKit./js/debug_kit', [
'block' => true,
'id' => '__debug_kit_app',
'data-id' => $toolbar->id,
'data-url' => Router::url('/', true),
'data-webroot' => $this->request->getAttribute("webroot"),
]) ?>
+317
View File
@@ -0,0 +1,317 @@
<?php
/**
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit;
use Cake\Core\Configure;
use Cake\Core\InstanceConfigTrait;
use Cake\Core\Plugin as CorePlugin;
use Cake\Event\Event;
use Cake\Event\EventManager;
use Cake\Http\Response;
use Cake\Http\ServerRequest;
use Cake\Log\Log;
use Cake\ORM\TableRegistry;
use Cake\Routing\Router;
use DebugKit\Panel\PanelRegistry;
use Psr\Http\Message\ResponseInterface;
/**
* Used to create the panels and inject a toolbar into
* matching responses.
*
* Used by the Routing Filter and Middleware.
*/
class ToolbarService
{
use InstanceConfigTrait;
/**
* The panel registry.
*
* @var \DebugKit\Panel\PanelRegistry
*/
protected $registry;
/**
* Default configuration.
*
* @var array
*/
protected $_defaultConfig = [
'panels' => [
'DebugKit.Cache' => true,
'DebugKit.Session' => true,
'DebugKit.Request' => true,
'DebugKit.SqlLog' => true,
'DebugKit.Timer' => true,
'DebugKit.Log' => true,
'DebugKit.Variables' => true,
'DebugKit.Environment' => true,
'DebugKit.Include' => true,
'DebugKit.History' => true,
'DebugKit.Routes' => true,
'DebugKit.Packages' => true,
'DebugKit.Mail' => true,
'DebugKit.Deprecations' => true,
],
'forceEnable' => false,
'safeTld' => [],
];
/**
* Constructor
*
* @param \Cake\Event\EventManager $events The event manager to use defaults to the global manager
* @param array $config The configuration data for DebugKit.
*/
public function __construct(EventManager $events, array $config)
{
$this->setConfig($config);
$this->registry = new PanelRegistry($events);
}
/**
* Fetch the PanelRegistry
*
* @return \DebugKit\Panel\PanelRegistry
*/
public function registry()
{
return $this->registry;
}
/**
* Check whether or not debug kit is enabled.
*
* @return bool
*/
public function isEnabled()
{
$enabled = (bool)Configure::read('debug');
if ($enabled && !$this->isSuspiciouslyProduction()) {
return true;
}
$force = $this->getConfig('forceEnable');
if (is_callable($force)) {
return $force();
}
return $force;
}
/**
* Returns true if this applications is being executed on a domain with a TLD
* that is commonly associated with a production environment.
*
* @return bool
*/
protected function isSuspiciouslyProduction()
{
$host = explode('.', parse_url('http://' . env('HTTP_HOST'), PHP_URL_HOST));
$first = current($host);
$isIP = is_numeric(implode('', $host));
if (count($host) === 1) {
return false;
}
if ($isIP && in_array($first, ['192', '10', '127'])) {
// Accessing the app by private IP, this is safe
return false;
}
$tld = end($host);
$safeTopLevelDomains = ['localhost', 'invalid', 'test', 'example', 'local'];
$safeTopLevelDomains = array_merge($safeTopLevelDomains, (array)$this->getConfig('safeTld'));
if (!in_array($tld, $safeTopLevelDomains, true) && !$this->getConfig('forceEnable')) {
$host = implode('.', $host);
$safeList = implode(', ', $safeTopLevelDomains);
Log::warning(
"DebugKit is disabling itself as your host `{$host}` " .
"is not in the known safe list of top-level-domains ({$safeList}). " .
"If you would like to force DebugKit on use the `DebugKit.forceEnable` Configure option."
);
return true;
}
return false;
}
/**
* Get the list of loaded panels
*
* @return array
*/
public function loadedPanels()
{
return $this->registry->loaded();
}
/**
* Get the a loaded panel
*
* @param string $name The name of the panel you want to get.
* @return \DebugKit\DebugPanel|null The panel or null.
*/
public function panel($name)
{
return $this->registry->{$name};
}
/**
* Load all the panels being used
*
* @return void
*/
public function loadPanels()
{
foreach ($this->getConfig('panels') as $panel => $enabled) {
list($panel, $enabled) = (is_numeric($panel)) ? [$enabled, true] : [$panel, $enabled];
if ($enabled) {
$this->registry->load($panel);
}
}
}
/**
* Call the initialize method onl all the loaded panels.
*
* @return void
*/
public function initializePanels()
{
foreach ($this->registry->loaded() as $panel) {
$this->registry->{$panel}->initialize();
}
}
/**
* Save the toolbar state.
*
* @param \Cake\Http\ServerRequest $request The request
* @param \Psr\Http\Message\ResponseInterface $response The response
* @return null|\DebugKit\Model\Entity\Request Saved request data.
*/
public function saveData(ServerRequest $request, ResponseInterface $response)
{
// Skip debugkit requests and requestAction()
$path = $request->getUri()->getPath();
if (
strpos($path, 'debug_kit') !== false ||
strpos($path, 'debug-kit') !== false ||
$request->is('requested')
) {
return null;
}
$data = [
'url' => $request->getUri()->getPath(),
'content_type' => $response->getHeaderLine('Content-Type'),
'method' => $request->getMethod(),
'status_code' => $response->getStatusCode(),
'requested_at' => $request->getEnv('REQUEST_TIME'),
'panels' => [],
];
/* @var \DebugKit\Model\Table\RequestsTable $requests */
$requests = TableRegistry::get('DebugKit.Requests');
$requests->gc();
$row = $requests->newEntity($data);
$row->isNew(true);
foreach ($this->registry->loaded() as $name) {
$panel = $this->registry->{$name};
try {
$content = serialize($panel->data());
} catch (\Exception $e) {
$content = serialize([
'error' => $e->getMessage(),
]);
}
$row->panels[] = $requests->Panels->newEntity([
'panel' => $name,
'element' => $panel->elementName(),
'title' => $panel->title(),
'summary' => $panel->summary(),
'content' => $content,
]);
}
return $requests->save($row);
}
/**
* Reads the modified date of a file in the webroot, and returns the integer
*
* @return string
*/
public function getToolbarUrl()
{
$url = 'js/toolbar.js';
$filePaths = [
str_replace('/', DIRECTORY_SEPARATOR, WWW_ROOT . 'debug_kit/' . $url),
str_replace('/', DIRECTORY_SEPARATOR, CorePlugin::path('DebugKit') . 'webroot/' . $url),
];
$url = '/debug_kit/' . $url;
foreach ($filePaths as $filePath) {
if (file_exists($filePath)) {
return $url . '?' . filemtime($filePath);
}
}
return $url;
}
/**
* Injects the JS to build the toolbar.
*
* The toolbar will only be injected if the response's content type
* contains HTML and there is a </body> tag.
*
* @param \DebugKit\Model\Entity\Request $row The request data to inject.
* @param \Psr\Http\Message\ResponseInterface $response The response to augment.
* @return \Psr\Http\Message\ResponseInterface The modified response
*/
public function injectScripts($row, ResponseInterface $response)
{
$response = $response->withHeader('X-DEBUGKIT-ID', $row->id);
if (strpos($response->getHeaderLine('Content-Type'), 'html') === false) {
return $response;
}
$body = $response->getBody();
if (!$body->isSeekable() || !$body->isWritable()) {
return $response;
}
$body->rewind();
$contents = $body->getContents();
$pos = strrpos($contents, '</body>');
if ($pos === false) {
return $response;
}
$url = Router::url('/', true);
$script = sprintf(
'<script id="__debug_kit" data-id="%s" data-url="%s" src="%s"></script>',
$row->id,
$url,
Router::url($this->getToolbarUrl())
);
$contents = substr($contents, 0, $pos) . $script . substr($contents, $pos);
$body->rewind();
$body->write($contents);
return $response->withBody($body);
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since 3.2.8
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\View;
use Cake\View\View;
/**
* A view class that is used for AJAX responses.
*
* Currently only switches the default layout and sets the response type
* which just maps to text/html by default.
*
* @property \DebugKit\View\Helper\SimpleGraphHelper $SimpleGraph
* @property \DebugKit\View\Helper\ToolbarHelper $Toolbar
* @property \DebugKit\View\Helper\TidyHelper $Tidy
*/
class AjaxView extends View
{
/**
* Initialization hook method.
*
* @return void
*/
public function initialize()
{
parent::initialize();
$this->response = $this->response->withType('ajax');
}
}
@@ -0,0 +1,68 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since 3.3.7
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\View\Helper;
use Cake\Core\Configure;
use Cake\Error\Debugger;
use Cake\Filesystem\File;
use Cake\Utility\Hash;
use Cake\View\Helper;
/**
* CredentialsHelper
*
* Filter sensitive data in screen, data will be displayed on mouse click
*
* @property \Cake\View\Helper\HtmlHelper $Html
* @property \DebugKit\View\Helper\ToolbarHelper $Toolbar
*/
class CredentialsHelper extends Helper
{
/**
* Helpers property
*
* @var array
*/
public $helpers = ['Html', 'DebugKit.Toolbar'];
/**
* Replace credentials in url's by *****
* Example mysql://username:password@localhost/my_db -> mysql://******@localhost/my_db
*
* @param string $in variable to filter
* @return string
*/
public function filter($in)
{
$regexp = '/^([^:;]+:\/\/)([^:;]+:?.*?)@(.*)$/i';
if (!is_string($in) || empty($in)) {
return $in;
}
preg_match_all($regexp, $in, $tokens);
if (empty($tokens[0])) {
return h($in);
}
$protocol = Hash::get($tokens, '1.0');
$credentials = Hash::get($tokens, '2.0');
$tail = Hash::get($tokens, '3.0');
$link = $this->Html->tag('a', '******', [
'class' => 'filtered-credentials',
'title' => h($credentials),
'onclick' => "this.innerHTML = this.title",
]);
return h($protocol) . $link . '@' . h($tail);
}
}
@@ -0,0 +1,79 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since 1.0.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\View\Helper;
use Cake\View\Helper;
/**
* Class SimpleGraphHelper
*
* Allows creation and display of extremely simple graphing elements
*
* @since DebugKit 1.0
*/
class SimpleGraphHelper extends Helper
{
/**
* Default settings to be applied to each Simple Graph
*
* Allowed options:
*
* - max => (int) Maximum value in the graphs
* - width => (int)
* - valueType => string (value, percentage)
* - style => array
*
* @var array
*/
protected $_defaultSettings = [
'max' => 100,
'width' => 350,
'valueType' => 'value',
];
/**
* bar method
*
* @param float $value Value to be graphed
* @param int $offset how much indentation
* @param array|\Graph $options Graph options
* @return string Html graph
*/
public function bar($value, $offset, $options = [])
{
$settings = array_merge($this->_defaultSettings, $options);
/* @var int $max */
/* @var int $width */
/* @var string $valueType */
extract($settings);
$graphValue = ($value / $max) * $width;
$graphValue = max(round($graphValue), 1);
if ($valueType === 'percentage') {
$graphOffset = 0;
} else {
$graphOffset = ($offset / $max) * $width;
$graphOffset = round($graphOffset);
}
return sprintf(
'<div class="graph-bar" style="%s"><div class="graph-bar-value" style="%s" title="%s"> </div></div>',
"width: {$width}px",
"margin-left: {$graphOffset}px; width: {$graphValue}px",
__d('debug_kit', "Starting {0}ms into the request, taking {1}ms", $offset, $value)
);
}
}
+189
View File
@@ -0,0 +1,189 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since 1.0.0
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\View\Helper;
use Cake\Core\Configure;
use Cake\Error\Debugger;
use Cake\Filesystem\File;
use Cake\View\Helper;
/**
* TidyHelper class
*
* Passes html through tidy on the command line, and reports markup errors
*
* @uses AppHelper
* @since v 1.0 (22-Jun-2009)
*
* @property \DebugKit\View\Helper\ToolbarHelper $Toolbar
*/
class TidyHelper extends Helper
{
/**
* helpers property
*
* @var array
*/
public $helpers = ['DebugKit.Toolbar'];
/**
* results property
*
* @var mixed null
*/
public $results = null;
/**
* Return a nested array of errors for the passed html string
* Fudge the markup slightly so that the tag which is invalid is highlighted
*
* @param string $html ''
* @param string $out ''
* @return array
*/
public function process($html = '', &$out = '')
{
$errors = $this->tidyErrors($html, $out);
if (!$errors) {
return [];
}
$result = ['Error' => [], 'Warning' => [], 'Misc' => []];
$errors = explode("\n", $errors);
$markup = explode("\n", $out);
foreach ($errors as $error) {
preg_match('@line (\d+) column (\d+) - (\w+): (.*)@', $error, $matches);
if ($matches) {
list($original, $line, $column, $type, $message) = $matches;
$line = $line - 1;
$string = '</strong>';
if (isset($markup[$line - 1])) {
$string .= h($markup[$line - 1]);
}
// phpcs:ignore
$string .= '<strong>' . h(@$markup[$line]) . '</strong>';
if (isset($markup[$line + 1])) {
$string .= h($markup[$line + 1]);
}
$string .= '</strong>';
$result[$type][$string][] = h($message);
} elseif ($error) {
$message = $error;
$result['Misc'][h($message)][] = h($message);
}
}
$this->results = $result;
return $result;
}
/**
* report method
*
* Call process if a string is passed, or no prior results exist - and return the results using
* the toolbar helper to generate a nested navigatable array
*
* @param mixed $html null
* @return string
*/
public function report($html = null)
{
if ($html) {
$this->process($html);
} elseif ($this->results === null) {
$this->process($this->_View->output);
}
if (!$this->results) {
return '<p>' . __d('debug_kit', 'No markup errors found') . '</p>';
}
foreach ($this->results as &$results) {
foreach ($results as $type => &$messages) {
foreach ($messages as &$message) {
$message = html_entity_decode($message, ENT_COMPAT, Configure::read('App.encoding'));
}
}
}
return $this->Toolbar->makeNeatArray(array_filter($this->results), 0, 0, false);
}
/**
* Run the html string through tidy, and return the (raw) errors. pass back a reference to the
* normalized string so that the error messages can be linked to the line that caused them.
*
* @param string $in ''
* @param string $out ''
* @return string
*/
public function tidyErrors($in = '', &$out = '')
{
$out = preg_replace('@>\s*<@s', ">\n<", $in);
// direct access? windows etc
if (function_exists('tidy_parse_string')) {
$tidy = tidy_parse_string($out, [], 'UTF8');
$tidy->cleanRepair();
$errors = $tidy->errorBuffer . "\n";
return $errors;
}
// cli
$File = new File(rtrim(TMP, DS) . DS . rand() . '.html', true);
$File->write($out);
$path = $File->pwd();
$errors = $path . '.err';
$this->_exec("tidy -eq -utf8 -f $errors $path");
$File->delete();
if (!file_exists($errors)) {
return '';
}
$Error = new File($errors);
$errors = $Error->read();
$Error->delete();
return $errors;
}
/**
* exec method
*
* @param mixed $cmd ''
* @param mixed $out null
* @return bool True if successful
*/
protected function _exec($cmd, &$out = null)
{
if (DS === '/') {
$_out = exec($cmd . ' 2>&1', $out, $return);
} else {
$_out = exec($cmd, $out, $return);
}
if (Configure::read('debug')) {
$source = Debugger::trace(['depth' => 1, 'start' => 2]) . "\n";
//Log::write('system_calls_' . date('Y-m-d'), "\n" . $source . Debugger::exportVar(compact('cmd','out','return')));
//Log::write('system_calls', "\n" . $source . Debugger::exportVar(compact('cmd','out','return')));
}
if ($return) {
return false;
}
return $_out ? $_out : true;
}
}
@@ -0,0 +1,149 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since 0.1
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace DebugKit\View\Helper;
use ArrayAccess;
use Cake\View\Helper;
use Closure;
use Iterator;
/**
* Provides Base methods for content specific debug toolbar helpers.
* Acts as a facade for other toolbars helpers as well.
*
* @property \Cake\View\Helper\HtmlHelper $Html
* @property \Cake\View\Helper\FormHelper $Form
* @property \Cake\View\Helper\UrlHelper $Url
*/
class ToolbarHelper extends Helper
{
/**
* helpers property
*
* @var array
*/
public $helpers = ['Html', 'Form', 'Url'];
/**
* Whether or not the top level keys should be sorted.
*
* @var bool
*/
protected $sort = false;
/**
* set sorting of values
*
* @param bool $sort Whether or not sort values by key
* @return void
*/
public function setSort($sort)
{
$this->sort = $sort;
}
/**
* Recursively goes through an array and makes neat HTML out of it.
*
* @param mixed $values Array to make pretty.
* @param int $openDepth Depth to add open class
* @param int $currentDepth current depth.
* @param bool $doubleEncode Whether or not to double encode.
* @param \SplObjectStorage $currentAncestors Object references found down
* the path.
* @return string
*/
public function makeNeatArray($values, $openDepth = 0, $currentDepth = 0, $doubleEncode = false, \SplObjectStorage $currentAncestors = null)
{
if ($currentAncestors === null) {
$ancestors = new \SplObjectStorage();
} elseif (is_object($values)) {
$ancestors = new \SplObjectStorage();
$ancestors->addAll($currentAncestors);
$ancestors->attach($values);
} else {
$ancestors = $currentAncestors;
}
$className = "neat-array depth-$currentDepth";
if ($openDepth > $currentDepth) {
$className .= ' expanded';
}
$nextDepth = $currentDepth + 1;
$out = "<ul class=\"$className\">";
if (!is_array($values)) {
if (is_bool($values)) {
$values = [$values];
}
if ($values === null) {
$values = [null];
}
if (is_object($values) && method_exists($values, 'toArray')) {
$values = $values->toArray();
}
}
if (empty($values)) {
$values[] = '(empty)';
}
if ($this->sort && is_array($values) && $currentDepth === 0) {
ksort($values);
}
foreach ($values as $key => $value) {
$out .= '<li><strong>' . h($key, $doubleEncode) . '</strong> ';
if (is_array($value) && count($value) > 0) {
$out .= '(array)';
} elseif (is_object($value)) {
$out .= '(' . (get_class($value) ?: 'object') . ')';
}
if ($value === null) {
$value = '(null)';
}
if ($value === false) {
$value = '(false)';
}
if ($value === true) {
$value = '(true)';
}
if (empty($value) && $value != 0) {
$value = '(empty)';
}
if ($value instanceof Closure) {
$value = 'function';
}
$isObject = is_object($value);
if ($isObject && $ancestors->contains($value)) {
$isObject = false;
$value = ' - recursion';
}
if (
(
$value instanceof ArrayAccess ||
$value instanceof Iterator ||
is_array($value) ||
$isObject
) && !empty($value)
) {
$out .= $this->makeNeatArray($value, $openDepth, $nextDepth, $doubleEncode, $ancestors);
} else {
$out .= h($value, $doubleEncode);
}
$out .= '</li>';
}
$out .= '</ul>';
return $out;
}
}