64 Commits
Author SHA1 Message Date
Andrey Savchenko c874c19c91 Updated documentation for v2 release. 2018-02-26 10:42:11 +02:00
Andrey Savchenko 221a902671 Adjusted SQL and default colors. 2018-02-26 09:17:39 +02:00
Andrey Savchenko ac0a187ec4 Excluded mustache cache from code coverage. 2018-02-23 17:06:53 +02:00
Andrey Savchenko 06d2136528 Dropped flat style from badges. 2018-02-23 17:06:37 +02:00
Andrey Savchenko beb68aede2 Added explicit PHP version requirement and updated badges. 2018-02-23 16:38:15 +02:00
Andrey Savchenko b837508181 Updated event colors. 2018-02-23 16:28:55 +02:00
Andrey Savchenko d92a778d10 Adjusted structure for PDS skeleton. 2018-02-23 15:32:18 +02:00
Andrey Savchenko 4238509861 Added PHP and core load collector. 2018-02-23 15:12:29 +02:00
Andrey Savchenko e74777c2a9 Added generic constructor–centric record class. 2018-02-23 14:40:45 +02:00
Andrey Savchenko 3cae2765cb Fixed stopwatch record duration test that didn’t test properly. 2018-02-22 18:19:34 +02:00
Andrey Savchenko 35e8caf1e4 Fixed stopwatch record test for origin time. 2018-02-22 18:04:00 +02:00
Andrey Savchenko 7cc65ca1a2 Fixed stacking of events that are just very close to each other. 2018-02-22 18:01:46 +02:00
Andrey Savchenko 1e75033f25 Normalized on operating in float seconds rather than milliseconds. 2018-02-22 13:14:15 +02:00
Andrey Savchenko 624e282905 Added container test. 2018-02-22 12:34:47 +02:00
Andrey Savchenko 56e9447dce Renamed manager tests for consistency. 2018-02-22 12:25:14 +02:00
Andrey Savchenko 560eb85f4b Added provider tests. 2018-02-22 12:23:47 +02:00
Andrey Savchenko 801f6585cb Added missing namespace in test. 2018-02-21 16:16:57 +02:00
Andrey Savchenko 61dd387484 Added SQL and iterators tests. 2018-02-21 16:12:34 +02:00
Andrey Savchenko 420305143b Added generic stopwatch collector and tests. 2018-02-20 18:09:44 +02:00
Andrey Savchenko 7020e84b2d Added hook event tests. 2018-02-19 16:50:38 +02:00
Andrey Savchenko 5ec0f6c677 Added manager tests. 2018-02-19 16:12:01 +02:00
Andrey Savchenko 1bf5e5c5ad Set up unit tests. 2018-02-16 20:19:16 +02:00
Andrey Savchenko 8cd280f12a Added inline documentation. 2018-02-14 17:54:28 +02:00
Andrey Savchenko d850fa1b02 Renamed main class to Plugin for more clear role. 2018-02-13 18:22:35 +02:00
Andrey Savchenko a19e4fb019 Moved timeline iterator to record namespace. 2018-02-13 18:18:36 +02:00
Andrey Savchenko 53efcd28af Added keys to collectors array. 2018-02-13 18:16:41 +02:00
Andrey Savchenko c74051e87d Adjusted hook events for a simpler interface and wrapped in a provider. 2018-02-13 17:26:41 +02:00
Andrey Savchenko 06e3bb4657 Added proper providers to handle registration and boot. 2018-02-13 11:37:54 +02:00
Andrey Savchenko 48b3383e06 Introduced explicit record interface for event results. 2018-02-12 18:30:00 +02:00
Andrey Savchenko ce7cf8eade Reorganized code to designate events as input and records as results. 2018-02-12 17:12:58 +02:00
Andrey Savchenko 67d686f601 Split functionality from container to manager classes. 2018-02-12 17:02:17 +02:00
Andrey Savchenko 2c074c1cea Simplified processing nested events to a single pass. 2018-02-12 15:53:40 +02:00
Andrey Savchenko a9484ed4ee Added timeline iterator for automatic splitting of nested events. 2018-02-09 18:47:13 +02:00
Andrey Savchenko bee765071b Implemented initial handling of arbitrary nesting events.
See #11
2018-02-09 15:15:23 +02:00
Andrey Savchenko baf5d293a1 Fixed screen reader text. 2018-02-08 21:17:25 +02:00
Andrey Savchenko 36e0d14c48 Fixed empty timelines filter. 2018-02-08 19:11:14 +02:00
Andrey Savchenko 228ea99b43 Dropped any events that aren't properly stopped. 2018-02-08 19:10:57 +02:00
Andrey Savchenko 79e197c4cc Moved another leftover action. 2018-02-08 18:36:31 +02:00
Andrey Savchenko 8396888ee2 Moved leftover event stop to the provider. 2018-02-08 18:10:01 +02:00
Andrey Savchenko d8febfeb3a Moved SQL events to a provider and simplified template. 2018-02-08 18:08:01 +02:00
Andrey Savchenko 31b80d0413 Adjusted from returning raw Stopwatch event objects from providers. 2018-02-08 17:07:35 +02:00
Andrey Savchenko c93249875b Changed Stopwatch service to a factory. 2018-02-08 16:20:30 +02:00
Andrey Savchenko 054bf2e0df Moved HTTP requests events to a provider. 2018-02-08 16:08:39 +02:00
Andrey Savchenko 267f389a40 Moved hooks events to a provider. 2018-02-08 15:00:57 +02:00
Andrey Savchenko 39f03e02bf Added provider interfaces. 2018-02-08 14:58:43 +02:00
Andrey Savchenko 6fc8a648ad Made main class into Pimple container. 2018-02-08 14:29:10 +02:00
Andrey Savchenko b709f968f9 Moved events classes. 2018-02-08 10:57:20 +02:00
Andrey Savchenko c966fb1552 Dropped Thematic events as the theme is obsolete. 2018-02-07 22:55:51 +02:00
Andrey Savchenko 62ea1f4246 Changed main class to concrete instance. 2018-02-07 22:08:41 +02:00
Andrey Savchenko 9527ad1508 Bumped Stopwatch version and updated dependencies. 2018-02-07 21:57:19 +02:00
Andrey Savchenko a77dd9e4c8 Renamed class files for PSR-4. 2018-02-07 21:53:22 +02:00
Andrey Savchenko 03ff2b61ff Updated header version to 1.4.4 2017-07-05 21:45:39 +03:00
Andrey Savchenko 3c0ae866e0 Added Robo tag task and updated changelog for 1.4.4
Fixes #10
2017-07-05 21:45:27 +03:00
Andrey Savchenko d2bece68e5 Added sanity check for invalid priority returned by core
Fixes #9
2017-06-28 14:25:45 +03:00
Andrey Savchenko f50faf6ba6 Added downloads badge to readme. 2016-12-06 21:32:31 +02:00
Andrey Savchenko bca3f42ee5 Updated version and change log for 1.4.2 release. 2016-12-02 19:18:58 +02:00
Andrey Savchenko 68302633c0 Restored functionality on WP 4.7 beta with new current_priority() method. 2016-12-02 19:18:41 +02:00
Andrey Savchenko 67ad1370ef Added Scrutinizer badge to readme. 2016-12-01 22:10:39 +02:00
Andrey Savchenko 4b7ee062c8 Updated change log and version for release 1.4.1. 2016-12-01 20:35:19 +02:00
Andrey Savchenko 2e7a948bfe Added access modifier to methods. 2016-12-01 20:34:15 +02:00
Andrey Savchenko 68ae657962 Added check for Toolbar event start before closing it. 2016-12-01 20:33:55 +02:00
Andrey Savchenko 3edaf2dbc9 Added check for hook priority failing on WP 4.7. 2016-12-01 20:30:48 +02:00
Andrey Savchenko fa155f00e7 Fixed return type hint. 2016-12-01 20:30:15 +02:00
Andrey Savchenko 82b3d1e4e3 Moved license to a separate file. 2016-12-01 20:29:52 +02:00
69 changed files with 2603 additions and 1036 deletions

No files matched your search

