• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

myaaghubi / Debench / 16988045395

15 Aug 2025 10:21AM UTC coverage: 95.377% (-4.6%) from 100.0%
16988045395

push

github

myaaghubi
Update README.MD

392 of 411 relevant lines covered (95.38%)

30.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.56
/lib/Debench/Debench.php
1
<?php
2

3
declare(strict_types=1);
4

5
/**
6
 * @package Debench
7
 * @link http://github.com/myaaghubi/debench Github
8
 * @author Mohammad Yaaghubi <m.yaaghubi.abc@gmail.com>
9
 * @copyright Copyright (c) 2025, Mohammad Yaaghubi
10
 * @license MIT License
11
 */
12

13
namespace DEBENCH;
14

15
class Debench
16
{
17
    private static bool $enable;
18
    private static string $ui;
19
    private static string $path;
20
    private static string $pathUI;
21
    private static bool $minimalOnly;
22

23
    private array $checkPoints;
24
    private array $exceptions;
25
    private static array $messages;
26

27
    private int $initPointMS;
28
    private int $endPointMS;
29
    private int $lastCheckPointInMS;
30
    private int $lastCheckPointNumber;
31

32
    private static ?Debench $instance = null;
33

34

35
    /**
36
     * Debench constructor
37
     *
38
     * @param  bool $enable
39
     * @param  string $ui
40
     * @return void
41
     */
42
    public function __construct(bool $enable = true, string $path = 'public', string $ui = 'assets')
96✔
43
    {
44
        self::$enable = $enable;
96✔
45

46
        $base = \Base::instance();
96✔
47
        if (!self::$enable|| $base->get('AJAX') || ($base->get("CLI")&&defined('PHPUnit'))) {
96✔
48
            return;
3✔
49
        }
50

51
        self::$ui = rtrim($ui, '/');
96✔
52
        self::$path = rtrim($path, '/');
96✔
53
        if (empty($path)) {
96✔
54
            self::$path = dirname((Utils::getBacktrace()[0])['file']);
×
55
        }
56
        self::$pathUI = self::$path . '/' . self::$ui . '/debench';
96✔
57

58

59

60
        $currentUrl = $base->get('URI');
96✔
61
        $path = parse_url($currentUrl, PHP_URL_PATH);
96✔
62
        $fileInfo = pathinfo($path);
96✔
63

64
        if (!empty($fileInfo['extension']) && in_array($fileInfo['extension'], ['js', 'css'])) {
96✔
65
            return;
×
66
        }
67

68
        // make sure about the theme
69
        Template::makeUI(self::$pathUI);
96✔
70

71
        // Script initial point
72
        $this->addScriptPoint('Script');
96✔
73

74
        // Debench initial point
75
        $this->newPoint('Debench');
96✔
76

77
        set_exception_handler([$this, 'addException']);
96✔
78

79
        set_error_handler([$this, 'addAsException']);
96✔
80

81
        register_shutdown_function([$this, 'shutdownFunction']);
96✔
82

83
        self::$instance = $this;
96✔
84
    }
85

86

87
    /**
88
     * To finalize the Debench
89
     * 
90
     * @return bool
91
     */
92
    private function shutdownFunction(): bool
3✔
93
    {
94
        if (!self::$enable) {
3✔
95
            return false;
3✔
96
        }
97

98
        $this->processCheckPoints();
3✔
99

100
        $output = $this->makeOutput();
3✔
101

102
        print Utils::isInTestMode() ? '' : $output;
3✔
103

104
        return !empty($output);
3✔
105
    }
106

107

108
    /**
109
     * Run session_start()
110
     * 
111
     * @return void
112
     */
113
    private function startSession(): void
3✔
114
    {
115
        if (session_status() != PHP_SESSION_ACTIVE) {
3✔
116
            @session_start();
3✔
117
        }
118
    }
119

120

121
    /**
122
     * Add a new item in checkpoint[]
123
     * 
124
     * @param  int $currentTime
125
     * @param  int $memory
126
     * @param  string $path
127
     * @param  int $lineNumber
128
     * @param  string $key
129
     * @return void
130
     */
131
    private function addCheckPoint(int $currentTime, int $memory, string $path, int $lineNumber, string $key = ''): void
96✔
132
    {
133
        if (empty($key) || !$this->checkTag($key)) {
96✔
134
            throw new \Exception("The `key` ($key) is empty or is not in the right format!!");
3✔
135
        }
136

137
        $checkPoint = new CheckPoint($currentTime, $memory, $path, $lineNumber);
96✔
138

139
        if (!isset($this->checkPoints)) {
96✔
140
            $this->checkPoints = [];
96✔
141
        }
142

143
        $this->checkPoints[$key] = $checkPoint;
96✔
144
    }
145

146

147
    /**
148
     * Add the first checkpoint
149
     *
150
     * @param  string $tag
151
     * @return void
152
     */
153
    private function addScriptPoint(string $tag = ''): void
96✔
154
    {
155
        $time = $this->getRequestTime();
96✔
156
        $path = $this->getScriptName();
96✔
157
        $tag = $this->makeTag($tag, $this->incrementLastCheckPointNumber(true));
96✔
158

159
        // add a check point as preload
160
        $this->addCheckPoint($time, 0, $path, 0, $tag);
96✔
161
    }
162

163

164
    /**
165
     * Add a new checkpoint
166
     * 
167
     * @param  string $tag
168
     * @return void
169
     */
170
    public function newPoint(string $tag = ''): void
96✔
171
    {
172
        if (!self::$enable) {
96✔
173
            return;
3✔
174
        }
175

176
        $currentTime = $this->getCurrentTime();
96✔
177
        if (empty($this->initPointMS)) {
96✔
178
            $this->initPointMS = $currentTime;
96✔
179
        }
180

181
        $ramUsage = $this->getRamUsage();
96✔
182

183
        // debug_backtrace
184
        $debugBT = Utils::getBacktrace()[0];
96✔
185

186
        $file = $debugBT['file'];
96✔
187
        $line = $debugBT['line'];
96✔
188

189

190
        $tag = $this->makeTag($tag, $this->incrementLastCheckPointNumber(true));
96✔
191

192
        $this->addCheckPoint($currentTime, $ramUsage, $file, $line, $tag);
96✔
193

194
        $this->setLastCheckPointInMS($currentTime);
96✔
195
    }
196

197

198
    /**
199
     * benchmark & compare
200
     * 
201
     * @param  string $tag
202
     * @return void
203
     */
204
    public static function compare($func1 = null, $func2 = null, string $tag = '', int $iterations = 1000): void
×
205
    {
206
        if (!self::$enable) {
×
207
            return;
×
208
        }
209

210
        $currentTime = self::currentTime();
×
211
        if ($func1) {
×
212
            for ($i = 0; $i < $iterations; $i++) {
×
213
                $func1();
×
214
            }
215
        }
216

217
        $result1 = self::currentTime() - $currentTime;
×
218

219
        if ($func2) {
×
220
            for ($i = 0; $i < $iterations; $i++) {
×
221
                $func2();
×
222
            }
223
        }
224

225
        $result2 = self::currentTime() - $currentTime - $result1;
×
226

227
        self::comparison($tag, $iterations, $result1, $result2);
×
228
    }
229

230

231
    /**
232
     * Calculate elapsed time for each checkpoint
233
     *
234
     * @return void
235
     */
236
    private function processCheckPoints(): void
6✔
237
    {
238
        // depends on the the array, it may the below loop take some time to process
239
        $this->endPointMS = $this->getCurrentTime();
6✔
240

241
        $prevKey = '';
6✔
242
        $prevCP = null;
6✔
243

244
        foreach ($this->getCheckPoints() as $key => $cp) {
6✔
245
            if (!is_null($prevCP) && !empty($prevKey)) {
6✔
246
                $diff = $cp->getTimestamp() - $prevCP->getTimestamp();
6✔
247
                $this->checkPoints[$prevKey]->setTimestamp($diff);
6✔
248
            }
249

250
            $prevKey = $key;
6✔
251
            $prevCP = $cp;
6✔
252
        }
253

254
        $diff = 0;
6✔
255
        if (!is_null($prevCP) && !empty($prevKey)) {
6✔
256
            $diff = $this->endPointMS - $prevCP->getTimestamp();
6✔
257
            $this->checkPoints[$prevKey]->setTimestamp($diff);
6✔
258
        }
259
    }
260

261

262
    /**
263
     * Is Debench in minimal mode
264
     *
265
     * @return bool
266
     */
267
    public function isMinimalOnly(): bool
9✔
268
    {
269
        return self::$minimalOnly ?? false;
9✔
270
    }
271

272

273
    /**
274
     * Set Debench to only minimal mode
275
     *
276
     * @param  bool $minimalModeOnly
277
     * @return void
278
     */
279
    public function setMinimalOnly(bool $minimalModeOnly): void
6✔
280
    {
281
        self::$minimalOnly = $minimalModeOnly;
6✔
282
    }
283

284

285
    /**
286
     * Set Debench to only minimal mode
287
     *
288
     * @param  bool $minimalModeOnly
289
     * @return void
290
     */
291
    public static function minimalOnly(bool $minimalModeOnly): void
3✔
292
    {
293
        self::$minimalOnly = $minimalModeOnly;
3✔
294
    }
295

296

297
    /**
298
     * Is Debench enable
299
     *
300
     * @return bool
301
     */
302
    public function isEnable(): bool
6✔
303
    {
304
        return self::$enable;
6✔
305
    }
306

307

308
    /**
309
     * Set Debench enable
310
     *
311
     * @param  bool $enable
312
     * @return void
313
     */
314
    public function setEnable(bool $enable): void
9✔
315
    {
316
        self::$enable = $enable;
9✔
317
    }
318

319

320
    /**
321
     * Set Debench enable
322
     *
323
     * @param  bool $enable
324
     * @return void
325
     */
326
    public static function enable(bool $enable): void
3✔
327
    {
328
        self::$enable = $enable;
3✔
329
    }
330

331

332
    /**
333
     * Get the last checkpoint in milliseconds
334
     *
335
     * @return int
336
     */
337
    private function getLastCheckPointInMS(): int
3✔
338
    {
339
        return $this->lastCheckPointInMS ?? 0;
3✔
340
    }
341

342

343
    /**
344
     * Set the last checkpoint in milliseconds
345
     *
346
     * @param  int $timestamp
347
     * @return void
348
     */
349
    private function setLastCheckPointInMS(int $timestamp): void
96✔
350
    {
351
        $this->lastCheckPointInMS = $timestamp;
96✔
352
    }
353

354

355
    /**
356
     * Get the last checkpoint number
357
     *
358
     * @return int
359
     */
360
    private function getLastCheckPointNumber(): int
3✔
361
    {
362
        return $this->lastCheckPointNumber ?? 0;
3✔
363
    }
364

365

366
    /**
367
     * Get the last checkpoint number, and increase it
368
     *
369
     * @param  bool $postfix
370
     * @return int
371
     */
372
    private function incrementLastCheckPointNumber(bool $postfix = true): int
96✔
373
    {
374
        if (!isset($this->lastCheckPointNumber)) {
96✔
375
            $this->lastCheckPointNumber = 0;
96✔
376
        }
377

378
        if ($postfix) {
96✔
379
            return $this->lastCheckPointNumber++;
96✔
380
        }
381

382
        return ++$this->lastCheckPointNumber;
3✔
383
    }
384

385

386
    /**
387
     * Get the $pathUI
388
     *
389
     * @return string
390
     */
391
    public function getPathUI(): string
96✔
392
    {
393
        return self::$pathUI ?? '';
96✔
394
    }
395

396

397
    /**
398
     * Get checkpoints array
399
     *
400
     * @return array<string,object>
401
     */
402
    public function getCheckPoints(): array
18✔
403
    {
404
        return $this->checkPoints ?? [];
18✔
405
    }
406

407

408
    /**
409
     * Get the ram usage
410
     *
411
     * @param  bool $formatted
412
     * @param  bool $roundUnderMB
413
     * @return int|string
414
     */
415
    public function getRamUsage(bool $formatted = false, bool $roundUnderMB = false): int|string
96✔
416
    {
417
        // true => memory_real_usage
418
        $peak = memory_get_usage();
96✔
419

420
        if ($formatted)
96✔
421
            return Utils::toFormattedBytes($peak, $roundUnderMB);
9✔
422

423
        return $peak;
96✔
424
    }
425

426

427
    /**
428
     * Get the real ram usage (peak)
429
     *
430
     * @param  bool $formatted
431
     * @param  bool $roundUnderMB
432
     * @return int|string
433
     */
434
    public function getRamUsagePeak(bool $formatted = false, bool $roundUnderMB = false): int|string
9✔
435
    {
436
        // true => memory_real_usage
437
        $peak = memory_get_peak_usage(true);
9✔
438

439
        if ($formatted)
9✔
440
            return Utils::toFormattedBytes($peak, $roundUnderMB);
9✔
441

442
        return $peak;
3✔
443
    }
444

445

446
    /**
447
     * Get the elapsed time from beginning till now in milliseconds
448
     *
449
     * @return int
450
     */
451
    public function getExecutionTime(): int
9✔
452
    {
453
        return $this->getCurrentTime() - $this->getRequestTime();
9✔
454
        // what about loads before Debench such as composer !?
455
        // return $this->endPointMS - $this->initPointMS;
456
    }
457

458

459
    /**
460
     * Get the request time in milliseconds
461
     *
462
     * @return int
463
     */
464
    public function getRequestTime(): int
96✔
465
    {
466
        return intval($_SERVER["REQUEST_TIME_FLOAT"] * 1000);
96✔
467
    }
468

469

470
    /**
471
     * Get the script name
472
     *
473
     * @return string
474
     */
475
    public function getScriptName(): string
96✔
476
    {
477
        return $_SERVER['SCRIPT_NAME'] ?? 'non';
96✔
478
    }
479

480

481
    /**
482
     * Get the $_SERVER['REQUEST_METHOD']
483
     *
484
     * @return string
485
     */
486
    public function getRequestMethod(): string
9✔
487
    {
488
        return $_SERVER['REQUEST_METHOD'] ?? 'non';
9✔
489
    }
490

491

492
    /**
493
     * Get the http_response_code()
494
     *
495
     * @return string
496
     */
497
    public function getResponseCode(): int
9✔
498
    {
499
        $rCode = http_response_code();
9✔
500
        // 501: Not Implemented
501
        return $rCode === false ? 501 : $rCode;
9✔
502
    }
503

504

505
    /**
506
     * Get the current time in milliseconds
507
     *
508
     * @return int
509
     */
510
    public function getCurrentTime(): int
96✔
511
    {
512
        return self::currentTime();
96✔
513
    }
514

515

516
    /**
517
     * Get the current time in milliseconds
518
     *
519
     * @return int
520
     */
521
    public static function currentTime(): int
96✔
522
    {
523
        $microtime = microtime(true) * 1000;
96✔
524
        return intval($microtime);
96✔
525
    }
526

527

528
    /**
529
     * Get the count of all loaded files 
530
     *
531
     * @return int
532
     */
533
    public function getLoadedFilesCount(): int
3✔
534
    {
535
        return count(get_required_files());
3✔
536
    }
537

538

539
    /**
540
     * Get the right tag name to show, just remove the '#' with checkpoint number
541
     *
542
     * @param  string $tag
543
     * @return string
544
     */
545
    private function getTagName(string $tag = ''): string
9✔
546
    {
547
        // return substr($tag, 0, strrpos($tag, '#'));
548
        return "#" . substr($tag, 0, strrpos($tag, '#'));
9✔
549
    }
550

551

552
    /**
553
     * Make the tag
554
     * 
555
     * @param  string $tag
556
     * @param  int $id
557
     * @return string
558
     */
559
    private function makeTag(string $tag, int $id): string
96✔
560
    {
561
        if (empty($tag)) {
96✔
562
            $tag = 'point ' . $id;
6✔
563
        }
564

565
        // tags(keys) should to be unique
566
        return $tag .= '#' . $id;
96✔
567
    }
568

569

570
    /**
571
     * Validate the tag
572
     * 
573
     * @param  string $tag
574
     * @return bool
575
     */
576
    private function checkTag(string $tag): bool
96✔
577
    {
578
        $regex = "/^([a-zA-Z0-9_ -]+)#[0-9]+$/";
96✔
579

580
        if (preg_match($regex, $tag)) {
96✔
581
            return true;
96✔
582
        }
583

584
        return false;
6✔
585
    }
586

587

588
    /**
589
     * Add an info
590
     * 
591
     * @param  string $message
592
     * @return void
593
     */
594
    public static function info(string $message): void
6✔
595
    {
596
        self::addMessage($message, MessageLevel::INFO);
6✔
597
    }
598

599

600
    /**
601
     * Add a warning
602
     * 
603
     * @param  string $message
604
     * @return void
605
     */
606
    public static function warning(string $message): void
3✔
607
    {
608
        self::addMessage($message, MessageLevel::WARNING);
3✔
609
    }
610

611

612
    /**
613
     * Add an error
614
     * 
615
     * @param  string $message
616
     * @return void
617
     */
618
    public static function error(string $message): void
3✔
619
    {
620
        self::addMessage($message, MessageLevel::ERROR);
3✔
621
    }
622

623

624
    /**
625
     * Dump var/s
626
     * 
627
     * @param  mixed $var/s
628
     * @return void
629
     */
630
    public static function dump(...$args): void
3✔
631
    {
632
        $messages = [];
3✔
633

634
        foreach (func_get_args() as $var) {
3✔
635
            ob_start();
3✔
636
            var_dump($var);
3✔
637
            $dumped = ob_get_clean();
3✔
638
            $messages[] = preg_replace("/\n*<small>.*?<\/small>/", "", $dumped, 1);
3✔
639
        }
640

641
        $messageString = implode('', $messages);
3✔
642

643
        self::addMessage($messageString, MessageLevel::DUMP);
3✔
644
    }
645

646

647
    /**
648
     * Add a comparison result
649
     * 
650
     * @param  string $tag
651
     * @param  int $iterations
652
     * @param  int $result1
653
     * @param  int $result2
654
     * @return void
655
     */
656
    protected static function comparison(string $tag, int $iterations, int $result1, int $result2): void
×
657
    {
658
        self::addMessage("$tag x" . number_format($iterations) . " > res1: " . number_format($result1) . " ms / res2: " . number_format($result2) . " ms", MessageLevel::COMPARISON);
×
659
    }
660

661

662
    /**
663
     * Add a message
664
     * 
665
     * @param  string $message
666
     * @param  MessageLevel $level
667
     * @return void
668
     */
669
    private static function addMessage(string $message, MessageLevel $level): void
15✔
670
    {
671
        $lastBT = Utils::getBacktrace()[0];
15✔
672
        $path = $lastBT['file'];
15✔
673
        $line = $lastBT['line'];
15✔
674

675
        $messageObject = new Message($message, $level, $path, $line);
15✔
676

677
        if (empty(self::$messages)) {
15✔
678
            self::$messages = [];
15✔
679
        }
680

681
        self::$messages[] = $messageObject;
15✔
682
    }
683

684

685
    /**
686
     * Get the exceptions array
687
     * 
688
     * @return array
689
     */
690
    public static function messages(): array
18✔
691
    {
692
        return self::$messages ?? [];
18✔
693
    }
694

695

696
    /**
697
     * Clear messages
698
     * 
699
     * @return void
700
     */
701
    public static function clearMessages(): void
12✔
702
    {
703
        self::$messages = [];
12✔
704
    }
705

706

707
    /**
708
     * Add an exception to exceptions array
709
     * 
710
     * @param  Throwable $exception
711
     * @return void
712
     */
713
    public function addException(\Throwable $exception): void
6✔
714
    {
715
        if (!isset($this->exceptions)) {
6✔
716
            $this->exceptions = [];
6✔
717
        }
718

719
        $this->exceptions[] = $exception;
6✔
720
    }
721

722

723
    /**
724
     * Get the exceptions array
725
     * 
726
     * @return array
727
     */
728
    private function getExceptions(): array
12✔
729
    {
730
        return $this->exceptions ?? [];
12✔
731
    }
732

733

734
    /**
735
     * Throw errors as exceptions
736
     * 
737
     * @param int $level
738
     * @param string $message
739
     * @param string $file
740
     * @param int $line
741
     * @param array $context
742
     * @return void
743
     */
744
    public function addAsException(int $level, string $message, string $file = '', int $line = 0, array $context = []): void
99✔
745
    {
746
        if (!isset($this->exceptions)) {
99✔
747
            $this->exceptions = [];
87✔
748
        }
749

750
        $this->exceptions[]  = new \ErrorException($message, 0, $level, $file, $line);
99✔
751
    }
752

753

754
    /**
755
     * Make formatted output
756
     *
757
     * @return string
758
     */
759
    private function makeOutput(): string
6✔
760
    {
761
        $eTime = $this->getExecutionTime();
6✔
762

763
        // ------- the minimal widget
764
        if ($this->isMinimalOnly()) {
6✔
765
            return Template::render(self::$pathUI . '/widget.minimal.htm', [
3✔
766
                'base' => self::$ui,
3✔
767
                'ramUsage' => $this->getRamUsage(true, true),
3✔
768
                'requestInfo' => $this->getRequestMethod() . ' ' . $this->getResponseCode(),
3✔
769
                'fullExecTime' => $eTime
3✔
770
            ]);
3✔
771
        }
772

773

774
        // ------- infoLog
775
        $infoLog = Template::render(self::$pathUI . '/widget.log.info.htm', [
6✔
776
            "phpVersion" => SystemInfo::getPHPVersion(),
6✔
777
            "opcache" => SystemInfo::getOPCacheStatus(),
6✔
778
            "systemAPI" => SystemInfo::getSystemAPI(),
6✔
779
        ]);
6✔
780

781

782
        // ------- timeLog
783
        $timeLog = '';
6✔
784

785
        foreach ($this->getCheckPoints() as $key => $cp) {
6✔
786
            $timeLog .= Template::render(self::$pathUI . '/widget.log.checkpoint.htm', [
6✔
787
                "name" => $this->getTagName($key),
6✔
788
                "path" => $cp->getPath(),
6✔
789
                "fileName" => basename($cp->getPath()),
6✔
790
                "lineNumber" => $cp->getLineNumber(),
6✔
791
                "timestamp" => $cp->getTimestamp(),
6✔
792
                "memory" => Utils::toFormattedBytes($cp->getMemory()),
6✔
793
                "percent" => round($cp->getTimestamp() / ($eTime > 1 ? $eTime : 1) * 100),
6✔
794
            ]);
6✔
795
        }
796

797

798
        // ------- logPost
799
        $logPost = $this->makeOutputLoop(self::$pathUI . '/widget.log.request.post.htm', $_POST, false);
6✔
800

801

802
        // ------- logGet
803
        $logGet = $this->makeOutputLoop(self::$pathUI . '/widget.log.request.get.htm', $_GET, false);
6✔
804

805

806
        // ------- logCookie
807
        $logCookie = $this->makeOutputLoop(self::$pathUI . '/widget.log.request.cookie.htm', $_COOKIE, false);
6✔
808

809

810
        if (empty($logPost . $logGet . $logCookie)) {
6✔
811
            $logPost = '<b>Nothing</b> Yet!';
×
812
        }
813

814

815
        // ------- logSession
816
        $session = isset($_SESSION) ? $_SESSION : null;
6✔
817

818
        $logSession = $this->makeOutputLoop(self::$pathUI . '/widget.log.request.session.htm', $session);
6✔
819

820
        if (!isset($session)) {
6✔
821
            $logSession = '<b>_SESSION</b> is not available!';
3✔
822
        }
823

824

825
        // ------- logMessages
826
        $logMessage = '<b>Nothing</b> Yet!';
6✔
827

828
        if (!empty(self::messages())) {
6✔
829
            $logMessage = '';
3✔
830
        }
831

832
        foreach (self::messages() as $message) {
6✔
833
            $file = basename($message->getPath());
3✔
834
            $path = str_replace($file, "<b>$file</b>", $message->getPath());
3✔
835

836
            $logMessage .= Template::render(self::$pathUI . '/widget.log.message.htm', [
3✔
837
                // "code" => $exception->getCode(),
838
                "level" => strtoupper($message->getLevel()->name()),
3✔
839
                "message" => $message->getMessage(),
3✔
840
                "path" => $path,
3✔
841
                "line" => $message->getLineNumber(),
3✔
842
            ]);
3✔
843
        }
844

845

846
        // ------- logException
847
        $logException = '';
6✔
848

849
        foreach ($this->getExceptions() as $exception) {
6✔
850
            $file = basename($exception->getFile());
3✔
851
            $path = str_replace($file, "<b>$file</b>", $exception->getFile());
3✔
852

853
            $fullTrace = '';
3✔
854
            $traces = $exception->getTrace();
3✔
855
            foreach ($traces as $trace) {
3✔
856
                $fullTrace .= $trace['file'] . ":" . $trace['line'] . "\n";
3✔
857
            }
858

859

860
            $logException .= Template::render(self::$pathUI . '/widget.log.exception.htm', [
3✔
861
                // "code" => $exception->getCode(),
862
                "message" => $exception->getMessage(),
3✔
863
                "path" => $path,
3✔
864
                "line" => $exception->getLine(),
3✔
865
                "fullTrace" => $fullTrace,
3✔
866
            ]);
3✔
867
        }
868

869
        if (empty($logException)) {
6✔
870
            $logException = '<b>Nothing</b> Yet!';
6✔
871
        }
872

873

874
        // ------- the main widget
875
        return Template::render(self::$pathUI . '/widget.htm', [
6✔
876
            'base' => self::$ui,
6✔
877
            'ramUsagePeak' => $this->getRamUsagePeak(true, true),
6✔
878
            'ramUsage' => $this->getRamUsage(true, true),
6✔
879
            // 'includedFilesCount' => $this->getLoadedFilesCount(),
880
            'preloadTime' => $this->initPointMS - $this->getRequestTime(),
6✔
881
            'pointsCount' => count($this->getCheckPoints()),
6✔
882
            'request' => count($_POST) + count($_GET) + count($_COOKIE),
6✔
883
            'logPost' => $logPost,
6✔
884
            'logGet' => $logGet,
6✔
885
            'logCookie' => $logCookie,
6✔
886
            'session' => count($_SESSION ?? []),
6✔
887
            'sessionLog' => $logSession,
6✔
888
            'infoLog' => $infoLog,
6✔
889
            'timeLog' => $timeLog,
6✔
890
            'logMessage' => $logMessage,
6✔
891
            'message' => count(self::messages()),
6✔
892
            'logException' => $logException,
6✔
893
            'exception' => count($this->getExceptions()),
6✔
894
            'requestInfo' => $this->getRequestMethod() . ' ' . $this->getResponseCode(),
6✔
895
            'fullExecTime' => $eTime
6✔
896
        ]);
6✔
897
    }
898

899

900
    /**
901
     * Make formatted output in loop, $key => $value
902
     *
903
     * @return string
904
     */
905
    private function makeOutputLoop(string $theme, array|null $data, string|false $message = ''): string
9✔
906
    {
907
        if (empty($data)) {
9✔
908
            if ($message === false) {
9✔
909
                return '';
6✔
910
            }
911
            return empty($message) || !is_string($message) ? '<b>Nothing</b> Yet!' : $message;
9✔
912
        }
913

914
        $output = '';
9✔
915

916
        foreach ($data as $key => $value) {
9✔
917
            if (is_array($value)) {
9✔
918
                $value = "array [" . implode(', ', $value) . "]";
×
919
            }
920
            $output .= Template::render($theme, [
9✔
921
                "key" => $key,
9✔
922
                "value" => $value
9✔
923
            ]);
9✔
924
        }
925

926
        return $output;
9✔
927
    }
928

929

930
    /**
931
     * Add a new checkpoint
932
     * 
933
     * @param  string $tag
934
     * @return object
935
     */
936
    public static function point(string $tag = ''): void
6✔
937
    {
938
        self::getInstance()->newPoint($tag);
6✔
939
    }
940

941

942
    /**
943
     * Gets the instance
944
     * 
945
     * @param  bool $enable
946
     * @param  string $ui
947
     * @return Debench
948
     */
949
    public static function getInstance(bool $enable = true, string $path = 'public', string $ui = 'assets'): Debench
96✔
950
    {
951
        if (self::$instance === null) {
96✔
952
            self::$instance = new self($enable, $path, $ui);
3✔
953
        }
954

955
        return self::$instance;
96✔
956
    }
957

958

959
    /**
960
     * Prevent from being unserialized
961
     * 
962
     * @return void
963
     */
964
    public function __wakeup(): void
3✔
965
    {
966
        throw new \Exception("Cannot unserialize singleton");
3✔
967
    }
968
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc