├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── config ├── module.config.php └── zenddevelopertools.local.php.dist ├── src ├── Collector │ ├── AbstractCollector.php │ ├── AutoHideInterface.php │ ├── CollectorInterface.php │ ├── ConfigCollector.php │ ├── DbCollector.php │ ├── EventCollectorInterface.php │ ├── ExceptionCollector.php │ ├── MailCollector.php │ ├── MemoryCollector.php │ ├── RequestCollector.php │ └── TimeCollector.php ├── Controller │ └── IndexController.php ├── EventLogging │ ├── EventContextInterface.php │ └── EventContextProvider.php ├── Exception │ ├── CollectorException.php │ ├── ExceptionInterface.php │ ├── InvalidOptionException.php │ ├── ParameterMissingException.php │ ├── ProfilerException.php │ └── SerializableException.php ├── Listener │ ├── EventLoggingListenerAggregate.php │ ├── FirePhpListener.php │ ├── FlushListener.php │ ├── ProfilerListener.php │ ├── StorageListener.php │ └── ToolbarListener.php ├── MatchInterface.php ├── Module.php ├── Options.php ├── Profiler.php ├── ProfilerEvent.php ├── Report.php ├── ReportInterface.php ├── Storage │ └── StorageInterface.php ├── Stub │ └── ClosureStub.php └── View │ └── Helper │ ├── DetailArray.php │ ├── Memory.php │ └── Time.php └── view └── zend-developer-tools ├── assets └── icons │ ├── glyphish-icons │ ├── LICENSE.txt │ └── small │ │ ├── 162-receipt.png │ │ ├── 18-envelope.png │ │ ├── 184-warning.png │ │ ├── 20-gear2.png │ │ ├── 33-cabinet.png │ │ └── 78-stopwatch.png │ └── wireframe-mono │ ├── LICENSE.txt │ └── small │ └── db.png └── toolbar ├── config.phtml ├── db.phtml ├── error.phtml ├── mail.phtml ├── memory.phtml ├── request.phtml ├── script.phtml ├── style.phtml ├── time.phtml ├── toolbar.css ├── toolbar.js ├── toolbar.phtml └── zendframework.phtml /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file, in reverse chronological order by release. 4 | 5 | ## 2.0.1 - TBD 6 | 7 | ### Added 8 | 9 | - Nothing. 10 | 11 | ### Changed 12 | 13 | - Nothing. 14 | 15 | ### Deprecated 16 | 17 | - Nothing. 18 | 19 | ### Removed 20 | 21 | - Nothing. 22 | 23 | ### Fixed 24 | 25 | - Nothing. 26 | 27 | ## 2.0.0 - 2019-12-26 28 | 29 | ### Added 30 | 31 | - Nothing. 32 | 33 | ### Changed 34 | 35 | - [#200](https://github.com/zendframework/zend-developer-tools/pull/200) changes the `ZendDeveloperTools\Collector\CollectorInterface::collectEvent()` method to typehint its second argument against `Zend\EventManager\EventInterface` instead of `Zend\EventManager\Event`. This allows for any `EventInterface` implementation, instead of artificially restricting to those events that implement the concrete type. As such, any collectors that are implementing that interface MUST be updated to the new signature. 36 | 37 | - [#200](https://github.com/zendframework/zend-developer-tools/pull/200) updates the `MemoryCollector` and `TimeCollector` to the new `CollectorInterface::collectEvent()` signature. 38 | 39 | - [#200](https://github.com/zendframework/zend-developer-tools/pull/200) updates the `EventLoggingListenerAggregate::onCollectEvent()` method to typehint against `Zend\EventManager\EventInterface` instead of `Zend\EventManager\Event`. This change should only affect extensions to the class. 40 | 41 | ### Deprecated 42 | 43 | - Nothing. 44 | 45 | ### Removed 46 | 47 | - Nothing. 48 | 49 | ### Fixed 50 | 51 | - Nothing. 52 | 53 | ## 1.3.0 - 2019-12-26 54 | 55 | ### Added 56 | 57 | - Nothing. 58 | 59 | ### Changed 60 | 61 | - [#261](https://github.com/zendframework/zend-developer-tools/pull/261) swaps in symfony/var-dumper for zendframework/zend-debug, as the latter is now archived and abandoned. 62 | 63 | - [#262](https://github.com/zendframework/zend-developer-tools/pull/262) moves the file `src/Controller/DeveloperToolsController.php` to `src/Controller/IndexController.php`, so that the filename matches the class name it defines. 64 | 65 | - [#262](https://github.com/zendframework/zend-developer-tools/pull/262) moves the file `src/Match/MatchInterface.php` to `src/MatchInterface.php`, so that the filename matches the class name it defines. 66 | 67 | - [#262](https://github.com/zendframework/zend-developer-tools/pull/262) moves the file `test/Collector/ConfigCollectionTest.php` to `src/Collector/ConfigCollectorTest.php`, so that the filename matches the class name it defines. 68 | 69 | ### Deprecated 70 | 71 | - Nothing. 72 | 73 | ### Removed 74 | 75 | - Nothing. 76 | 77 | ### Fixed 78 | 79 | - Nothing. 80 | 81 | ## 1.2.4 - 2019-12-26 82 | 83 | ### Added 84 | 85 | - Nothing. 86 | 87 | ### Changed 88 | 89 | - Nothing. 90 | 91 | ### Deprecated 92 | 93 | - Nothing. 94 | 95 | ### Removed 96 | 97 | - Nothing. 98 | 99 | ### Fixed 100 | 101 | - [#259](https://github.com/zendframework/zend-developer-tools/pull/259) adjusts the casing used when retrieving the "config" service within the `ConfigCollector` to be all lowercase, ensuring it works with all versions of zend-mvc. 102 | 103 | ## 1.2.3 - 2019-03-28 104 | 105 | ### Added 106 | 107 | - Nothing. 108 | 109 | ### Changed 110 | 111 | - Nothing. 112 | 113 | ### Deprecated 114 | 115 | - Nothing. 116 | 117 | ### Removed 118 | 119 | - Nothing. 120 | 121 | ### Fixed 122 | 123 | - Fixes an information disclosure vulnerability; see https://framework.zend.com/security/advisory/ZF2019-01 124 | for more details. 125 | 126 | ## 1.2.2 - 2019-03-05 127 | 128 | ### Added 129 | 130 | - [#253](https://github.com/zendframework/zend-developer-tools/pull/253) and [#254](https://github.com/zendframework/zend-developer-tools/pull/254) add support for PHP 7.3. 131 | 132 | ### Changed 133 | 134 | - Nothing. 135 | 136 | ### Deprecated 137 | 138 | - Nothing. 139 | 140 | ### Removed 141 | 142 | - Nothing. 143 | 144 | ### Fixed 145 | 146 | - Nothing. 147 | 148 | ## 1.2.1 - 2018-04-18 149 | 150 | ### Added 151 | 152 | - Nothing. 153 | 154 | ### Changed 155 | 156 | - [#249](https://github.com/zendframework/zend-developer-tools/pull/249) changes the repository name to "zend-developer-tools", in order to make the name 157 | consistent with other repositories, as well as play nicely with existing tooling. The Packagist 158 | entry has been updated to point to the new repository name, and GitHub auto-redirects any 159 | references to the old name to the new repository, making this a non-BC-breaking change. 160 | 161 | ### Deprecated 162 | 163 | - Nothing. 164 | 165 | ### Removed 166 | 167 | - Nothing. 168 | 169 | ### Fixed 170 | 171 | - [#248](https://github.com/zendframework/zend-developer-tools/pull/248) fixes dependency configuration within the `Module` class to replace 172 | incorrect class and service name references. 173 | 174 | ## 1.2.0 - 2018-04-17 175 | 176 | ### Added 177 | 178 | - [#242](https://github.com/zendframework/zend-developer-tools/pull/242) adds support for PHP 7.1 and 7.2. 179 | 180 | ### Changed 181 | 182 | - [#235](https://github.com/zendframework/zend-developer-tools/pull/235) modifies the module bootstrap to defer retrieval of services until they are needed. 183 | 184 | ### Deprecated 185 | 186 | - Nothing. 187 | 188 | ### Removed 189 | 190 | - Nothing. 191 | 192 | ### Fixed 193 | 194 | - [#240](https://github.com/zendframework/zend-developer-tools/pull/240) fixes an issue with slide-in of the toolbar when resizing the browser window. 195 | 196 | - [#231](https://github.com/zendframework/zend-developer-tools/pull/231) ensures literal `$` characters are escaped within toolbar content. 197 | 198 | ## 1.1.1 - 2016-09-08 199 | 200 | ### Added 201 | 202 | - [#217](https://github.com/zendframework/zend-developer-tools/pull/217) adds 203 | support in the `SerializableException` for PHP 7 Throwables, including Error 204 | types. 205 | - [#220](https://github.com/zendframework/zend-developer-tools/pull/220) adds 206 | support for displaying matched route parameters other than just the controller 207 | and action. 208 | 209 | ### Deprecated 210 | 211 | - Nothing. 212 | 213 | ### Removed 214 | 215 | - Nothing. 216 | 217 | ### Fixed 218 | 219 | - [#215](https://github.com/zendframework/zend-developer-tools/pull/215) replaces 220 | the ZF logo to remove the "2". 221 | - [#218](https://github.com/zendframework/zend-developer-tools/pull/218) updates 222 | the logic for retrieving a zend-db `Adapter` to only do so if `db` 223 | configuration also exists; this ensures the toolbar does not cause a fatal 224 | error if zend-db is installed but no adapter configured. 225 | 226 | ## 1.1.0 - 2016-06-27 227 | 228 | ### Added 229 | 230 | - [#213](https://github.com/zendframework/zend-developer-tools/pull/213) adds 231 | support for zend-mvc, zend-eventmanager, and zend-servicemanager v3. 232 | 233 | ### Deprecated 234 | 235 | - Nothing. 236 | 237 | ### Removed 238 | 239 | - Nothing. 240 | 241 | ### Fixed 242 | 243 | - Nothing. 244 | 245 | ## 1.0.0 - 2016-06-27 246 | 247 | First stable release. 248 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2018, Zend Technologies USA, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | - Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | - Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | - Neither the name of Zend Technologies USA, Inc. nor the names of its 15 | contributors may be used to endorse or promote products derived from this 16 | software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Zend Developer Tools 2 | 3 | > ## Repository abandoned 2019-12-31 4 | > 5 | > This repository has moved to [laminas/laminas-developer-tools](https://github.com/laminas/laminas-developer-tools). 6 | 7 | [![Build Status](https://secure.travis-ci.org/zendframework/zend-developer-tools.svg?branch=master)](https://secure.travis-ci.org/zendframework/zend-developer-tools) 8 | [![Coverage Status](https://coveralls.io/repos/github/zendframework/zend-developer-tools/badge.svg?branch=master)](https://coveralls.io/github/zendframework/zend-developer-tools?branch=master) 9 | 10 | Module providing debug tools for use with [zend-mvc](https://docs.zendframework.com/zend-mvc) applications. 11 | 12 | ## Installation 13 | 14 | 1. Install the module via composer by running: 15 | 16 | ```bash 17 | $ composer require --dev zendframework/zend-developer-tools 18 | ``` 19 | 20 | or download it directly from github and place it in your application's `module/` directory. 21 | 22 | 2. Add the `ZendDeveloperTools` module to the module section of your `config/application.config.php`. 23 | Starting with version 1.1.0, if you are using [zend-component-installer](https://docs.zendframework.com/zend-component-installer), 24 | this will be done for you automatically. 25 | 26 | 3. Copy `./vendor/zendframework/zend-developer-tools/config/zenddevelopertools.local.php.dist` to 27 | `./config/autoload/zenddevelopertools.local.php`. Change any settings in it 28 | according to your needs. 29 | 30 | ## Extensions 31 | 32 | - [BjyProfiler](https://github.com/bjyoungblood/BjyProfiler) - profile `Zend\Db` queries 33 | - [OcraServiceManager](https://github.com/Ocramius/OcraServiceManager) - track dependencies within your application 34 | - [SanSessionToolbar](https://github.com/samsonasik/SanSessionToolbar) - preview `Zend\Session` data 35 | - [ZfSnapEventDebugger](https://github.com/snapshotpl/ZfSnapEventDebugger) - debug events from `Zend\EventManager` 36 | - [DoctrineORMModule](https://github.com/doctrine/DoctrineORMModule) - profile `DoctrineORM` queries 37 | - [JhuZdtLoggerModule](https://github.com/jhuet/JhuZdtLoggerModule) - log data from `Zend\Log` 38 | - [aist-git-tools](https://github.com/ma-si/aist-git-tools) - informations about current GIT repository 39 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zendframework/zend-developer-tools", 3 | "description": "Module for developer and debug tools for use with zend-mvc applications.", 4 | "license": "BSD-3-Clause", 5 | "keywords": [ 6 | "zendframework", 7 | "debug", 8 | "developer", 9 | "zf", 10 | "module" 11 | ], 12 | "support": { 13 | "issues": "https://github.com/zendframework/zend-developer-tools/issues", 14 | "source": "https://github.com/zendframework/zend-developer-tools", 15 | "rss": "https://github.com/zendframework/zend-developer-tools/releases.atom", 16 | "chat": "https://zendframework-slack.herokuapp.com", 17 | "forum": "https://discourse.zendframework.com/c/questions/components" 18 | }, 19 | "require": { 20 | "php": "^5.6 || ^7.0", 21 | "symfony/var-dumper": "^3.4.36 || ^4.4.1 || ^5.0.1", 22 | "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", 23 | "zendframework/zend-modulemanager": "^2.7", 24 | "zendframework/zend-mvc": "^2.7 || ^3.0.1", 25 | "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3", 26 | "zendframework/zend-stdlib": "^2.7 || ^3.0", 27 | "zendframework/zend-view": "^2.6" 28 | }, 29 | "require-dev": { 30 | "phpunit/phpunit": "^5.7.25 || ^6.4.4 || ^7.1", 31 | "zendframework/zend-coding-standard": "~1.0.0" 32 | }, 33 | "suggest": { 34 | "bjyoungblood/bjy-profiler": "Version: dev-master, allows the usage of the (Zend) Db collector.", 35 | "ocramius/ocra-service-manager": "OcraServiceManager can help you track dependencies within your application.", 36 | "san/san-session-toolbar": "SanSessionToolbar can help you see current Zend\\Session data you're using within your application.", 37 | "snapshotpl/zf-snap-event-debugger": "ZfSnapEventDebugger can help you debug events from Zend\\EventManager", 38 | "doctrine/doctrine-orm-module": "Profile DoctrineORM queries", 39 | "jhuet/zdt-logger-module": "Show you log data from Zend\\Log", 40 | "aist/aist-git-tools": "Show you informations about current GIT repository" 41 | }, 42 | "autoload": { 43 | "psr-4": { 44 | "ZendDeveloperTools\\": "src/" 45 | } 46 | }, 47 | "autoload-dev": { 48 | "psr-4": { 49 | "ZendDeveloperToolsTest\\": "test/" 50 | } 51 | }, 52 | "config": { 53 | "sort-packages": true 54 | }, 55 | "extra": { 56 | "branch-alias": { 57 | "dev-master": "2.0.x-dev", 58 | "dev-develop": "2.1.x-dev" 59 | }, 60 | "zf": { 61 | "module": "ZendDeveloperTools" 62 | } 63 | }, 64 | "scripts": { 65 | "check": [ 66 | "@cs-check", 67 | "@test" 68 | ], 69 | "cs-check": "phpcs", 70 | "cs-fix": "phpcbf", 71 | "test": "phpunit --colors=always", 72 | "test-coverage": "phpunit --colors=always --coverage-clover clover.xml" 73 | }, 74 | "authors": [ 75 | { 76 | "name": "Evan Coury", 77 | "email": "me@evancoury.com", 78 | "homepage": "http://blog.evan.pro/" 79 | }, 80 | { 81 | "name": "Eric Boh", 82 | "email": "cossish@gmail.com" 83 | } 84 | ] 85 | } 86 | -------------------------------------------------------------------------------- /config/module.config.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'template_path_stack' => [ 5 | 'zenddevelopertools' => __DIR__ . '/../view', 6 | ], 7 | ], 8 | ]; 9 | -------------------------------------------------------------------------------- /config/zenddevelopertools.local.php.dist: -------------------------------------------------------------------------------- 1 | [ 4 | /** 5 | * General Profiler settings 6 | */ 7 | 'profiler' => [ 8 | /** 9 | * Enables or disables the profiler. 10 | * 11 | * Expects: bool 12 | * Default: true 13 | */ 14 | 'enabled' => true, 15 | 16 | /** 17 | * Enables or disables the strict mode. If the strict mode is enabled, any error will throw an exception, 18 | * otherwise all errors will be added to the report (and shown in the toolbar). 19 | * 20 | * Expects: bool 21 | * Default: true 22 | */ 23 | 'strict' => true, 24 | 25 | /** 26 | * If enabled, the profiler tries to flush the content before the it starts collecting data. This option 27 | * will be ignored if the Toolbar is enabled. 28 | * 29 | * Note: The flush listener listens to the MvcEvent::EVENT_FINISH event with a priority of -9400. You have 30 | * to disable this function if you wish to modify the output with a lower priority. 31 | * 32 | * Expects: bool 33 | * Default: false 34 | */ 35 | 'flush_early' => false, 36 | 37 | /** 38 | * The cache directory is used in the version check and for every storage type that writes to the disk. 39 | * Note: The default value assumes that the current working directory is the application root. 40 | * 41 | * Expects: string 42 | * Default: 'data/cache' 43 | */ 44 | 'cache_dir' => 'data/cache', 45 | 46 | /** 47 | * If a matches is defined, the profiler will be disabled if the request does not match the pattern. 48 | * 49 | * Example: 'matcher' => array('ip' => '127.0.0.1') 50 | * OR 51 | * 'matcher' => array('url' => array('path' => '/admin') 52 | * Note: The matcher is not implemented yet! 53 | */ 54 | 'matcher' => [], 55 | 56 | /** 57 | * Contains a list with all collector the profiler should run. Zend Developer Tools ships with 58 | * 'db' (Zend\Db), 'time', 'event', 'memory', 'exception', 'request' and 'mail' (Zend\Mail). If you wish to 59 | * disable a default collector, simply set the value to null or false. 60 | * 61 | * Example: 'collectors' => array('db' => null) 62 | * Expects: array 63 | */ 64 | 'collectors' => [], 65 | ], 66 | 'events' => [ 67 | /** 68 | * Set to true to enable event-level logging for collectors that will support it. This enables a wildcard 69 | * listener onto the shared event manager that will allow profiling of user-defined events as well as the 70 | * built-in ZF events. 71 | * 72 | * Expects: bool 73 | * Default: false 74 | */ 75 | 'enabled' => true, 76 | 77 | /** 78 | * Contains a list with all event-level collectors that should run. Zend Developer Tools ships with 'time' 79 | * and 'memory'. If you wish to disable a default collector, simply set the value to null or false. 80 | * 81 | * Example: 'collectors' => array('memory' => null) 82 | * Expects: array 83 | */ 84 | 'collectors' => [], 85 | 86 | /** 87 | * Contains event identifiers used with the event listener. Zend Developer Tools defaults to listen to all 88 | * events. If you wish to disable the default all-inclusive identifier, simply set the value to null or 89 | * false. 90 | * 91 | * Example: 'identifiers' => array('all' => null, 'dispatchable' => 'Zend\Stdlib\DispatchableInterface') 92 | * Expects: array 93 | */ 94 | 'identifiers' => [], 95 | ], 96 | /** 97 | * General Toolbar settings 98 | */ 99 | 'toolbar' => [ 100 | /** 101 | * Enables or disables the Toolbar. 102 | * 103 | * Expects: bool 104 | * Default: false 105 | */ 106 | 'enabled' => true, 107 | 108 | /** 109 | * If enabled, every empty collector will be hidden. 110 | * 111 | * Expects: bool 112 | * Default: false 113 | */ 114 | 'auto_hide' => false, 115 | 116 | /** 117 | * The Toolbar position. 118 | * 119 | * Expects: string ('bottom' or 'top') 120 | * Default: bottom 121 | */ 122 | 'position' => 'bottom', 123 | 124 | /** 125 | * If enabled, the Toolbar will check if your current Zend Framework version is up-to-date. 126 | * Note: The check will only occur once every hour. 127 | * 128 | * Expects: bool 129 | * Default: false 130 | */ 131 | 'version_check' => false, 132 | 133 | /** 134 | * Contains a list with all collector toolbar templates. The name of the array key must be same as the name 135 | * of the collector. 136 | * 137 | * Example: 'profiler' => array( 138 | * 'collectors' => array( 139 | * // My_Collector_Example::getName() -> mycollector 140 | * 'MyCollector' => 'My_Collector_Example', 141 | * ) 142 | * ), 143 | * 'toolbar' => array( 144 | * 'entries' => array( 145 | * 'mycollector' => 'example/toolbar/my-collector', 146 | * ) 147 | * ), 148 | * Expects: array 149 | */ 150 | 'entries' => [], 151 | ], 152 | ], 153 | ]; 154 | -------------------------------------------------------------------------------- /src/Collector/AbstractCollector.php: -------------------------------------------------------------------------------- 1 | data); 29 | } 30 | 31 | /** 32 | * @see \Serializable 33 | */ 34 | public function unserialize($data) 35 | { 36 | $this->data = unserialize($data); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Collector/AutoHideInterface.php: -------------------------------------------------------------------------------- 1 | getApplication()) { 57 | return; 58 | } 59 | 60 | $serviceLocator = $application->getServiceManager(); 61 | 62 | if ($serviceLocator->has('config')) { 63 | $this->config = $this->makeArraySerializable($serviceLocator->get('config')); 64 | } 65 | 66 | if ($serviceLocator->has('ApplicationConfig')) { 67 | $this->applicationConfig = $this->makeArraySerializable($serviceLocator->get('ApplicationConfig')); 68 | } 69 | } 70 | 71 | /** 72 | * @return array|null 73 | */ 74 | public function getConfig() 75 | { 76 | return isset($this->config) ? $this->unserializeArray($this->config) : null; 77 | } 78 | 79 | /** 80 | * @return array|null 81 | */ 82 | public function getApplicationConfig() 83 | { 84 | return isset($this->applicationConfig) ? $this->unserializeArray($this->applicationConfig) : null; 85 | } 86 | 87 | /** 88 | * {@inheritDoc} 89 | */ 90 | public function serialize() 91 | { 92 | return serialize(['config' => $this->config, 'applicationConfig' => $this->applicationConfig]); 93 | } 94 | 95 | /** 96 | * {@inheritDoc} 97 | */ 98 | public function unserialize($serialized) 99 | { 100 | $data = unserialize($serialized); 101 | $this->config = $data['config']; 102 | $this->applicationConfig = $data['applicationConfig']; 103 | } 104 | 105 | /** 106 | * Replaces the un-serializable items in an array with stubs 107 | * 108 | * @param array|\Traversable $data 109 | * @return array 110 | */ 111 | private function makeArraySerializable($data) 112 | { 113 | $serializable = []; 114 | 115 | foreach (ArrayUtils::iteratorToArray($data) as $key => $value) { 116 | if ($value instanceof Traversable || is_array($value)) { 117 | $serializable[$key] = $this->makeArraySerializable($value); 118 | continue; 119 | } 120 | 121 | if ($value instanceof Closure) { 122 | $serializable[$key] = new ClosureStub(); 123 | continue; 124 | } 125 | 126 | $serializable[$key] = $value; 127 | } 128 | 129 | return $serializable; 130 | } 131 | 132 | /** 133 | * Opposite of {@see makeArraySerializable} - replaces stubs in an array with actual un-serializable objects 134 | * 135 | * @param array $data 136 | * @return array 137 | */ 138 | private function unserializeArray(array $data) 139 | { 140 | $unserialized = []; 141 | 142 | foreach (ArrayUtils::iteratorToArray($data) as $key => $value) { 143 | if ($value instanceof Traversable || is_array($value)) { 144 | $unserialized[$key] = $this->unserializeArray($value); 145 | continue; 146 | } 147 | 148 | if ($value instanceof ClosureStub) { 149 | $unserialized[$key] = function () { 150 | }; 151 | continue; 152 | } 153 | 154 | $unserialized[$key] = $value; 155 | } 156 | 157 | return $unserialized; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/Collector/DbCollector.php: -------------------------------------------------------------------------------- 1 | profiler)) { 53 | return false; 54 | } 55 | 56 | if ($this->getQueryCount() > 0) { 57 | return false; 58 | } 59 | 60 | return true; 61 | } 62 | 63 | /** 64 | * Has the collector access to Bjy's Db Profiler? 65 | * 66 | * @return bool 67 | */ 68 | public function hasProfiler() 69 | { 70 | return isset($this->profiler); 71 | } 72 | 73 | /** 74 | * Returns Bjy's Db Profiler 75 | * 76 | * @return Profiler 77 | */ 78 | public function getProfiler() 79 | { 80 | return $this->profiler; 81 | } 82 | 83 | /** 84 | * Sets Bjy's Db Profiler 85 | * 86 | * @param Profiler $profiler 87 | * @return self 88 | */ 89 | public function setProfiler(Profiler $profiler) 90 | { 91 | $this->profiler = $profiler; 92 | return $this; 93 | } 94 | 95 | /** 96 | * Returns the number of queries send to the database. 97 | * 98 | * You can use the constants in the Profiler class to specify 99 | * what kind of queries you want to get, e.g. Profiler::INSERT. 100 | * 101 | * @param integer $mode 102 | * @return self 103 | */ 104 | public function getQueryCount($mode = null) 105 | { 106 | return count($this->profiler->getQueryProfiles($mode)); 107 | } 108 | 109 | /** 110 | * Returns the total time the queries took to execute. 111 | * 112 | * You can use the constants in the Profiler class to specify 113 | * what kind of queries you want to get, e.g. Profiler::INSERT. 114 | * 115 | * @param integer $mode 116 | * @return float|integer 117 | */ 118 | public function getQueryTime($mode = null) 119 | { 120 | $time = 0; 121 | 122 | foreach ($this->profiler->getQueryProfiles($mode) as $query) { 123 | $time += $query->getElapsedTime(); 124 | } 125 | 126 | return $time; 127 | } 128 | 129 | /** 130 | * @see Serializable 131 | */ 132 | public function serialize() 133 | { 134 | return serialize($this->profiler); 135 | } 136 | 137 | /** 138 | * @see Serializable 139 | */ 140 | public function unserialize($profiler) 141 | { 142 | $this->profiler = unserialize($profiler); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/Collector/EventCollectorInterface.php: -------------------------------------------------------------------------------- 1 | getError() === Application::ERROR_EXCEPTION) { 41 | $this->data = [ 42 | 'exception' => new SerializableException($mvcEvent->getParam('exception')) 43 | ]; 44 | } 45 | } 46 | 47 | /** 48 | * Checks if an exception was thrown during the runtime. 49 | * 50 | * @return bool 51 | */ 52 | public function hasException() 53 | { 54 | return isset($this->data['exception']); 55 | } 56 | 57 | /** 58 | * Checks if an exception was re-thrown during the runtime. 59 | * 60 | * @return bool 61 | */ 62 | public function hasPreviousException() 63 | { 64 | return (isset($this->data['exception']) && $this->data['exception']->getPrevious() !== null); 65 | } 66 | 67 | /** 68 | * Returns the exception. 69 | * 70 | * @return SerializableException 71 | */ 72 | public function getException() 73 | { 74 | return $this->data['exception']; 75 | } 76 | 77 | /** 78 | * Returns the previous exception. 79 | * 80 | * @return SerializableException 81 | */ 82 | public function getPreviousException() 83 | { 84 | return $this->data['exception']->getPrevious(); 85 | } 86 | 87 | /** 88 | * Returns the exception message. 89 | * 90 | * @return string 91 | */ 92 | public function getMessage() 93 | { 94 | return $this->data['exception']->getMessage(); 95 | } 96 | 97 | /** 98 | * Returns the previous exception message. 99 | * 100 | * @return string 101 | */ 102 | public function getPreviousMessage() 103 | { 104 | return $this->data['exception']->getPrevious()->getMessage(); 105 | } 106 | 107 | /** 108 | * Returns the file in which the exception occurred. 109 | * 110 | * @return string 111 | */ 112 | public function getFile() 113 | { 114 | return $this->data['exception']->getFile(); 115 | } 116 | 117 | /** 118 | * Returns the file in which the previous exception occurred. 119 | * 120 | * @return string 121 | */ 122 | public function getPreviousFile() 123 | { 124 | return $this->data['exception']->getPrevious()->getFile(); 125 | } 126 | 127 | /** 128 | * Returns the exception code. 129 | * 130 | * @return integer 131 | */ 132 | public function getCode() 133 | { 134 | return $this->data['exception']->getCode(); 135 | } 136 | 137 | /** 138 | * Returns the previous exception code. 139 | * 140 | * @return integer 141 | */ 142 | public function getPreviousCode() 143 | { 144 | return $this->data['exception']->getPrevious()->getCode(); 145 | } 146 | 147 | /** 148 | * Returns the exception trace. 149 | * 150 | * @return array 151 | */ 152 | public function getTrace() 153 | { 154 | return $this->data['exception']->getTrace(); 155 | } 156 | 157 | /** 158 | * Returns the previous exception trace. 159 | * 160 | * @return array 161 | */ 162 | public function getPreviousTrace() 163 | { 164 | return $this->data['exception']->getPrevious()->getTrace(); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/Collector/MailCollector.php: -------------------------------------------------------------------------------- 1 | data)) { 40 | $this->data = []; 41 | } 42 | 43 | $this->data['memory'] = memory_get_peak_usage(true); 44 | $this->data['end'] = memory_get_usage(true); 45 | } 46 | 47 | /** 48 | * Saves the current memory usage. 49 | * 50 | * @param string $id 51 | * @param EventInterface $event 52 | */ 53 | public function collectEvent($id, EventInterface $event) 54 | { 55 | $contextProvider = new EventContextProvider($event); 56 | $context['name'] = $contextProvider->getEvent()->getName(); 57 | $context['target'] = $contextProvider->getEventTarget(); 58 | $context['file'] = $contextProvider->getEventTriggerFile(); 59 | $context['line'] = $contextProvider->getEventTriggerLine(); 60 | $context['memory'] = memory_get_usage(true); 61 | 62 | if (! isset($this->data['event'][$id])) { 63 | $this->data['event'][$id] = []; 64 | } 65 | 66 | $this->data['event'][$id][] = $context; 67 | } 68 | 69 | /** 70 | * Returns the used Memory (peak) 71 | * 72 | * @return integer Memory 73 | */ 74 | public function getMemory() 75 | { 76 | return $this->data['memory']; 77 | } 78 | 79 | /** 80 | * Event memory collected? 81 | * 82 | * @return integer Memory 83 | */ 84 | public function hasEventMemory() 85 | { 86 | return isset($this->data['event']); 87 | } 88 | 89 | /** 90 | * Returns the detailed application memory. 91 | * 92 | * @return array 93 | */ 94 | public function getApplicationEventMemory() 95 | { 96 | $result = []; 97 | 98 | if (! isset($this->data['event']['application'])) { 99 | return $result; 100 | } 101 | 102 | $app = $this->data['event']['application']; 103 | 104 | $previous = null; 105 | foreach ($app as $name => $context) { 106 | $result[$name] = $context; 107 | $result[$name]['difference'] = ($previous) 108 | ? ($context['memory'] - $previous['memory']) 109 | : ($context['memory']); 110 | $previous = prev($app); 111 | next($app); 112 | } 113 | 114 | return $result; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Collector/RequestCollector.php: -------------------------------------------------------------------------------- 1 | getRouteMatch(); 42 | $viewModel = $mvcEvent->getViewModel(); 43 | 44 | $addToViewFromModel = function (ModelInterface $child) use (&$views) { 45 | $vars = $child->getVariables(); 46 | 47 | if ($vars instanceof Variables) { 48 | $vars = $vars->getArrayCopy(); 49 | } 50 | $vars = (array) $vars; 51 | 52 | foreach ($vars as $key => &$var) { 53 | $var = $key . ': ' . (is_object($var) ? get_class($var) : gettype($var)); 54 | } 55 | sort($vars); 56 | 57 | $views[] = [ 58 | 'template' => $child->getTemplate(), 59 | 'vars' => $vars, 60 | ]; 61 | }; 62 | 63 | $addToViewFromModel($viewModel); 64 | $this->addChildrenToView($viewModel, $addToViewFromModel); 65 | 66 | $this->data = [ 67 | 'views' => $views, 68 | 'method' => $mvcEvent->getRequest()->getMethod(), 69 | 'status' => $mvcEvent->getResponse()->getStatusCode(), 70 | 'route' => ($match === null) ? 'N/A' : $match->getMatchedRouteName(), 71 | 'action' => ($match === null) ? 'N/A' : $match->getParam('action', 'N/A'), 72 | 'controller' => ($match === null) ? 'N/A' : $match->getParam('controller', 'N/A'), 73 | 'other_route_parameters' => ($match === null) ? 'N/A' : array_filter($match->getParams(), function ($key) { 74 | return ! in_array($key, ['action', 'controller']); 75 | }, ARRAY_FILTER_USE_KEY), 76 | ]; 77 | } 78 | 79 | /** 80 | * @param ModelInterface $viewModel 81 | * @param callable $addToViewFromModel 82 | */ 83 | protected function addChildrenToView(ModelInterface $viewModel, $addToViewFromModel) 84 | { 85 | if (! $viewModel->hasChildren()) { 86 | return; 87 | } 88 | 89 | foreach ($viewModel->getChildren() as $child) { 90 | $addToViewFromModel($child); 91 | $this->addChildrenToView($child, $addToViewFromModel); 92 | } 93 | } 94 | 95 | /** 96 | * Returns the response status code. 97 | * 98 | * @return string 99 | */ 100 | public function getStatusCode() 101 | { 102 | return $this->data['status']; 103 | } 104 | 105 | /** 106 | * Returns the request method 107 | * 108 | * @return string 109 | */ 110 | public function getMethod() 111 | { 112 | return $this->data['method']; 113 | } 114 | 115 | /** 116 | * Returns the matched route name if possible, otherwise N/A. 117 | * 118 | * @return string 119 | */ 120 | public function getRouteName() 121 | { 122 | return $this->data['route']; 123 | } 124 | 125 | /** 126 | * Returns the action name if possible, otherwise N/A. 127 | * 128 | * @return string 129 | */ 130 | public function getActionName() 131 | { 132 | return $this->data['action']; 133 | } 134 | 135 | /** 136 | * Returns the controller name if possible, otherwise N/A. 137 | * 138 | * @return string 139 | */ 140 | public function getControllerName() 141 | { 142 | return $this->data['controller']; 143 | } 144 | 145 | /** 146 | * Returns parameters except controller and actions 147 | * @return array 148 | */ 149 | public function getOtherParameters() 150 | { 151 | return $this->data['other_route_parameters']; 152 | } 153 | 154 | /** 155 | * Returns the controller and action name if possible, otherwise N/A. 156 | * 157 | * @param bool $short 158 | * Removes the namespace. 159 | * @return string 160 | */ 161 | public function getFullControllerName($short = true) 162 | { 163 | $controller = $this->data['controller']; 164 | 165 | if ($short) { 166 | $controller = explode('\\', $controller); 167 | $controller = array_pop($controller); 168 | } 169 | 170 | $return = sprintf('%s::%s', $controller, $this->data['action']); 171 | 172 | if ($return === 'N/A::N/A') { 173 | return 'N/A'; 174 | } 175 | 176 | return $return; 177 | } 178 | 179 | /** 180 | * Returns the template name if possible, otherwise N/A. 181 | * 182 | * @return string 183 | */ 184 | public function getViews() 185 | { 186 | return $this->data['views']; 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/Collector/TimeCollector.php: -------------------------------------------------------------------------------- 1 | marshalStartTime($mvcEvent); 40 | 41 | if (! isset($this->data)) { 42 | $this->data = []; 43 | } 44 | 45 | $this->data['start'] = $start; 46 | $this->data['end'] = microtime(true); 47 | } 48 | 49 | /** 50 | * Saves the current time in microseconds for a specific event. 51 | * 52 | * @param string $id 53 | * @param EventInterface $event 54 | */ 55 | public function collectEvent($id, EventInterface $event) 56 | { 57 | $contextProvider = new EventContextProvider($event); 58 | $context['time'] = microtime(true); 59 | $context['name'] = $contextProvider->getEvent()->getName(); 60 | $context['target'] = $contextProvider->getEventTarget(); 61 | $context['file'] = $contextProvider->getEventTriggerFile(); 62 | $context['line'] = $contextProvider->getEventTriggerLine(); 63 | 64 | if (! isset($this->data['event'][$id])) { 65 | $this->data['event'][$id] = []; 66 | } 67 | 68 | $this->data['event'][$id][] = $context; 69 | } 70 | 71 | /** 72 | * Returns the total execution time. 73 | * 74 | * @return float 75 | */ 76 | public function getStartTime() 77 | { 78 | return $this->data['start']; 79 | } 80 | 81 | /** 82 | * Returns the total execution time. 83 | * 84 | * @return float 85 | */ 86 | public function getExecutionTime() 87 | { 88 | return $this->data['end'] - $this->data['start']; 89 | } 90 | 91 | /** 92 | * Event times collected? 93 | * 94 | * @return bool 95 | */ 96 | public function hasEventTimes() 97 | { 98 | return isset($this->data['event']); 99 | } 100 | 101 | /** 102 | * Returns the detailed application execution time. 103 | * 104 | * @return array 105 | */ 106 | public function getApplicationEventTimes() 107 | { 108 | $result = []; 109 | 110 | if (! isset($this->data['event']['application'])) { 111 | return $result; 112 | } 113 | 114 | $app = $this->data['event']['application']; 115 | 116 | $previous = null; 117 | foreach ($app as $index => $context) { 118 | $result[$index] = $context; 119 | $result[$index]['elapsed'] = ($previous) 120 | ? ($context['time'] - $previous['time']) 121 | : ($context['time'] - $this->data['start']); 122 | $previous = prev($app); 123 | next($app); 124 | } 125 | 126 | return $result; 127 | } 128 | 129 | /** 130 | * Determine the start time 131 | * 132 | * @param MvcEvent $mvcEvent 133 | * @return float 134 | */ 135 | private function marshalStartTime(MvcEvent $mvcEvent) 136 | { 137 | if (PHP_VERSION_ID >= 50400) { 138 | return $mvcEvent->getRequest()->getServer()->get('REQUEST_TIME_FLOAT'); 139 | } 140 | 141 | if (defined('REQUEST_MICROTIME')) { 142 | return REQUEST_MICROTIME; 143 | } 144 | 145 | return $mvcEvent->getRequest()->getServer()->get('REQUEST_TIME'); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/Controller/IndexController.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | interface EventContextInterface 18 | { 19 | /** 20 | * Sets the event. 21 | * 22 | * @return null 23 | */ 24 | public function setEvent(EventInterface $event); 25 | 26 | /** 27 | * Collector Priority. 28 | * 29 | * @return Event 30 | */ 31 | public function getEvent(); 32 | } 33 | -------------------------------------------------------------------------------- /src/EventLogging/EventContextProvider.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | class EventContextProvider implements EventContextInterface 19 | { 20 | /** 21 | * @var EventInterface 22 | */ 23 | protected $event; 24 | 25 | /** 26 | * 27 | * @var array 28 | */ 29 | private $debugBacktrace = []; 30 | 31 | /** 32 | * @param EventInterface $event (Optional) The event to provide context to. The event must be set either here or 33 | * with {@see setEvent()} before any other methods can be used. 34 | */ 35 | public function __construct(EventInterface $event = null) 36 | { 37 | if ($event) { 38 | $this->setEvent($event); 39 | } 40 | } 41 | 42 | /** 43 | * @see \ZendDeveloperTools\EventLogging\EventContextInterface::setEvent() 44 | * @param Event $event The event to add context to. 45 | * @return null 46 | */ 47 | public function setEvent(EventInterface $event) 48 | { 49 | $this->event = $event; 50 | } 51 | 52 | /** 53 | * @see \ZendDeveloperTools\EventLogging\EventContextInterface::getEvent() 54 | * @return Event 55 | */ 56 | public function getEvent() 57 | { 58 | if (! $this->event) { 59 | throw new InvalidArgumentException(sprintf( 60 | '%s: expects an event to have been set.', 61 | __METHOD__ 62 | )); 63 | } 64 | 65 | return $this->event; 66 | } 67 | 68 | /** 69 | * Returns either the class name of the target, or the target string 70 | * 71 | * @return string 72 | */ 73 | public function getEventTarget() 74 | { 75 | $event = $this->getEvent(); 76 | 77 | return $this->getEventTargetAsString($event->getTarget()); 78 | } 79 | 80 | /** 81 | * Determines a string label to represent an event target. 82 | * 83 | * @param mixed $target 84 | * @return String 85 | */ 86 | private function getEventTargetAsString($target) 87 | { 88 | if (is_object($target)) { 89 | return get_class($target); 90 | } 91 | 92 | if (is_resource($target)) { 93 | return get_resource_type($target); 94 | } 95 | 96 | if (is_scalar($target)) { 97 | return (string) $target; 98 | } 99 | 100 | return gettype($target); 101 | } 102 | 103 | /** 104 | * Returns the debug_backtrace() for this object with two levels removed so that array starts where this 105 | * class method was called. 106 | * 107 | * @return string 108 | */ 109 | private function getDebugBacktrace() 110 | { 111 | if (! $this->debugBacktrace) { 112 | //Remove the levels this method introduces 113 | $trace = debug_backtrace(); 114 | $this->debugBacktrace = array_splice($trace, 2); 115 | } 116 | 117 | return $this->debugBacktrace; 118 | } 119 | 120 | /** 121 | * Returns the filename and parent directory of the file from which the event was triggered. 122 | * 123 | * @return string 124 | */ 125 | public function getEventTriggerFile() 126 | { 127 | $backtrace = $this->getDebugBacktrace(); 128 | 129 | if (file_exists($backtrace[4]['file'])) { 130 | return basename(dirname($backtrace[4]['file'])) . '/' . basename($backtrace[4]['file']); 131 | } 132 | 133 | return ''; 134 | } 135 | 136 | /** 137 | * Returns the line number of the file from which the event was triggered. 138 | * 139 | * @return integer 140 | */ 141 | public function getEventTriggerLine() 142 | { 143 | $backtrace = $this->getDebugBacktrace(); 144 | 145 | if (isset($backtrace[4]['line'])) { 146 | return $backtrace[4]['line']; 147 | } 148 | 149 | return ''; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/Exception/CollectorException.php: -------------------------------------------------------------------------------- 1 | data = [ 29 | 'code' => $exception->getCode(), 30 | 'file' => $exception->getFile(), 31 | 'line' => $exception->getLine(), 32 | 'class' => get_class($exception), 33 | 'message' => $exception->getMessage(), 34 | 'previous' => $exception->getPrevious() ? new self($exception->getPrevious()) : null, 35 | 'trace' => $this->filterTrace( 36 | $exception->getTrace(), 37 | $exception->getFile(), 38 | $exception->getLine() 39 | ), 40 | ]; 41 | } 42 | 43 | /** 44 | * @return integer|string 45 | */ 46 | public function getCode() 47 | { 48 | return $this->data['code']; 49 | } 50 | 51 | /** 52 | * @return string 53 | */ 54 | public function getFile() 55 | { 56 | return $this->data['file']; 57 | } 58 | 59 | /** 60 | * @return integer 61 | */ 62 | public function getLine() 63 | { 64 | return $this->data['line']; 65 | } 66 | 67 | /** 68 | * @return array 69 | */ 70 | public function getTrace() 71 | { 72 | return $this->data['trace']; 73 | } 74 | 75 | /** 76 | * @return string 77 | */ 78 | public function getTraceAsString() 79 | { 80 | return implode("\n", $this->data['trace']); 81 | } 82 | 83 | /** 84 | * @return string 85 | */ 86 | public function getMessage() 87 | { 88 | return $this->data['message']; 89 | } 90 | 91 | /** 92 | * @return self|null 93 | */ 94 | public function getPrevious() 95 | { 96 | return $this->data['previous']; 97 | } 98 | 99 | /** 100 | * This function uses code coming from Symfony 2. 101 | * 102 | * @copyright Copyright (c) Fabien Potencier (http://symfony.com/) 103 | * @license http://symfony.com/doc/current/contributing/code/license.html MIT license 104 | * 105 | * @param array $trace 106 | * @param string $file 107 | * @param integer $line 108 | * @return array 109 | */ 110 | protected function filterTrace($trace, $file, $line) 111 | { 112 | $filteredTrace = []; 113 | 114 | $filteredTrace[] = [ 115 | 'namespace' => '', 116 | 'short_class' => '', 117 | 'class' => '', 118 | 'type' => '', 119 | 'function' => '', 120 | 'file' => $file, 121 | 'line' => $line, 122 | 'args' => [], 123 | ]; 124 | 125 | foreach ($trace as $entry) { 126 | $class = ''; 127 | $namespace = ''; 128 | 129 | if (isset($entry['class'])) { 130 | $parts = explode('\\', $entry['class']); 131 | $class = array_pop($parts); 132 | $namespace = implode('\\', $parts); 133 | } 134 | 135 | $filteredTrace[] = [ 136 | 'namespace' => $namespace, 137 | 'short_class' => $class, 138 | 'class' => isset($entry['class']) ? $entry['class'] : '', 139 | 'type' => isset($entry['type']) ? $entry['type'] : '', 140 | 'function' => $entry['function'], 141 | 'file' => isset($entry['file']) ? $entry['file'] : null, 142 | 'line' => isset($entry['line']) ? $entry['line'] : null, 143 | 'args' => isset($entry['args']) ? $this->filterArgs($entry['args']) : [], 144 | ]; 145 | } 146 | 147 | return $filteredTrace; 148 | } 149 | 150 | /** 151 | * This function uses code coming from Symfony 2. 152 | * 153 | * @copyright Copyright (c) Fabien Potencier (http://symfony.com/) 154 | * @license http://symfony.com/doc/current/contributing/code/license.html MIT license 155 | * 156 | * @param array $args 157 | * @param integer $level 158 | * @return array 159 | */ 160 | protected function filterArgs($args, $level = 0) 161 | { 162 | $result = []; 163 | 164 | foreach ($args as $key => $value) { 165 | if (is_object($value)) { 166 | $result[$key] = ['object', get_class($value)]; 167 | continue; 168 | } 169 | 170 | if (is_array($value)) { 171 | if ($level > 10) { 172 | $result[$key] = ['array', '*DEEP NESTED ARRAY*']; 173 | continue; 174 | } 175 | $result[$key] = ['array', $this->filterArgs($value, ++$level)]; 176 | continue; 177 | } 178 | 179 | if (null === $value) { 180 | $result[$key] = ['null', null]; 181 | continue; 182 | } 183 | 184 | if (is_bool($value)) { 185 | $result[$key] = ['boolean', $value]; 186 | continue; 187 | } 188 | 189 | if (is_resource($value)) { 190 | $result[$key] = ['resource', get_resource_type($value)]; 191 | continue; 192 | } 193 | 194 | $result[$key] = ['string', (string) $value]; 195 | } 196 | 197 | return $result; 198 | } 199 | 200 | /** 201 | * @see \Serializable 202 | */ 203 | public function serialize() 204 | { 205 | return serialize($this->data); 206 | } 207 | 208 | /** 209 | * @see \Serializable 210 | */ 211 | public function unserialize($data) 212 | { 213 | $this->data = unserialize($data); 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/Listener/EventLoggingListenerAggregate.php: -------------------------------------------------------------------------------- 1 | 20 | * @since 0.0.3 21 | */ 22 | class EventLoggingListenerAggregate 23 | { 24 | /** 25 | * @var EventCollectorInterface[] 26 | */ 27 | protected $collectors; 28 | 29 | /** 30 | * @var The event identifiers to collect 31 | */ 32 | protected $identifiers; 33 | 34 | /** 35 | * Constructor. 36 | * 37 | * @param EventCollectorInterface[] $collectors 38 | * @param string[] $identifiers 39 | */ 40 | public function __construct(array $collectors, array $identifiers) 41 | { 42 | $this->collectors = array_map( 43 | function (CollectorInterface $collector) { 44 | return $collector; 45 | }, 46 | $collectors 47 | ); 48 | $this->identifiers = array_values(array_map( 49 | function ($identifier) { 50 | return (string) $identifier; 51 | }, 52 | $identifiers 53 | )); 54 | } 55 | 56 | /** 57 | * {@inheritdoc} 58 | */ 59 | public function attachShared(SharedEventManagerInterface $events) 60 | { 61 | foreach ($this->identifiers as $id) { 62 | $events->attach($id, '*', [$this, 'onCollectEvent'], Profiler::PRIORITY_EVENT_COLLECTOR); 63 | } 64 | } 65 | 66 | /** 67 | * {@inheritdoc} 68 | */ 69 | public function detachShared(SharedEventManagerInterface $events) 70 | { 71 | // can't be detached 72 | } 73 | 74 | /** 75 | * Callback to process events 76 | * 77 | * @param EventInterface $event 78 | * @return bool 79 | * @throws ServiceNotFoundException 80 | */ 81 | public function onCollectEvent(EventInterface $event) 82 | { 83 | foreach ($this->collectors as $collector) { 84 | $collector->collectEvent('application', $event); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Listener/FirePhpListener.php: -------------------------------------------------------------------------------- 1 | serviceLocator = $serviceLocator; 37 | } 38 | 39 | /** 40 | * {@inheritdoc} 41 | */ 42 | public function attach(EventManagerInterface $events, $priority = 1) 43 | { 44 | $this->listeners[] = $events->attach( 45 | ProfilerEvent::EVENT_COLLECTED, 46 | [$this, 'onCollected'], 47 | Profiler::PRIORITY_FIREPHP 48 | ); 49 | } 50 | 51 | /** 52 | * ProfilerEvent::EVENT_COLLECTED event callback. 53 | * 54 | * @param ProfilerEvent $event 55 | */ 56 | public function onCollected(ProfilerEvent $event) 57 | { 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Listener/FlushListener.php: -------------------------------------------------------------------------------- 1 | listeners[] = $events->attach( 32 | MvcEvent::EVENT_FINISH, 33 | [$this, 'onFinish'], 34 | Profiler::PRIORITY_FLUSH 35 | ); 36 | } 37 | 38 | /** 39 | * MvcEvent::EVENT_FINISH event callback 40 | * 41 | * @param MvcEvent $event 42 | */ 43 | public function onFinish(MvcEvent $event) 44 | { 45 | $response = $event->getResponse(); 46 | if (! $response instanceof ResponseInterface) { 47 | return; 48 | } 49 | 50 | if (is_callable([$response, 'send'])) { 51 | return $response->send(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Listener/ProfilerListener.php: -------------------------------------------------------------------------------- 1 | options = $options; 48 | $this->serviceLocator = $serviceLocator; 49 | } 50 | 51 | /** 52 | * {@inheritdoc} 53 | */ 54 | public function attach(EventManagerInterface $events, $priority = 1) 55 | { 56 | $this->listeners[] = $events->attach( 57 | MvcEvent::EVENT_FINISH, 58 | [$this, 'onFinish'], 59 | Profiler::PRIORITY_PROFILER 60 | ); 61 | } 62 | 63 | /** 64 | * MvcEvent::EVENT_FINISH event callback 65 | * 66 | * @param MvcEvent $event 67 | * @throws ServiceNotFoundException 68 | */ 69 | public function onFinish(MvcEvent $event) 70 | { 71 | $strict = $this->options->isStrict(); 72 | $collectors = $this->options->getCollectors(); 73 | $report = $this->serviceLocator->get(Report::class); 74 | $profiler = $this->serviceLocator->get(Profiler::class); 75 | 76 | $profiler->setErrorMode($strict); 77 | 78 | foreach ($collectors as $name => $collector) { 79 | if ($this->serviceLocator->has($collector)) { 80 | $profiler->addCollector($this->serviceLocator->get($collector)); 81 | continue; 82 | } 83 | 84 | $error = sprintf('Unable to fetch or create an instance for %s.', $collector); 85 | if ($strict === true) { 86 | throw new ServiceNotFoundException($error); 87 | } 88 | $report->addError($error); 89 | } 90 | 91 | $profiler->collect($event); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/Listener/StorageListener.php: -------------------------------------------------------------------------------- 1 | serviceLocator = $serviceLocator; 37 | } 38 | 39 | /** 40 | * {@inheritdoc} 41 | */ 42 | public function attach(EventManagerInterface $events, $priority = 1) 43 | { 44 | $this->listeners[] = $events->attach( 45 | ProfilerEvent::EVENT_COLLECTED, 46 | [$this, 'onCollected'], 47 | Profiler::PRIORITY_STORAGE 48 | ); 49 | } 50 | 51 | /** 52 | * ProfilerEvent::EVENT_COLLECTED event callback. 53 | * 54 | * @param ProfilerEvent $event 55 | */ 56 | public function onCollected(ProfilerEvent $event) 57 | { 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Listener/ToolbarListener.php: -------------------------------------------------------------------------------- 1 | options = $options; 61 | $this->renderer = $viewRenderer; 62 | } 63 | 64 | /** 65 | * {@inheritdoc} 66 | */ 67 | public function attach(EventManagerInterface $events, $priority = 1) 68 | { 69 | $this->listeners[] = $events->getSharedManager()->attach( 70 | 'profiler', 71 | ProfilerEvent::EVENT_COLLECTED, 72 | [$this, 'onCollected'], 73 | Profiler::PRIORITY_TOOLBAR 74 | ); 75 | } 76 | 77 | /** 78 | * ProfilerEvent::EVENT_COLLECTED event callback. 79 | * 80 | * @param ProfilerEvent $event 81 | */ 82 | public function onCollected(ProfilerEvent $event) 83 | { 84 | $application = $event->getApplication(); 85 | $request = $application->getRequest(); 86 | 87 | if ($request->isXmlHttpRequest()) { 88 | return; 89 | } 90 | 91 | $response = $application->getResponse(); 92 | $headers = $response->getHeaders(); 93 | if ($headers->has('Content-Type') 94 | && false === strpos($headers->get('Content-Type')->getFieldValue(), 'html') 95 | ) { 96 | return; 97 | } 98 | 99 | // todo: X-Debug-Token logic? 100 | // todo: redirect logic 101 | 102 | $this->injectToolbar($event); 103 | } 104 | 105 | /** 106 | * Tries to injects the toolbar into the view. The toolbar is only injected in well 107 | * formed HTML by replacing the closing body tag, leaving ESI untouched. 108 | * 109 | * @param ProfilerEvent $event 110 | */ 111 | protected function injectToolbar(ProfilerEvent $event) 112 | { 113 | $entries = $this->renderEntries($event); 114 | $response = $event->getApplication()->getResponse(); 115 | 116 | $toolbarView = new ViewModel(['entries' => $entries]); 117 | $toolbarView->setTemplate('zend-developer-tools/toolbar/toolbar'); 118 | $toolbar = $this->renderer->render($toolbarView); 119 | 120 | $toolbarCss = new ViewModel([ 121 | 'position' => $this->options->getToolbarPosition(), 122 | ]); 123 | $toolbarCss->setTemplate('zend-developer-tools/toolbar/style'); 124 | $style = $this->renderer->render($toolbarCss); 125 | 126 | $toolbarJs = new ViewModel(); 127 | $toolbarJs->setTemplate('zend-developer-tools/toolbar/script'); 128 | $script = $this->renderer->render($toolbarJs); 129 | 130 | $toolbar = str_replace('$', '\$', $toolbar); 131 | $injected = preg_replace( 132 | '/<\/body>(?![\s\S]*<\/body>)/i', 133 | $toolbar . $script . "\n", 134 | $response->getBody(), 135 | 1 136 | ); 137 | $injected = preg_replace('/<\/head>/i', $style . "\n", $injected, 1); 138 | 139 | $response->setContent($injected); 140 | } 141 | 142 | /** 143 | * Renders all toolbar entries. 144 | * 145 | * @param ProfilerEvent $event 146 | * @return array 147 | * @throws InvalidOptionException 148 | */ 149 | protected function renderEntries(ProfilerEvent $event) 150 | { 151 | $entries = []; 152 | $report = $event->getReport(); 153 | $zfEntry = new ViewModel([ 154 | 'php_version' => phpversion(), 155 | 'has_intl' => extension_loaded('intl'), 156 | 'doc_uri' => self::DOC_URI, 157 | 'modules' => $this->getModules($event), 158 | ]); 159 | $zfEntry->setTemplate('zend-developer-tools/toolbar/zendframework'); 160 | 161 | $entries[] = $this->renderer->render($zfEntry); 162 | $errors = []; 163 | $collectors = $this->options->getCollectors(); 164 | $templates = $this->options->getToolbarEntries(); 165 | 166 | foreach ($templates as $name => $template) { 167 | if (isset($collectors[$name])) { 168 | try { 169 | $collectorInstance = $report->getCollector($name); 170 | 171 | if ($this->options->getToolbarAutoHide() 172 | && $collectorInstance instanceof AutoHideInterface 173 | && $collectorInstance->canHide() 174 | ) { 175 | continue; 176 | } 177 | 178 | $collector = new ViewModel([ 179 | 'report' => $report, 180 | 'collector' => $collectorInstance, 181 | ]); 182 | $collector->setTemplate($template); 183 | $entries[] = $this->renderer->render($collector); 184 | } catch (RuntimeException $e) { 185 | $errors[$name] = $template; 186 | } 187 | } 188 | } 189 | 190 | if (! empty($errors) || $report->hasErrors()) { 191 | $tmp = []; 192 | foreach ($errors as $name => $template) { 193 | $cur = sprintf('Unable to render toolbar template %s (%s).', $name, $template); 194 | $tmp[] = $cur; 195 | $report->addError($cur); 196 | } 197 | 198 | if ($this->options->isStrict()) { 199 | throw new InvalidOptionException(implode(' ', $tmp)); 200 | } 201 | } 202 | 203 | if ($report->hasErrors()) { 204 | $errorTpl = new ViewModel(['errors' => $report->getErrors()]); 205 | $errorTpl->setTemplate('zend-developer-tools/toolbar/error'); 206 | $entries[] = $this->renderer->render($errorTpl); 207 | } 208 | 209 | return $entries; 210 | } 211 | 212 | /** 213 | * Wrapper for Zend\Version::getLatest with caching functionality, so that 214 | * ZendDeveloperTools won't act as a "DDoS bot-network". 215 | * 216 | * @param string $currentVersion 217 | * @return array 218 | */ 219 | protected function getLatestVersion($currentVersion) 220 | { 221 | if (! $this->options->isVersionCheckEnabled()) { 222 | return [true, '']; 223 | } 224 | 225 | $cacheDir = $this->options->getCacheDir(); 226 | 227 | // exit early if the cache dir doesn't exist, 228 | // to prevent hitting the GitHub API for every request. 229 | if (! is_dir($cacheDir)) { 230 | return [true, '']; 231 | } 232 | 233 | if (file_exists($cacheDir . '/ZDT_ZF_Version.cache')) { 234 | $cache = file_get_contents($cacheDir . '/ZDT_ZF_Version.cache'); 235 | $cache = explode('|', $cache); 236 | 237 | if ($cache[0] + self::VERSION_CACHE_TTL > time()) { 238 | // the cache file was written before the version was upgraded. 239 | if ($currentVersion === $cache[2] || $cache[2] === 'N/A') { 240 | return [true, '']; 241 | } 242 | 243 | return [ 244 | ($cache[1] === 'yes') ? true : false, 245 | $cache[2] 246 | ]; 247 | } 248 | } 249 | 250 | $isLatest = Version::isLatest(); 251 | $latest = Version::getLatest(); 252 | 253 | file_put_contents( 254 | $cacheDir . '/ZDT_ZF_Version.cache', 255 | sprintf( 256 | '%d|%s|%s', 257 | time(), 258 | ($isLatest) ? 'yes' : 'no', 259 | ($latest === null) ? 'N/A' : $latest 260 | ) 261 | ); 262 | 263 | return [$isLatest, $latest]; 264 | } 265 | 266 | private function getModules(ProfilerEvent $event) 267 | { 268 | if (! $application = $event->getApplication()) { 269 | return; 270 | } 271 | 272 | $serviceManager = $application->getServiceManager(); 273 | $moduleManager = $serviceManager->get('ModuleManager'); 274 | 275 | return array_keys($moduleManager->getLoadedModules()); 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /src/MatchInterface.php: -------------------------------------------------------------------------------- 1 | getEventManager(); 41 | $eventManager->attach( 42 | ModuleEvent::EVENT_LOAD_MODULES_POST, 43 | [$this, 'onLoadModulesPost'], 44 | -1100 45 | ); 46 | } 47 | 48 | /** 49 | * loadModulesPost callback 50 | * 51 | * @param $event 52 | */ 53 | public function onLoadModulesPost($event) 54 | { 55 | $eventManager = $event->getTarget()->getEventManager(); 56 | $configuration = $event->getConfigListener()->getMergedConfig(false); 57 | 58 | if (isset($configuration['zenddevelopertools']['profiler']['enabled']) 59 | && $configuration['zenddevelopertools']['profiler']['enabled'] === true 60 | ) { 61 | $eventManager->trigger(ProfilerEvent::EVENT_PROFILER_INIT, $event); 62 | } 63 | } 64 | 65 | /** 66 | * Zend\Mvc\MvcEvent::EVENT_BOOTSTRAP event callback 67 | * 68 | * @param EventInterface $event 69 | * @throws Exception\InvalidOptionException 70 | * @throws Exception\ProfilerException 71 | */ 72 | public function onBootstrap(EventInterface $event) 73 | { 74 | if (PHP_SAPI === 'cli') { 75 | return; 76 | } 77 | 78 | $app = $event->getApplication(); 79 | $sm = $app->getServiceManager(); 80 | 81 | $options = $sm->get('ZendDeveloperTools\Config'); 82 | 83 | if (! $options->isToolbarEnabled()) { 84 | return; 85 | } 86 | 87 | $em = $app->getEventManager(); 88 | $report = $sm->get(Report::class); 89 | 90 | if ($options->canFlushEarly()) { 91 | $flushListener = $sm->get('ZendDeveloperTools\FlushListener'); 92 | $flushListener->attach($em); 93 | } 94 | 95 | if ($options->isStrict() && $report->hasErrors()) { 96 | throw new Exception\InvalidOptionException(implode(' ', $report->getErrors())); 97 | } 98 | 99 | if ($options->eventCollectionEnabled()) { 100 | $sem = $em->getSharedManager(); 101 | $eventLoggingListener = $sm->get(Listener\EventLoggingListenerAggregate::class); 102 | $eventLoggingListener->attachShared($sem); 103 | } 104 | 105 | $profilerListener = $sm->get(Listener\ProfilerListener::class); 106 | $profilerListener->attach($em); 107 | 108 | if ($options->isToolbarEnabled()) { 109 | $toolbarListener = $sm->get(Listener\ToolbarListener::class); 110 | $toolbarListener->attach($em); 111 | } 112 | 113 | if ($options->isStrict() && $report->hasErrors()) { 114 | throw new Exception\ProfilerException(implode(' ', $report->getErrors())); 115 | } 116 | } 117 | 118 | /** 119 | * @inheritdoc 120 | */ 121 | public function getConfig() 122 | { 123 | return include __DIR__ . '/../config/module.config.php'; 124 | } 125 | 126 | public function getViewHelperConfig() 127 | { 128 | return [ 129 | 'invokables' => [ 130 | 'ZendDeveloperToolsTime' => View\Helper\Time::class, 131 | 'ZendDeveloperToolsMemory' => View\Helper\Memory::class, 132 | 'ZendDeveloperToolsDetailArray' => View\Helper\DetailArray::class, 133 | ], 134 | ]; 135 | } 136 | 137 | /** 138 | * @inheritdoc 139 | */ 140 | public function getServiceConfig() 141 | { 142 | return [ 143 | 'aliases' => [ 144 | 'ZendDeveloperTools\ReportInterface' => Report::class, 145 | ], 146 | 'invokables' => [ 147 | Report::class => Report::class, 148 | 'ZendDeveloperTools\ExceptionCollector' => Collector\ExceptionCollector::class, 149 | 'ZendDeveloperTools\RequestCollector' => Collector\RequestCollector::class, 150 | 'ZendDeveloperTools\ConfigCollector' => Collector\ConfigCollector::class, 151 | 'ZendDeveloperTools\MailCollector' => Collector\MailCollector::class, 152 | 'ZendDeveloperTools\MemoryCollector' => Collector\MemoryCollector::class, 153 | 'ZendDeveloperTools\TimeCollector' => Collector\TimeCollector::class, 154 | 'ZendDeveloperTools\FlushListener' => Listener\FlushListener::class, 155 | ], 156 | 'factories' => [ 157 | Profiler::class => function ($sm) { 158 | $a = new Profiler($sm->get(Report::class)); 159 | $a->setEvent($sm->get('ZendDeveloperTools\Event')); 160 | return $a; 161 | }, 162 | 'ZendDeveloperTools\Config' => function ($sm) { 163 | $config = $sm->get('Configuration'); 164 | $config = isset($config['zenddevelopertools']) ? $config['zenddevelopertools'] : null; 165 | 166 | return new Options($config, $sm->get(Report::class)); 167 | }, 168 | 'ZendDeveloperTools\Event' => function ($sm) { 169 | $event = new ProfilerEvent(); 170 | $event->setReport($sm->get(Report::class)); 171 | $event->setApplication($sm->get('Application')); 172 | 173 | return $event; 174 | }, 175 | 'ZendDeveloperTools\StorageListener' => function ($sm) { 176 | return new Listener\StorageListener($sm); 177 | }, 178 | Listener\ToolbarListener::class => function ($sm) { 179 | return new Listener\ToolbarListener( 180 | $sm->get('ViewRenderer'), 181 | $sm->get('ZendDeveloperTools\Config') 182 | ); 183 | }, 184 | Listener\ProfilerListener::class => function ($sm) { 185 | return new Listener\ProfilerListener($sm, $sm->get('ZendDeveloperTools\Config')); 186 | }, 187 | Listener\EventLoggingListenerAggregate::class => function ($sm) { 188 | $config = $sm->get('ZendDeveloperTools\Config'); 189 | 190 | return new Listener\EventLoggingListenerAggregate( 191 | array_map([$sm, 'get'], $config->getEventCollectors()), 192 | $config->getEventIdentifiers() 193 | ); 194 | }, 195 | 'ZendDeveloperTools\DbCollector' => function ($sm) { 196 | $p = false; 197 | $db = new Collector\DbCollector(); 198 | 199 | if ($sm->has('Zend\Db\Adapter\Adapter') && isset($sm->get('config')['db'])) { 200 | $adapter = $sm->get('Zend\Db\Adapter\Adapter'); 201 | if ($adapter instanceof ProfilingAdapter) { 202 | $p = true; 203 | $db->setProfiler($adapter->getProfiler()); 204 | } 205 | } 206 | 207 | if (! $p && $sm->has('Zend\Db\Adapter\AdapterInterface') && isset($sm->get('config')['db'])) { 208 | $adapter = $sm->get('Zend\Db\Adapter\AdapterInterface'); 209 | if ($adapter instanceof ProfilingAdapter) { 210 | $p = true; 211 | $db->setProfiler($adapter->getProfiler()); 212 | } 213 | } 214 | 215 | if (! $p && $sm->has('Zend\Db\Adapter\ProfilingAdapter')) { 216 | $adapter = $sm->get('Zend\Db\Adapter\ProfilingAdapter'); 217 | if ($adapter instanceof ProfilingAdapter) { 218 | $db->setProfiler($adapter->getProfiler()); 219 | } 220 | } 221 | 222 | return $db; 223 | }, 224 | ], 225 | ]; 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/Options.php: -------------------------------------------------------------------------------- 1 | false, 27 | 'strict' => true, 28 | 'flush_early' => false, 29 | 'cache_dir' => 'data/cache', 30 | 'matcher' => [], 31 | 'collectors' => [ 32 | 'db' => DbCollector::class, 33 | 'exception' => ExceptionCollector::class, 34 | 'request' => RequestCollector::class, 35 | 'config' => ConfigCollector::class, 36 | 'memory' => MemoryCollector::class, 37 | 'time' => TimeCollector::class, 38 | ], 39 | ]; 40 | 41 | /** 42 | * Defaults for event-level logging 43 | * @var array 44 | */ 45 | protected $events = [ 46 | 'enabled' => false, 47 | 'collectors' => [ 48 | 'memory' => MemoryCollector::class, 49 | 'time' => TimeCollector::class, 50 | ], 51 | 'identifiers' => [ 52 | 'all' => '*' 53 | ] 54 | ]; 55 | 56 | /** 57 | * @var array 58 | */ 59 | protected $toolbar = [ 60 | 'enabled' => false, 61 | 'auto_hide' => false, 62 | 'position' => 'bottom', 63 | 'version_check' => false, 64 | 'entries' => [ 65 | 'request' => 'zend-developer-tools/toolbar/request', 66 | 'time' => 'zend-developer-tools/toolbar/time', 67 | 'memory' => 'zend-developer-tools/toolbar/memory', 68 | 'config' => 'zend-developer-tools/toolbar/config', 69 | 'db' => 'zend-developer-tools/toolbar/db', 70 | ], 71 | ]; 72 | 73 | /** 74 | * Overloading Constructor. 75 | * 76 | * @param array|Traversable|null $options 77 | * @param ReportInterface $report 78 | * @throws \Zend\Stdlib\Exception\InvalidArgumentException 79 | */ 80 | public function __construct($options, ReportInterface $report) 81 | { 82 | $this->report = $report; 83 | 84 | parent::__construct($options); 85 | } 86 | 87 | /** 88 | * Sets Profiler options. 89 | * 90 | * @param array $options 91 | */ 92 | public function setProfiler(array $options) 93 | { 94 | foreach ($options as $key => $value) { 95 | switch ($key) { 96 | case 'enabled': 97 | // fall-through 98 | case 'strict': 99 | // fall-through 100 | case 'flush_early': 101 | $this->profiler[$key] = (bool) $value; 102 | break; 103 | case 'cache_dir': 104 | $this->profiler[$key] = (string) $value; 105 | break; 106 | case 'matcher': 107 | $this->setMatcher($value); 108 | break; 109 | case 'collectors': 110 | $this->setCollectors($value); 111 | break; 112 | default: 113 | // unknown option 114 | break; 115 | } 116 | } 117 | } 118 | 119 | /** 120 | * Sets Event-level profiling options. 121 | * 122 | * @param array $options 123 | */ 124 | public function setEvents(array $options) 125 | { 126 | if (isset($options['enabled'])) { 127 | $this->events['enabled'] = (bool) $options['enabled']; 128 | } 129 | 130 | if (isset($options['collectors'])) { 131 | $this->setEventCollectors($options['collectors']); 132 | } 133 | 134 | if (isset($options['identifiers'])) { 135 | $this->setEventIdentifiers($options['identifiers']); 136 | } 137 | } 138 | 139 | 140 | /** 141 | * Sets Profiler matcher options. 142 | * 143 | * @param array $options 144 | */ 145 | protected function setMatcher($options) 146 | { 147 | if (! is_array($options)) { 148 | $this->report->addError(sprintf( 149 | "['zenddevelopertools']['profiler']['matcher'] must be an array, %s given.", 150 | gettype($options) 151 | )); 152 | return; 153 | } 154 | 155 | $this->profiler['matcher'] = $options; 156 | } 157 | 158 | /** 159 | * Sets Profiler collectors options. 160 | * 161 | * @param array $options 162 | */ 163 | protected function setCollectors($options) 164 | { 165 | if (! is_array($options)) { 166 | $this->report->addError(sprintf( 167 | "['zenddevelopertools']['profiler']['collectors'] must be an array, %s given.", 168 | gettype($options) 169 | )); 170 | return; 171 | } 172 | 173 | foreach ($options as $name => $collector) { 174 | if (($collector === false || $collector === null)) { 175 | unset($this->profiler['collectors'][$name]); 176 | continue; 177 | } 178 | 179 | $this->profiler['collectors'][$name] = $collector; 180 | } 181 | } 182 | 183 | /** 184 | * Is the Profiler enabled? 185 | * 186 | * @return bool 187 | */ 188 | public function isEnabled() 189 | { 190 | return $this->profiler['enabled']; 191 | } 192 | 193 | /** 194 | * Sets Event-level collectors. 195 | * 196 | * @param array $options 197 | */ 198 | public function setEventCollectors(array $options) 199 | { 200 | if (! is_array($options)) { 201 | $this->report->addError(sprintf( 202 | "['zenddevelopertools']['events']['collectors'] must be an array, %s given.", 203 | gettype($options) 204 | )); 205 | return; 206 | } 207 | 208 | foreach ($options as $name => $collector) { 209 | if (($collector === false || $collector === null)) { 210 | unset($this->events['collectors'][$name]); 211 | continue; 212 | } 213 | 214 | $this->events['collectors'][$name] = $collector; 215 | } 216 | } 217 | 218 | /** 219 | * Set Event-level collectors to listen to certain event identifiers. Defaults to '*' which causes the listener to 220 | * attach to all events. 221 | * 222 | * @param array $options 223 | */ 224 | public function setEventIdentifiers(array $options) 225 | { 226 | if (! is_array($options)) { 227 | $this->report->addError(sprintf( 228 | '[\'zenddevelopertools\'][\'events\'][\'identifiers\'] must be an array, %s given.', 229 | gettype($options) 230 | )); 231 | return; 232 | } 233 | 234 | foreach ($options as $name => $identifier) { 235 | if (($identifier === false || $identifier === null)) { 236 | unset($this->events['identifiers'][$name]); 237 | continue; 238 | } 239 | 240 | $this->events['identifiers'][$name] = $identifier; 241 | } 242 | } 243 | 244 | /** 245 | * Is the event-level statistics collection enabled? 246 | * 247 | * @return bool 248 | */ 249 | public function eventCollectionEnabled() 250 | { 251 | return $this->events['enabled']; 252 | } 253 | 254 | /** 255 | * Is strict mode enabled? 256 | * 257 | * @return bool 258 | */ 259 | public function isStrict() 260 | { 261 | return $this->profiler['strict']; 262 | } 263 | 264 | /** 265 | * Is it allowed to flush the page before the collector runs? 266 | * 267 | * Note: Only possible if the toolbar, firephp and the strict mode is 268 | * disabled. 269 | * 270 | * @return bool 271 | */ 272 | public function canFlushEarly() 273 | { 274 | return ($this->profiler['flush_early'] 275 | && ! $this->profiler['strict'] 276 | && ! $this->toolbar['enabled']); 277 | } 278 | 279 | /** 280 | * Returns the cache directory that is used to store the version cache or 281 | * any report storage that writes to the disk. 282 | * 283 | * @return string 284 | */ 285 | public function getCacheDir() 286 | { 287 | return $this->profiler['cache_dir']; 288 | } 289 | 290 | // todo: getter for matcher 291 | 292 | /** 293 | * Returns the collectors. 294 | * 295 | * @return array 296 | */ 297 | public function getCollectors() 298 | { 299 | return $this->profiler['collectors']; 300 | } 301 | 302 | /** 303 | * Returns the event-level collectors. 304 | * 305 | * @return array 306 | */ 307 | public function getEventCollectors() 308 | { 309 | return $this->events['collectors']; 310 | } 311 | 312 | /** 313 | * Returns the event identifiers. 314 | * 315 | * @return array 316 | */ 317 | public function getEventIdentifiers() 318 | { 319 | return $this->events['identifiers']; 320 | } 321 | 322 | 323 | /** 324 | * Sets Toolbar options. 325 | * 326 | * @param array $options 327 | */ 328 | public function setToolbar(array $options) 329 | { 330 | foreach ($options as $key => $value) { 331 | switch ($key) { 332 | case 'enabled': 333 | // fall-through 334 | case 'auto_hide': 335 | // fall-through 336 | case 'version_check': 337 | $this->toolbar[$key] = (bool) $value; 338 | break; 339 | case 'position': 340 | if ($value !== 'bottom' && $value !== 'top') { 341 | $this->report->addError(sprintf( 342 | "['zenddevelopertools']['toolbar']['position'] must be 'top' or 'bottom', %s given.", 343 | $value 344 | )); 345 | break; 346 | } 347 | $this->toolbar[$key] = $value; 348 | break; 349 | case 'entries': 350 | if (! is_array($value)) { 351 | $this->report->addError(sprintf( 352 | "['zenddevelopertools']['toolbar']['entries'] must be an array, %s given.", 353 | gettype($value) 354 | )); 355 | } 356 | 357 | foreach ($value as $collector => $template) { 358 | if ($template === false || $template === null) { 359 | unset($this->toolbar[$key][$collector]); 360 | continue; 361 | } 362 | 363 | $this->toolbar[$key][$collector] = $template; 364 | } 365 | 366 | break; 367 | default: 368 | // Unknown type; ignore 369 | break; 370 | } 371 | } 372 | } 373 | 374 | /** 375 | * Is the Toolbar enabled? 376 | * 377 | * @return bool 378 | */ 379 | public function isToolbarEnabled() 380 | { 381 | return $this->toolbar['enabled']; 382 | } 383 | 384 | /** 385 | * Is the Zend Framework version check enabled? 386 | * 387 | * @return bool 388 | */ 389 | public function isVersionCheckEnabled() 390 | { 391 | return $this->toolbar['version_check']; 392 | } 393 | 394 | /** 395 | * Can hide Toolbar entries? 396 | * 397 | * @return bool 398 | */ 399 | public function getToolbarAutoHide() 400 | { 401 | return $this->toolbar['auto_hide']; 402 | } 403 | 404 | /** 405 | * Returns the Toolbar position. 406 | * 407 | * @return array 408 | */ 409 | public function getToolbarPosition() 410 | { 411 | return $this->toolbar['position']; 412 | } 413 | 414 | /** 415 | * Returns the Toolbar entries. 416 | * 417 | * @return array 418 | */ 419 | public function getToolbarEntries() 420 | { 421 | return $this->toolbar['entries']; 422 | } 423 | } 424 | -------------------------------------------------------------------------------- /src/Profiler.php: -------------------------------------------------------------------------------- 1 | report = $report; 96 | } 97 | 98 | /** 99 | * Set the error mode. 100 | * 101 | * @param bool $mode 102 | * @return self 103 | */ 104 | public function setErrorMode($mode) 105 | { 106 | $this->strict = $mode; 107 | return $this; 108 | } 109 | 110 | /** 111 | * Set the profiler event object. 112 | * 113 | * @param EventInterface $event 114 | * @return self 115 | */ 116 | public function setEvent(EventInterface $event) 117 | { 118 | $this->event = $event; 119 | return $this; 120 | } 121 | 122 | /** 123 | * Returns the profiler event object. 124 | * 125 | * @return self 126 | */ 127 | public function getEvent() 128 | { 129 | if (! isset($this->event)) { 130 | $this->event = new ProfilerEvent(); 131 | $this->event->setTarget($this); 132 | $this->event->setProfiler($this); 133 | } 134 | 135 | return $this->event; 136 | } 137 | 138 | /** 139 | * Set the event manager instance 140 | * 141 | * @param EventManagerInterface $eventManager 142 | * @return self 143 | */ 144 | public function setEventManager(EventManagerInterface $eventManager) 145 | { 146 | $eventManager->addIdentifiers([__CLASS__, get_called_class(), 'profiler']); 147 | $this->eventManager = $eventManager; 148 | 149 | return $this; 150 | } 151 | 152 | /** 153 | * Get the event manager instance 154 | * 155 | * @return EventManagerInterface 156 | */ 157 | public function getEventManager() 158 | { 159 | return $this->eventManager; 160 | } 161 | 162 | /** 163 | * Adds a collector. 164 | * 165 | * @param Collector\CollectorInterface $collector 166 | * @return self 167 | * @throws Exception\CollectorException 168 | */ 169 | public function addCollector($collector) 170 | { 171 | if (! isset($this->collectors)) { 172 | $this->collectors = new PriorityQueue(); 173 | } 174 | 175 | if ($collector instanceof Collector\CollectorInterface) { 176 | $this->collectors->insert($collector, $collector->getPriority()); 177 | return $this; 178 | } 179 | 180 | $error = sprintf('%s must implement CollectorInterface.', get_class($collector)); 181 | if ($this->strict === true) { 182 | throw new Exception\CollectorException($error); 183 | } 184 | $this->report->addError($error); 185 | 186 | return $this; 187 | } 188 | 189 | /** 190 | * Runs all collectors. 191 | * 192 | * @triggers ProfilerEvent::EVENT_COLLECTED 193 | * @param MvcEvent $mvcEvent 194 | * @return Profiler 195 | * @throws Exception\ProfilerException 196 | */ 197 | public function collect(MvcEvent $mvcEvent) 198 | { 199 | $this->report->setToken(uniqid('zdt')) 200 | ->setUri($mvcEvent->getRequest()->getUriString()) 201 | ->setMethod($mvcEvent->getRequest()->getMethod()) 202 | ->setTime(new DateTime('now', new DateTimeZone('UTC'))) 203 | ->setIp($mvcEvent->getRequest()->getServer()->get('REMOTE_ADDR')); 204 | 205 | if (isset($this->collectors)) { 206 | foreach ($this->collectors as $collector) { 207 | $collector->collect($mvcEvent); 208 | $this->report->addCollector($collector); 209 | } 210 | 211 | $event = $this->getEvent(); 212 | $event->setName(ProfilerEvent::EVENT_COLLECTED); 213 | $this->eventManager->triggerEvent($event); 214 | 215 | return $this; 216 | } 217 | 218 | if ($this->strict === true) { 219 | throw new Exception\ProfilerException('There is nothing to collect.'); 220 | } 221 | 222 | $this->report->addError('There is nothing to collect.'); 223 | 224 | return $this; 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /src/ProfilerEvent.php: -------------------------------------------------------------------------------- 1 | getParam('application'); 41 | } 42 | 43 | /** 44 | * Set Application 45 | * 46 | * @param ApplicationInterface $application 47 | * @return self 48 | */ 49 | public function setApplication(ApplicationInterface $application) 50 | { 51 | $this->setParam('application', $application); 52 | return $this; 53 | } 54 | 55 | /** 56 | * Get profiler 57 | * 58 | * @return string 59 | */ 60 | public function getProfiler() 61 | { 62 | return $this->getParam('profiler'); 63 | } 64 | 65 | /** 66 | * Set profiler 67 | * 68 | * @param Profiler $profiler 69 | * @return self 70 | */ 71 | public function setProfiler(Profiler $profiler) 72 | { 73 | $this->setParam('profiler', $profiler); 74 | return $this; 75 | } 76 | 77 | /** 78 | * Get report 79 | * 80 | * @return ReportInterface 81 | */ 82 | public function getReport() 83 | { 84 | return $this->getParam('report'); 85 | } 86 | 87 | /** 88 | * Set report 89 | * 90 | * @param ReportInterface $report 91 | * @return self 92 | */ 93 | public function setReport(ReportInterface $report) 94 | { 95 | $this->setParam('report', $report); 96 | return $this; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Report.php: -------------------------------------------------------------------------------- 1 | ip = $ip; 53 | return $this; 54 | } 55 | 56 | /** 57 | * {@inheritdoc} 58 | */ 59 | public function getIp() 60 | { 61 | return $this->ip; 62 | } 63 | 64 | /** 65 | * {@inheritdoc} 66 | */ 67 | public function setUri($uri) 68 | { 69 | $this->uri = $uri; 70 | return $this; 71 | } 72 | 73 | /** 74 | * {@inheritdoc} 75 | */ 76 | public function getUri() 77 | { 78 | return $this->uri; 79 | } 80 | 81 | /** 82 | * {@inheritdoc} 83 | */ 84 | public function setTime($time) 85 | { 86 | $this->time = $time; 87 | return $this; 88 | } 89 | 90 | /** 91 | * {@inheritdoc} 92 | */ 93 | public function getTime() 94 | { 95 | return $this->time; 96 | } 97 | 98 | /** 99 | * {@inheritdoc} 100 | */ 101 | public function setToken($token) 102 | { 103 | $this->token = $token; 104 | return $this; 105 | } 106 | 107 | /** 108 | * {@inheritdoc} 109 | */ 110 | public function getToken() 111 | { 112 | return $this->token; 113 | } 114 | 115 | /** 116 | * {@inheritdoc} 117 | */ 118 | public function addError($error) 119 | { 120 | if (! isset($this->errors)) { 121 | $this->errors = []; 122 | } 123 | 124 | $this->errors[] = $error; 125 | return $this; 126 | } 127 | 128 | /** 129 | * {@inheritdoc} 130 | */ 131 | public function getErrors() 132 | { 133 | return $this->errors; 134 | } 135 | 136 | /** 137 | * {@inheritdoc} 138 | */ 139 | public function hasErrors() 140 | { 141 | return ! empty($this->errors); 142 | } 143 | 144 | /** 145 | * {@inheritdoc} 146 | */ 147 | public function setMethod($method) 148 | { 149 | $this->method = $method; 150 | return $this; 151 | } 152 | 153 | /** 154 | * {@inheritdoc} 155 | */ 156 | public function getMethod() 157 | { 158 | return $this->method; 159 | } 160 | 161 | /** 162 | * {@inheritdoc} 163 | */ 164 | public function hasCollector($name) 165 | { 166 | return isset($this->collectors[$name]); 167 | } 168 | 169 | /** 170 | * {@inheritdoc} 171 | */ 172 | public function getCollector($name) 173 | { 174 | if (isset($this->collectors[$name])) { 175 | return $this->collectors[$name]; 176 | } 177 | } 178 | 179 | /** 180 | * {@inheritdoc} 181 | */ 182 | public function getCollectors() 183 | { 184 | return $this->collectors; 185 | } 186 | 187 | /** 188 | * {@inheritdoc} 189 | */ 190 | public function getCollectorNames() 191 | { 192 | return array_keys($this->collectors); 193 | } 194 | 195 | /** 196 | * {@inheritdoc} 197 | */ 198 | public function setCollectors(array $collectors) 199 | { 200 | foreach ($collectors as $collector) { 201 | $this->addCollector($collector); 202 | } 203 | 204 | return $this; 205 | } 206 | 207 | /** 208 | * {@inheritdoc} 209 | */ 210 | public function addCollector(Collector\CollectorInterface $collector) 211 | { 212 | $this->collectors[$collector->getName()] = $collector; 213 | } 214 | 215 | /** 216 | * @return array 217 | */ 218 | public function __sleep() 219 | { 220 | return ['ip', 'uri', 'time', 'token', 'errors', 'method', 'collectors']; 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/ReportInterface.php: -------------------------------------------------------------------------------- 1 | '; 29 | 30 | $r[] = ''; 31 | $r[] = $label; 32 | $r[] = ''; 33 | 34 | 35 | $extraCss = ''; 36 | $newLine = false; 37 | 38 | foreach ($details as $entry) { 39 | if ($newLine === true) { 40 | $r[] = ''; 43 | } 44 | 45 | $r[] = sprintf('%s', $extraCss, $entry); 46 | 47 | $newLine = true; 48 | $extraCss = ' zdt-detail-extra-value'; 49 | } 50 | 51 | $r[] = ''; 52 | 53 | return implode('', $r); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/View/Helper/Memory.php: -------------------------------------------------------------------------------- 1 | = 1) { 28 | return sprintf('%.' . $precision . 'f s', $time); 29 | } 30 | 31 | if ($time * 1000 >= 1) { 32 | return sprintf('%.' . $precision . 'f ms', $time * 1000); 33 | } 34 | 35 | return sprintf('%.' . $precision . 'f µs', $time * 1000000); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /view/zend-developer-tools/assets/icons/glyphish-icons/LICENSE.txt: -------------------------------------------------------------------------------- 1 | GLYPHISH ICONS FREE - 200 icons 2 | Created by Joseph Wain, 2010 – 2012 3 | Web: http://glyphish.com or http://penandthink.com 4 | Twitter: @glyphish or @jpwain 5 | 6 | Created by Joseph Wain and downloaded from http://glyphish.com 7 | 8 | This work is licensed under the Creative Commons Attribution 3.0 United States License. To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/us/ 9 | 10 | You are free to share and to remix it remix under the following conditions: 11 | 12 | * You must attribute the work in the manner specified by the author (SEE BELOW). 13 | 14 | * For any reuse or distribution, you must make clear to others the license terms of this work. 15 | 16 | * The above conditions can be waived if you get permission from the copyright holder 17 | (send me an email to discuss). 18 | 19 | You're free to use these icons for commercial and non-commercial purposes, for yourself, your company and your clients, and to edit, remix and otherwise modify them, as long as clear attribution is provided. You may not sell or redistribute the icons themselves as icons. 20 | 21 | Additionally, you may not use them in a way that encourages downstream distribution -- no templates or skins or theme kits or similar uses, no app-building tools or similar. (The person using the theme or template might not know where the icons came from and thus wouldn't be following the license.) This specifically prohibits the use in any kind of "app builder" tool or platform, especially where the icons are provided as choices to people using the tool to create apps. 22 | 23 | ATTRIBUTION -- A note on your app's website, like "Icons by Glyphish" or similar, plus a link back to glyphish.com, is the preferred form of attribution. For questions about attribution, contact me via the Glyphish website. 24 | 25 | USE WITHOUT ATTRIBUTION -- If attribution is not possible, workable or desirable for your application, get in touch to discuss other options. 26 | 27 | OMG! GET TONS MORE ICONS! -- Get Glyphish Pro! 400 icons, each in two sizes and two colors, totally awesome.. Available from http://glyphish.com, or purchase directly here: http://pul.ly/b/19959 -------------------------------------------------------------------------------- /view/zend-developer-tools/assets/icons/glyphish-icons/small/162-receipt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendframework/zend-developer-tools/a84a9d98374311203567025ab729a2e46f11c622/view/zend-developer-tools/assets/icons/glyphish-icons/small/162-receipt.png -------------------------------------------------------------------------------- /view/zend-developer-tools/assets/icons/glyphish-icons/small/18-envelope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendframework/zend-developer-tools/a84a9d98374311203567025ab729a2e46f11c622/view/zend-developer-tools/assets/icons/glyphish-icons/small/18-envelope.png -------------------------------------------------------------------------------- /view/zend-developer-tools/assets/icons/glyphish-icons/small/184-warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendframework/zend-developer-tools/a84a9d98374311203567025ab729a2e46f11c622/view/zend-developer-tools/assets/icons/glyphish-icons/small/184-warning.png -------------------------------------------------------------------------------- /view/zend-developer-tools/assets/icons/glyphish-icons/small/20-gear2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendframework/zend-developer-tools/a84a9d98374311203567025ab729a2e46f11c622/view/zend-developer-tools/assets/icons/glyphish-icons/small/20-gear2.png -------------------------------------------------------------------------------- /view/zend-developer-tools/assets/icons/glyphish-icons/small/33-cabinet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendframework/zend-developer-tools/a84a9d98374311203567025ab729a2e46f11c622/view/zend-developer-tools/assets/icons/glyphish-icons/small/33-cabinet.png -------------------------------------------------------------------------------- /view/zend-developer-tools/assets/icons/glyphish-icons/small/78-stopwatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendframework/zend-developer-tools/a84a9d98374311203567025ab729a2e46f11c622/view/zend-developer-tools/assets/icons/glyphish-icons/small/78-stopwatch.png -------------------------------------------------------------------------------- /view/zend-developer-tools/assets/icons/wireframe-mono/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright Gentleface [http://gentleface.com/]. All rights reserved. 2 | 3 | Creative Commons Attribution Non-commercial (by-nc) 4 | 5 | Allows for use in wireframes, on personal and community web sites, in free and open source software applications and other types of non-commercial usage. The set includes transparent PNG versions of the original Gentleface Mono Icon Set in black and white colors in 16x16, 32x32, 48x48 sizes. 6 | Read license terms here [http://creativecommons.org/licenses/by-nc/3.0/]. For clarification of non commercial usage contacting us [design@gentleface.com]. -------------------------------------------------------------------------------- /view/zend-developer-tools/assets/icons/wireframe-mono/small/db.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zendframework/zend-developer-tools/a84a9d98374311203567025ab729a2e46f11c622/view/zend-developer-tools/assets/icons/wireframe-mono/small/db.png -------------------------------------------------------------------------------- /view/zend-developer-tools/toolbar/config.phtml: -------------------------------------------------------------------------------- 1 |
2 |
3 | Config 4 | Config 5 |
6 |
7 | 8 | Merged Config (Config) 9 | 10 | 11 | collector->getConfig()); ?> 12 | 13 |
14 |
15 |
16 |
17 | Application Config 18 | ApplicationConfig 19 |
20 |
21 | 22 | Application Config (ApplicationConfig) 23 | 24 | 25 | collector->getApplicationConfig()); ?> 26 | 27 |
28 |
29 | -------------------------------------------------------------------------------- /view/zend-developer-tools/toolbar/db.phtml: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | Database (Zend\Db) 5 | 6 | collector->hasProfiler()): ?> 7 | collector->getQueryCount(); ?> in ZendDeveloperToolsTime($this->collector->getQueryTime()); ?> 8 | 9 | N/A 10 | 11 | 12 |
13 |
14 |
15 | collector->hasProfiler()): ?> 16 | Quantity 17 | 18 | create 19 | collector->getQueryCount(Profiler::INSERT); ?> 20 | 21 | 22 | read 23 | collector->getQueryCount(Profiler::SELECT); ?> 24 | 25 | 26 | update 27 | collector->getQueryCount(Profiler::UPDATE); ?> 28 | 29 | 30 | delete 31 | collector->getQueryCount(Profiler::DELETE); ?> 32 | 33 | 34 | Time 35 | 36 | create 37 | ZendDeveloperToolsTime($this->collector->getQueryTime(Profiler::INSERT)); ?> 38 | 39 | 40 | read 41 | ZendDeveloperToolsTime($this->collector->getQueryTime(Profiler::SELECT)); ?> 42 | 43 | 44 | update 45 | ZendDeveloperToolsTime($this->collector->getQueryTime(Profiler::UPDATE)); ?> 46 | 47 | 48 | delete 49 | ZendDeveloperToolsTime($this->collector->getQueryTime(Profiler::DELETE)); ?> 50 | 51 | 52 | Query Profiles 53 | 54 | collector->getProfiler(); ?> 55 | getQueryProfiles() as $profile): ?> 56 | toArray(); ?> 57 |
58 | SQL 59 | 60 | 61 | $1', $this->escapeHtml($query['sql'])) ?> 62 |
63 | 64 | 0): ?> 65 | Params 66 | 67 | $value): ?> 68 | escapeHtml("{$key} => " . var_export($value, true)); ?>
69 | 70 |
71 | 72 | 73 | Time 74 | 75 | escapeHtml($this->ZendDeveloperToolsTime($query['elapsed'])) ?> 76 | 77 | 78 | 79 | 80 | 81 | Error 82 | 83 | You have to install or enable @bjyoungblood's Zend\Db Profiler to use this feature. 84 | 85 | 86 |
87 |
88 |
89 | 126 | -------------------------------------------------------------------------------- /view/zend-developer-tools/toolbar/error.phtml: -------------------------------------------------------------------------------- 1 |
2 |
3 | Profiler Errors 4 | 5 | errors); ?> 6 | 7 |
8 |
9 | Errors 10 | errors as $i => $error): ?> 11 | 12 | . 13 | 14 | 15 |
16 |
-------------------------------------------------------------------------------- /view/zend-developer-tools/toolbar/mail.phtml: -------------------------------------------------------------------------------- 1 |
2 |
3 | Mail (Zend\Mail) 4 | collector->getMailsSend(); ?> 5 |
6 |
7 | 8 | Mails send 9 | collector->getMailsSend(); ?> 10 | 11 |
12 |
-------------------------------------------------------------------------------- /view/zend-developer-tools/toolbar/memory.phtml: -------------------------------------------------------------------------------- 1 |
2 |
3 | Memory Peak 4 | 5 | ZendDeveloperToolsMemory($this->collector->getMemory()); ?> 6 | 7 |
8 |
9 | collector->hasEventMemory()): ?> 10 | collector->getApplicationEventMemory() as $key => $value): ?> 11 | 12 | 13 | ZendDeveloperToolsMemory($value['difference']); ?> 14 |