+44
View File
@@ -6,6 +6,50 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## Unreleased ## Unreleased
## 2.0 - 2018-02-26
### Added
- unified processing of events from arbitrary sources
- unit tests
- tracking PHP and WP core load times
### Changed
- minimum PHP version requirement to >=5.6
- class files names to PSR–4
- main class into concrete instance and Pimple container
- timeline to unified processing with as necessary nesting
- directory structure to PDS skeleton
- baseline timings to float in seconds (from milliseconds)
- color scheme
### Removed
- Thematic hook events
### Fixed
- handling or not properly started/stopped hook events
## 1.4.4 - 2017-07-05
### Added
- Robo task to update header version and tag in Git
### Fixed
- version in header
## 1.4.3 - 2017-06-28
### Fixed
- sanity check for invalid priority returned by core
## 1.4.2 - 2016-12-02
### Fixed
- back to fully functional on WP 4.7 beta
## 1.4.1 - 2016-12-01
### Fixed
- errors and crash on WP 4.7 beta (not fully functional yet)
## 1.4 - 2016-10-01 ## 1.4 - 2016-10-01
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2013 Andrey Savchenko
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.
+29 -33
View File
@@ -1,46 +1,42 @@
# Laps — light WordPress profiler # Laps — light WordPress profiler
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/Rarst/laps/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/Rarst/laps/?branch=master)
[![Build Status](https://scrutinizer-ci.com/g/Rarst/laps/badges/build.png?b=master)](https://scrutinizer-ci.com/g/Rarst/laps/build-status/master)
[![Code Coverage](https://scrutinizer-ci.com/g/Rarst/laps/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/Rarst/laps/?branch=master)
[![Total Downloads](https://img.shields.io/packagist/dt/rarst/laps.svg)](https://packagist.org/packages/rarst/laps)
[![Latest Stable Version](https://img.shields.io/packagist/v/rarst/laps.svg?label=version)](https://packagist.org/packages/rarst/laps)
[![PHP from Packagist](https://img.shields.io/packagist/php-v/rarst/laps.svg)](https://packagist.org/packages/rarst/laps)
[![PDS Skeleton](https://img.shields.io/badge/pds-skeleton-blue.svg)](https://github.com/php-pds/skeleton)
![Laps screenshot](http://i.imgur.com/zFokmkU.png) _Make a site, make it fast._
Laps is light WordPress profiler (in a plugin), which aims to be: ![Laps v2 screenshot](https://i.imgur.com/6n36KPE.png)
- always on and zero click (just hover on toolbar entry) away Laps is a plugin that **shows performance information** about WordPress page load.
- quick indicator of what hogs page in general or right now
It is no match or replacement for *real* profiler, but is friendly and cute. It provides a visual summary in toolbar that is quick and easy to inspect.
Out of the box Laps supports common stage of WordPress page life cycle: Laps tracks many events, such as:
- PHP, core, plugins, and themes load
- plugins load - database queries (with [`SAVEQUERIES` constant](http://codex.wordpress.org/Editing_wp-config.php#Save_queries_for_analysis) enabled)
- themes load - network requests to other sites
- core init - main posts loop
- main loop - events for supported theme frameworks:
- [Theme Hook Alliance](http://zamoose.github.io/themehookalliance/)
And some of third party hooks conventions for themes using: - [Hybrid](http://themehybrid.com/)
- [Genesis](http://my.studiopress.com/themes/genesis/)
- [Theme Hook Alliance](http://zamoose.github.io/themehookalliance/) - events for supported plugins:
- [Hybrid](http://themehybrid.com/) - [Yoast SEO](https://wordpress.org/plugins/wordpress-seo/)
- [Genesis](http://my.studiopress.com/themes/genesis/)
- [Thematic](http://thematictheme.com/)
There are also additional optional timelines, displaying:
- SQL queries (with [`SAVEQUERIES` constant](http://codex.wordpress.org/Editing_wp-config.php#Save_queries_for_analysis) enabled)
- HTTP requests performed
## Installation ## Installation
Download plugin archive from [releases section](https://github.com/Rarst/laps/releases). Require with [Composer](https://getcomposer.org/):
Or install in plugin directory via [Composer](https://getcomposer.org/): ```bash
composer require rarst/laps
```
composer create-project rarst/laps --no-dev Or download plugin archive from [releases section](https://github.com/Rarst/laps/releases).
## License Info ## License
Laps own code is licensed under MIT and it makes use of code from: MIT
- Composer Installers (MIT)
- Symfony Stopwatch (MIT)
- Mustache.php (MIT)
- Twitter Bootstrap (MIT)
+26 -10
View File
@@ -15,10 +15,10 @@ class RoboFile extends \Robo\Tasks {
public function watch() { public function watch() {
$this->taskWatch() $this->taskWatch()
->monitor( [ 'css/variables.less', 'css/laps.less' ], function () { ->monitor( [ 'public/css/variables.less', 'public/css/laps.less' ], function () {
$this->makeCss(); $this->makeCss();
} ) } )
->monitor( 'js/source.js', function () { ->monitor( 'public/js/source.js', function () {
$this->makeJs(); $this->makeJs();
} ) } )
->run(); ->run();
@@ -39,9 +39,9 @@ class RoboFile extends \Robo\Tasks {
*/ */
public function makeCss() { public function makeCss() {
$this->taskExec( 'lessc css/laps.less css/laps.css --source-map=css/laps.css.map' )->run(); $this->taskExec( 'lessc public/css/laps.less public/css/laps.css --source-map=public/css/laps.css.map' )->run();
$this->taskMinify( 'css/laps.css' ) $this->taskMinify( 'public/css/laps.css' )
->run(); ->run();
} }
@@ -52,12 +52,12 @@ class RoboFile extends \Robo\Tasks {
$this->taskConcat( [ $this->taskConcat( [
'vendor/twbs/bootstrap/js/tooltip.js', 'vendor/twbs/bootstrap/js/tooltip.js',
'js/source.js', 'public/js/source.js',
] ) ] )
->to( 'js/laps.js' ) ->to( 'public/js/laps.js' )
->run(); ->run();
$this->taskMinify( 'js/laps.js' ) $this->taskMinify( 'public/js/laps.js' )
->run(); ->run();
} }
@@ -65,18 +65,34 @@ class RoboFile extends \Robo\Tasks {
* Compiles plugin's mustache template * Compiles plugin's mustache template
*/ */
public function makeMustache() { public function makeMustache() {
$dir = __DIR__ . '/views/cache'; $dir = __DIR__ . '/src/mustache/cache';
$this->_cleanDir( $dir ); $this->_cleanDir( $dir );
$mustache = new \Mustache_Engine( $mustache = new \Mustache_Engine(
array( array(
'loader' => new \Mustache_Loader_FilesystemLoader( __DIR__ . '/views' ), 'loader' => new \Mustache_Loader_FilesystemLoader( __DIR__ . '/src/mustache' ),
'cache' => $dir, 'cache' => $dir,
) )
); );
$mustache->loadTemplate( 'laps' ); $mustache->loadTemplate( 'laps' );
} }
/**
* Updates version in header and makes git version tag.
*
* @param string $version Version string.
*/
public function tag( $version ) {
$this->versionSet( $version );
$this->taskGitStack()
->stopOnFail()
->add( 'laps.php' )
->commit( "Updated header version to {$version}" )
->tag( $version )
->run();
}
/** /**
* Sets the plugin version in header. * Sets the plugin version in header.
* *
+15 -5
View File
@@ -8,7 +8,6 @@
"authors" : [ "authors" : [
{ {
"name" : "Andrey Savchenko", "name" : "Andrey Savchenko",
"email" : "contact@rarst.net",
"homepage": "http://www.Rarst.net/" "homepage": "http://www.Rarst.net/"
} }
], ],
@@ -16,14 +15,25 @@
"issues": "https://github.com/Rarst/laps/issues" "issues": "https://github.com/Rarst/laps/issues"
}, },
"autoload" : { "autoload" : {
"classmap": ["src/"] "psr-4": {
"Rarst\\Laps\\" : "src/"
}
},
"autoload-dev": {
"psr-4": {
"Rarst\\Laps\\Tests\\": "tests/"
}
}, },
"require" : { "require" : {
"php" : ">=5.6",
"composer/installers": "~1.0", "composer/installers": "~1.0",
"symfony/stopwatch" : "~2.4", "symfony/stopwatch" : "^3.4.4",
"mustache/mustache" : "~2.4" "mustache/mustache" : "~2.4",
"pimple/pimple" : "^3.2.3"
}, },
"require-dev": { "require-dev": {
"twbs/bootstrap": "~3.2" "twbs/bootstrap": "~3.2",
"brain/monkey" : "^2.2",
"pds/skeleton": "^1.0"
} }
} }
Generated
+386 -24
View File
@@ -4,21 +4,20 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"hash": "7495421e8df337988c9906e05aab637b", "content-hash": "b88211148a5c0893e190e574ac18bd30",
"content-hash": "3a8ac6bb545eee9eb5d1ce8b87136995",
"packages": [ "packages": [
{ {
"name": "composer/installers", "name": "composer/installers",
"version": "v1.2.0", "version": "v1.5.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/composer/installers.git", "url": "https://github.com/composer/installers.git",
"reference": "d78064c68299743e0161004f2de3a0204e33b804" "reference": "049797d727261bf27f2690430d935067710049c2"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/composer/installers/zipball/d78064c68299743e0161004f2de3a0204e33b804", "url": "https://api.github.com/repos/composer/installers/zipball/049797d727261bf27f2690430d935067710049c2",
"reference": "d78064c68299743e0161004f2de3a0204e33b804", "reference": "049797d727261bf27f2690430d935067710049c2",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -30,7 +29,7 @@
}, },
"require-dev": { "require-dev": {
"composer/composer": "1.0.*@dev", "composer/composer": "1.0.*@dev",
"phpunit/phpunit": "4.1.*" "phpunit/phpunit": "^4.8.36"
}, },
"type": "composer-plugin", "type": "composer-plugin",
"extra": { "extra": {
@@ -60,12 +59,17 @@
"keywords": [ "keywords": [
"Craft", "Craft",
"Dolibarr", "Dolibarr",
"Eliasis",
"Hurad", "Hurad",
"ImageCMS", "ImageCMS",
"Kanboard",
"Lan Management System",
"MODX Evo", "MODX Evo",
"Mautic", "Mautic",
"Maya",
"OXID", "OXID",
"Plentymarkets", "Plentymarkets",
"Porto",
"RadPHP", "RadPHP",
"SMF", "SMF",
"Thelia", "Thelia",
@@ -83,28 +87,36 @@
"croogo", "croogo",
"dokuwiki", "dokuwiki",
"drupal", "drupal",
"eZ Platform",
"elgg", "elgg",
"expressionengine", "expressionengine",
"fuelphp", "fuelphp",
"grav", "grav",
"installer", "installer",
"itop",
"joomla", "joomla",
"kohana", "kohana",
"laravel", "laravel",
"lavalite",
"lithium", "lithium",
"magento", "magento",
"majima",
"mako", "mako",
"mediawiki", "mediawiki",
"modulework", "modulework",
"modx",
"moodle", "moodle",
"osclass",
"phpbb", "phpbb",
"piwik", "piwik",
"ppi", "ppi",
"puppet", "puppet",
"pxcms",
"reindex", "reindex",
"roundcube", "roundcube",
"shopware", "shopware",
"silverstripe", "silverstripe",
"sydes",
"symfony", "symfony",
"typo3", "typo3",
"wordpress", "wordpress",
@@ -112,20 +124,20 @@
"zend", "zend",
"zikula" "zikula"
], ],
"time": "2016-08-13 20:53:52" "time": "2017-12-29T09:13:20+00:00"
}, },
{ {
"name": "mustache/mustache", "name": "mustache/mustache",
"version": "v2.11.1", "version": "v2.12.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/bobthecow/mustache.php.git", "url": "https://github.com/bobthecow/mustache.php.git",
"reference": "a3f6d55996dd28b58f3a909d474189a3c1aa693f" "reference": "fe8fe72e9d580591854de404cc59a1b83ca4d19e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/a3f6d55996dd28b58f3a909d474189a3c1aa693f", "url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/fe8fe72e9d580591854de404cc59a1b83ca4d19e",
"reference": "a3f6d55996dd28b58f3a909d474189a3c1aa693f", "reference": "fe8fe72e9d580591854de404cc59a1b83ca4d19e",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -158,29 +170,128 @@
"mustache", "mustache",
"templating" "templating"
], ],
"time": "2016-07-31 06:18:27" "time": "2017-07-11T12:54:05+00:00"
}, },
{ {
"name": "symfony/stopwatch", "name": "pimple/pimple",
"version": "v2.8.11", "version": "v3.2.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/stopwatch.git", "url": "https://github.com/silexphp/Pimple.git",
"reference": "35bae476693150728b0eb51647faac82faf9aaca" "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/stopwatch/zipball/35bae476693150728b0eb51647faac82faf9aaca", "url": "https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32",
"reference": "35bae476693150728b0eb51647faac82faf9aaca", "reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.3.9" "php": ">=5.3.0",
"psr/container": "^1.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^3.2"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "2.8-dev" "dev-master": "3.2.x-dev"
}
},
"autoload": {
"psr-0": {
"Pimple": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Pimple, a simple Dependency Injection Container",
"homepage": "http://pimple.sensiolabs.org",
"keywords": [
"container",
"dependency injection"
],
"time": "2018-01-21T07:42:36+00:00"
},
{
"name": "psr/container",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/container.git",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common Container Interface (PHP FIG PSR-11)",
"homepage": "https://github.com/php-fig/container",
"keywords": [
"PSR-11",
"container",
"container-interface",
"container-interop",
"psr"
],
"time": "2017-02-14T16:28:37+00:00"
},
{
"name": "symfony/stopwatch",
"version": "v3.4.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/stopwatch.git",
"reference": "c865551df7c17e63fc1f09f763db04387f91ae4d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/stopwatch/zipball/c865551df7c17e63fc1f09f763db04387f91ae4d",
"reference": "c865551df7c17e63fc1f09f763db04387f91ae4d",
"shasum": ""
},
"require": {
"php": "^5.5.9|>=7.0.8"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.4-dev"
} }
}, },
"autoload": { "autoload": {
@@ -207,10 +318,259 @@
], ],
"description": "Symfony Stopwatch Component", "description": "Symfony Stopwatch Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2016-06-29 05:29:29" "time": "2018-01-03T07:37:34+00:00"
} }
], ],
"packages-dev": [ "packages-dev": [
{
"name": "antecedent/patchwork",
"version": "2.1.8",
"source": {
"type": "git",
"url": "https://github.com/antecedent/patchwork.git",
"reference": "3bb81ace3914c220aa273d1c0603d5e1b454c0d7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/antecedent/patchwork/zipball/3bb81ace3914c220aa273d1c0603d5e1b454c0d7",
"reference": "3bb81ace3914c220aa273d1c0603d5e1b454c0d7",
"shasum": ""
},
"require": {
"php": ">=5.4.0"
},
"type": "library",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ignas Rudaitis",
"email": "ignas.rudaitis@gmail.com"
}
],
"description": "Method redefinition (monkey-patching) functionality for PHP.",
"homepage": "http://patchwork2.org/",
"keywords": [
"aop",
"aspect",
"interception",
"monkeypatching",
"redefinition",
"runkit",
"testing"
],
"time": "2018-02-19T18:52:50+00:00"
},
{
"name": "brain/monkey",
"version": "2.2.0",
"source": {
"type": "git",
"url": "https://github.com/Brain-WP/BrainMonkey.git",
"reference": "ed9e0698bc1292f33698719da8ca1aa2e18acc51"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Brain-WP/BrainMonkey/zipball/ed9e0698bc1292f33698719da8ca1aa2e18acc51",
"reference": "ed9e0698bc1292f33698719da8ca1aa2e18acc51",
"shasum": ""
},
"require": {
"antecedent/patchwork": "^2.0",
"mockery/mockery": ">=0.9 <2",
"php": ">=5.6.0"
},
"require-dev": {
"phpunit/phpunit": "~5.7.9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-version/1": "1.x-dev",
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Brain\\Monkey\\": "src/"
},
"files": [
"inc/api.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Giuseppe Mazzapica",
"email": "giuseppe.mazzapica@gmail.com",
"homepage": "https://gmazzap.me",
"role": "Developer"
}
],
"description": "Mocking utility for PHP functions and WordPress plugin API",
"keywords": [
"Monkey Patching",
"interception",
"mock",
"mock functions",
"mockery",
"patchwork",
"redefinition",
"runkit",
"test",
"testing"
],
"time": "2017-12-01T16:32:09+00:00"
},
{
"name": "hamcrest/hamcrest-php",
"version": "v2.0.0",
"source": {
"type": "git",
"url": "https://github.com/hamcrest/hamcrest-php.git",
"reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/776503d3a8e85d4f9a1148614f95b7a608b046ad",
"reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad",
"shasum": ""
},
"require": {
"php": "^5.3|^7.0"
},
"replace": {
"cordoval/hamcrest-php": "*",
"davedevelopment/hamcrest-php": "*",
"kodova/hamcrest-php": "*"
},
"require-dev": {
"phpunit/php-file-iterator": "1.3.3",
"phpunit/phpunit": "~4.0",
"satooshi/php-coveralls": "^1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
}
},
"autoload": {
"classmap": [
"hamcrest"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD"
],
"description": "This is the PHP port of Hamcrest Matchers",
"keywords": [
"test"
],
"time": "2016-01-20T08:20:44+00:00"
},
{
"name": "mockery/mockery",
"version": "1.0",
"source": {
"type": "git",
"url": "https://github.com/mockery/mockery.git",
"reference": "1bac8c362b12f522fdd1f1fa3556284c91affa38"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mockery/mockery/zipball/1bac8c362b12f522fdd1f1fa3556284c91affa38",
"reference": "1bac8c362b12f522fdd1f1fa3556284c91affa38",
"shasum": ""
},
"require": {
"hamcrest/hamcrest-php": "~2.0",
"lib-pcre": ">=7.0",
"php": ">=5.6.0"
},
"require-dev": {
"phpunit/phpunit": "~5.7|~6.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-0": {
"Mockery": "library/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Pádraic Brady",
"email": "padraic.brady@gmail.com",
"homepage": "http://blog.astrumfutura.com"
},
{
"name": "Dave Marshall",
"email": "dave.marshall@atstsolutions.co.uk",
"homepage": "http://davedevelopment.co.uk"
}
],
"description": "Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succinct API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending.",
"homepage": "http://github.com/mockery/mockery",
"keywords": [
"BDD",
"TDD",
"library",
"mock",
"mock objects",
"mockery",
"stub",
"test",
"test double",
"testing"
],
"time": "2017-10-06T16:20:43+00:00"
},
{
"name": "pds/skeleton",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-pds/skeleton.git",
"reference": "95e476e5d629eadacbd721c5a9553e537514a231"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-pds/skeleton/zipball/95e476e5d629eadacbd721c5a9553e537514a231",
"reference": "95e476e5d629eadacbd721c5a9553e537514a231",
"shasum": ""
},
"bin": [
"bin/pds-skeleton"
],
"type": "standard",
"autoload": {
"psr-4": {
"Pds\\Skeleton\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"CC-BY-SA-4.0"
],
"description": "Standard for PHP package skeletons.",
"homepage": "https://github.com/php-pds/skeleton",
"time": "2017-01-25T23:30:41+00:00"
},
{ {
"name": "twbs/bootstrap", "name": "twbs/bootstrap",
"version": "v3.3.7", "version": "v3.3.7",
@@ -260,7 +620,7 @@
"responsive", "responsive",
"web" "web"
], ],
"time": "2016-07-25 15:51:55" "time": "2016-07-25T15:51:55+00:00"
} }
], ],
"aliases": [], "aliases": [],
@@ -268,6 +628,8 @@
"stability-flags": [], "stability-flags": [],
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false, "prefer-lowest": false,
"platform": [], "platform": {
"php": ">=5.6"
},
"platform-dev": [] "platform-dev": []
} }
-1
View File
@@ -1 +0,0 @@
{"version":3,"sources":["laps.less","C:/server/www/dev/wp-content/plugins/laps/vendor/twbs/bootstrap/less/progress-bars.less","C:/server/www/dev/wp-content/plugins/laps/vendor/twbs/bootstrap/less/mixins/vendor-prefixes.less","C:/server/www/dev/wp-content/plugins/laps/vendor/twbs/bootstrap/less/scaffolding.less","C:/server/www/dev/wp-content/plugins/laps/vendor/twbs/bootstrap/less/tooltip.less","C:/server/www/dev/wp-content/plugins/laps/vendor/twbs/bootstrap/less/mixins/reset-text.less","C:/server/www/dev/wp-content/plugins/laps/vendor/twbs/bootstrap/less/variables.less","C:/server/www/dev/wp-content/plugins/laps/vendor/twbs/bootstrap/less/mixins/opacity.less"],"names":[],"mappings":"AAMA,WAEC,mBAEC;EACC,cAAA;EACA,UAAA;EACA,eAAA;EACA,QAAA;;AARH,WAEC,mBASC;EACC,UAAA;;AAZH,WAgBC;EAEC,eAAA;;AAEA,WAJD,0BAIE;EACA,6BAAA;;AArBH,WAgBC,0BAQC;EACC,aAAA;;AAzBH,WA6BC;ECTC,gBAAA;EACA,YAAA;EACA,mBAAA;EACA,yBAAA;EACA,kBAAA;ECsCA,sDAAA;EACQ,8CAAA;EFhCR,aAAA;EACA,kBAAA;;AAhCF,WA6BC,eAKC;ECJA,WAAA;EACA,SAAA;EACA,YAAA;EACA,eAAA;EACA,iBAAA;EACA,WAAA;EACA,kBAAA;EACA,yBAAA;ECyBA,sDAAA;EACQ,8CAAA;EAyHR,mCAAA;EACK,8BAAA;EACG,2BAAA;EFtJP,kBAAA;EACA,8BAAA;EACA,cAAA;;AAtCH,WA6BC,eAYC;EACC,yBAAA;;AA1CH,WA6BC,eAeC;EACC,yBAAA;;AA7CH,WA6BC,eAkBC;EACC,yBAAA;;AAhDH,WA6BC,eAqBC;EACC,yBAAA;;AAnDH,WA6BC,eAwBC;EACC,yBAAA;;AAtDH,WA6BC,eA2BC;EACC,yBAAA;;AAzDH,WA6BC,eA+BC;EG2DA,kBAAA;EACA,UAAA;EACA,WAAA;EACA,YAAA;EACA,UAAA;EACA,gBAAA;EACA,MAAM,gBAAN;EACA,SAAA;;AH9HF,WIAA;EACE,kBAAA;EACA,eAAA;EACA,cAAA;ECRA,aC2CwB,8CD3CxB;EAEA,kBAAA;EACA,mBAAA;EACA,sBAAA;EACA,gBAAA;EACA,uBAAA;EACA,gBAAA;EACA,iBAAA;EACA,qBAAA;EACA,iBAAA;EACA,oBAAA;EACA,mBAAA;EACA,kBAAA;EACA,oBAAA;EACA,iBAAA;EDHA,eAAA;EGVA,UAAA;EAGA,wBAAA;;AHWA,WAXF,SAWG;EGdD,YAAA;EAGA,yBAAA;;AHYA,WAZF,SAYG;EAAU,gBAAA;EAAmB,cAAA;;AAC9B,WAbF,SAaG;EAAU,gBAAA;EAAmB,cAAA;;AAC9B,WAdF,SAcG;EAAU,eAAA;EAAmB,cAAA;;AAC9B,WAfF,SAeG;EAAU,iBAAA;EAAmB,cAAA;;AJfhC,WImBA;EACE,gBAAA;EACA,gBAAA;EACA,WAAA;EACA,kBAAA;EACA,yBAAA;EACA,kBAAA;;AJzBF,WI6BA;EACE,kBAAA;EACA,QAAA;EACA,SAAA;EACA,yBAAA;EACA,mBAAA;;AAIA,WADF,SACG,IAAK;EACJ,SAAA;EACA,SAAA;EACA,iBAAA;EACA,uBAAA;EACA,yBAAA;;AAEF,WARF,SAQG,SAAU;EACT,SAAA;EACA,UAAA;EACA,mBAAA;EACA,uBAAA;EACA,yBAAA;;AAEF,WAfF,SAeG,UAAW;EACV,SAAA;EACA,SAAA;EACA,mBAAA;EACA,uBAAA;EACA,yBAAA;;AAEF,WAtBF,SAsBG,MAAO;EACN,QAAA;EACA,OAAA;EACA,gBAAA;EACA,2BAAA;EACA,2BAAA;;AAEF,WA7BF,SA6BG,KAAM;EACL,QAAA;EACA,QAAA;EACA,gBAAA;EACA,2BAAA;EACA,0BAAA;;AAEF,WApCF,SAoCG,OAAQ;EACP,MAAA;EACA,SAAA;EACA,iBAAA;EACA,uBAAA;EACA,4BAAA;;AAEF,WA3CF,SA2CG,YAAa;EACZ,MAAA;EACA,UAAA;EACA,gBAAA;EACA,uBAAA;EACA,4BAAA;;AAEF,WAlDF,SAkDG,aAAc;EACb,MAAA;EACA,SAAA;EACA,gBAAA;EACA,uBAAA;EACA,4BAAA;;AJ5FJ,WAmEC,SAAQ;EOtEP,UAAA;EAGA,0BAAA;;APAF,WAuEC;EACC,iBAAA;EACA,gBAAA","file":"laps.css"}
-1
View File
@@ -1 +0,0 @@
#wpadminbar #wp-admin-bar-laps .ab-sub-wrapper{min-width:90%;width:90%;position:fixed;left:5%}#wpadminbar #wp-admin-bar-laps .ab-submenu{padding:0}#wpadminbar #wp-admin-bar-laps_output{padding:0 10px}#wpadminbar #wp-admin-bar-laps_output:hover{background-color:transparent}#wpadminbar #wp-admin-bar-laps_output .ab-empty-item{display:none}#wpadminbar .laps-timeline{overflow:hidden;height:20px;margin-bottom:20px;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);margin:6px 0;position:relative}#wpadminbar .laps-timeline .event{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#5bc0de;-webkit-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-webkit-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease;position:absolute;border-left:1px solid #f5f5f5;min-width:2px}#wpadminbar .laps-timeline .event-theme{background-color:#5cb85c}#wpadminbar .laps-timeline .event-plugin{background-color:#428bca}#wpadminbar .laps-timeline .event-core{background-color:#f0ad4e}#wpadminbar .laps-timeline .event-query-read{background-color:#5bc0de}#wpadminbar .laps-timeline .event-query-write{background-color:#d9534f}#wpadminbar .laps-timeline .event-http{background-color:#d9534f}#wpadminbar .laps-timeline .sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}#wpadminbar .tooltip{position:absolute;z-index:100000;display:block;font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;font-style:normal;font-weight:normal;letter-spacing:normal;line-break:auto;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal;font-size:12px;opacity:0;filter:alpha(opacity=0)}#wpadminbar .tooltip.in{opacity:0.9;filter:alpha(opacity=90)}#wpadminbar .tooltip.top{margin-top:-3px;padding:5px 0}#wpadminbar .tooltip.right{margin-left:3px;padding:0 5px}#wpadminbar .tooltip.bottom{margin-top:3px;padding:5px 0}#wpadminbar .tooltip.left{margin-left:-3px;padding:0 5px}#wpadminbar .tooltip-inner{max-width:300px;padding:3px 8px;color:#ccc;text-align:center;background-color:#464646;border-radius:4px}#wpadminbar .tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}#wpadminbar .tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#464646}#wpadminbar .tooltip.top-left .tooltip-arrow{bottom:0;right:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#464646}#wpadminbar .tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#464646}#wpadminbar .tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#464646}#wpadminbar .tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#464646}#wpadminbar .tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#464646}#wpadminbar .tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#464646}#wpadminbar .tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#464646}#wpadminbar .tooltip.in{opacity:1;filter:alpha(opacity=100)}#wpadminbar .tooltip-inner{line-height:20px;text-align:left}
+7 -22
View File
@@ -1,36 +1,21 @@
<?php <?php
/* /*
Plugin Name: Laps Plugin Name: Laps
Plugin URI: https://github.com/Rarst/laps Plugin URI: https://github.com/Rarst/laps
Description: Light WordPress profiler. Description: Light WordPress profiler.
Author: Andrey "Rarst" Savchenko Author: Andrey “Rarst” Savchenko
Version: 1.4 Version: 2.0
Author URI: http://www.rarst.net/ Author URI: https://www.rarst.net/
Text Domain: laps Text Domain: laps
Domain Path: /lang Domain Path: /lang
License: MIT License: MIT
Copyright (c) 2013 Andrey "Rarst" Savchenko
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.
*/ */
if ( file_exists( __DIR__ . '/vendor/autoload.php' ) ) { if ( file_exists( __DIR__ . '/vendor/autoload.php' ) ) {
require __DIR__ . '/vendor/autoload.php'; require __DIR__ . '/vendor/autoload.php';
} }
Rarst\Laps\Laps::on_load(); $laps = new \Rarst\Laps\Plugin();
$laps->run();
+15
View File
@@ -0,0 +1,15 @@
<phpunit bootstrap="vendor/autoload.php">
<testsuites>
<testsuite name="Laps">
<directory>tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">src</directory>
<exclude>
<directory suffix=".php">src/mustache/cache</directory>
</exclude>
</whitelist>
</filter>
</phpunit>
+13 -10
View File
@@ -35,7 +35,7 @@
line-height: 20px; line-height: 20px;
color: #fff; color: #fff;
text-align: center; text-align: center;
background-color: #5bc0de; background-color: #AAAAAA;
-webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
-webkit-transition: width 0.6s ease; -webkit-transition: width 0.6s ease;
@@ -45,23 +45,26 @@
border-left: 1px solid #f5f5f5; border-left: 1px solid #f5f5f5;
min-width: 2px; min-width: 2px;
} }
#wpadminbar .laps-timeline .event-php {
background-color: #B10DC9;
}
#wpadminbar .laps-timeline .event-theme { #wpadminbar .laps-timeline .event-theme {
background-color: #5cb85c; background-color: #2ECC40;
} }
#wpadminbar .laps-timeline .event-plugin { #wpadminbar .laps-timeline .event-plugin {
background-color: #428bca; background-color: #0074D9;
} }
#wpadminbar .laps-timeline .event-core { #wpadminbar .laps-timeline .event-core {
background-color: #f0ad4e; background-color: #FF851B;
} }
#wpadminbar .laps-timeline .event-query-read { #wpadminbar .laps-timeline .event-sql-read {
background-color: #5bc0de; background-color: #7FDBFF;
} }
#wpadminbar .laps-timeline .event-query-write { #wpadminbar .laps-timeline .event-sql-write {
background-color: #d9534f; background-color: #FF4136;
} }
#wpadminbar .laps-timeline .event-http { #wpadminbar .laps-timeline .event-http {
background-color: #d9534f; background-color: #FF4136;
} }
#wpadminbar .laps-timeline .sr-only { #wpadminbar .laps-timeline .sr-only {
position: absolute; position: absolute;
@@ -119,7 +122,7 @@
#wpadminbar .tooltip-inner { #wpadminbar .tooltip-inner {
max-width: 300px; max-width: 300px;
padding: 3px 8px; padding: 3px 8px;
color: #ccc; color: #eee;
text-align: center; text-align: center;
background-color: #464646; background-color: #464646;
border-radius: 4px; border-radius: 4px;
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":["laps.less","C:/server/www/dev/wp-content/plugins/laps/vendor/twbs/bootstrap/less/progress-bars.less","C:/server/www/dev/wp-content/plugins/laps/vendor/twbs/bootstrap/less/mixins/vendor-prefixes.less","C:/server/www/dev/wp-content/plugins/laps/vendor/twbs/bootstrap/less/scaffolding.less","C:/server/www/dev/wp-content/plugins/laps/vendor/twbs/bootstrap/less/tooltip.less","C:/server/www/dev/wp-content/plugins/laps/vendor/twbs/bootstrap/less/mixins/reset-text.less","C:/server/www/dev/wp-content/plugins/laps/vendor/twbs/bootstrap/less/variables.less","C:/server/www/dev/wp-content/plugins/laps/vendor/twbs/bootstrap/less/mixins/opacity.less"],"names":[],"mappings":"AAMA,WAEC,mBAEC;EACC,cAAA;EACA,UAAA;EACA,eAAA;EACA,QAAA;;AARH,WAEC,mBASC;EACC,UAAA;;AAZH,WAgBC;EAEC,eAAA;;AAEA,WAJD,0BAIE;EACA,6BAAA;;AArBH,WAgBC,0BAQC;EACC,aAAA;;AAzBH,WA6BC;ECTC,gBAAA;EACA,YAAA;EACA,mBAAA;EACA,yBAAA;EACA,kBAAA;ECsCA,sDAAA;EACQ,8CAAA;EFhCR,aAAA;EACA,kBAAA;;AAhCF,WA6BC,eAKC;ECJA,WAAA;EACA,SAAA;EACA,YAAA;EACA,eAAA;EACA,iBAAA;EACA,WAAA;EACA,kBAAA;EACA,yBAAA;ECyBA,sDAAA;EACQ,8CAAA;EAyHR,mCAAA;EACK,8BAAA;EACG,2BAAA;EFtJP,kBAAA;EACA,8BAAA;EACA,cAAA;;AAtCH,WA6BC,eAYC;EACC,yBAAA;;AA1CH,WA6BC,eAeC;EACC,yBAAA;;AA7CH,WA6BC,eAkBC;EACC,yBAAA;;AAhDH,WA6BC,eAqBC;EACC,yBAAA;;AAnDH,WA6BC,eAwBC;EACC,yBAAA;;AAtDH,WA6BC,eA2BC;EACC,yBAAA;;AAzDH,WA6BC,eA8BC;EACC,yBAAA;;AA5DH,WA6BC,eAkCC;EGwDA,kBAAA;EACA,UAAA;EACA,WAAA;EACA,YAAA;EACA,UAAA;EACA,gBAAA;EACA,MAAM,gBAAN;EACA,SAAA;;AH9HF,WIAA;EACE,kBAAA;EACA,eAAA;EACA,cAAA;ECRA,aC2CwB,8CD3CxB;EAEA,kBAAA;EACA,mBAAA;EACA,sBAAA;EACA,gBAAA;EACA,uBAAA;EACA,gBAAA;EACA,iBAAA;EACA,qBAAA;EACA,iBAAA;EACA,oBAAA;EACA,mBAAA;EACA,kBAAA;EACA,oBAAA;EACA,iBAAA;EDHA,eAAA;EGVA,UAAA;EAGA,wBAAA;;AHWA,WAXF,SAWG;EGdD,YAAA;EAGA,yBAAA;;AHYA,WAZF,SAYG;EAAU,gBAAA;EAAmB,cAAA;;AAC9B,WAbF,SAaG;EAAU,gBAAA;EAAmB,cAAA;;AAC9B,WAdF,SAcG;EAAU,eAAA;EAAmB,cAAA;;AAC9B,WAfF,SAeG;EAAU,iBAAA;EAAmB,cAAA;;AJfhC,WImBA;EACE,gBAAA;EACA,gBAAA;EACA,WAAA;EACA,kBAAA;EACA,yBAAA;EACA,kBAAA;;AJzBF,WI6BA;EACE,kBAAA;EACA,QAAA;EACA,SAAA;EACA,yBAAA;EACA,mBAAA;;AAIA,WADF,SACG,IAAK;EACJ,SAAA;EACA,SAAA;EACA,iBAAA;EACA,uBAAA;EACA,yBAAA;;AAEF,WARF,SAQG,SAAU;EACT,SAAA;EACA,UAAA;EACA,mBAAA;EACA,uBAAA;EACA,yBAAA;;AAEF,WAfF,SAeG,UAAW;EACV,SAAA;EACA,SAAA;EACA,mBAAA;EACA,uBAAA;EACA,yBAAA;;AAEF,WAtBF,SAsBG,MAAO;EACN,QAAA;EACA,OAAA;EACA,gBAAA;EACA,2BAAA;EACA,2BAAA;;AAEF,WA7BF,SA6BG,KAAM;EACL,QAAA;EACA,QAAA;EACA,gBAAA;EACA,2BAAA;EACA,0BAAA;;AAEF,WApCF,SAoCG,OAAQ;EACP,MAAA;EACA,SAAA;EACA,iBAAA;EACA,uBAAA;EACA,4BAAA;;AAEF,WA3CF,SA2CG,YAAa;EACZ,MAAA;EACA,UAAA;EACA,gBAAA;EACA,uBAAA;EACA,4BAAA;;AAEF,WAlDF,SAkDG,aAAc;EACb,MAAA;EACA,SAAA;EACA,gBAAA;EACA,uBAAA;EACA,4BAAA;;AJ5FJ,WAsEC,SAAQ;EOzEP,UAAA;EAGA,0BAAA;;APAF,WA0EC;EACC,iBAAA;EACA,gBAAA","file":"laps.css"}
+11 -8
View File
@@ -1,7 +1,7 @@
@import (reference) "../vendor/twbs/bootstrap/less/variables.less"; @import (reference) "../../vendor/twbs/bootstrap/less/variables.less";
@import (reference) "../vendor/twbs/bootstrap/less/mixins.less"; @import (reference) "../../vendor/twbs/bootstrap/less/mixins.less";
@import (reference) "../vendor/twbs/bootstrap/less/progress-bars.less"; @import (reference) "../../vendor/twbs/bootstrap/less/progress-bars.less";
@import (reference) "../vendor/twbs/bootstrap/less/scaffolding.less"; @import (reference) "../../vendor/twbs/bootstrap/less/scaffolding.less";
@import "variables.less"; @import "variables.less";
#wpadminbar { #wpadminbar {
@@ -45,6 +45,9 @@
min-width: 2px; min-width: 2px;
} }
.event-php {
background-color: @event-php-color;
}
.event-theme { .event-theme {
background-color: @event-theme-color; background-color: @event-theme-color;
} }
@@ -54,10 +57,10 @@
.event-core { .event-core {
background-color: @event-core-color; background-color: @event-core-color;
} }
.event-query-read { .event-sql-read {
background-color: @event-info-color; background-color: @event-sql-color;
} }
.event-query-write { .event-sql-write {
background-color: @event-warning-color; background-color: @event-warning-color;
} }
.event-http { .event-http {
@@ -69,7 +72,7 @@
} }
} }
@import "../vendor/twbs/bootstrap/less/tooltip.less"; @import "../../vendor/twbs/bootstrap/less/tooltip.less";
.tooltip.in { .tooltip.in {
.opacity(1); .opacity(1);
+1
View File
@@ -0,0 +1 @@
#wpadminbar #wp-admin-bar-laps .ab-sub-wrapper{min-width:90%;width:90%;position:fixed;left:5%}#wpadminbar #wp-admin-bar-laps .ab-submenu{padding:0}#wpadminbar #wp-admin-bar-laps_output{padding:0 10px}#wpadminbar #wp-admin-bar-laps_output:hover{background-color:transparent}#wpadminbar #wp-admin-bar-laps_output .ab-empty-item{display:none}#wpadminbar .laps-timeline{overflow:hidden;height:20px;margin-bottom:20px;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);margin:6px 0;position:relative}#wpadminbar .laps-timeline .event{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#AAAAAA;-webkit-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-webkit-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease;position:absolute;border-left:1px solid #f5f5f5;min-width:2px}#wpadminbar .laps-timeline .event-php{background-color:#B10DC9}#wpadminbar .laps-timeline .event-theme{background-color:#2ECC40}#wpadminbar .laps-timeline .event-plugin{background-color:#0074D9}#wpadminbar .laps-timeline .event-core{background-color:#FF851B}#wpadminbar .laps-timeline .event-sql-read{background-color:#7FDBFF}#wpadminbar .laps-timeline .event-sql-write{background-color:#FF4136}#wpadminbar .laps-timeline .event-http{background-color:#FF4136}#wpadminbar .laps-timeline .sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}#wpadminbar .tooltip{position:absolute;z-index:100000;display:block;font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;font-style:normal;font-weight:normal;letter-spacing:normal;line-break:auto;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal;font-size:12px;opacity:0;filter:alpha(opacity=0)}#wpadminbar .tooltip.in{opacity:0.9;filter:alpha(opacity=90)}#wpadminbar .tooltip.top{margin-top:-3px;padding:5px 0}#wpadminbar .tooltip.right{margin-left:3px;padding:0 5px}#wpadminbar .tooltip.bottom{margin-top:3px;padding:5px 0}#wpadminbar .tooltip.left{margin-left:-3px;padding:0 5px}#wpadminbar .tooltip-inner{max-width:300px;padding:3px 8px;color:#eee;text-align:center;background-color:#464646;border-radius:4px}#wpadminbar .tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}#wpadminbar .tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#464646}#wpadminbar .tooltip.top-left .tooltip-arrow{bottom:0;right:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#464646}#wpadminbar .tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#464646}#wpadminbar .tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#464646}#wpadminbar .tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#464646}#wpadminbar .tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#464646}#wpadminbar .tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#464646}#wpadminbar .tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#464646}#wpadminbar .tooltip.in{opacity:1;filter:alpha(opacity=100)}#wpadminbar .tooltip-inner{line-height:20px;text-align:left}
@@ -3,18 +3,20 @@
// colors // colors
@timeline-bg: #f5f5f5; @timeline-bg: #f5f5f5;
@event-theme-color: #5cb85c; @event-php-color: #B10DC9;
@event-plugin-color: #428bca; @event-theme-color: #2ECC40;
@event-core-color: #f0ad4e; @event-plugin-color: #0074D9;
@event-info-color: #5bc0de; @event-core-color: #FF851B;
@event-warning-color: #d9534f; @event-sql-color: #7FDBFF;
@event-info-color: #AAAAAA;
@event-warning-color: #FF4136;
@progress-bg: @timeline-bg; @progress-bg: @timeline-bg;
@progress-bar-bg: @event-info-color; @progress-bar-bg: @event-info-color;
// because admin bar is 99999 // because admin bar is 99999
@zindex-tooltip: 100000; @zindex-tooltip: 100000;
@tooltip-max-width: 300px; @tooltip-max-width: 300px;
@tooltip-color: #ccc; @tooltip-color: #eee;
@tooltip-bg: #464646; @tooltip-bg: #464646;
@tooltip-arrow-width: 5px; @tooltip-arrow-width: 5px;
@tooltip-arrow-color: @tooltip-bg; @tooltip-arrow-color: @tooltip-bg;
View File
File renamed without changes.
View File
File renamed without changes.
File renamed without changes.
@@ -1,105 +1,106 @@
<?php <?php
namespace Rarst\Laps; namespace Rarst\Laps\Event;
/** /**
* Main set of events for WordPress core * Main set of events for WordPress core
*/ */
class Core_Events extends Laps_Events { class Core_Events implements Hook_Event_Config_Interface {
/** /**
* @return array * @return array
*/ */
public function get_events() { public function get_events() {
$stops = array(); $stops = [];
$stops['plugins_loaded'][ - 2 ] = array( $stops['plugins_loaded'][-2] = [
'action' => 'stop', 'action' => 'stop',
'event' => 'Plugins Load', 'event' => 'Plugins Load',
'category' => 'plugin', 'category' => 'plugin',
); ];
$stops['plugins_loaded'][ - 1 ] = array( $stops['plugins_loaded'][-1] = [
'event' => 'Plugins Loaded Hook', 'event' => 'Plugins Loaded Hook',
'category' => 'plugin', 'category' => 'plugin',
); ];
$stops['plugins_loaded'][20] = array( $stops['plugins_loaded'][20] = [
'action' => 'stop', 'action' => 'stop',
'event' => 'Plugins Loaded Hook', 'event' => 'Plugins Loaded Hook',
'category' => 'plugin', 'category' => 'plugin',
); ];
$stops['setup_theme'][0] = array( $stops['setup_theme'][0] = [
'event' => 'Themes Load', 'event' => 'Themes Load',
'category' => 'theme', 'category' => 'theme',
); ];
$stops['after_setup_theme'][20] = array( $stops['after_setup_theme'][20] = [
'action' => 'stop', 'action' => 'stop',
'event' => 'Themes Load', 'event' => 'Themes Load',
'category' => 'theme', 'category' => 'theme',
); ];
$stops['init'][- 1] = array( $stops['init'][-1] = [
'event' => 'Core Init', 'event' => 'Core Init',
'category' => 'core', 'category' => 'core',
); ];
$stops['wp_loaded'][20] = array( $stops['wp_loaded'][20] = [
'action' => 'stop', 'action' => 'stop',
'event' => 'Core Init', 'event' => 'Core Init',
'category' => 'core', 'category' => 'core',
); ];
$stops['admin_init'][- 1] = array( $stops['admin_init'][-1] = [
'event' => 'Core Admin Init', 'event' => 'Core Admin Init',
'category' => 'core', 'category' => 'core',
); ];
$stops['admin_init'][ PHP_INT_MAX -1 ] = array( $stops['admin_init'][ PHP_INT_MAX - 1 ] = [
'action' => 'stop', 'action' => 'stop',
'event' => 'Core Admin Init', 'event' => 'Core Admin Init',
'category' => 'core', 'category' => 'core',
); ];
$stops['_admin_menu'][- 1] = array( $stops['_admin_menu'][-1] = [
'event' => 'Admin Menu', 'event' => 'Admin Menu',
'category' => 'core', 'category' => 'core',
); ];
$stops['admin_menu'][ PHP_INT_MAX -1 ] = array(
$stops['admin_menu'][ PHP_INT_MAX - 1 ] = [
'action' => 'stop', 'action' => 'stop',
'event' => 'Admin Menu', 'event' => 'Admin Menu',
'category' => 'core', 'category' => 'core',
); ];
$stops['admin_bar_menu'][-1] = array( $stops['admin_bar_menu'][-1] = [
'event' => 'Toolbar', 'event' => 'Toolbar',
'category' => 'core', 'category' => 'core',
); ];
$stops['loop_start'][10] = array( $stops['loop_start'][10] = [
'event' => 'Main Loop', 'event' => 'Main Loop',
'category' => 'theme', 'category' => 'theme',
); ];
$stops['loop_end'][10] = array( $stops['loop_end'][10] = [
'action' => 'stop', 'action' => 'stop',
'event' => 'Main Loop', 'event' => 'Main Loop',
'category' => 'theme', 'category' => 'theme',
); ];
$stops['admin_enqueue_scripts'][10] = array( $stops['admin_enqueue_scripts'][10] = [
'event' => 'Admin Print Scripts', 'event' => 'Admin Print Scripts',
'category' => 'core', 'category' => 'core',
); ];
$stops['admin_print_scripts'][10] = array( $stops['admin_print_scripts'][10] = [
'action' => 'stop', 'action' => 'stop',
'event' => 'Admin Print Scripts', 'event' => 'Admin Print Scripts',
'category' => 'core', 'category' => 'core',
); ];
return $stops; return $stops;
} }
+58
View File
@@ -0,0 +1,58 @@
<?php
namespace Rarst\Laps\Event;
/**
* Events for Genesis Framework based themes
*
* @link http://my.studiopress.com/themes/genesis/
*/
class Genesis_Events implements Hook_Event_Config_Interface {
/**
* @return array
*/
public function get_events() {
$events = [];
if ( ! function_exists( 'genesis' ) ) {
return $events;
}
$events['genesis_before_header'][10] = [
'event' => 'Header',
'category' => 'theme',
];
$events['genesis_after_header'][10] = [
'action' => 'stop',
'event' => 'Header',
'category' => 'theme',
];
$events['genesis_before_sidebar_widget_area'][10] = [
'event' => 'Sidebar',
'category' => 'theme',
];
$events['genesis_after_sidebar_widget_area'][10] = [
'action' => 'stop',
'event' => 'Sidebar',
'category' => 'theme',
];
$events['genesis_before_sidebar_alt_widget_area'][10] = [
'event' => 'Sidebar (alternate)',
'category' => 'theme',
];
$events['genesis_after_sidebar_alt_widget_area'][10] = [
'action' => 'stop',
'event' => 'Sidebar (alternate)',
'category' => 'theme',
];
return $events;
}
}
+11
View File
@@ -0,0 +1,11 @@
<?php
namespace Rarst\Laps\Event;
interface Hook_Event_Config_Interface {
/**
* @return array
*/
public function get_events();
}
@@ -1,56 +1,53 @@
<?php <?php
namespace Rarst\Laps; namespace Rarst\Laps\Event;
/** /**
* Events for Hybrid Core based themes * Events for Hybrid Core based themes
* *
* @link http://themehybrid.com/hybrid-core * @link http://themehybrid.com/hybrid-core
*/ */
class Hybrid_Events extends Laps_Events { class Hybrid_Events implements Hook_Event_Config_Interface {
/**
* @return bool
*/
protected function is_applicable() {
return defined( 'HYBRID_VERSION' ) && function_exists( 'hybrid_get_prefix' );
}
/** /**
* @return array * @return array
*/ */
protected function get_events() { public function get_events() {
$events = [];
if ( ! function_exists( 'hybrid_get_prefix' ) ) {
return $events;
}
$events = array();
$prefix = hybrid_get_prefix(); $prefix = hybrid_get_prefix();
$events["{$prefix}_before_header"][10] = array( $events["{$prefix}_before_header"][10] = [
'event' => 'Header', 'event' => 'Header',
'category' => 'theme', 'category' => 'theme',
); ];
$events["{$prefix}_after_header"][10] = array( $events["{$prefix}_after_header"][10] = [
'action' => 'stop', 'action' => 'stop',
'event' => 'Header', 'event' => 'Header',
'category' => 'theme', 'category' => 'theme',
); ];
$sidebars = get_theme_support( 'hybrid-core-sidebars' ); $sidebars = get_theme_support( 'hybrid-core-sidebars' );
if ( ! empty( $sidebars[0] ) ) { if ( ! empty( $sidebars[0] ) ) {
foreach ( $sidebars[0] as $sidebar ) { foreach ( $sidebars[0] as $sidebar ) {
$events["{$prefix}_before_{$sidebar}"][10] = array( $events["{$prefix}_before_{$sidebar}"][10] = [
'event' => "Sidebar ({$sidebar})", 'event' => "Sidebar ({$sidebar})",
'category' => 'theme', 'category' => 'theme',
); ];
$events["{$prefix}_after_{$sidebar}"][10] = array( $events["{$prefix}_after_{$sidebar}"][10] = [
'action' => 'stop', 'action' => 'stop',
'event' => "Sidebar ({$sidebar})", 'event' => "Sidebar ({$sidebar})",
'category' => 'theme', 'category' => 'theme',
); ];
} }
} }
+47
View File
@@ -0,0 +1,47 @@
<?php
namespace Rarst\Laps\Event;
/**
* Events for Theme Hook Alliance
*
* @link http://zamoose.github.io/themehookalliance/
*/
class THA_Events implements Hook_Event_Config_Interface {
/**
* @return array
*/
public function get_events() {
$events = [];
if ( ! defined( 'THA_HOOKS_VERSION' ) ) {
return $events;
}
$events['tha_header_before'][10] = [
'event' => 'Header',
'category' => 'theme',
];
$events['tha_header_after'][10] = [
'action' => 'stop',
'event' => 'Header',
'category' => 'theme',
];
$events['tha_sidebars_before'][10] = [
'event' => 'Sidebars',
'category' => 'theme',
];
$events['tha_sidebars_after'][10] = [
'action' => 'stop',
'event' => 'Sidebars',
'category' => 'theme',
];
return $events;
}
}
@@ -1,37 +1,33 @@
<?php <?php
namespace Rarst\Laps; namespace Rarst\Laps\Event;
/** /**
* Events for Yoast plugins * Events for Yoast plugins
*/ */
class Yoast_Events extends Laps_Events { class Yoast_Events implements Hook_Event_Config_Interface {
/**
* @return bool
*/
protected function is_applicable() {
return class_exists( 'WPSEO_Frontend' );
}
/** /**
* @return array * @return array
*/ */
public function get_events() { public function get_events() {
$stops = array(); $stops = [];
$stops['wpseo_head'][1] = array( if ( ! class_exists( 'WPSEO_Frontend' ) ) {
return $stops;
}
$stops['wpseo_head'][1] = [
'event' => 'WP SEO head hook', 'event' => 'WP SEO head hook',
'category' => 'plugin', 'category' => 'plugin',
); ];
$stops['wpseo_head'][100] = array( $stops['wpseo_head'][100] = [
'action' => 'stop', 'action' => 'stop',
'event' => 'WP SEO head hook', 'event' => 'WP SEO head hook',
'category' => 'plugin', 'category' => 'plugin',
); ];
return $stops; return $stops;
} }
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace Rarst\Laps\Manager;
/**
* Implements load of necessary front–end assets.
*/
class Asset_Manager {
/**
* Sets up the hooks.
*/
public function __construct() {
add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
}
/**
* Registers assets and queues as necessary.
*/
public function enqueue_scripts() {
$suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
wp_register_script( 'laps', plugins_url( "public/js/laps{$suffix}.js", dirname( __DIR__ ) ), [ 'jquery' ], '3.3.1', true );
wp_register_style( 'laps', plugins_url( "public/css/laps{$suffix}.css", dirname( __DIR__ ) ) );
if ( is_admin_bar_showing() ) {
wp_enqueue_script( 'laps' );
wp_enqueue_style( 'laps' );
}
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Rarst\Laps\Manager;
/**
* Reorders plugin load to put Laps first and time this core boot stage.
*/
class Load_Order_Manager {
/**
* Sets up the hooks.
*/
public function __construct() {
add_action( 'pre_update_option_active_plugins', [ $this, 'pre_update_option_active_plugins' ] );
add_action( 'pre_update_site_option_active_sitewide_plugins', [ $this, 'pre_update_option_active_plugins' ] );
}
/**
* Reorder active plugins so Laps is first and starts timing load early.
*
* @param array $plugins Filtered array of active plugins’ basenames.
*
* @return array
*/
public function pre_update_option_active_plugins( $plugins ) {
$plugin = plugin_basename( dirname( dirname( __DIR__ ) ) . '/laps.php' );
$key = array_search( $plugin, $plugins, true );
if ( false !== $key && $key > 0 ) {
unset( $plugins[ $key ] );
array_unshift( $plugins, $plugin );
$plugins = array_values( $plugins );
}
return $plugins;
}
}
+55
View File
@@ -0,0 +1,55 @@
<?php
namespace Rarst\Laps\Manager;
use Rarst\Laps\Record\Recursive_Record_Iterator;
use Rarst\Laps\Plugin;
use Rarst\Laps\Record\Timeline_Iterator;
/**
* Implements toolbar menu and visualization.
*/
class Toolbar_Manager {
/** @var Plugin $laps */
protected $laps;
/**
* @param Plugin $laps Container instance.
*/
public function __construct( Plugin $laps ) {
$this->laps = $laps;
add_action( 'admin_bar_menu', [ $this, 'admin_bar_menu' ], 100 );
}
/**
* Render interface and add to the toolbar.
*
* @param \WP_Admin_Bar $wp_admin_bar WordPress core toolbar object.
*/
public function admin_bar_menu( $wp_admin_bar ) {
if ( ! apply_filters( 'laps_can_see', current_user_can( 'manage_options' ) ) ) {
return;
}
global $timestart;
$wp_admin_bar->add_node( [
'id' => 'laps',
'title' => sprintf( 'Lap: %ss', round( microtime( true ) - $timestart, 3 ) ),
] );
$wp_admin_bar->add_node( [
'id' => 'laps_output',
'parent' => 'laps',
'meta' => [
'html' => $this->laps['mustache']->render( 'laps', [
'timelines' => new Timeline_Iterator( new Recursive_Record_Iterator( $this->laps['records'] ) ),
] ),
],
] );
}
}
@@ -9,8 +9,8 @@ class Mustache_Cache_FrozenCache extends \Mustache_Cache_FilesystemCache {
/** /**
* Cache and load a compiled Mustache_Template class. * Cache and load a compiled Mustache_Template class.
* *
* @param string $key * @param string $key Key.
* @param string $value * @param string $value Value.
* *
* @return void * @return void
*/ */
+76
View File
@@ -0,0 +1,76 @@
<?php
namespace Rarst\Laps;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Rarst\Laps\Provider\Bootable_Provider_Interface;
use Rarst\Laps\Provider\Hook_Event_Provider;
use Rarst\Laps\Provider\Manager_Provider;
use Rarst\Laps\Provider\Record_Provider;
/**
* Main plugin's class.
*/
class Plugin extends Container {
/** @var array $providers */
protected $providers = [];
/**
* @param array $values Configuration values to apply.
*/
public function __construct( array $values = [] ) {
parent::__construct();
$laps = $this;
$laps['mustache'] = function () {
return new \Mustache_Engine( [
'loader' => new \Mustache_Loader_FilesystemLoader( dirname( __DIR__ ) . '/src/mustache' ),
'cache' => new Mustache_Cache_FrozenCache( dirname( __DIR__ ) . '/src/mustache/cache' ),
] );
};
$laps->register( new Manager_Provider() );
$laps->register( new Hook_Event_Provider() );
$laps->register( new Record_Provider() );
foreach ( $values as $key => $value ) {
$this->offsetSet( $key, $value );
}
}
/**
* @param ServiceProviderInterface $provider Provider.
* @param array $values Optional configuration.
*
* @return $this|static
*/
public function register( ServiceProviderInterface $provider, array $values = [] ) {
$this->providers[] = $provider;
parent::register( $provider, $values );
return $this;
}
/**
* Start Stopwatch and timing plugin load immediately, then set up core events and needed hooks.
*/
public function run() {
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
return;
}
foreach ( $this->providers as $provider ) {
if ( $provider instanceof Bootable_Provider_Interface ) {
$provider->boot( $this );
}
}
}
}
@@ -0,0 +1,15 @@
<?php
namespace Rarst\Laps\Provider;
use Rarst\Laps\Plugin;
interface Bootable_Provider_Interface {
/**
* @param Plugin $laps Container instance.
*
* @return void
*/
public function boot( Plugin $laps );
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace Rarst\Laps\Provider;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Rarst\Laps\Event\Core_Events;
use Rarst\Laps\Event\Genesis_Events;
use Rarst\Laps\Event\Hybrid_Events;
use Rarst\Laps\Event\THA_Events;
use Rarst\Laps\Event\Yoast_Events;
/**
* Registers hook event configuration for bundled vendor events.
*/
class Hook_Event_Provider implements ServiceProviderInterface {
/**
* @param Container $pimple Container instance.
*/
public function register( Container $pimple ) {
$pimple['hook.events'] = function () {
return [
'core' => new Core_Events(),
'genesis' => new Genesis_Events(),
'hybrid' => new Hybrid_Events(),
'tha' => new THA_Events(),
'yoast' => new Yoast_Events(),
];
};
}
}
+37
View File
@@ -0,0 +1,37 @@
<?php
namespace Rarst\Laps\Provider;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Rarst\Laps\Plugin;
use Rarst\Laps\Manager\Asset_Manager;
use Rarst\Laps\Manager\Load_Order_Manager;
use Rarst\Laps\Manager\Toolbar_Manager;
/**
* Registers manager classes that implement functionality.
*/
class Manager_Provider implements ServiceProviderInterface, Bootable_Provider_Interface {
/**
* @param Container $pimple Container instance.
*/
public function register( Container $pimple ) {
$pimple['managers'] = function ( Plugin $laps ) {
return [
new Load_Order_Manager(),
new Asset_Manager(),
new Toolbar_Manager( $laps ),
];
};
}
/**
* @param Plugin $laps Container instance.
*/
public function boot( Plugin $laps ) {
$laps['managers'];
}
}
+57
View File
@@ -0,0 +1,57 @@
<?php
namespace Rarst\Laps\Provider;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Rarst\Laps\Plugin;
use Rarst\Laps\Record\Core_Load_Collector;
use Rarst\Laps\Record\Hook_Record_Collector;
use Rarst\Laps\Record\Http_Record_Collector;
use Rarst\Laps\Record\Record_Collector_Interface;
use Rarst\Laps\Record\Sql_Record_Collector;
use Symfony\Component\Stopwatch\Stopwatch;
/**
* Registers record collectors (responsible to gather and compile event data) and their dependencies.
*/
class Record_Provider implements ServiceProviderInterface, Bootable_Provider_Interface {
/**
* @param Container $pimple Container instance.
*/
public function register( Container $pimple ) {
$pimple['stopwatch'] = $pimple->factory( function () {
return new Stopwatch( true );
} );
$pimple['collectors'] = function ( Plugin $laps ) {
return [
'core' => new Core_Load_Collector(),
'hook' => new Hook_Record_Collector( $laps['stopwatch'], $laps['hook.events'] ),
'http' => new Http_Record_Collector( $laps['stopwatch'] ),
'sql' => new Sql_Record_Collector(),
];
};
$pimple['records'] = function ( Plugin $laps ) {
$records = [];
/** @var Record_Collector_Interface $collector */
foreach ( $laps['collectors'] as $collector ) {
$records[] = $collector->get_records();
}
return array_merge( ...$records );
};
}
/**
* @param Plugin $laps Container instance.
*/
public function boot( Plugin $laps ) {
$laps['collectors'];
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
namespace Rarst\Laps\Record;
/**
* Covers pre–plugin load time from available data.
*/
class Core_Load_Collector implements Record_Collector_Interface {
/** @var float $timeload */
protected $timeload;
/**
* Store core load end, plugin load start mark.
*/
public function __construct() {
$this->timeload = microtime( true );
}
/**
* @return Record_Interface[]
*/
public function get_records() {
global $timestart;
$request_time = $_SERVER['REQUEST_TIME_FLOAT'];
return [
new Record( 'PHP Load', $request_time, $timestart - $request_time, '', 'php' ),
// TODO This includes network plugins on multisite, need conditional label if Laps is network–activated.
new Record( 'Core and MU Plugins Load', $timestart, $this->timeload - $timestart, '', 'core' ),
];
}
}
+104
View File
@@ -0,0 +1,104 @@
<?php
namespace Rarst\Laps\Record;
use Rarst\Laps\Event\Hook_Event_Config_Interface;
use Symfony\Component\Stopwatch\Stopwatch;
/**
* Processes events based on hooked starts and stops.
*/
class Hook_Record_Collector extends Stopwatch_Record_Collector {
/** @var Hook_Event_Config_Interface[] $event_configs */
protected $event_configs = [];
/** @var array $events */
protected $events = [];
/**
* @param Stopwatch $stopwatch Stopwatch instance.
* @param array $event_configs Starts and stops configuration.
*/
public function __construct( Stopwatch $stopwatch, array $event_configs ) {
parent::__construct( $stopwatch );
$this->start( 'Plugins Load', 'plugin' );
$this->add_events( $event_configs['core']->get_events() );
unset( $event_configs['core'] );
$this->event_configs = $event_configs;
add_action( 'after_setup_theme', [ $this, 'after_setup_theme' ], 15 );
}
/**
* Hook events by name and priority from array.
*
* @param array $stops Starts and stops to hook.
*/
public function add_events( $stops ) {
$this->events = array_merge( $this->events, $stops );
foreach ( $stops as $hook_name => $data ) {
foreach ( array_keys( $data ) as $priority ) {
add_action( $hook_name, [ $this, 'tick' ], $priority );
}
}
}
/**
* When theme is done possibly add vendor-specific events.
*/
public function after_setup_theme() {
foreach ( $this->event_configs as $config ) {
$this->add_events( $config->get_events() );
}
}
/**
* Mark action for the event on Stopwatch.
*
* @param mixed $input Pass through if added to filter.
*
* @return mixed
*/
public function tick( $input = null ) {
global $wp_filter;
$filter_name = current_filter();
$filter_instance = $wp_filter[ $filter_name ];
$priority = $filter_instance instanceof \WP_Hook ? $filter_instance->current_priority() : key( $filter_instance );
// See https://core.trac.wordpress.org/ticket/41185 on broken priority, but more general sanity check.
if ( empty( $this->events[ $filter_name ][ $priority ] ) ) {
return $input;
}
$event = wp_parse_args( $this->events[ $filter_name ][ $priority ], [
'action' => 'start',
'category' => null,
] );
if ( 'start' === $event['action'] ) {
$this->start( $event['event'], $event['category'] );
} else {
$this->stop( $event['event'] );
}
return $input;
}
/**
* @return Stopwatch_Record[]
*/
public function get_records() {
$this->stopwatch->stop( 'Toolbar' );
return parent::get_records();
}
}
+58
View File
@@ -0,0 +1,58 @@
<?php
namespace Rarst\Laps\Record;
use Symfony\Component\Stopwatch\Stopwatch;
/**
* Captures time of network requests made with HTTP API.
*/
class Http_Record_Collector extends Stopwatch_Record_Collector {
/**
* @param Stopwatch $stopwatch Stopwatch instance.
*/
public function __construct( Stopwatch $stopwatch ) {
parent::__construct( $stopwatch );
add_action( 'pre_http_request', [ $this, 'pre_http_request' ], 10, 3 );
add_action( 'http_api_debug', [ $this, 'http_api_debug' ], 10, 5 );
}
/**
* Capture start time of HTTP request
*
* @param boolean $false Whether to preempt an HTTP request's return value. Default false.
* @param array $args HTTP request arguments.
* @param string $url The request URL.
*
* @return boolean
*/
public function pre_http_request( $false, $args, $url ) {
$this->start( $url, 'http' );
return $false;
}
/**
* Capture end time of HTTP request
*
* @param array|\WP_Error $response HTTP response or WP_Error object.
* @param string $type Context under which the hook is fired.
* @param object $class HTTP transport used.
* @param array $args HTTP request arguments.
* @param string $url The request URL.
*
* @return mixed
*
* @noinspection MoreThanThreeArgumentsInspection
*/
public function http_api_debug( $response, $type, $class, $args, $url ) {
$this->stop( $url );
return $response;
}
}
+81
View File
@@ -0,0 +1,81 @@
<?php
namespace Rarst\Laps\Record;
/**
* Generic record with all properties set by constructor.
*/
class Record implements Record_Interface {
/** @var string $name */
protected $name;
/** @var float $origin */
protected $origin;
/** @var float $duration */
protected $duration;
/** @var string $description */
protected $description;
/** @var string $category */
protected $category;
/**
* @param string $name Name.
* @param float $origin Start time.
* @param float $duration Duration.
* @param string $description Optional description.
* @param string $category Optional category.
*/
public function __construct( $name, $origin, $duration, $description = '', $category = '' ) {
$this->name = $name;
$this->origin = $origin;
$this->duration = $duration;
$this->description = $description;
$this->category = $category;
}
/**
* @return string
*/
public function get_name() {
return $this->name;
}
/**
* @return string
*/
public function get_description() {
if ( empty( $this->description ) ) {
$duration = round( $this->get_duration() * 1000 );
return "{$this->name} – {$duration}&nbsp;ms";
}
return $this->description;
}
/**
* @return float
*/
public function get_origin() {
return $this->origin;
}
/**
* @return float
*/
public function get_duration() {
return $this->duration;
}
/**
* @return string
*/
public function get_category() {
return $this->category;
}
}
+11
View File
@@ -0,0 +1,11 @@
<?php
namespace Rarst\Laps\Record;
interface Record_Collector_Interface {
/**
* @return Record_Interface[]
*/
public function get_records();
}
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace Rarst\Laps\Record;
/**
* Interface for recorded results of individual event.
*/
interface Record_Interface {
/**
* @return string
*/
public function get_name();
/**
* @return string
*/
public function get_description();
/**
* @return float Timestamp of record start.
*/
public function get_origin();
/**
* @return float Record duration in seconds.
*/
public function get_duration();
/**
* @return string
*/
public function get_category();
}
+73
View File
@@ -0,0 +1,73 @@
<?php
namespace Rarst\Laps\Record;
/**
* Processes records, recursively bumping overlapping ones to children.
*/
class Recursive_Record_Iterator extends \ArrayIterator implements \RecursiveIterator {
/** @var array $children */
protected $children = [];
/**
* @param Record_Interface[] $records Records to process.
* @param int $flags Configuration flags.
*/
public function __construct( array $records, $flags = 0 ) {
usort( $records, [ $this, 'sort_origin' ] );
$end = 0;
foreach ( $records as $key => $record ) {
$origin = $record->get_origin();
$overlaps = $origin < $end; // Float comparison alarm.
$borders = abs( $origin - $end ) < 0.0001; // If effectively equal.
if ( $overlaps && ! $borders ) {
unset( $records[ $key ] );
$this->children[] = $record;
continue;
}
$end = $origin + $record->get_duration();
}
parent::__construct( $records, $flags );
}
/**
* @param Record_Interface $record_a Record to compare.
* @param Record_Interface $record_b Record to compare.
*
* @return int
*/
protected function sort_origin( Record_Interface $record_a, Record_Interface $record_b ) {
$origin_a = $record_a->get_origin();
$origin_b = $record_b->get_origin();
if ( $origin_a === $origin_b ) {
return 0;
}
return ( $origin_a < $origin_b ) ? - 1 : 1;
}
/**
* @return bool
*/
public function hasChildren() {
return ! empty( $this->children );
}
/**
* @return static
*/
public function getChildren() {
return new static( $this->children );
}
}
+82
View File
@@ -0,0 +1,82 @@
<?php
namespace Rarst\Laps\Record;
/**
* Processes SQL events from data logged by wpdb.
*/
class Sql_Record_Collector implements Record_Collector_Interface {
/** @var array $query_starts Log of query start times. */
protected $query_starts = [];
/**
* Sets up the query start time log.
*/
public function __construct() {
if ( $this->is_savequeries() ) {
add_filter( 'query', [ $this, 'query' ], 20 );
}
}
/**
* Capture SQL queries start times
*
* @param string $query SQL query.
*
* @return string
*/
public function query( $query ) {
global $wpdb;
if ( empty( $this->query_starts ) && ! empty( $wpdb->queries ) ) {
$this->query_starts[ count( $wpdb->queries ) ] = microtime( true );
} else {
$this->query_starts[] = microtime( true );
}
return $query;
}
/**
* @return Record[]
*/
public function get_records() {
if ( ! $this->is_savequeries() ) {
return [];
}
global $wpdb;
$query_data = [];
$last_query_end = 0;
// TODO process call trace from the data.
foreach ( $wpdb->queries as $key => list( $sql, $duration ) ) {
$query_start = isset( $this->query_starts[ $key ] ) ? $this->query_starts[ $key ] : $last_query_end;
$sql = trim( $sql );
$category = 'sql-read';
if ( 0 === stripos( $sql, 'INSERT' ) || 0 === stripos( $sql, 'UPDATE' ) ) {
$category = 'sql-write';
}
$last_query_end = $query_start + $duration;
$query_data[] = new Record( $sql, $query_start, $duration, '', $category );
}
return $query_data;
}
/**
* @return bool
*/
protected function is_savequeries() {
return defined( 'SAVEQUERIES' ) && SAVEQUERIES;
}
}
+65
View File
@@ -0,0 +1,65 @@
<?php
namespace Rarst\Laps\Record;
use Symfony\Component\Stopwatch\StopwatchEvent;
/**
* Record wrapper for an event recorded with Stopwatch.
*/
class Stopwatch_Record implements Record_Interface {
/** @var string $name */
protected $name;
/** @var StopwatchEvent */
protected $stopwatch_event;
/**
* @param string $name Event name.
* @param StopwatchEvent $stopwatch_event Stopwatch event instance.
*/
public function __construct( $name, StopwatchEvent $stopwatch_event ) {
$this->name = $name;
$this->stopwatch_event = $stopwatch_event;
}
/**
* @return string
*/
public function get_name() {
return $this->name;
}
/**
* @return string
*/
public function get_description() {
$duration = round( $this->stopwatch_event->getDuration() );
$memory = $this->stopwatch_event->getMemory() / 1024 / 1024;
return "{$this->name} – {$duration} ms – {$memory} MB";
}
/**
* @return float Timestamp of record start.
*/
public function get_origin() {
return $this->stopwatch_event->getOrigin() / 1000; // ms to s.
}
/**
* @return float Record duration in seconds.
*/
public function get_duration() {
return $this->stopwatch_event->getDuration() / 1000; // ms to s.
}
/**
* @return string
*/
public function get_category() {
return $this->stopwatch_event->getCategory();
}
}
+73
View File
@@ -0,0 +1,73 @@
<?php
namespace Rarst\Laps\Record;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\Stopwatch\StopwatchEvent;
/**
* Generic Stopwatch–based collector. Can be used by itself or extended.
*/
class Stopwatch_Record_Collector implements Record_Collector_Interface {
/** @var Stopwatch $stopwatch */
protected $stopwatch;
/**
* @param Stopwatch $stopwatch Stopwatch instance.
*/
public function __construct( Stopwatch $stopwatch ) {
$this->stopwatch = $stopwatch;
}
/**
* @param string $name The event name.
* @param string $category The event category.
*
* @return StopwatchEvent
*/
public function start( $name, $category = null ) {
return $this->stopwatch->start( $name, $category );
}
/**
* @param string $name The event name.
*
* @return bool|StopwatchEvent
*/
public function stop( $name ) {
if ( $this->stopwatch->isStarted( $name ) ) {
return $this->stopwatch->stop( $name );
}
return false;
}
/**
* @return Stopwatch_Record[]
*/
public function get_records() {
$events = $this->stopwatch->getSectionEvents( '__root__' );
foreach ( array_keys( $events ) as $name ) {
if ( $this->stopwatch->isStarted( $name ) ) {
unset( $events[ $name ] );
}
}
return array_map( [ $this, 'transform' ], array_keys( $events ), $events );
}
/**
* @param string $name Event name.
* @param StopwatchEvent $event Stopwatch event instance.
*
* @return Stopwatch_Record
*/
protected function transform( $name, StopwatchEvent $event ) {
return new Stopwatch_Record( $name, $event );
}
}
+92
View File
@@ -0,0 +1,92 @@
<?php
namespace Rarst\Laps\Record;
/**
* Processes records into sets for display by template.
*/
class Timeline_Iterator implements \Iterator {
/** @var float $origin Start point for the timeline. */
protected $origin;
/** @var float $end Timeline duration. */
protected $total;
/** @var Recursive_Record_Iterator */
protected $iterator;
/** @var Recursive_Record_Iterator */
protected $current;
/**
* @param Recursive_Record_Iterator $iterator Record iterator.
*/
public function __construct( Recursive_Record_Iterator $iterator ) {
$this->origin = $_SERVER['REQUEST_TIME_FLOAT'];
$this->iterator = $iterator;
}
/**
* @return array
*/
public function current() {
$data = [];
foreach ( $this->current as $record ) {
$data[] = $this->prepare( $record );
}
return $data;
}
/**
* Sets current context to the next set of nested records.
*/
public function next() {
$this->current = $this->current->getChildren();
}
/**
* @codeCoverageIgnore
*
* @return void
*/
public function key() {
}
/**
* @return bool If current set contains records.
*/
public function valid() {
return (bool) count( $this->current );
}
/**
* Rewind to a topmost record set.
*/
public function rewind() {
$this->total = microtime( true ) - $this->origin;
$this->current = $this->iterator;
}
/**
* @param Record_Interface $record Record instance.
*
* @return array Record data for display by the template.
*/
protected function prepare( Record_Interface $record ) {
$data = [
'description' => $record->get_description(),
'category' => $record->get_category(),
'offset' => round( ( $record->get_origin() - $this->origin ) / $this->total * 100, 2 ),
'width' => round( $record->get_duration() / $this->total * 100, 2 ),
];
return $data;
}
}
-40
View File
@@ -1,40 +0,0 @@
<?php
namespace Rarst\Laps;
/**
* Base class for bulk event providers
*/
abstract class Laps_Events {
/**
* Retrieve array of event data
*
* @return array
*/
public function get() {
if ( $this->is_applicable() ) {
return $this->get_events();
}
return array();
}
/**
* Is event pack relevant to current runtime
*
* @return bool
*/
protected function is_applicable() {
return true;
}
/**
* Inherit with internal events compiling and return
*
* @return array()
*/
abstract protected function get_events();
}
-62
View File
@@ -1,62 +0,0 @@
<?php
namespace Rarst\Laps;
/**
* Events for Genesis Framework based themes
*
* @link http://my.studiopress.com/themes/genesis/
*/
class Genesis_Events extends Laps_Events {
/**
* @return bool
*/
protected function is_applicable() {
return function_exists( 'genesis' );
}
/**
* @return array
*/
protected function get_events() {
$events = array();
$events['genesis_before_header'][10] = array(
'event' => 'Header',
'category' => 'theme',
);
$events['genesis_after_header'][10] = array(
'action' => 'stop',
'event' => 'Header',
'category' => 'theme',
);
$events['genesis_before_sidebar_widget_area'][10] = array(
'event' => 'Sidebar',
'category' => 'theme',
);
$events['genesis_after_sidebar_widget_area'][10] = array(
'action' => 'stop',
'event' => 'Sidebar',
'category' => 'theme',
);
$events['genesis_before_sidebar_alt_widget_area'][10] = array(
'event' => 'Sidebar (alternate)',
'category' => 'theme',
);
$events['genesis_after_sidebar_alt_widget_area'][10] = array(
'action' => 'stop',
'event' => 'Sidebar (alternate)',
'category' => 'theme',
);
return $events;
}
}
-310
View File
@@ -1,310 +0,0 @@
<?php
namespace Rarst\Laps;
use Symfony\Component\Stopwatch\Stopwatch;
/**
* Main plugin's class.
*/
class Laps {
/** @var Stopwatch $stopwatch */
public static $stopwatch;
public static $events = array();
public static $query_starts = array();
/**
* Start Stopwatch and timing plugin load immediately, then set up core events and needed hooks.
*/
static function on_load() {
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
return;
}
self::$stopwatch = new Stopwatch();
self::$stopwatch->start( 'Plugins Load', 'plugin' );
$events = new Core_Events();
self::add_events( $events->get() );
add_action( 'pre_update_option_active_plugins', array( __CLASS__, 'pre_update_option_active_plugins' ) );
add_action( 'pre_update_site_option_active_sitewide_plugins', array( __CLASS__, 'pre_update_option_active_plugins' ) );
add_action( 'pre_http_request', array( __CLASS__, 'pre_http_request' ), 10, 3 );
add_action( 'http_api_debug', array( __CLASS__, 'http_api_debug' ), 10, 5 );
add_action( 'after_setup_theme', array( __CLASS__, 'after_setup_theme' ), 15 );
add_action( 'init', array( __CLASS__, 'init' ) );
if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) {
add_filter( 'query', array( __CLASS__, 'query' ), 20 );
}
}
/**
* Hook events by name and priority from array.
*
* @param array $stops
*/
static function add_events( $stops ) {
self::$events = array_merge( self::$events, $stops );
foreach ( $stops as $hook_name => $data ) {
foreach ( array_keys( $data ) as $priority ) {
add_action( $hook_name, array( __CLASS__, 'tick' ), $priority );
}
}
}
/**
* Reorder active plugins so Laps is first and starts timing load early.
*
* @param array $plugins
*
* @return array
*/
static function pre_update_option_active_plugins( $plugins ) {
$plugin = plugin_basename( dirname( __DIR__ ) . '/laps.php' );
$key = array_search( $plugin, $plugins );
if ( false !== $key && $key > 0 ) {
unset( $plugins[ $key ] );
array_unshift( $plugins, $plugin );
$plugins = array_values( $plugins );
}
return $plugins;
}
/**
* Mark action for the event on Stopwatch.
*
* @param mixed $input pass through if added to filter
*
* @return mixed
*/
static function tick( $input = null ) {
global $wp_filter;
$filter = current_filter();
$priority = key( $wp_filter[ $filter ] );
$event = wp_parse_args(
self::$events[ $filter ][ $priority ],
array(
'action' => 'start',
'category' => null,
)
);
if ( 'stop' === $event['action'] && ! self::$stopwatch->isStarted( $event['event'] ) ) {
return $input;
}
self::$stopwatch->{$event['action']}( $event['event'], $event['category'] );
return $input;
}
/**
* Capture SQL queries start times
*
* @param string $query
*
* @return string
*/
static function query( $query ) {
global $wpdb;
if ( empty( self::$query_starts ) && ! empty( $wpdb->queries ) ) {
self::$query_starts[ count( $wpdb->queries ) ] = microtime( true ) * 1000;
} else {
self::$query_starts[] = microtime( true ) * 1000;
}
return $query;
}
/**
* Capture start time of HTTP request
*
* @param boolean $false
* @param array $args
* @param string $url
*
* @return boolean
*/
static function pre_http_request( $false, $args, $url ) {
self::$stopwatch->start( $url, 'http' );
return $false;
}
/**
* Capture end time of HTTP request
*
* @param array|\WP_Error $response
* @param string $type
* @param object $class
* @param array $args
* @param string $url
*
* @return mixed
*/
static function http_api_debug( $response, $type, $class, $args, $url ) {
self::$stopwatch->stop( $url );
return $response;
}
/**
* When theme is done possibly add vendor-specific events.
*/
static function after_setup_theme() {
foreach ( array( 'THA', 'Hybrid', 'Genesis', 'Thematic', 'Yoast' ) as $vendor ) {
$class = "Rarst\\Laps\\{$vendor}_Events";
/** @var Laps_Events $events */
$events = new $class;
self::add_events( $events->get() );
}
}
static function init() {
add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_scripts' ) );
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_scripts' ) );
add_action( 'admin_bar_menu', array( __CLASS__, 'admin_bar_menu' ), 100 );
}
static function enqueue_scripts() {
$suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
wp_register_script( 'laps', plugins_url( "js/laps{$suffix}.js", __DIR__ ), array( 'jquery' ), '3.3.1', true );
wp_register_style( 'laps', plugins_url( "css/laps{$suffix}.css", __DIR__ ) );
if ( is_admin_bar_showing() ) {
wp_enqueue_script( 'laps' );
wp_enqueue_style( 'laps' );
}
}
/**
* Render interface and add to the toolbar.
*
* @param \WP_Admin_Bar $wp_admin_bar
*/
static function admin_bar_menu( $wp_admin_bar ) {
if ( ! apply_filters( 'laps_can_see', current_user_can( 'manage_options' ) ) ) {
return;
}
global $timestart, $wpdb;
$mustache = new \Mustache_Engine(
array(
'loader' => new \Mustache_Loader_FilesystemLoader( dirname( __DIR__ ) . '/views' ),
'cache' => new Mustache_Cache_FrozenCache( dirname( __DIR__ ) . '/views/cache' ),
)
);
self::$stopwatch->stop( 'Toolbar' );
$events = self::$stopwatch->getSectionEvents( '__root__' );
$start = $timestart * 1000;
$end = microtime( true ) * 1000;
$total = $end - $start;
$event_data = array();
$http_data = array();
foreach ( $events as $name => $event ) {
$offset = round( ( $event->getOrigin() - $start ) / $total * 100, 2 );
$duration = $event->getDuration();
$width = round( $duration / $total * 100, 2 );
$category = $event->getCategory();
if ( 'http' === $category ) {
$http_data[] = compact( 'name', 'offset', 'duration', 'width', 'category' );
continue;
}
$memory = $event->getMemory() / 1024 / 1024;
$event_data[] = compact( 'name', 'offset', 'duration', 'width', 'category', 'memory' );
}
$query_data = array();
$last_query_end = 0;
$last_offset = 0;
$last_duration = 0;
if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) {
foreach ( $wpdb->queries as $key => $query ) {
$query_start = isset( self::$query_starts[ $key ] ) ? self::$query_starts[ $key ] : $last_query_end;
list( $sql, $duration, $trace ) = $query;
$sql = trim( $sql );
$category = 'query-read';
if ( 0 === stripos( $sql, 'INSERT' ) || 0 === stripos( $sql, 'UPDATE' ) ) {
$category = 'query-write';
}
$duration *= 1000;
$last_query_end = $query_start + $duration;
$offset = round( ( $query_start - $start ) / $total * 100, 2 );
// if query is indistinguishably close to previous then stack it
if ( $offset === $last_offset ) {
$key = count( $query_data ) - 1;
$query_data[ $key ]['sql'] .= '<br />' . $sql;
$last_duration += $duration;
$width = round( $last_duration / $total * 100, 2 );
$query_data[ $key ]['width'] = $width;
continue;
}
$width = round( $duration / $total * 100, 2 );
$last_offset = $offset;
$last_duration = $duration;
$query_data[] = compact( 'sql', 'duration', 'offset', 'width', 'category' );
}
}
$html = $mustache->render(
'laps',
array(
'events' => $event_data,
'queries' => $query_data,
'savequeries' => defined( 'SAVEQUERIES' ) && SAVEQUERIES,
'http' => $http_data,
'savehttp' => ! empty( $http_data ),
)
);
$wp_admin_bar->add_node( array(
'id' => 'laps',
'title' => sprintf( 'Lap: %ss', round( $total / 1000, 3 ) ),
) );
$wp_admin_bar->add_node( array(
'id' => 'laps_output',
'parent' => 'laps',
'meta' => array( 'html' => $html ),
) );
}
}
-51
View File
@@ -1,51 +0,0 @@
<?php
namespace Rarst\Laps;
/**
* Events for Theme Hook Alliance
*
* @link http://zamoose.github.io/themehookalliance/
*/
class THA_Events extends Laps_Events {
/**
* @return bool
*/
protected function is_applicable() {
return defined( 'THA_HOOKS_VERSION' );
}
/**
* @return array
*/
protected function get_events() {
$events = array();
$events['tha_header_before'][10] = array(
'event' => 'Header',
'category' => 'theme',
);
$events['tha_header_after'][10] = array(
'action' => 'stop',
'event' => 'Header',
'category' => 'theme',
);
$events['tha_sidebars_before'][10] = array(
'event' => 'Sidebars',
'category' => 'theme',
);
$events['tha_sidebars_after'][10] = array(
'action' => 'stop',
'event' => 'Sidebars',
'category' => 'theme',
);
return $events;
}
}
-62
View File
@@ -1,62 +0,0 @@
<?php
namespace Rarst\Laps;
/**
* Events for Thematic Framework based themes
*
* @link http://thematictheme.com/
*/
class Thematic_Events extends Laps_Events {
/**
* @return bool
*/
protected function is_applicable() {
return function_exists( 'thematic_init' );
}
/**
* @return array
*/
protected function get_events() {
$events = array();
$events['thematic_aboveheader'][10] = array(
'event' => 'Header',
'category' => 'theme',
);
$events['thematic_belowheader'][10] = array(
'action' => 'stop',
'event' => 'Header',
'category' => 'theme',
);
$events['widget_area_primary_aside'][10] = array(
'event' => 'Sidebar (primary aside)',
'category' => 'theme',
);
$events['thematic_betweenmainasides'][10] = array(
'action' => 'stop',
'event' => 'Sidebar (primary aside)',
'category' => 'theme',
);
$events['widget_area_secondary_aside'][10] = array(
'event' => 'Sidebar (secondary aside)',
'category' => 'theme',
);
$events['thematic_belowmainasides'][10] = array(
'action' => 'stop',
'event' => 'Sidebar (secondary aside)',
'category' => 'theme',
);
return $events;
}
}
@@ -0,0 +1,111 @@
<?php
class __Mustache_a04731de0bb9e043ecde3814b3a901bb extends Mustache_Template
{
private $lambdaHelper;
public function renderInternal(Mustache_Context $context, $indent = '')
{
$this->lambdaHelper = new Mustache_LambdaHelper($this->mustache, $context);
$buffer = '';
// 'timelines' section
$value = $context->find('timelines');
$buffer .= $this->sectionAdac57c50f147efcda3ea1c17e7a6c61($context, $indent, $value);
return $buffer;
}
private function section9f1840fed1e29fb14453f0ea2bfe30ad(Mustache_Context $context, $indent, $value)
{
$buffer = '';
if (!is_string($value) && is_callable($value)) {
$source = '
<div class="event event-{{category}}" data-toggle="tooltip" title="{{description}}" style="width:{{width}}%; left:{{offset}}%;">
<span class="sr-only">{{description}}</span>
</div>
';
$result = call_user_func($value, $source, $this->lambdaHelper);
if (strpos($result, '{{') === false) {
$buffer .= $result;
} else {
$buffer .= $this->mustache
->loadLambda((string) $result)
->renderInternal($context);
}
} elseif (!empty($value)) {
$values = $this->isIterable($value) ? $value : array($value);
foreach ($values as $value) {
$context->push($value);
$buffer .= $indent . ' <div class="event event-';
$value = $this->resolveValue($context->find('category'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= '" data-toggle="tooltip" title="';
$value = $this->resolveValue($context->find('description'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= '" style="width:';
$value = $this->resolveValue($context->find('width'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= '%; left:';
$value = $this->resolveValue($context->find('offset'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= '%;">
';
$buffer .= $indent . ' <span class="sr-only">';
$value = $this->resolveValue($context->find('description'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= '</span>
';
$buffer .= $indent . ' </div>
';
$context->pop();
}
}
return $buffer;
}
private function sectionAdac57c50f147efcda3ea1c17e7a6c61(Mustache_Context $context, $indent, $value)
{
$buffer = '';
if (!is_string($value) && is_callable($value)) {
$source = '
<div class="laps-timeline">
{{#.}}
<div class="event event-{{category}}" data-toggle="tooltip" title="{{description}}" style="width:{{width}}%; left:{{offset}}%;">
<span class="sr-only">{{description}}</span>
</div>
{{/.}}
</div>
';
$result = call_user_func($value, $source, $this->lambdaHelper);
if (strpos($result, '{{') === false) {
$buffer .= $result;
} else {
$buffer .= $this->mustache
->loadLambda((string) $result)
->renderInternal($context);
}
} elseif (!empty($value)) {
$values = $this->isIterable($value) ? $value : array($value);
foreach ($values as $value) {
$context->push($value);
$buffer .= $indent . ' <div class="laps-timeline">
';
// '.' section
$value = $context->last('.');
$buffer .= $this->section9f1840fed1e29fb14453f0ea2bfe30ad($context, $indent, $value);
$buffer .= $indent . ' </div>
';
$context->pop();
}
}
return $buffer;
}
}
+9
View File
@@ -0,0 +1,9 @@
{{#timelines}}
<div class="laps-timeline">
{{#.}}
<div class="event event-{{category}}" data-toggle="tooltip" title="{{description}}" style="width:{{width}}%; left:{{offset}}%;">
<span class="sr-only">{{description}}</span>
</div>
{{/.}}
</div>
{{/timelines}}
+77
View File
@@ -0,0 +1,77 @@
<?php
namespace Rarst\Laps\Tests\Event;
use Brain\Monkey\Functions;
use Rarst\Laps\Event\Core_Events;
use Rarst\Laps\Event\Genesis_Events;
use Rarst\Laps\Event\Hybrid_Events;
use Rarst\Laps\Event\THA_Events;
use Rarst\Laps\Event\Yoast_Events;
use Rarst\Laps\Tests\LapsTestCase;
class EventTest extends LapsTestCase {
public function testCore() {
$events = ( new Core_Events() )->get_events();
$this->assertInternalType( 'array', $events );
$this->assertNotEmpty( $events );
}
public function testGenesis() {
$genesis = new Genesis_Events();
$this->assertEmpty( $genesis->get_events() );
Functions\expect( 'genesis' )->zeroOrMoreTimes();
$events = $genesis->get_events();
$this->assertInternalType( 'array', $events );
$this->assertNotEmpty( $events );
}
public function testHybrid() {
$hybrid = new Hybrid_Events();
$this->assertEmpty( $hybrid->get_events() );
Functions\expect( 'hybrid_get_prefix' )->once()->andReturn( 'hybrid' );
Functions\expect( 'get_theme_support' )
->once()
->with( 'hybrid-core-sidebars' )
->andReturn( [ [ 'primary' ] ] );
$events = $hybrid->get_events();
$this->assertInternalType( 'array', $events );
$this->assertNotEmpty( $events );
}
public function testTha() {
$tha = new THA_Events();
$this->assertEmpty( $tha->get_events() );
define( 'THA_HOOKS_VERSION', '1.0' );
$events = $tha->get_events();
$this->assertInternalType( 'array', $events );
$this->assertNotEmpty( $events );
}
public function testYoast() {
$yoast = new Yoast_Events();
$this->assertEmpty( $yoast->get_events() );
$this->getMockBuilder( 'WPSEO_Frontend' )->allowMockingUnknownTypes()->getMock();
$events = $yoast->get_events();
$this->assertInternalType( 'array', $events );
$this->assertNotEmpty( $events );
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
namespace Rarst\Laps\Tests;
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
use PHPUnit\Framework\TestCase;
use Brain\Monkey;
class LapsTestCase extends TestCase {
use MockeryPHPUnitIntegration;
protected function setUp() {
parent::setUp();
Monkey\setUp();
}
protected function tearDown() {
Monkey\tearDown();
parent::tearDown();
}
}
+44
View File
@@ -0,0 +1,44 @@
<?php
namespace Rarst\Laps\Tests\Manager;
use Brain\Monkey\Functions;
use Rarst\Laps\Manager\Asset_Manager;
use Rarst\Laps\Tests\LapsTestCase;
/**
* @coversDefaultClass Rarst\Laps\Manager\Asset_Manager
*/
class AssetTest extends LapsTestCase {
/**
* @covers ::__construct
*
* @return Asset_Manager
*/
public function test__construct() {
$manager = new Asset_Manager();
$this->assertTrue( has_action( 'wp_enqueue_scripts', [ $manager, 'enqueue_scripts' ] ) );
$this->assertTrue( has_action( 'admin_enqueue_scripts', [ $manager, 'enqueue_scripts' ] ) );
return $manager;
}
/**
* @covers ::enqueue_scripts
* @depends test__construct
*/
public function testEnqueue_scripts( Asset_Manager $manager ) {
Functions\expect( 'wp_register_script' )->once();
Functions\expect( 'wp_register_style' )->once();
Functions\expect( 'plugins_url' )->zeroOrMoreTimes();
Functions\expect( 'is_admin_bar_showing' )->once()->andReturn( true );
Functions\expect( 'wp_enqueue_script' )->once();
Functions\expect( 'wp_enqueue_style' )->once();
$manager->enqueue_scripts();
}
}
+50
View File
@@ -0,0 +1,50 @@
<?php
namespace Rarst\Laps\Tests\Manager;
use Brain\Monkey\Functions;
use Rarst\Laps\Manager\Load_Order_Manager;
use Rarst\Laps\Tests\LapsTestCase;
/**
* @coversDefaultClass Rarst\Laps\Manager\Load_Order_Manager
*/
class LoadOrderTest extends LapsTestCase {
/**
* @covers ::__construct
*
* @return Load_Order_Manager
*/
public function test__construct() {
$manager = new Load_Order_Manager();
$this->assertTrue( has_action( 'pre_update_option_active_plugins', [ $manager, 'pre_update_option_active_plugins' ] ) );
$this->assertTrue( has_action( 'pre_update_site_option_active_sitewide_plugins', [ $manager, 'pre_update_option_active_plugins' ] ) );
return $manager;
}
/**
* @covers ::pre_update_option_active_plugins
* @depends test__construct
*/
public function testPre_update_option_active_plugins( Load_Order_Manager $manager ) {
$plugins = [
'foo/bar.php',
'laps/laps.php',
'baz/qux.php',
];
Functions\expect( 'plugin_basename' )->once()->andReturn( 'laps/laps.php' );
$plugins = $manager->pre_update_option_active_plugins( $plugins );
$this->assertInternalType( 'array', $plugins );
$this->assertCount( 3, $plugins );
$this->assertEquals( 'laps/laps.php', $plugins[0] );
}
}
+58
View File
@@ -0,0 +1,58 @@
<?php
namespace Rarst\Laps\Tests\Manager;
use Brain\Monkey\Functions;
use Rarst\Laps\Manager\Toolbar_Manager;
use Rarst\Laps\Plugin;
use Rarst\Laps\Tests\LapsTestCase;
/**
* @coversDefaultClass Rarst\Laps\Manager\Toolbar_Manager
*/
class ToolbarTest extends LapsTestCase {
/**
* @covers ::__construct
*
* @return Toolbar_Manager
*/
public function test__construct() {
$laps = new Plugin( [ 'records' => [] ] );
$manager = new Toolbar_Manager( $laps );
$this->assertTrue( has_action( 'admin_bar_menu', [ $manager, 'admin_bar_menu' ] ) );
return $manager;
}
/**
* @covers ::admin_bar_menu
*
* @depends test__construct
*/
public function testAdmin_bar_menu( Toolbar_Manager $manager ) {
$wp_admin_bar = $this->getMockBuilder( 'WP_Admin_Bar' )
->allowMockingUnknownTypes()
->setMethods( [ 'add_node' ] )
->getMock();
$wp_admin_bar->method( 'add_node' )->willReturn( null );
$wp_admin_bar->expects( $this->exactly( 2 ) )
->method( 'add_node' )
->withConsecutive(
[ $this->isType( 'array' ) ],
[ $this->isType( 'array' ) ]
);
Functions\expect( 'current_user_can' )
->once()
->with( 'manage_options' )
->andReturn( true );
$manager->admin_bar_menu( $wp_admin_bar );
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace Rarst\Laps\Tests;
use Pimple\ServiceProviderInterface;
use Rarst\Laps\Provider\Bootable_Provider_Interface;
class PluginTest extends LapsTestCase {
public function testContainer() {
$container = new \Rarst\Laps\Plugin();
$service = $this->getMockBuilder( [ ServiceProviderInterface::class, Bootable_Provider_Interface::class ] )
->getMock();
$service->expects( $this->once() )
->method( 'register' )
->with( $container );
$container->register( $service );
$service->expects( $this->once() )
->method( 'boot' )
->with( $container );
$container->run();
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace Rarst\Laps\Tests\Provider;
use Pimple\Container;
use Rarst\Laps\Event\Hook_Event_Config_Interface;
use Rarst\Laps\Provider\Hook_Event_Provider;
use Rarst\Laps\Tests\LapsTestCase;
class EventTest extends LapsTestCase {
public function testProvider() {
$container = new Container();
$provider = new Hook_Event_Provider();
$provider->register( $container );
$this->assertArrayHasKey( 'hook.events', $container );
foreach ( $container['hook.events'] as $config ) {
$this->assertInstanceOf( Hook_Event_Config_Interface::class, $config );
}
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace Rarst\Laps\Tests\Provider;
use Pimple\Exception\FrozenServiceException;
use Rarst\Laps\Plugin;
use Rarst\Laps\Provider\Manager_Provider;
use Rarst\Laps\Tests\LapsTestCase;
class ManagerTest extends LapsTestCase {
/**
* @covers \Rarst\Laps\Provider\Manager_Provider
*/
public function testProvider() {
$container = new Plugin();
$provider = new Manager_Provider();
$provider->register( $container );
$this->assertArrayHasKey( 'managers', $container );
$container->extend( 'managers', function () {} );
$provider->boot( $container );
$this->expectException( FrozenServiceException::class );
$container->extend( 'managers', function () {} );
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
namespace Rarst\Laps\Tests\Provider;
use Pimple\Exception\FrozenServiceException;
use Rarst\Laps\Plugin;
use Rarst\Laps\Provider\Record_Provider;
use Rarst\Laps\Tests\LapsTestCase;
class RecordTest extends LapsTestCase {
/**
* @covers \Rarst\Laps\Provider\Record_Provider
*/
public function testProvider() {
$container = new Plugin();
$provider = new Record_Provider();
$provider->register( $container );
$stopwatch_a = $container['stopwatch'];
$stopwatch_b = $container['stopwatch'];
$this->assertNotSame( $stopwatch_a, $stopwatch_b );
$this->assertArrayHasKey( 'records', $container );
$container->extend( 'collectors', function () {} );
$provider->boot( $container );
$this->expectException( FrozenServiceException::class );
$container->extend( 'collectors', function () {} );
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace Rarst\Laps\Tests\Record;
use Rarst\Laps\Record\Core_Load_Collector;
use Rarst\Laps\Record\Record_Interface;
use Rarst\Laps\Tests\LapsTestCase;
class CoreTest extends LapsTestCase {
public function testCollector() {
$collector = new Core_Load_Collector();
$records = $collector->get_records();
$this->assertInternalType( 'array', $records );
$this->assertCount( 2, $records );
$this->assertInstanceOf( Record_Interface::class, $records[0] );
}
}
+46
View File
@@ -0,0 +1,46 @@
<?php
namespace Rarst\Laps\Tests\Record;
use Brain\Monkey\Functions;
use Rarst\Laps\Event\Core_Events;
use Rarst\Laps\Record\Hook_Record_Collector;
use Rarst\Laps\Tests\LapsTestCase;
use Symfony\Component\Stopwatch\Stopwatch;
class HookTest extends LapsTestCase {
public function testCollector() {
$stopwatch = new Stopwatch();
$collector = new Hook_Record_Collector( $stopwatch, [ 'core' => new Core_Events() ] );
$this->assertTrue( $stopwatch->isStarted( 'Plugins Load' ) );
$this->assertTrue( has_action( 'after_setup_theme', [ $collector, 'after_setup_theme' ] ) );
$collector->after_setup_theme();
Functions\expect( 'current_filter' )->once()->andReturn( 'plugins_loaded' );
Functions\expect( 'wp_parse_args' )->once()->andReturnFirstArg();
global $wp_filter;
$wp_hook = $this->getMockBuilder( 'WP_Hook' )
->allowMockingUnknownTypes()
->setMethods( [ 'current_priority' ] )
->getMock();
$wp_hook->method( 'current_priority' )->willReturn( - 2 );
$wp_filter['plugins_loaded'] = $wp_hook;
$collector->tick();
$this->assertFalse( $stopwatch->isStarted( 'Plugins Load' ) );
$stopwatch->start( 'Toolbar' );
$collector->get_records();
$this->assertFalse( $stopwatch->isStarted( 'Toolbar' ) );
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace Rarst\Laps\Tests\Record;
use Rarst\Laps\Record\Http_Record_Collector;
use Rarst\Laps\Tests\LapsTestCase;
use Symfony\Component\Stopwatch\Stopwatch;
class HttpTest extends LapsTestCase {
public function testCollector() {
$stopwatch = new Stopwatch();
$url = 'https://example.com/';
$collector = new Http_Record_Collector( $stopwatch );
$this->assertTrue( has_action( 'pre_http_request', [ $collector, 'pre_http_request' ] ) );
$this->assertTrue( has_action( 'http_api_debug', [ $collector, 'http_api_debug' ] ) );
$collector->pre_http_request( false, [], $url );
$this->assertTrue( $stopwatch->isStarted( $url ) );
$collector->http_api_debug( [], '', new \stdClass(), [], $url );
$this->assertFalse( $stopwatch->isStarted( $url ) );
}
}
+72
View File
@@ -0,0 +1,72 @@
<?php
namespace Rarst\Laps\Tests\Record;
use Rarst\Laps\Record\Recursive_Record_Iterator;
use Rarst\Laps\Record\Record;
use Rarst\Laps\Record\Timeline_Iterator;
use Rarst\Laps\Tests\LapsTestCase;
class IteratorTest extends LapsTestCase {
/**
* @covers \Rarst\Laps\Record\Recursive_Record_Iterator
*
* @return Recursive_Record_Iterator
*/
public function testRecord() {
$record_a = new Record( 'Parent', 0, 100 );
$record_b = new Record( 'Child', 50, 100 );
$records = [ $record_b, $record_a ];
$iterator = new Recursive_Record_Iterator( $records );
$this->assertCount( 1, $iterator->getArrayCopy() );
foreach ( $iterator as $record ) {
$this->assertEquals( 'Parent', $record->get_name() );
}
$this->assertTrue( $iterator->hasChildren() );
$children = $iterator->getChildren();
$this->assertCount( 1, $children );
foreach ( $children as $record ) {
$this->assertEquals( 'Child', $record->get_name() );
}
return $iterator;
}
/**
* @covers \Rarst\Laps\Record\Timeline_Iterator
*
* @depends testRecord
*/
public function testTimeline( Recursive_Record_Iterator $iterator ) {
$iterator = new Timeline_Iterator( $iterator );
$iterator->rewind();
$this->assertTrue( $iterator->valid() );
$records = $iterator->current();
$this->assertInternalType( 'array', $records );
$this->assertArrayHasKey( 'description', $records[0] );
$this->assertArrayHasKey( 'category', $records[0] );
$this->assertArrayHasKey( 'offset', $records[0] );
$this->assertArrayHasKey( 'width', $records[0] );
$iterator->next();
$this->assertTrue( $iterator->valid() );
$records = $iterator->current();
$this->assertInternalType( 'array', $records );
$iterator->next();
$this->assertFalse( $iterator->valid() );
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
namespace Rarst\Laps\Tests\Record;
use Rarst\Laps\Record\Record;
use Rarst\Laps\Tests\LapsTestCase;
class RecordTest extends LapsTestCase {
/**
* @covers \Rarst\Laps\Record\Record
*/
public function testRecord() {
$name = 'Name';
$origin = 0;
$duration = 1;
$description = 'description';
$category = 'info';
$record = new Record( $name, $origin, $duration );
$this->assertEquals( $name, $record->get_name() );
$this->assertEquals( $origin, $record->get_origin() );
$this->assertEquals( $duration, $record->get_duration() );
$this->assertContains( $name, $record->get_description() );
$this->assertContains( (string) ( $duration * 1000 ), $record->get_description() );
$this->assertEmpty( $record->get_category() );
$record = new Record( $name, $origin, $duration, $description, $category );
$this->assertEquals( $description, $record->get_description() );
$this->assertEquals( $category, $record->get_category() );
}
}
+46
View File
@@ -0,0 +1,46 @@
<?php
namespace Rarst\Laps\Tests\Record;
use Rarst\Laps\Record\Record;
use Rarst\Laps\Record\Sql_Record_Collector;
use Rarst\Laps\Tests\LapsTestCase;
class SqlTest extends LapsTestCase {
/**
* @covers \Rarst\Laps\Record\Sql_Record_Collector
*/
public function testCollector() {
define( 'SAVEQUERIES', true );
$collector = new Sql_Record_Collector();
$this->assertTrue( has_filter( 'query', [ $collector, 'query' ] ) );
$query = 'SELECT * FROM wp_posts';
$collector->query( $query );
global $wpdb;
$wpdb = new \stdClass();
$duration = 100;
$wpdb->queries[] = [ $query, $duration ];
$records = $collector->get_records();
$this->assertInternalType( 'array', $records );
$this->assertCount( 1, $records );
$record = $records[0];
$this->assertInstanceOf( Record::class, $record );
$this->assertEquals( $query, $record->get_name() );
$this->assertContains( $query, $record->get_description() );
$this->assertInternalType( 'float', $record->get_origin() );
$this->assertEquals( $duration, $record->get_duration() );
$this->assertEquals( 'sql-read', $record->get_category() );
}
}
+52
View File
@@ -0,0 +1,52 @@
<?php
namespace Rarst\Laps\Tests\Record;
use Rarst\Laps\Record\Stopwatch_Record;
use Rarst\Laps\Record\Stopwatch_Record_Collector;
use Rarst\Laps\Tests\LapsTestCase;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\Stopwatch\StopwatchEvent;
class StopwatchTest extends LapsTestCase {
/**
* @covers \Rarst\Laps\Record\Stopwatch_Record_Collector
*/
public function testCollector() {
$collector = new Stopwatch_Record_Collector( new Stopwatch() );
$collector->start( 'Event', 'info' );
$collector->start( 'Not stopped', 'info' );
usleep( 1000 );
$event = $collector->stop( 'Event' );
$this->assertFalse( $collector->stop( 'Not event' ) );
$records = $collector->get_records();
$this->assertInternalType( 'array', $records );
$this->assertCount( 1, $records );
$this->assertInstanceOf( Stopwatch_Record::class, $records[0] );
return $event;
}
/**
* @depends testCollector
* @covers \Rarst\Laps\Record\Stopwatch_Record
*
* @param StopwatchEvent $event Event instance.
*/
public function testRecord( StopwatchEvent $event ) {
$record = new Stopwatch_Record( 'Event', $event );
$this->assertEquals( 'Event', $record->get_name() );
$this->assertEquals( 'info', $record->get_category() );
$this->assertEquals( $event->getOrigin() / 1000, $record->get_origin() );
$this->assertEquals( $event->getDuration() / 1000, $record->get_duration() );
$this->assertContains( 'Event', $record->get_description() );
}
}
@@ -1,292 +0,0 @@
<?php
class __Mustache_797f2a84b9a5a7b601644494da75d61e extends Mustache_Template
{
private $lambdaHelper;
public function renderInternal(Mustache_Context $context, $indent = '')
{
$this->lambdaHelper = new Mustache_LambdaHelper($this->mustache, $context);
$buffer = '';
$blocksContext = array();
$buffer .= $indent . '<div class="laps-timeline">
';
// 'events' section
$value = $context->find('events');
$buffer .= $this->section940385acb9865793d8943db356869c18($context, $indent, $value);
$buffer .= $indent . '</div>
';
$buffer .= $indent . '
';
// 'savequeries' section
$value = $context->find('savequeries');
$buffer .= $this->sectionB93c1beb6bc5c41326a972381b0dd94a($context, $indent, $value);
$buffer .= $indent . '
';
// 'savehttp' section
$value = $context->find('savehttp');
$buffer .= $this->section238f05e38d8d133266503e545e18de7e($context, $indent, $value);
return $buffer;
}
private function section940385acb9865793d8943db356869c18(Mustache_Context $context, $indent, $value)
{
$buffer = '';
$blocksContext = array();
if (!is_string($value) && is_callable($value)) {
$source = '
<div class="event event-{{category}}" data-toggle="tooltip" title="{{name}} - {{duration}} ms - {{memory}} MB" style="width:{{width}}%; left:{{offset}}%;">
<span class="sr-only">{{name}} - {{duration}} ms - {{memory}} MB</span>
</div>
';
$result = call_user_func($value, $source, $this->lambdaHelper);
if (strpos($result, '{{') === false) {
$buffer .= $result;
} else {
$buffer .= $this->mustache
->loadLambda((string) $result)
->renderInternal($context);
}
} elseif (!empty($value)) {
$values = $this->isIterable($value) ? $value : array($value);
foreach ($values as $value) {
$context->push($value);
$buffer .= $indent . ' <div class="event event-';
$value = $this->resolveValue($context->find('category'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= '" data-toggle="tooltip" title="';
$value = $this->resolveValue($context->find('name'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= ' - ';
$value = $this->resolveValue($context->find('duration'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= ' ms - ';
$value = $this->resolveValue($context->find('memory'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= ' MB" style="width:';
$value = $this->resolveValue($context->find('width'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= '%; left:';
$value = $this->resolveValue($context->find('offset'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= '%;">
';
$buffer .= $indent . ' <span class="sr-only">';
$value = $this->resolveValue($context->find('name'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= ' - ';
$value = $this->resolveValue($context->find('duration'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= ' ms - ';
$value = $this->resolveValue($context->find('memory'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= ' MB</span>
';
$buffer .= $indent . ' </div>
';
$context->pop();
}
}
return $buffer;
}
private function section372053fedd826e7e297c29f607930cbc(Mustache_Context $context, $indent, $value)
{
$buffer = '';
$blocksContext = array();
if (!is_string($value) && is_callable($value)) {
$source = '
<div class="event event-{{category}}" title="{{sql}}" style="width:{{width}}%; left:{{offset}}%;">
<span class="sr-only">{{sql}}</span>
</div>
';
$result = call_user_func($value, $source, $this->lambdaHelper);
if (strpos($result, '{{') === false) {
$buffer .= $result;
} else {
$buffer .= $this->mustache
->loadLambda((string) $result)
->renderInternal($context);
}
} elseif (!empty($value)) {
$values = $this->isIterable($value) ? $value : array($value);
foreach ($values as $value) {
$context->push($value);
$buffer .= $indent . ' <div class="event event-';
$value = $this->resolveValue($context->find('category'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= '" title="';
$value = $this->resolveValue($context->find('sql'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= '" style="width:';
$value = $this->resolveValue($context->find('width'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= '%; left:';
$value = $this->resolveValue($context->find('offset'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= '%;">
';
$buffer .= $indent . ' <span class="sr-only">';
$value = $this->resolveValue($context->find('sql'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= '</span>
';
$buffer .= $indent . ' </div>
';
$context->pop();
}
}
return $buffer;
}
private function sectionB93c1beb6bc5c41326a972381b0dd94a(Mustache_Context $context, $indent, $value)
{
$buffer = '';
$blocksContext = array();
if (!is_string($value) && is_callable($value)) {
$source = '
<div class="laps-timeline">
{{#queries}}
<div class="event event-{{category}}" title="{{sql}}" style="width:{{width}}%; left:{{offset}}%;">
<span class="sr-only">{{sql}}</span>
</div>
{{/queries}}
</div>
';
$result = call_user_func($value, $source, $this->lambdaHelper);
if (strpos($result, '{{') === false) {
$buffer .= $result;
} else {
$buffer .= $this->mustache
->loadLambda((string) $result)
->renderInternal($context);
}
} elseif (!empty($value)) {
$values = $this->isIterable($value) ? $value : array($value);
foreach ($values as $value) {
$context->push($value);
$buffer .= $indent . '<div class="laps-timeline">
';
// 'queries' section
$value = $context->find('queries');
$buffer .= $this->section372053fedd826e7e297c29f607930cbc($context, $indent, $value);
$buffer .= $indent . '</div>
';
$context->pop();
}
}
return $buffer;
}
private function section26362c0132f6d74333e6154005595603(Mustache_Context $context, $indent, $value)
{
$buffer = '';
$blocksContext = array();
if (!is_string($value) && is_callable($value)) {
$source = '
<div class="event event-{{category}}" title="{{name}} - {{duration}} ms" style="width:{{width}}%; left:{{offset}}%;">
<span class="sr-only">{{name}} - {{duration}} ms</span>
</div>
';
$result = call_user_func($value, $source, $this->lambdaHelper);
if (strpos($result, '{{') === false) {
$buffer .= $result;
} else {
$buffer .= $this->mustache
->loadLambda((string) $result)
->renderInternal($context);
}
} elseif (!empty($value)) {
$values = $this->isIterable($value) ? $value : array($value);
foreach ($values as $value) {
$context->push($value);
$buffer .= $indent . ' <div class="event event-';
$value = $this->resolveValue($context->find('category'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= '" title="';
$value = $this->resolveValue($context->find('name'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= ' - ';
$value = $this->resolveValue($context->find('duration'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= ' ms" style="width:';
$value = $this->resolveValue($context->find('width'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= '%; left:';
$value = $this->resolveValue($context->find('offset'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= '%;">
';
$buffer .= $indent . ' <span class="sr-only">';
$value = $this->resolveValue($context->find('name'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= ' - ';
$value = $this->resolveValue($context->find('duration'), $context);
$buffer .= htmlspecialchars($value, 2, 'UTF-8');
$buffer .= ' ms</span>
';
$buffer .= $indent . ' </div>
';
$context->pop();
}
}
return $buffer;
}
private function section238f05e38d8d133266503e545e18de7e(Mustache_Context $context, $indent, $value)
{
$buffer = '';
$blocksContext = array();
if (!is_string($value) && is_callable($value)) {
$source = '
<div class="laps-timeline">
{{#http}}
<div class="event event-{{category}}" title="{{name}} - {{duration}} ms" style="width:{{width}}%; left:{{offset}}%;">
<span class="sr-only">{{name}} - {{duration}} ms</span>
</div>
{{/http}}
</div>
';
$result = call_user_func($value, $source, $this->lambdaHelper);
if (strpos($result, '{{') === false) {
$buffer .= $result;
} else {
$buffer .= $this->mustache
->loadLambda((string) $result)
->renderInternal($context);
}
} elseif (!empty($value)) {
$values = $this->isIterable($value) ? $value : array($value);
foreach ($values as $value) {
$context->push($value);
$buffer .= $indent . '<div class="laps-timeline">
';
// 'http' section
$value = $context->find('http');
$buffer .= $this->section26362c0132f6d74333e6154005595603($context, $indent, $value);
$buffer .= $indent . '</div>
';
$context->pop();
}
}
return $buffer;
}
}
-27
View File
@@ -1,27 +0,0 @@
<div class="laps-timeline">
{{#events}}
<div class="event event-{{category}}" data-toggle="tooltip" title="{{name}} - {{duration}} ms - {{memory}} MB" style="width:{{width}}%; left:{{offset}}%;">
<span class="sr-only">{{name}} - {{duration}} ms - {{memory}} MB</span>
</div>
{{/events}}
</div>
{{#savequeries}}
<div class="laps-timeline">
{{#queries}}
<div class="event event-{{category}}" title="{{sql}}" style="width:{{width}}%; left:{{offset}}%;">
<span class="sr-only">{{sql}}</span>
</div>
{{/queries}}
</div>
{{/savequeries}}
{{#savehttp}}
<div class="laps-timeline">
{{#http}}
<div class="event event-{{category}}" title="{{name}} - {{duration}} ms" style="width:{{width}}%; left:{{offset}}%;">
<span class="sr-only">{{name}} - {{duration}} ms</span>
</div>
{{/http}}
</div>
{{/savehttp}}