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

+24
View File
@@ -0,0 +1,24 @@
# Basic docker based environment
# Necessary to trick dokku into building the documentation
# using dockerfile instead of herokuish
FROM ubuntu:17.04
# Add basic tools
RUN apt-get update && \
apt-get install -y build-essential \
software-properties-common \
curl \
git \
libxml2 \
libffi-dev \
libssl-dev
RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php && \
apt-get update && \
apt-get install -y php7.2-cli php7.2-mbstring php7.2-xml php7.2-zip php7.2-intl php7.2-opcache php7.2-sqlite
WORKDIR /code
VOLUME ["/code"]
CMD [ '/bin/bash' ]
+28
View File
@@ -0,0 +1,28 @@
The MIT License
CakePHP(tm) : The Rapid Development PHP Framework (http://cakephp.org)
Copyright (c) 2005-present, Cake Software Foundation, Inc.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
Cake Software Foundation, Inc.
1785 E. Sahara Avenue,
Suite 490-204
Las Vegas, Nevada 89104,
United States of America.
+79
View File
@@ -0,0 +1,79 @@
# CakePHP DebugKit
[![Build Status](https://secure.travis-ci.org/cakephp/debug_kit.png?branch=master)](http://travis-ci.org/cakephp/debug_kit)
[![Coverage Status](https://img.shields.io/codecov/c/github/cakephp/debug_kit.svg?style=flat-square)](https://codecov.io/github/cakephp/debug_kit)
[![License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.txt)
[![Total Downloads](https://img.shields.io/packagist/dt/cakephp/cakephp.svg?style=flat-square)](https://packagist.org/packages/cakephp/debug_kit)
DebugKit provides a debugging toolbar and enhanced debugging tools for CakePHP
applications. It lets you quickly see configuration data, log messages, SQL
queries, and timing data for your application.
:warning: DebugKit is only intended for use in single-user local development
environments. You should avoid using DebugKit in shared development
environments, staging environments, or any environment where you need to keep
configuration data and environment variables hidden. :warning:
## Requirements
The `master` branch has the following requirements:
* SQLite (pdo_sqlite) or another database driver that CakePHP can talk to. By
default DebugKit will use SQLite, if you need to use a different database see
the Database Configuration section below.
## DebugKit for CakePHP 2.x
If you want DebugKit for your 2.x application, you can use the latest `2.2.y` tag or the [2.2 branch](https://github.com/cakephp/debug_kit/tree/2.2).
## Installation
* Install the plugin with [Composer](https://getcomposer.org/) from your CakePHP Project's ROOT directory (where the **composer.json** file is located)
```sh
php composer.phar require --dev cakephp/debug_kit:"~3.0"
```
* [Load the plugin](http://book.cakephp.org/3.0/en/plugins.html#loading-a-plugin)
```php
// src/Application.php
$this->addPlugin('DebugKit');
```
Prior to 3.6.0
```php
Plugin::load('DebugKit', ['bootstrap' => true, 'routes' => true]);
```
* Set `'debug' => true,` in `config/app.php`.
## Reporting Issues
If you have a problem with DebugKit please open an issue on [GitHub](https://github.com/cakephp/debug_kit/issues).
## Contributing
If you'd like to contribute to DebugKit, check out the
[roadmap](https://github.com/cakephp/debug_kit/wiki/roadmap) for any
planned features. You can [fork](https://help.github.com/articles/fork-a-repo)
the project, add features, and send [pull
requests](https://help.github.com/articles/using-pull-requests) or open
[issues](https://github.com/cakephp/debug_kit/issues).
## Versions
DebugKit has several releases, each compatible with different releases of
CakePHP. Use the appropriate version by downloading a tag, or checking out the
correct branch.
* `1.0, 1.1, 1.2` are compatible with CakePHP 1.2.x. These releases of DebugKit
will not work with CakePHP 1.3. You can also use the `1.2-branch` for the mos
recent updates and bugfixes.
* `1.3.0` is compatible with CakePHP 1.3.x only. It will not work with CakePHP
1.2. You can also use the `1.3` branch to get the most recent updates and
bugfixes.
* `2.2.x` are compatible with CakePHP 2.2.0 and greater. It is a necessary
upgrade for people using CakePHP 2.4 as the naming conventions around loggers
changed in that release. 2.2.x is not actively being developed.
* `3.x` is compatible with CakePHP 3.x and is still under active development.
# Documentation
Documentation for DebugKit can be found in the
[CakePHP documentation](https://book.cakephp.org/debugkit/3.x/en/index.html).
+63
View File
@@ -0,0 +1,63 @@
{
"name": "cakephp/debug_kit",
"description": "CakePHP Debug Kit",
"type": "cakephp-plugin",
"keywords": ["cakephp", "debug", "kit"],
"homepage": "https://github.com/cakephp/debug_kit",
"license": "MIT",
"authors": [
{
"name": "Mark Story",
"homepage": "https://mark-story.com",
"role": "Author"
},
{
"name": "CakePHP Community",
"homepage": "https://github.com/cakephp/debug_kit/graphs/contributors"
}
],
"support": {
"issues": "https://github.com/cakephp/debug_kit/issues",
"forum": "http://stackoverflow.com/tags/cakephp",
"irc": "irc://irc.freenode.org/cakephp",
"source": "https://github.com/cakephp/debug_kit"
},
"require": {
"php": ">=5.6.0",
"cakephp/cakephp": "^3.7.0",
"cakephp/chronos": "^1.0.0",
"cakephp/plugin-installer": "^1.0.0",
"composer/composer": "^1.3.0",
"jdorn/sql-formatter": "^1.2.0"
},
"require-dev": {
"cakephp/authorization": "^1.3.2",
"cakephp/cakephp-codesniffer": "^3.0",
"phpunit/phpunit": "^5.7.14|^6.0"
},
"autoload": {
"psr-4": {
"DebugKit\\": "src/",
"DebugKit\\Test\\Fixture\\": "tests/Fixture/"
}
},
"autoload-dev": {
"psr-4": {
"Cake\\Test\\": "vendor/cakephp/cakephp/tests/",
"DebugKit\\Test\\": "tests/",
"DebugKit\\TestApp\\": "tests/test_app/",
"DebugkitTestPlugin\\": "tests/test_app/Plugin/DebugkitTestPlugin/src/"
}
},
"suggest": {
"ext-pdo_sqlite": "DebugKit needs to store panel data in a database. SQLite is simple and easy to use."
},
"scripts": {
"check": [
"@cs-check",
"@test"
],
"cs-check": "phpcs --colors -p src/ tests/",
"cs-fix": "phpcbf --colors -p src/ tests/"
}
}
+123
View File
@@ -0,0 +1,123 @@
<?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
*/
use Cake\Core\Configure;
use Cake\Core\Plugin as CorePlugin;
use Cake\Database\Query;
use Cake\Datasource\ConnectionManager;
use Cake\Event\EventManager;
use Cake\Log\Log;
use Cake\Routing\DispatcherFactory;
use DebugKit\DebugSql;
use DebugKit\Middleware\DebugKitMiddleware;
use DebugKit\Panel\DeprecationsPanel;
use DebugKit\Routing\Filter\DebugBarFilter;
use DebugKit\ToolbarService;
$service = new ToolbarService(EventManager::instance(), (array)Configure::read('DebugKit'));
if (!$service->isEnabled() || php_sapi_name() === 'cli' || php_sapi_name() === 'phpdbg') {
return;
}
if (!empty($service->getConfig('panels')['DebugKit.Deprecations'])) {
$errorLevel = Configure::read('Error.errorLevel', E_ALL | E_STRICT);
$previousHandler = set_error_handler(
function ($code, $message, $file, $line, $context = null) use (&$previousHandler) {
if ($code == E_USER_DEPRECATED || $code == E_DEPRECATED) {
DeprecationsPanel::addDeprecatedError(compact('code', 'message', 'file', 'line', 'context'));
return;
}
if ($previousHandler) {
$context['_trace_frame_offset'] = 1;
return $previousHandler($code, $message, $file, $line, $context);
}
},
$errorLevel | E_USER_DEPRECATED | E_DEPRECATED
);
}
$hasDebugKitConfig = ConnectionManager::getConfig('debug_kit');
if (!$hasDebugKitConfig && !in_array('sqlite', PDO::getAvailableDrivers())) {
$msg = 'DebugKit not enabled. You need to either install pdo_sqlite, ' .
'or define the "debug_kit" connection name.';
Log::warning($msg);
return;
}
if (!$hasDebugKitConfig) {
ConnectionManager::setConfig('debug_kit', [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Sqlite',
'database' => TMP . 'debug_kit.sqlite',
'encoding' => 'utf8',
'cacheMetadata' => true,
'quoteIdentifiers' => false,
]);
}
if (!CorePlugin::getCollection()->get('DebugKit')->isEnabled('routes')) {
include dirname(__FILE__) . DIRECTORY_SEPARATOR . 'routes.php';
}
$appClass = Configure::read('App.namespace') . '\Application';
if (class_exists($appClass)) {
EventManager::instance()->on('Server.buildMiddleware', function ($event, $queue) use ($service) {
$middleware = new DebugKitMiddleware($service);
$queue->insertAt(0, $middleware);
});
} else {
// Setup dispatch filter
$debugBar = new DebugBarFilter(EventManager::instance(), (array)Configure::read('DebugKit'));
$debugBar->setup();
DispatcherFactory::add($debugBar);
}
if (!function_exists('sql')) {
/**
* 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.
* @return Query
*/
function sql(Query $query, $showValues = true, $showHtml = null)
{
return DebugSql::sql($query, $showValues, $showHtml, 1);
}
}
if (!function_exists('sqld')) {
/**
* 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.
* @return void
*/
function sqld(Query $query, $showValues = true, $showHtml = null)
{
DebugSql::sqld($query, $showValues, $showHtml, 2);
}
}
+46
View File
@@ -0,0 +1,46 @@
<?php
use Cake\Routing\RouteBuilder;
use Cake\Routing\Route\DashedRoute;
use Cake\Routing\Router;
Router::plugin('DebugKit', ['path' => '/debug-kit'], function (RouteBuilder $routes) {
$routes->setExtensions('json');
$routes->setRouteClass(DashedRoute::class);
$routes->connect(
'/toolbar/clear-cache',
['controller' => 'Toolbar', 'action' => 'clearCache']
);
$routes->connect(
'/toolbar/*',
['controller' => 'Requests', 'action' => 'view']
);
$routes->connect(
'/panels/view/*',
['controller' => 'Panels', 'action' => 'view']
);
$routes->connect(
'/panels/*',
['controller' => 'Panels', 'action' => 'index']
);
$routes->connect(
'/composer/check-dependencies',
['controller' => 'Composer', 'action' => 'checkDependencies']
);
$routes->scope(
'/mail-preview',
['controller' => 'MailPreview'],
function (RouteBuilder $routes) {
$routes->connect('/', ['action' => 'index']);
$routes->connect('/preview', ['action' => 'email']);
$routes->connect('/preview/*', ['action' => 'email']);
$routes->connect('/sent/:panel/:id', ['action' => 'sent'], ['pass' => ['panel', 'id']]);
}
);
$routes->get('/', ['controller' => 'Dashboard', 'action' => 'index']);
$routes->get('/dashboard', ['controller' => 'Dashboard', 'action' => 'index']);
$routes->post('/dashboard/reset', ['controller' => 'Dashboard', 'action' => 'reset']);
});
+24
View File
@@ -0,0 +1,24 @@
# Generate the HTML output.
FROM markstory/cakephp-docs-builder as builder
RUN pip install git+https://github.com/sphinx-contrib/video.git@master
# Copy entire repo in with .git so we can build all versions in one image.
COPY docs /data/src
# Make docs
RUN cd /data/docs-builder \
&& make website LANGS="en fr ja pt" SOURCE=/data/src DEST=/data/website \
# Move media files into the output directory so video elements work.
&& mkdir -p /data/website/html/_static \
&& cp /data/src/static/* /data/website/html/_static/
# Build a small nginx container with just the static site in it.
FROM nginx:1.15-alpine
COPY --from=builder /data/website /data/website
COPY --from=builder /data/docs-builder/nginx.conf /etc/nginx/conf.d/default.conf
# Move files into final location
RUN cp -R /data/website/html/* /usr/share/nginx/html \
&& rm -rf /data/website/
View File
Whitespace-only changes.
+52
View File
@@ -0,0 +1,52 @@
# Global configuration information used across all the
# translations of documentation.
#
# Import the base theme configuration
from cakephpsphinx.config.all import *
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The full version, including alpha/beta/rc tags.
release = '3.x'
# The search index version.
search_version = 'debugkit-3'
# The marketing display name for the book.
version_name = ''
# Project name shown in the black header bar
project = 'CakePHP DebugKit'
# Other versions that display in the version picker menu.
version_list = [
{'name': '3.x', 'number': '/debugkit/3', 'title': '3.x', 'current': True},
{'name': '4.x', 'number': '/debugkit/4', 'title': '4.x'},
]
# Languages available.
languages = ['en', 'fr', 'ja', 'pt']
# The GitHub branch name for this version of the docs
# for edit links to point at.
branch = '3.x'
# Current version being built
version = '3.x'
# Language in use for this directory.
language = 'en'
show_root_link = True
repository = 'cakephp/debug_kit'
source_path = 'docs/'
hide_page_contents = ('search', '404', 'contents')
# DebugKit docs use mp4 videos to show the UI
extensions.append('sphinxcontrib.video')
+9
View File
@@ -0,0 +1,9 @@
import sys, os
# Append the top level directory of the docs, so we can import from the config dir.
sys.path.insert(0, os.path.abspath('..'))
# Pull in all the configuration options defined in the global config file..
from config.all import *
language = 'en'
+5
View File
@@ -0,0 +1,5 @@
.. toctree::
:maxdepth: 2
:caption: CakePHP DebugKit
/index
+286
View File
@@ -0,0 +1,286 @@
Debug Kit
#########
DebugKit provides a debugging toolbar and enhanced debugging tools for CakePHP
applications. It lets you quickly see configuration data, log messages, SQL
queries, and timing data for your application.
.. warning::
DebugKit is only intended for use in single-user local development
environments. You should avoid using DebugKit in shared development
environments, staging environments, or any environment where you need to
keep configuration data and environment variables hidden.
Installation
============
By default DebugKit is installed with the default application skeleton. If
you've removed it and want to re-install it, you can do so by running the
following from your application's ROOT directory (where composer.json file is
located)::
php composer.phar require --dev cakephp/debug_kit "~3.0"
Then, you need to enable the plugin by executing the following line::
bin/cake plugin load DebugKit
Configuration
=============
* ``DebugKit.panels`` - Enable or disable panels for DebugKit. You can disable any of the
standard panels by::
// Before loading DebugKit
Configure::write('DebugKit.panels', ['DebugKit.Packages' => false]);
* ``DebugKit.includeSchemaReflection`` - Set to true to enable logging of schema
reflection queries. Disabled by default.
* ``DebugKit.safeTld`` - Set an array of whitelisted TLDs for local development.
This can be used to make sure DebugKit displays on hosts it otherwise determines unsafe. ::
// Allow e.g. http://foo.bar.dev or http://my-shop.local domains locally
Configure::write('DebugKit.safeTld', ['dev', 'local', 'example']);
* ``DebugKit.forceEnable`` - Force DebugKit to display. Careful with this, it is usually
safer to simply whitelist your local TLDs. Example usage::
// Before loading DebugKit
Configure::write('DebugKit.forceEnable', true);
* ``DebugKit.ignoreAuthorization`` - Set to true to ignore Cake Authorization plugin for DebugKit requests. Disabled by default.
Database Configuration
----------------------
By default DebugKit will store panel data into a SQLite database in your
application's ``tmp`` directory. If you cannot install pdo_sqlite, you can
configure DebugKit to use a different database by defining a ``debug_kit``
connection in your **config/app.php** file. For example::
/**
* The debug_kit connection stores DebugKit meta-data.
*/
'debug_kit' => [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'host' => 'localhost',
//'port' => 'nonstandard_port_number',
'username' => 'dbusername', // Your DB username here
'password' => 'dbpassword', // Your DB password here
'database' => 'debug_kit',
'encoding' => 'utf8',
'timezone' => 'UTC',
'cacheMetadata' => true,
'quoteIdentifiers' => false,
//'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'],
],
You can safely remove the **tmp/debug_kit.sqlite** file at any point.
DebugKit will regenerate it when necessary.
Toolbar Usage
=============
The DebugKit Toolbar is comprised of several panels, which are shown by clicking
the CakePHP icon in the bottom right-hand corner of your browser after DebugKit
has been installed and loaded. Each panel is comprised of a panel class and view
element. Typically, a panel handles the collection and display of a single type
of information such as Logs or Request information. You can choose to view panels
from the toolbar or add your own custom panels.
Each panel lets you look at a different aspect of your application:
* **Cache** See cache usage during a request and clear caches.
* **Environment** Display environment variables related to PHP + CakePHP.
* **History** Displays a list of previous requests, and allows you to load
and view toolbar data from previous requests.
* **Include** View the included files grouped by type.
* **Log** Display any entries made to the log files this request.
* **Packages** Display the list of packages dependencies with their actual
version and allow you to check for outdated packages.
* **Mail** Display all emails sent during the request and allow to preview
emails during development without sending them.
* **Request** Displays information about the current request, GET, POST, Cake
Parameters, Current Route information and Cookies.
* **Session** Display the information currently in the Session.
* **Sql Logs** Displays SQL logs for each database connection.
* **Timer** Display any timers that were set during the request with
``DebugKit\DebugTimer``, and memory usage collected with
``DebugKit\DebugMemory``.
* **Variables** Display View variables set in controller.
* **Deprecations** Display deprecation warnings in a more readable and less
disruptive format.
Using the History Panel
=======================
The history panel is one of the most frequently misunderstood features of
DebugKit. It provides a way to view toolbar data from previous requests,
including errors and redirects.
.. figure:: ../_static/history-panel.png
:alt: Screenshot of the history panel in debug kit.
As you can see, the panel contains a list of requests. On the left you can see
a dot marking the active request. Clicking any request data will load the panel
data for that request. When historical data is loaded the panel titles will
transition to indicate that alternative data has been loaded.
.. only:: html or epub
.. video:: ../_static/history-panel-use.mp4
:alt: Video of history panel in action.
Using The Mail Panel
====================
The mail panel allow you to track all emails sent during a request.
.. only:: html or epub
.. video:: ../_static/mail-panel.mp4
:alt: Video of Mail panel in action.
The mailer preview allows you to easily check emails during development.
.. only:: html or epub
.. video:: ../_static/mail-previewer.mp4
:alt: Video of Mail panel in action.
Creating Preview Classes
------------------------
In order to preview emails before sending them, you need to create a preview
class that defines the receipient and required template variables for your
mailer methods::
// in src/Mailer/MailPreview/WelcomePreview.php
namespace App\Mailer\Preview;
use DebugKit\Mailer\MailPreview;
class WelcomePreview extends MailPreview
{
public function welcome()
{
$mailer = $this->getMailer('Welcome');
// set any template variables receipients for the mailer.
return $mailer;
}
}
MailPreview classes should live in the ``Mailer\Preview`` namespace of your
application or plugin, and use the ``Preview`` class suffix.
Developing Your Own Panels
==========================
You can create your own custom panels for DebugKit to help in debugging your
applications.
Creating a Panel Class
----------------------
Panel Classes simply need to be placed in the **src/Panel** directory. The
filename should match the classname, so the class ``MyCustomPanel`` would be
expected to have a filename of **src/Panel/MyCustomPanel.php**::
namespace App\Panel;
use DebugKit\DebugPanel;
/**
* My Custom Panel
*/
class MyCustomPanel extends DebugPanel
{
...
}
Notice that custom panels are required to extend the ``DebugPanel`` class.
Callbacks
---------
By default Panel objects have two callbacks, allowing them to hook into the
current request. Panels subscribe to the ``Controller.initialize`` and
``Controller.shutdown`` events. If your panel needs to subscribe to additional
events, you can use the ``implementedEvents()`` method to define all of the events
your panel is interested in.
You should refer to the built-in panels for some examples on how you can build
panels.
Panel Elements
--------------
Each Panel is expected to have a view element that renders the content from the
panel. The element name must be the underscored inflection of the class name.
For example ``SessionPanel`` has an element named **session_panel.ctp**, and
SqllogPanel has an element named **sqllog_panel.ctp**. These elements should be
located in the root of your **src/Template/Element** directory.
Custom Titles and Elements
--------------------------
Panels should pick up their title and element name by convention. However, if
you need to choose a custom element name or title, you can define methods to
customize your panel's behavior:
- ``title()`` - Configure the title that is displayed in the toolbar.
- ``elementName()`` - Configure which element should be used for a given panel.
Panel Hook Methods
------------------
You can also implement the following hook methods to customize how your panel
behaves and appears:
* ``shutdown(Event $event)`` This method typically collects and prepares the
data for the panel. Data is generally stored in ``$this->_data``.
* ``summary()`` Can return a string of summary data to be displayed in the
toolbar even when a panel is collapsed. Often this is a counter, or short
summary information.
* ``data()`` Returns the panel's data to be used as element context. This hook
method lets you further manipulate the data collected in the ``shutdown()``
method. This method **must** return data that can be serialized.
Panels in Other Plugins
-----------------------
Panels provided by `plugins <https://book.cakephp.org/3.0/en/plugins.html>`_ work
almost entirely the same as other plugins, with one minor difference: You must
set ``public $plugin`` to be the name of the plugin directory, so that the
panel's Elements can be located at render time::
namespace MyPlugin\Panel;
use DebugKit\DebugPanel;
class MyCustomPanel extends DebugPanel
{
public $plugin = 'MyPlugin';
...
}
To use a plugin or app panel, update your application's DebugKit configuration
to include the panel::
// in src/Application.php bootstrap() method add
Configure::write('DebugKit.panels', ['App', 'MyPlugin.MyCustom']);
$this->addPlugin('DebugKit', ['bootstrap' => true]);
The above would load all the default panels as well as the ``AppPanel``, and
``MyCustomPanel`` panel from ``MyPlugin``.
Helper Functions
================
* ``sql()`` Dumps out the SQL from an ORM query.
* ``sqld()`` Dumps out the SQL from an ORM query, and exits.
+9
View File
@@ -0,0 +1,9 @@
import sys, os
# Append the top level directory of the docs, so we can import from the config dir.
sys.path.insert(0, os.path.abspath('..'))
# Pull in all the configuration options defined in the global config file..
from config.all import *
language = 'fr'
+5
View File
@@ -0,0 +1,5 @@
.. toctree::
:maxdepth: 2
:caption: CakePHP DebugKit
/index
+239
View File
@@ -0,0 +1,239 @@
Debug Kit
#########
DebugKit est un plugin supporté par la core team qui fournit une toolbar pour
vous aider à debugger les applications CakePHP plus facilement.
DebugKit fournit une barre d'outils de débuggage et des outils de débuggage
améliorés pour les applications CakePHP. Il vous permet de voir rapidement les
données de configuration, les messages de logs, les requêtes SQL et des données
de temps d'exécution pour votre application.
.. warning::
DebugKit est uniquement destiné à être utilisé dans des environnements de
développement local à un seul utilisateur. Vous devez éviter d'utiliser
DebugKit dans les environnements de développement partagés, les
environnements de stagging où vous devez garder les données de configuration
et les variables d'environnement cachées.
Installation
============
Par défaut DebugKit est installé avec le squelette d'application. Si vous
le retirez et voulez le réinstaller, vous pouvez le faire en lançant ce qui
suit à partir du répertoire ROOT de votre application (où le fichier
composer.json est localisé)::
php composer.phar require --dev cakephp/debug_kit "~3.0"
Ensuite, vous devez activer le plugin en exécutant la ligne suivante::
bin/cake plugin load DebugKit
Configuration
=============
* ``DebugKit.ignoreAuthorization`` - Définie à true pour ignorer le plugin Cake Authorization uniquement pour les requêtes DebugKit. Par défaut à false.
Stockage de DebugKit
====================
Par défaut, DebugKit utilise une petite base de données SQLite dans le
répertoire ``/tmp`` de votre application pour stocker les données du panneau.
Si vous voulez que DebugKit stocke ses données ailleurs, vous pouvez définir
une connexion ``debug_kit``.
Configuration de Base de Données
--------------------------------
Par défaut DebugKit va stocker les données du panneau dans une base de données
SQLite dans le répertoire ``tmp`` de votre application. Si vous ne pouvez pas
installer pdo_sqlite, vous pouvez configurer DebugKit pour utiliser une base
de données différente en définissant une connexion ``debug_kit`` dans votre
fichier **config/app.php**.
Utilisation de la Toolbar
=========================
La Toolbar de DebugKit comprend plusieurs panneaux, qui apparaissent en
cliquant sur l'icone CakePHP dans le coin en bas à droite de la fenêtre de
votre navigateur. Une fois que la toolbar est ouverte, vous devriez voir une
série de boutons. Chacun de ces boutons s'agrandit en un panneau avec des
informations liées.
Chaque panneau vous permet d'inspecter plusieurs aspects de votre application:
* **Cache** Voir l'utilisation du cache pendant une requête et nettoyer les
caches.
* **Environment** Affiche les variables d'environnement liées à PHP + CakePHP.
* **History** Affiche une liste des requêtes précédentes, et vous permet de
charger et de voir les données de la toolbar des requêtes précédentes.
* **Include** Voir les fichiers inclus groupés par type.
* **Log** Affiche toute entrée faite dans les fichiers de log par cette requête.
* **Packages** Affiche la liste des dépendances avec leur version courante et
vous permet de vérifier les packages qui ne sont pas à jour.
* **Mail** Affiche tous les emails envoyés pendant la requête et permet de
prévisualiser les emails pendant le développement sans les envoyer.
* **Request** Affiche les informations sur la requête courante, GET, POST, les
paramètre de Cake, sur la Route Courante et les Cookies.
* **Session** Affiche les informations actuellement dans la Session.
* **Sql Logs** Affiche les logs SQL pour chaque connexion à la base de données.
* **Timer** Affiche tout timers qui a été défini pendant la requête avec
``DebugKit\DebugTimer``, et l'utilisation de la mémoire collectée avec
``DebugKit\DebugMemory``.
* **Variables** Affiche les variables de View définies dans le controller.
Typiquement, un panneau gère la collection et affiche un type unique
d'information comme les Logs ou les informations de la Requête. Vous pouvez
choisir de voir les panneaux de la toolbar ou ajouter vos panneaux
personnalisés.
Utiliser le Panneau d'Historique
================================
Le panneau d'historique est l'une des fonctionnalités les plus souvent mal
comprise de DebugKit. Elle est un moyen de voir les données de la toolbar des
requêtes précédentes, d'inclure les erreurs et les redirects.
.. figure:: ../_static/history-panel.png
:alt: Capture d'écran du panneau historique dans debug kit.
Comme vous pouvez le voir, le panneau contient une liste des requêtes. Sur la
gauche, vous pouvez voir un point marquant la requête actuelle. Cliquer
sur n'importe quelles données de requête va charger les données du panneau
pour cette requête. Quand les données historiques sont chargées, les titres
du panneau seront colorés pour indiquer que des données alternatives ont été
chargées.
.. only:: html or epub
.. video:: ../_static/history-panel-use.mp4
:alt: Video du panneau historique en action.
Utiliser le Panneau Mail
========================
Le panneau de mail vous permet de pister tous les emails envoyés pendant une
requête.
.. only:: html or epub
.. video:: ../_static//mail-panel.mp4
:alt: Video du panneau Mail en action.
La prévisualisation de mail vous permet de facilement vérifier les emails
pendant le développement.
.. only:: html or epub
.. video:: ../_static/img/mail-previewer.gif
:alt: Video du panneau Mail en action.
Développer vos Propres Panneaux
===============================
Vous pouvez créer vos propres panneaux personnalisés pour DebugKit pour
améliorer le debug de vos applications.
Créer une Classe Panel
----------------------
Les Classes Panel doivent simplement être placées dans le répertoire
**src/Panel**. Le nom de fichier doit correspondre au nom de la classe, pour
que la classe ``MyCustomPanel`` s'attende à avoir un fichier au nom
**src/Panel/MyCustomPanel.php**::
namespace App\Panel;
use DebugKit\DebugPanel;
/**
* Mon panneau Personnalisé
*/
class MyCustomPanel extends DebugPanel
{
...
}
Remarquez que les panneaux personnalisés doivent étendre la classe
``DebugPanel``.
Callbacks
---------
Par défaut, les objets Panel ont deux callbacks, leur permettant de s'insérer
dans la requête actuelle. Les panneaux s'inscrivent aux events
``Controller.initialize`` et ``Controller.shutdown``. Si votre panneau doit
s'inscrire à des events supplémentaires, vous pouvez utiliser la méthode
``implementedEvents()`` pour définir tous les events auxquels votre panneau
doit s'intéresser.
Vous devez vous référer aux panneaux intégrés pour avoir quelques exemples sur
la façon de construire des panneaux.
Elements de Panneau
-------------------
Chaque panneau s'attend à avoir un element de view qui rend le contenu du
panneau. Le nom de l'element doit être avec une inflection en underscore du
nom de la classe.
Par exemple ``SessionPanel`` a un element nommé **session_panel.ctp**, et
SqllogPanel a un element nommé **sqllog_panel.ctp**. Ces elements doivent être
localisés à la racine de votre répertoire **src/Template/Element**.
Titres Personnalisés et Elements
--------------------------------
Les panneaux doivent choisir leur titre et leur nom d'element par convention.
Cependant, si vous avez besoin de choisir un nom ou un titre d'element
personnalisé, vous pouvez définir des méthodes pour personnaliser le
comportement de votre panneau:
- ``title()`` - Configure le titre qui est affiché dans la toolbar.
- ``elementName()`` Configure l'element qui doit être utilisé pour un panneau
donné.
Méthodes de Hook pour Panneaux
------------------------------
Vous pouvez également implémenter les méthodes suivantes pour personnaliser
la manière dont votre panneau se comporte et s'affiche:
* ``shutdown(Event $event)`` Cette méthode collecte et prépare les données pour
panneau. Les données sont généralement stockées dans ``$this->_data``.
* ``summary()`` Peut retourner une chaine de caractères contenu un résumé de
données qui sera affiché dans la barre lorsque le panneau est replié. C'est
souvent un compteur ou un court résumé.
* ``data()`` Retourne les données du panneau pour être utilisées dans un
element. Cette méthode vous laisse manipuler les données collectées dans
la méthode ``shutdown()``. cette méthode **doit** retourner des données
sérializables.
Panneaux dans d'autres Plugins
------------------------------
Les panneaux fournis par les `plugins
<https://book.cakephp.org/3.0/fr/plugins.html>`_ fonctionnent presque entièrement de
la même façon que les autres plugins, avec quelques différences mineures: Vous
devez définir ``public $plugin`` comme nom de répertoire de plugin, pour
que les elements du panneau puissent être localisés au moment de les afficher::
namespace MyPlugin\Panel;
use DebugKit\DebugPanel;
class MyCustomPanel extends DebugPanel
{
public $plugin = 'MyPlugin';
...
}
Pour utiliser un panneau de plugin ou de l'application, mettez à jour
la configuration du DebugKit de votre application pour ajouter le panneau::
// dans config/bootstrap.php
Configure::write('DebugKit.panels', ['App', 'MyPlugin.MyCustom']);
Plugin::load('DebugKit', ['bootstrap' => true]);
Ce qui est au-dessus charge tous les panneaux par défaut ainsi que le panneau
``AppPanel``et le panneau ``MyCustomPanel`` depuis ``MyPlugin``.
+9
View File
@@ -0,0 +1,9 @@
import sys, os
# Append the top level directory of the docs, so we can import from the config dir.
sys.path.insert(0, os.path.abspath('..'))
# Pull in all the configuration options defined in the global config file..
from config.all import *
language = 'ja'
+5
View File
@@ -0,0 +1,5 @@
.. toctree::
:maxdepth: 2
:caption: CakePHP DebugKit
/index
+267
View File
@@ -0,0 +1,267 @@
Debug Kit
#########
DebugKit は、CakePHP アプリケーション用のデバッグツールバーと拡張デバッグツールを提供します。
アプリケーションの設定データ、ログメッセージ、SQL クエリー、およびタイミングデータをすばやく
確認できます。
.. warning::
DebugKit は、シングルユーザーのローカル開発環境での使用を目的としています。
共有開発環境、ステージング環境、または設定データと環境変数を隠しておく必要がある環境では、
DebugKit を使用しないでください。
インストール
============
デフォルトで、DebugKit はデフォルトのアプリケーションスケルトンにインストールされています。
もし削除してしまって再インストールしたい場合は、以下のコマンドをアプリケーションの
ルートディレクトリー (composer.json ファイルのある場所) で実行してください。 ::
php composer.phar require --dev cakephp/debug_kit "~3.0"
そして以下のコマンドでプラグインを有効化する必要があります。 ::
bin/cake plugin load DebugKit
設定
====
* ``DebugKit.panels`` - DebugKit のパネルを有効化または無効化します。
次のようにして、任意の標準パネルを無効にすることができます。 ::
// DebugKit をロードする前に
Configure::write('DebugKit.panels', ['DebugKit.Packages' => false]);
* ``DebugKit.includeSchemaReflection`` - スキーマリフレクションクエリーの
ロギングを有効にするには、true に設定します。デフォルトは無効になっています。
* ``DebugKit.safeTld`` - ローカル開発用のトップレベルドメインのホワイトリストを設定します。
これにより安全でないと判断したホスト上で DebugKit を表示できるようになります。 ::
// 例: http://foo.bar.dev または http://my-shop.local のドメインはローカルとして許可する
Configure::write('DebugKit.safeTld', ['dev', 'local', 'example']);
* ``DebugKit.forceEnable`` - DebugKit を強制的に表示します。これを使うときは注意してください。
通常は単純にローカルのトップレベルドメインをホワイトリストに追加したほうが安全です。 使用例::
// DebugKit をロードする前に
Configure::write('DebugKit.forceEnable', true);
データベース設定
----------------
デフォルトでは DebugKit はアプリケーションの ``/tmp`` 内の SQLite データベースに
パネルデータを保持します。もし pdo_sqlite をインストールできない場合は、
**config/app.php** ファイル に ``debug_kit`` コネクションを定義すると
DebugKit が異なるデータベースを使用するように設定できます。例::
/**
* debug_kit コネクションは DebugKit のメタデータを格納します。
*/
'debug_kit' => [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'host' => 'localhost',
//'port' => 'nonstandard_port_number',
'username' => 'dbusername', // Your DB username here
'password' => 'dbpassword', // Your DB password here
'database' => 'debug_kit',
'encoding' => 'utf8',
'timezone' => 'UTC',
'cacheMetadata' => true,
'quoteIdentifiers' => false,
//'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'],
],
**tmp/debug_kit.sqlite** ファイルは、いつでも削除することができます。
DebugKit は、必要に応じて、それを再生成します。
ツールバーの使い方
==================
DebugKit ツールバーは、いくつかのパネルで構成されています。これらのパネルは、
DebugKit をインストールしてロードした後、ブラウザーの右下隅にある CakePHP
アイコンをクリックすると表示されます。各パネルは、パネルクラスとビューエレメントで構成されています。
一般的に、パネルは単一の種類の情報のコレクションの処理やログやリクエスト情報の表示を行います。
ツールバーからパネルを表示したり、独自のカスタムパネルを追加することを選択できます。
各パネルはアプリケーションの様々な側面を見せてくれます。
* **Cache** リクエスト中のキャッシュの使用状況の確認とキャッシュのクリアー。
* **Environment** PHP 及び CakePHP に関連する環境変数の表示。
* **History** 以前のリクエストの一覧の表示と、以前のリクエストのツールバーデータの読み込みと表示。
* **Include** タイプごとにグループ化されたインクルードファイルを表示。
* **Log** このリクエスト中にログファイルに加えられたすべてのエントリーを表示。
* **Packages** 現在のバージョンのパッケージの依存関係の一覧を表示し、
古いパッケージをチェックすることができます。
* **Mail** このリクエスト中の全てのメール送信を表示し、
送信せずに開発中のメールのプレビューができます。
* **Request** 現在のリクエスト、GET、POST、Cake パラメーター、現在のルート情報、クッキー情報の表示。
* **Session** 現在のセッション情報の表示。
* **Sql Logs** 各データベースコネクションの SQL ログの表示。
* **Timer** リクエスト中に ``DebugKit\DebugTimer`` で設定されたすべてのタイマーと、
``DebugKit\DebugMemory`` で収集されたメモリー使用状況の表示。
* **Variables** コントローラーでセットされたビュー変数の表示。
履歴パネルを使用する
====================
履歴パネルは DebugKit で最も勘違いされやすい機能の一つです。
エラーやリダイレクトを含む以前のリクエストのツールバーのデータを表示することができます。
.. figure:: ../_static/history-panel.png
:alt: Screenshot of the history panel in debug kit.
ご覧のようにパネルにはリクエストの一覧が含まれます。左側にドットがついているのがアクティブな
リクエストです。どれかのリクエストをクリックするとそのリクエストのデータが読み込まれます。
履歴データがロードされると代替のデータが読み込まれたことを示すようにパネルのタイトルが遷移します。
.. only:: html or epub
.. video:: ../_static/history-panel-use.mp4
:alt: Video of history panel in action.
メールパネルを使用
==================
メールパネルは、リクエストの間に送信された全てのメールを追跡することができます。
.. only:: html or epub
.. video:: ../_static/mail-panel.mp4
:alt: Video of mail panel in action
メーラープレビューは、開発中のメールを簡単にプレビューすることができます。
.. only:: html or epub
.. video:: ../_static/mail-previewer.mp4
:alt: Video of mail panel in action
プレビュークラスの作成
------------------------
メールを送信する前にプレビューするには、受信者と mailer メソッドに必要なテンプレート変数を
定義するプレビュークラスを作成する必要があります。 ::
// src/Mailer/MailPreview/WelcomePreview.php の中で
namespace App\Mailer\Preview;
use DebugKit\Mailer\MailPreview;
class WelcomePreview extends MailPreview
{
public function welcome()
{
$mailer = $this->getMailer('Welcome');
// メーラーのテンプレート変数受信者を設定します。
return $mailer;
}
}
MailPreview クラスは、 アプリケーションまたはプラグインの ``Mailer\Preview``
名前空間に存在し、 ``Preview`` を使用する必要があります。
独自のパネルを開発する
======================
アプリケーションのデバッグを補助するための DebugKit の独自のカスタムパネルを
作成することができます。
パネルクラスを作成する
----------------------
パネルクラスは単に **src/Panel** ディレクトリーに設置してください。ファイル名はクラス名と
一致する必要があります。 つまり ``MyCustomPanel`` クラスは
**src/Panel/MyCustomPanel.php** というファイル名であることを想定しています。 ::
namespace App\Panel;
use DebugKit\DebugPanel;
/**
* My Custom Panel
*/
class MyCustomPanel extends DebugPanel
{
...
}
カスタムパネルは ``DebugPanel`` クラスを拡張する必要があることに注意してください。
コールバック
------------
デフォルトではパネルオブジェクトには、現在のリクエストをフックすることができる
2つのコールバックがあります。パネルは ``Controller.initialize`` と
``Controller.shutdown`` のイベントを取得します。もしパネルが追加のイベントを
取得したい場合は、 ``implementedEvents()`` メソッドを使用し、
パネルが必要とするすべてのイベントを定義できます。
どのようにパネルを構築するかについてのいくつかの例として、ビルトインのパネルを参照してください。
パネルの構成要素
----------------
各パネルはパネルのコンテンツを描画するためのビューエレメントがあることを想定しています。
エレメント名はアンダースコアー区切りのクラス名である必要があります。
たとえば、 ``SessionPanel`` は **session_panel.ctp** という名前のエレメントを持ちます。
また、SqllogPanelは **sqllog_panel.ctp** という名前のエレメントを持ちます。
これらのエレメントは **src/Template/Element** ディレクトリーのルートに設置する必要があります。
カスタムのタイトルとエレメント
------------------------------
パネルは慣例を元にそのタイトルとエレメント名を補足します。もしカスタムのタイトルやエレメント名を
付けたい場合は、パネルの振る舞いをカスタムするメソッドを定義することができます。
- ``title()`` - ツールバー上に表示されるタイトルを設定します
- ``elementName()`` - 与えられたパネルがどのエレメントを使用するかを設定します
パネルフックメソッド
--------------------
また、パネルの動作や表示方法をカスタムするために以下のフックメソッドを実装することができます。
* ``shutdown(Event $event)`` このメソッドは通常はパネルのデータの収集と準備を行います。
* ``summary()`` パネルが折りたたまれている時に表示されるサマリーデータの文字列を
返すことができます。多くの場合、これは件数や短いサマリー情報です。
* ``data()`` エレメントのコンテキストで使用されるパネルのデータを返します。
このフックメソッドは ``shutdown()`` で収集されるデータを更に操作することができます。
このメソッドはシリアライズ化可能なデータを **必ず** 返す必要があります。
他のプラグインのパネル
----------------------
パネルはひとつの小さな違いを除き、`plugins <https://book.cakephp.org/3.0/ja/plugins.html>`_
とほぼ同じ動作を提供します。レンダリング時にパネルのエレメントを配置できるように、
``public $plugin`` にプラグインディレクトリーの名前を必ずセットする必要があります。 ::
namespace MyPlugin\Panel;
use DebugKit\DebugPanel;
class MyCustomPanel extends DebugPanel
{
public $plugin = 'MyPlugin';
...
}
プラグインやアプリケーションパネルを使用するには、アプリケーションの DebugKit の設定を
更新します。 ::
// src/Application.php の bootstrap() メソッドの中に追加
Configure::write('DebugKit.panels', ['App', 'MyPlugin.MyCustom']);
$this->addPlugin('DebugKit', ['bootstrap' => true]);
上記は、すべてのデフォルトのパネルと同じように ``AppPanel`` と ``MyPlugin`` の
``MyCustomPanel`` パネルを読みこみます。
ヘルパー関数
================
* ``sql()`` ORM クエリーから SQL をダンプします。
* ``sqld()`` ORM クエリーから SQL をダンプして終了します。
+9
View File
@@ -0,0 +1,9 @@
import sys, os
# Append the top level directory of the docs, so we can import from the config dir.
sys.path.insert(0, os.path.abspath('..'))
# Pull in all the configuration options defined in the global config file..
from config.all import *
language = 'pt'
+5
View File
@@ -0,0 +1,5 @@
.. toctree::
:maxdepth: 2
:caption: CakePHP DebugKit
/index
+216
View File
@@ -0,0 +1,216 @@
Debug Kit
#########
DebugKit é um plugin suportado pelo time principal do CakePHP que oferece uma
barra de ferramentas para auxiliar na depuração de aplicações do CakePHP.
Instalação
==========
Por padrão o DebugKit é instalado com o esqueleto padrão da aplicação. Se
você o removeu e gostaria de reinstalá-lo, você pode executar o seguinte comando
a partir do diretório raiz da aplicação
(onde o arquivo composer.json está localizado)::
php composer.phar require --dev cakephp/debug_kit "~3.0"
Então, você precisará habilitar o plugin ao executar o seguinte comando::
bin/cake plugin load DebugKit
Armazenamento do DebugKit
=========================
Por padrão, o DebugKit usa um pequeno banco de dados SQLite no diretório
``/tmp`` de sua aplicação para armazenar os dados referentes ao painel
de informações. Se você quiser que o DebugKit armazene seus dados em outro
lugar, é necessário configurar uma nova conexão com o nome ``debug_kit`` .
Configuração do banco de dados
------------------------------
Como informado anteriormente, por padrão, o DebugKit armazenará os dados do
painel em um banco de dados SQLite no diretório ``/tmp`` de sua aplicação. Se
você não puder instalar a extensão pdo_sqlite do PHP, você pode configurar o
DebugKit para usar um banco de dados diferente ao definir uma conexão
``debug_kit`` em seu arquivo **config/app.php**. Por exemplo::
/**
* A conexão debug_kit armazena meta-dados do DebugKit.
*/
'debug_kit' => [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'host' => 'localhost',
//'port' => 'nonstandard_port_number',
'username' => 'dbusername', // Your DB username here
'password' => 'dbpassword', // Your DB password here
'database' => 'debug_kit',
'encoding' => 'utf8',
'timezone' => 'UTC',
'cacheMetadata' => true,
'quoteIdentifiers' => false,
//'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'],
],
Uso da barra de ferramentas
===========================
A barra de ferramentas do DebugKit é composta por vários painéis, que são
exibidos ao clicar no ícone do CakePHP no canto inferior direito da janela do
seu navegador. Uma vez que a barra de ferramentas é aberta, você deverá ver uma
série de botões. Cada um desses botões se expande em um painel de informações
relacionadas.
Cada painel permite a você observar aspectos diferentes da sua aplicação:
* **Cache** Exibe o uso de cache durante uma solicitação e limpa caches.
* **Environment** Exibe variáveis de ambiente relacionadas com PHP + CakePHP.
* **History** Exibe uma lista de requisições anteriores, e permite que você
carregue e veja dados da barra de ferramentas a partir de solicitações
anteriores.
* **Include** Exibe os arquivos inclusos divididos por grupo.
* **Log** Exibe todas as entradas feitas nos arquivos de log este pedido.
* **Request** Exibe informações sobre a requisição atual, GET, POST, informações
sobre a rota atual do Cake e Cookies.
* **Session** Exibe a informação atual da sessão.
* **Sql Logs** Exibe logs SQL para cada conexão com o banco de dados.
* **Timer** Exibe qualquer temporizador que fora definido durante a requisição
com ``DebugKit\DebugTimer``, e memória utilizada coletada com
``DebugKit\DebugMemory``.
* **Variables** Exibe variáveis de View definidas em um Controller.
Tipicamente, um painel manipula a recolha e apresentação de um único tipo
de informações como logs ou informações de requisições. Você pode optar por
visualizar os painéis padrões da barra de ferramentas ou adicionar seus próprios
painéis personalizados.
Usando o painel History
=======================
O painel History é uma das características mais frequentemente confundidas do
DebugKit. Ele oferece uma forma de visualizar os dados de requisições
anteriores na barra de ferramentas, incluindo erros e redirecionamentos.
.. figure:: /_static/img/history-panel.png
:alt: Screenshot do painel History no DebugKit.
Como você pode ver, o painel contém uma lista de requisições. Na esquerda você
pode ver um ponto marcando a requisição ativa. Clicar em qualquer requisição vai
carregar os dados referentes a mesma no painel. Quando os dados são carregados,
os títulos do painel vão sofrer uma transição para indicar que informações
alternativos foram carregados.
.. only:: html or epub
.. figure:: /_static/img/history-panel-use.gif
:alt: Video do painel History em ação.
Desenvolvendo seus próprios painéis
===================================
Você pode criar seus próprios painéis customizados do DebugKit para ajudar
na depuração de suas aplicações.
Criando uma Panel Class
-----------------------
Panel Classes precisam ser colocadas no diretório **src/Panel**. O
nome do arquivo deve combinar com o nome da classe, então a classe
``MyCustomPanel`` deveria remeter ao nome de arquivo
**src/Panel/MyCustomPanel.php**::
namespace App\Panel;
use DebugKit\DebugPanel;
/**
* My Custom Panel
*/
class MyCustomPanel extends DebugPanel
{
...
}
Perceba que painéis customizados são necessários para extender a classe
``DebugPanel``.
Callbacks
---------
Por padrão objetos do painel possuem dois callbacks, permitindo-lhes acoplar-se
na requisição atual. Painéis inscrevem-se aos eventos ``Controller.initialize``
e ``Controller.shutdown``. Se o seu painel precisa inscrever-se a eventos
adicionais, você pode usar o método ``implementedEvents`` para definir todos os
eventos aos quais o seu painel possa precisar estar inscrito.
Você deveria estudar os painéis nativos para absorver alguns exemplos de como
construir painéis.
Elementos do painel
-------------------
Cada painel deve ter um elemento view que renderiza o conteúdo do mesmo.
O nome do elemento deve ser sublinhado e flexionado a partir do nome da classe.
Por exemplo ``SessionPanel`` deve possuir um elemento nomeado
**session_panel.ctp**, e SqllogPanel deve possuir um elemento nomeado
**sqllog_panel.ctp**. Estes elementos devem estar localizados na raiz do seu
diretório **src/Template/Element**.
Títulos personalizados e Elementos
----------------------------------
Os painéis devem relacionar o seu título e o nome do elemento por convenção. No
entanto, se você precisa escolher um nome de elemento personalizado ou título,
você pode definir métodos para customizar o comportamento do seu painel:
- ``title()`` - Configure o título que é exibido na barra de ferramentas.
- ``elementName()`` - Configure qual elemento deve ser utilizada para um
determinado painel.
Métodos de captura
------------------
Você também pode implementar os seguintes métodos para customizar como o seu
painel se comporta e se aparenta:
* ``shutdown(Event $event)`` Esse método coleta e prepara os dados para o
painel. Os dados são geralmente armazenados em ``$this->_data``.
* ``summary()`` Este método retorna uma *string* de dados resumidos para serem
exibidos na *toolbar*, mesmo quando um painel está minimizado. Frequentemente,
é um contador ou um pequeno resumo de informações.
* ``data()`` Este método retorna os dados do painel que serão usados como
contexto para o elemento. Você pode manipular os dados coletados no método
``shutdown()``. Esse método **deve** retornar dados que podem ser
serializados.
Painéis em outros plugins
-------------------------
Painéis disponibilizados por `plugins
<https://book.cakephp.org/3.0/pt/plugins.html>`_ funcionam quase que totalmente
como outros plugins, com uma pequena diferença: Você deve definir ``public
$plugin`` como o nome do diretório do plugin, com isso os elementos do painel
poderão ser encontrados no momento de renderização::
namespace MyPlugin\Panel;
use DebugKit\DebugPanel;
class MyCustomPanel extends DebugPanel
{
public $plugin = 'MyPlugin';
...
}
Para usar um plugin ou painel da aplicação, atualize a configuração do DebugKit
de sua aplicação para incluir o painel::
Configure::write(
'DebugKit.panels',
array_merge(Configure::read('DebugKit.panels'), ['MyCustomPanel'])
);
O código acima deve carregar todos os painéis padrão tanto como os outros
painéis customizados do ``MyPlugin``.
@@ -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);
}
}
Loaded 100 of 6561 files, more files were not shown because too many files have changed in this diff. Show more