15 |
16 | 17 | 18 | 19 | Memory Peak 20 | 21 | ZendDeveloperToolsMemory($this->collector->getMemory()); ?> 22 | 23 | 24 |
25 |
-------------------------------------------------------------------------------- /view/zend-developer-tools/toolbar/request.phtml: -------------------------------------------------------------------------------- 1 | collector->getStatusCode(); 3 | $color = ($statusCode < 400) 4 | ? ($statusCode === 200) ? 'green' : 'yellow' 5 | : 'red'; 6 | ?> 7 |
8 |
9 | Request and Response 10 | 11 | 12 | 13 | 14 | 15 | collector->getFullControllerName(); ?> 16 | on collector->getRouteName(); ?> 17 | 18 | 19 |
20 |
21 | 22 | Status code 23 | 24 | 25 | 26 | 27 | 28 | Method 29 | collector->getMethod(); ?> 30 | 31 | 32 | Controller 33 | collector->getControllerName(); ?> 34 | 35 | 36 | Action 37 | collector->getActionName(); ?> 38 | 39 | 40 | Other Route Parameters 41 | collector->getOtherParameters() ); ?> 42 | 43 | 44 | Route 45 | collector->getRouteName(); ?> 46 | 47 | collector->getViews() as $value): ?> 48 | 49 | Template 50 | 51 |

    ', $value['vars']); ?>

