Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationDI
      • ApplicationLatte
      • ApplicationTracy
      • CacheDI
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsDI
      • FormsLatte
      • Framework
      • HttpDI
      • HttpTracy
      • MailDI
      • ReflectionDI
      • SecurityDI
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Conventions
      • Drivers
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
      • Traits
    • Reflection
    • Security
    • Tokenizer
    • Utils
  • Tracy
    • Bridges
      • Nette
  • none

Classes

  • Bar
  • BlueScreen
  • Debugger
  • DefaultBarPanel
  • Dumper
  • FireLogger
  • Helpers
  • Logger
  • OutputDebugger

Interfaces

  • IBarPanel
  • ILogger
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Other releases
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Tracy (https://tracy.nette.org)
  5:  * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
  6:  */
  7: 
  8: namespace Tracy;
  9: 
 10: 
 11: /**
 12:  * Debug Bar.
 13:  */
 14: class Bar
 15: {
 16:     /** @var IBarPanel[] */
 17:     private $panels = [];
 18: 
 19:     /** @var bool  initialized by dispatchAssets() */
 20:     private $useSession = false;
 21: 
 22:     /** @var string|NULL  generated by renderLoader() */
 23:     private $contentId;
 24: 
 25: 
 26:     /**
 27:      * Add custom panel.
 28:      * @param  IBarPanel  $panel
 29:      * @param  string  $id
 30:      * @return static
 31:      */
 32:     public function addPanel(IBarPanel $panel, $id = null)
 33:     {
 34:         if ($id === null) {
 35:             $c = 0;
 36:             do {
 37:                 $id = get_class($panel) . ($c++ ? "-$c" : '');
 38:             } while (isset($this->panels[$id]));
 39:         }
 40:         $this->panels[$id] = $panel;
 41:         return $this;
 42:     }
 43: 
 44: 
 45:     /**
 46:      * Returns panel with given id
 47:      * @param  string  $id
 48:      * @return IBarPanel|null
 49:      */
 50:     public function getPanel($id)
 51:     {
 52:         return isset($this->panels[$id]) ? $this->panels[$id] : null;
 53:     }
 54: 
 55: 
 56:     /**
 57:      * Renders loading <script>
 58:      * @return void
 59:      */
 60:     public function renderLoader()
 61:     {
 62:         if (!$this->useSession) {
 63:             throw new \LogicException('Start session before Tracy is enabled.');
 64:         }
 65:         $contentId = $this->contentId = $this->contentId ?: substr(md5(uniqid('', true)), 0, 10);
 66:         $nonce = Helpers::getNonce();
 67:         $async = true;
 68:         require __DIR__ . '/assets/Bar/loader.phtml';
 69:     }
 70: 
 71: 
 72:     /**
 73:      * Renders debug bar.
 74:      * @return void
 75:      */
 76:     public function render()
 77:     {
 78:         $useSession = $this->useSession && session_status() === PHP_SESSION_ACTIVE;
 79:         $redirectQueue = &$_SESSION['_tracy']['redirect'];
 80: 
 81:         foreach (['bar', 'redirect', 'bluescreen'] as $key) {
 82:             $queue = &$_SESSION['_tracy'][$key];
 83:             $queue = array_slice((array) $queue, -10, null, true);
 84:             $queue = array_filter($queue, function ($item) {
 85:                 return isset($item['time']) && $item['time'] > time() - 60;
 86:             });
 87:         }
 88: 
 89:         $rows = [];
 90: 
 91:         if (Helpers::isAjax()) {
 92:             if ($useSession) {
 93:                 $rows[] = (object) ['type' => 'ajax', 'panels' => $this->renderPanels('-ajax')];
 94:                 $contentId = $_SERVER['HTTP_X_TRACY_AJAX'] . '-ajax';
 95:                 $_SESSION['_tracy']['bar'][$contentId] = ['content' => self::renderHtmlRows($rows), 'dumps' => Dumper::fetchLiveData(), 'time' => time()];
 96:             }
 97: 
 98:         } elseif (preg_match('#^Location:#im', implode("\n", headers_list()))) { // redirect
 99:             if ($useSession) {
100:                 Dumper::fetchLiveData();
101:                 Dumper::$livePrefix = count($redirectQueue) . 'p';
102:                 $redirectQueue[] = [
103:                     'panels' => $this->renderPanels('-r' . count($redirectQueue)),
104:                     'dumps' => Dumper::fetchLiveData(),
105:                     'time' => time(),
106:                 ];
107:             }
108: 
109:         } elseif (Helpers::isHtmlMode()) {
110:             $rows[] = (object) ['type' => 'main', 'panels' => $this->renderPanels()];
111:             $dumps = Dumper::fetchLiveData();
112:             foreach (array_reverse((array) $redirectQueue) as $info) {
113:                 $rows[] = (object) ['type' => 'redirect', 'panels' => $info['panels']];
114:                 $dumps += $info['dumps'];
115:             }
116:             $redirectQueue = null;
117:             $content = self::renderHtmlRows($rows);
118: 
119:             if ($this->contentId) {
120:                 $_SESSION['_tracy']['bar'][$this->contentId] = ['content' => $content, 'dumps' => $dumps, 'time' => time()];
121:             } else {
122:                 $contentId = substr(md5(uniqid('', true)), 0, 10);
123:                 $nonce = Helpers::getNonce();
124:                 $async = false;
125:                 require __DIR__ . '/assets/Bar/loader.phtml';
126:             }
127:         }
128:     }
129: 
130: 
131:     /**
132:      * @return string
133:      */
134:     private static function renderHtmlRows(array $rows)
135:     {
136:         ob_start(function () {});
137:         require __DIR__ . '/assets/Bar/panels.phtml';
138:         require __DIR__ . '/assets/Bar/bar.phtml';
139:         return Helpers::fixEncoding(ob_get_clean());
140:     }
141: 
142: 
143:     /**
144:      * @return array
145:      */
146:     private function renderPanels($suffix = null)
147:     {
148:         set_error_handler(function ($severity, $message, $file, $line) {
149:             if (error_reporting() & $severity) {
150:                 throw new \ErrorException($message, 0, $severity, $file, $line);
151:             }
152:         });
153: 
154:         $obLevel = ob_get_level();
155:         $panels = [];
156: 
157:         foreach ($this->panels as $id => $panel) {
158:             $idHtml = preg_replace('#[^a-z0-9]+#i', '-', $id) . $suffix;
159:             try {
160:                 $tab = (string) $panel->getTab();
161:                 $panelHtml = $tab ? (string) $panel->getPanel() : null;
162:                 if ($tab && $panel instanceof \Nette\Diagnostics\IBarPanel) {
163:                     $e = new \Exception('Support for Nette\Diagnostics\IBarPanel is deprecated');
164:                 }
165: 
166:             } catch (\Exception $e) {
167:             } catch (\Throwable $e) {
168:             }
169:             if (isset($e)) {
170:                 while (ob_get_level() > $obLevel) { // restore ob-level if broken
171:                     ob_end_clean();
172:                 }
173:                 $idHtml = "error-$idHtml";
174:                 $tab = "Error in $id";
175:                 $panelHtml = "<h1>Error: $id</h1><div class='tracy-inner'>" . nl2br(Helpers::escapeHtml($e)) . '</div>';
176:                 unset($e);
177:             }
178:             $panels[] = (object) ['id' => $idHtml, 'tab' => $tab, 'panel' => $panelHtml];
179:         }
180: 
181:         restore_error_handler();
182:         return $panels;
183:     }
184: 
185: 
186:     /**
187:      * Renders debug bar assets.
188:      * @return bool
189:      */
190:     public function dispatchAssets()
191:     {
192:         $asset = isset($_GET['_tracy_bar']) ? $_GET['_tracy_bar'] : null;
193:         if ($asset === 'js') {
194:             header('Content-Type: text/javascript');
195:             header('Cache-Control: max-age=864000');
196:             header_remove('Pragma');
197:             header_remove('Set-Cookie');
198:             $this->renderAssets();
199:             return true;
200:         }
201: 
202:         $this->useSession = session_status() === PHP_SESSION_ACTIVE;
203: 
204:         if ($this->useSession && Helpers::isAjax()) {
205:             header('X-Tracy-Ajax: 1'); // session must be already locked
206:         }
207: 
208:         if ($this->useSession && $asset && preg_match('#^content(-ajax)?\.(\w+)$#', $asset, $m)) {
209:             $session = &$_SESSION['_tracy']['bar'][$m[2] . $m[1]];
210:             header('Content-Type: text/javascript');
211:             header('Cache-Control: max-age=60');
212:             header_remove('Set-Cookie');
213:             if (!$m[1]) {
214:                 $this->renderAssets();
215:             }
216:             if ($session) {
217:                 $method = $m[1] ? 'loadAjax' : 'init';
218:                 echo "Tracy.Debug.$method(", json_encode($session['content']), ', ', json_encode($session['dumps']), ');';
219:                 $session = null;
220:             }
221:             $session = &$_SESSION['_tracy']['bluescreen'][$m[2]];
222:             if ($session) {
223:                 echo 'Tracy.BlueScreen.loadAjax(', json_encode($session['content']), ', ', json_encode($session['dumps']), ');';
224:                 $session = null;
225:             }
226:             return true;
227:         }
228: 
229:         return false;
230:     }
231: 
232: 
233:     private function renderAssets()
234:     {
235:         $css = array_map('file_get_contents', array_merge([
236:             __DIR__ . '/assets/Bar/bar.css',
237:             __DIR__ . '/assets/Toggle/toggle.css',
238:             __DIR__ . '/assets/Dumper/dumper.css',
239:             __DIR__ . '/assets/BlueScreen/bluescreen.css',
240:         ], Debugger::$customCssFiles));
241:         $css = json_encode(preg_replace('#\s+#u', ' ', implode($css)));
242:         echo "(function(){var el = document.createElement('style'); el.className='tracy-debug'; el.textContent=$css; document.head.appendChild(el);})();\n";
243: 
244:         array_map('readfile', array_merge([
245:             __DIR__ . '/assets/Bar/bar.js',
246:             __DIR__ . '/assets/Toggle/toggle.js',
247:             __DIR__ . '/assets/Dumper/dumper.js',
248:             __DIR__ . '/assets/BlueScreen/bluescreen.js',
249:         ], Debugger::$customJsFiles));
250:     }
251: }
252: 
Nette 2.4-20180918 API API documentation generated by ApiGen 2.8.0