52 |
53 | 54 |
55 |
56 | -------------------------------------------------------------------------------- /view/zend-developer-tools/toolbar/script.phtml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /view/zend-developer-tools/toolbar/style.phtml: -------------------------------------------------------------------------------- 1 | 2 | 28 | 29 | -------------------------------------------------------------------------------- /view/zend-developer-tools/toolbar/time.phtml: -------------------------------------------------------------------------------- 1 |
2 |
3 | Execution Time 4 | 5 | ZendDeveloperToolsTime($this->collector->getExecutionTime()); ?> 6 | 7 |
8 |
9 | collector->hasEventTimes()): ?> 10 | collector->getApplicationEventTimes() as $key => $value): ?> 11 | 12 | 13 | ZendDeveloperToolsTime($value['elapsed']); ?> 14 |
Target: %s', $value['file'], $value['line'], $value['target']); ?>
15 |
16 | 17 | 18 | 19 | Total Time 20 | 21 | ZendDeveloperToolsTime($this->collector->getExecutionTime()); ?> 22 | 23 | 24 |
25 |
-------------------------------------------------------------------------------- /view/zend-developer-tools/toolbar/toolbar.css: -------------------------------------------------------------------------------- 1 | #zend-developer-toolbar { 2 | left: 0; 3 | bottom: 0; 4 | width: 100%; 5 | height: 40px; 6 | color: #eee; 7 | z-index: 1337; 8 | position: fixed; 9 | border-top: 1px solid #111; 10 | background: #2C2C2C; 11 | background: -moz-linear-gradient(to bottom, #333, #222); 12 | background: -ms-linear-gradient(to bottom, #333, #222); 13 | background: -webkit-gradient(linear, 0 0, 0 100%, from(#333), to(#222)); 14 | background: -webkit-linear-gradient(to bottom, 333, #222); 15 | background: -o-linear-gradient(to bottom, #333, #222); 16 | background: linear-gradient(to bottom, #333, #222); 17 | font: normal 13px/40px 'Helvetica Neue', Helvetica, Arial, sans-serif; 18 | direction: ltr; 19 | text-align: left; 20 | } 21 | 22 | #zend-developer-toolbar a { 23 | color: #80dc09; 24 | text-decoration: none; 25 | } 26 | 27 | #zend-developer-toolbar a:hover { 28 | text-decoration: underline; 29 | } 30 | 31 | .zdt-toolbar-bold { 32 | font-weight: bold; 33 | } 34 | 35 | .zdt-toolbar-entry,.zdt-toolbar-detail { 36 | float: left; 37 | display: block; 38 | padding: 0 8px; 39 | background: transparent; 40 | } 41 | 42 | .zdt-toolbar-info { 43 | vertical-align: middle; 44 | } 45 | 46 | .zdt-toolbar-info abbr { 47 | border: 0; 48 | font-size: 100%; 49 | cursor: pointer; 50 | text-transform: none; 51 | } 52 | 53 | .zdt-toolbar-detail .zdt-toolbar-info { 54 | clear: both; 55 | display: block; 56 | max-width: 300px; 57 | white-space: normal; 58 | } 59 | 60 | .zdt-toolbar-detail .zdt-toolbar-info-heading { 61 | color: #80dc09; 62 | font-weight: bold; 63 | } 64 | 65 | .zdt-toolbar-detail .zdt-toolbar-info-heading:before { 66 | content: '» '; 67 | } 68 | 69 | .zdt-toolbar-info .zdt-toolbar-spacing { 70 | padding: 0; 71 | margin-right: 7px; 72 | } 73 | 74 | .zdt-toolbar-detail .zdt-toolbar-spacing { 75 | padding-bottom: 15px; 76 | } 77 | 78 | .zdt-toolbar-detail .zdt-toolbar-topspacing { 79 | padding-top: 15px; 80 | } 81 | 82 | .zdt-toolbar-detail { 83 | bottom: 39px; 84 | display: none; 85 | line-height: 20px; 86 | padding: 7px 10px; 87 | position: absolute; 88 | -webkit-border-top-left-radius: 5px; 89 | -webkit-border-top-right-radius: 5px; 90 | -moz-border-radius-topleft: 5px; 91 | -moz-border-radius-topright: 5px; 92 | border-top-left-radius: 5px; 93 | border-top-right-radius: 5px; 94 | } 95 | 96 | .zdt-toolbar-detail:before,.zdt-toolbar-detail:after { 97 | content: ''; 98 | width: 0; 99 | height: 0; 100 | left: 14px; 101 | bottom: -10px; 102 | position: absolute; 103 | border-top: 10px solid #68B604; 104 | border-left: 8px solid transparent; 105 | border-right: 8px solid transparent; 106 | } 107 | 108 | .zdt-toolbar-detail-overflow { 109 | overflow-y: auto !important; 110 | overflow-x: hidden; 111 | max-height: 475px; 112 | } 113 | 114 | .zdt-toolbar-detail-overflow:before, .zdt-toolbar-detail-overflow:after { 115 | display: none; 116 | } 117 | 118 | .zdt-toolbar-detail-redundant { 119 | display: none; 120 | opacity: 0; 121 | } 122 | 123 | .zdt-toolbar-detail .zdt-toolbar-info-redundant,.zdt-toolbar-detail .zdt-toolbar-extra-info-redundant 124 | { 125 | display: none; 126 | } 127 | 128 | .zdt-toolbar-preview img { 129 | border: 0; 130 | width: auto; 131 | display: inline; 132 | margin-right: 5px; 133 | vertical-align: middle; 134 | } 135 | 136 | .zdt-toolbar-entry:hover .zdt-toolbar-detail { 137 | display: block; 138 | background: #333; 139 | box-shadow: rgba(0, 0, 0, 0.75) 0 0 7px; 140 | } 141 | 142 | .zdt-toolbar-info .zdt-detail-label { 143 | float: left; 144 | display: block; 145 | text-align: left; 146 | min-width: 100px; 147 | font-weight: bold; 148 | text-transform: capitalize; 149 | } 150 | 151 | .zdt-toolbar-info .zdt-detail-context { 152 | text-align: left; 153 | clear: both; 154 | min-width: 100px; 155 | color: #aaaaaa; 156 | font-size: 9px; 157 | margin-bottom: 3px; 158 | line-height: 10px; 159 | white-space: nowrap; 160 | } 161 | 162 | .zdt-toolbar-detail-overflow .zdt-detail-context { 163 | padding-right: 15px; 164 | } 165 | 166 | .zdt-toolbar-info .zdt-detail-label:after { 167 | content: ':'; 168 | } 169 | 170 | .zdt-toolbar-info .zdt-detail-value { 171 | float: left; 172 | display: block; 173 | white-space: nowrap; 174 | } 175 | 176 | .zdt-toolbar-info .zdt-detail-pre { 177 | display: block; 178 | font-family: monospace; 179 | overflow-y: auto !important; 180 | overflow-x: hidden; 181 | line-height: 15px; 182 | max-height: 400px; 183 | unicode-bidi: embed; 184 | white-space: pre-wrap; 185 | word-break: break-all; 186 | word-wrap: break-word; 187 | } 188 | 189 | .zdt-toolbar-info .zdt-detail-extra-value { 190 | margin-left: 100px; 191 | } 192 | 193 | .zdt-toolbar-info .zdt-detail-value-right { 194 | float: right; 195 | } 196 | 197 | .zdt-toolbar-info .zdt-toolbar-status { 198 | color: #fff; 199 | padding: 0 3px; 200 | text-align: center; 201 | border-radius: 5px; 202 | display: inline-block; 203 | } 204 | 205 | .zdt-toolbar-status-green { 206 | background: #558f0b; 207 | } 208 | 209 | .zdt-toolbar-status-red { 210 | background: #8f2727; 211 | } 212 | 213 | .zdt-toolbar-color-green { 214 | color: #80dc09; 215 | } 216 | 217 | .zdt-toolbar-color-red { 218 | color: #e83535; 219 | } 220 | 221 | .zdt-toolbar-color-yellow { 222 | color: #d9cd0f; 223 | } 224 | 225 | .zdt-toolbar-color-grey { 226 | color: #9d9d9d; 227 | } 228 | 229 | #zend-developer-toolbar .zdf-toolbar-hide-button { 230 | top: 0; 231 | right: 8px; 232 | color: #bebebe; 233 | display: block; 234 | cursor: pointer; 235 | position: absolute; 236 | text-decoration: none; 237 | } 238 | 239 | #zend-developer-toolbar .zdf-toolbar-hide-button:hover { 240 | color: #eb4a4a; 241 | text-decoration: none; 242 | } 243 | 244 | #zend-developer-toolbar .zdt-toolbar-entry .zdt-toolbar-detail,#zend-developer-toolbar .zdt-toolbar-entry .zdt-toolbar-info { 245 | font-size: 11px; 246 | max-width: 700px; 247 | } 248 | 249 | #zend-developer-toolbar .zdt-toolbar-info .zdt-detail-value pre { 250 | max-height: 350px; 251 | max-width: 650px; 252 | overflow-y: scroll; 253 | overflow-x: scroll; 254 | display: inline-block; 255 | white-space: pre; 256 | word-break: keep-all; 257 | word-wrap: normal; 258 | } 259 | 260 | @media screen and (max-width: 780px) { 261 | .zdt-toolbar-preview .zdt-toolbar-extra-info { 262 | display: none; 263 | } 264 | .zdt-toolbar-entry:hover .zdt-toolbar-extra-info-redundant { 265 | display: block; 266 | } 267 | .zdt-toolbar-preview .zdt-toolbar-spacing { 268 | margin: 0; 269 | } 270 | } 271 | 272 | @media screen and (max-width: 600px) { 273 | .zdt-toolbar-preview .zdt-toolbar-info { 274 | display: none; 275 | } 276 | .zdt-toolbar-entry:hover .zdt-toolbar-detail-redundant { 277 | display: block; 278 | opacity: 1; 279 | } 280 | .zdt-toolbar-entry:hover .zdt-toolbar-info-redundant { 281 | display: block; 282 | } 283 | } 284 | 285 | @media screen and (max-width: 420px) { 286 | .zdt-toolbar-preview img { 287 | margin: 0; 288 | } 289 | } 290 | 291 | @media print { 292 | #zend-developer-toolbar { 293 | display: none; 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /view/zend-developer-tools/toolbar/toolbar.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | /** 4 | * @param {Cookie} cookie 5 | * @returns {Toolbar} 6 | * @constructor 7 | */ 8 | var Toolbar = function(cookie) { 9 | /** @type {Toolbar} */ 10 | var self = this; 11 | /** @type {HTMLElement} */ 12 | var container = document.getElementById("zend-developer-toolbar"); 13 | /** @type {number} */ 14 | var width = container.offsetWidth; 15 | /** @type {number} */ 16 | var windowWidthDifference = window.innerWidth - width; 17 | /** @type {HTMLElement} */ 18 | var toggleTrigger = document.getElementById("zdf-toolbar-toggle"); 19 | /** @type {boolean} */ 20 | var hidden; 21 | /** @type {string} */ 22 | var cookieKeyHidden = "zdt-hidden"; 23 | /** @type {number} */ 24 | var widthHiddenState = 25; 25 | 26 | var timer; 27 | 28 | self.toggle = function() { 29 | !self.isHidden() ? self.hide() : self.show(); 30 | }; 31 | 32 | /** 33 | * @returns {boolean} 34 | * @throws {Error} 35 | */ 36 | self.isHidden = function() { 37 | if (typeof(hidden) == "undefined") { 38 | throw new Error("Field 'hidden' didn't initialize."); 39 | } 40 | 41 | return hidden; 42 | }; 43 | 44 | self.hide = function() { 45 | slide((widthHiddenState - width)); 46 | 47 | toggleTrigger.innerHTML = "►"; 48 | toggleTrigger.setAttribute("title", "Show Toolbar"); 49 | hidden = true; 50 | 51 | cookie.set(cookieKeyHidden, 1); 52 | }; 53 | 54 | self.show = function() { 55 | slide(0); 56 | 57 | toggleTrigger.innerHTML = "◄"; 58 | toggleTrigger.setAttribute("title", "Hide Toolbar"); 59 | hidden = false; 60 | 61 | cookie.set(cookieKeyHidden, 0); 62 | }; 63 | 64 | init(); 65 | 66 | function init() { 67 | (cookie.get(cookieKeyHidden) == 1) 68 | ? self.hide() 69 | : self.show(); 70 | 71 | initEvents(); 72 | } 73 | 74 | function initEvents() { 75 | bindEvent(toggleTrigger, "click", self.toggle); 76 | bindEvent(window, "resize", resize); 77 | } 78 | 79 | /** 80 | * @param {number} toPosition 81 | */ 82 | function slide(toPosition) { 83 | 84 | // Clear timer 85 | clearTimeout(timer); 86 | 87 | 88 | var increment = 30; 89 | 90 | var currentPosition = (container.style.left.length > 0) 91 | ? parseInt(container.style.left) 92 | : 0; 93 | 94 | if (currentPosition == toPosition) { 95 | return; 96 | } 97 | 98 | var moveLeft = (toPosition < currentPosition); 99 | var newPosition = toPosition; 100 | 101 | if (moveLeft) { 102 | var leftStep = currentPosition - increment; 103 | 104 | if (leftStep > toPosition) { 105 | newPosition = leftStep; 106 | } 107 | } else { 108 | var rightStep = currentPosition + increment; 109 | 110 | if (rightStep < toPosition) { 111 | newPosition = rightStep; 112 | } 113 | } 114 | 115 | container.style.left = newPosition + "px"; 116 | 117 | timer = setTimeout(function() { slide(toPosition); }, 3); 118 | } 119 | 120 | /** 121 | * @param {HTMLElement} node 122 | * @param {string} event 123 | * @param {function} handler 124 | */ 125 | function bindEvent(node, event, handler) { 126 | if (node.attachEvent) { 127 | node.attachEvent("on" + event, handler); 128 | } else if (node.addEventListener) { 129 | node.addEventListener(event, handler, false); 130 | } 131 | } 132 | 133 | function resize() { 134 | var newWidth = window.innerWidth - windowWidthDifference; 135 | 136 | container.style.width = newWidth + "px"; 137 | width = newWidth; 138 | 139 | self.isHidden() ? self.hide() : self.show(); 140 | } 141 | 142 | return self; 143 | }; 144 | 145 | /** 146 | * @returns {Cookie} 147 | * @constructor 148 | */ 149 | var Cookie = function() { 150 | /** @type {Cookie} */ 151 | var self = this; 152 | 153 | /** 154 | * @param {string} key 155 | * @returns {string|null} 156 | */ 157 | self.get = function(key) { 158 | var cookie = document.cookie; 159 | 160 | if (cookie.indexOf(key + "=") == -1) { 161 | return null; 162 | } 163 | 164 | var regexp = new RegExp(key + "\=([^;]+)"); 165 | 166 | return regexp.exec(cookie)[1]; 167 | }; 168 | 169 | /** 170 | * @param {string} key 171 | * @param {string} value 172 | */ 173 | self.set = function(key, value) { 174 | document.cookie = key + "=" + value; 175 | }; 176 | 177 | return self; 178 | }; 179 | 180 | window.ZDT = new Toolbar(new Cookie()); 181 | 182 | })(); -------------------------------------------------------------------------------- /view/zend-developer-tools/toolbar/toolbar.phtml: -------------------------------------------------------------------------------- 1 | 2 |
3 | entries as $entry): ?> 4 | 5 | 6 | 7 |
8 | -------------------------------------------------------------------------------- /view/zend-developer-tools/toolbar/zendframework.phtml: -------------------------------------------------------------------------------- 1 |
2 |
3 | Zend Framework 4 |
5 | 6 | Documentation 7 | 8 | 9 | Modules Gallery 10 | 11 | 12 | PHP Version 13 | php_version; ?> 14 | 15 | 16 | Extensions 17 | 18 | intl 21 | 22 | 23 | modules)) : ?> 24 | 25 | ZendDeveloperToolsDetailArray('Modules', $this->modules); ?> 26 | 27 | 28 |
29 |
30 |
31 | --------------------------------------------------------------------------------