diff --git a/.gitattributes b/.gitattributes
index b5d42df..c2b4bb0 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -37,7 +37,3 @@ rector.php export-ignore
user_guide_src/ export-ignore
.nojekyll export-ignore
phpdoc.dist.xml export-ignore
-
-# deprecated folders - not part of the library
-Wizdam_DEPRICATED/ export-ignore
-src_DEPRICATED/ export-ignore
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 782a220..e9aabce 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,6 +1,6 @@
# These are supported funding model platforms
-github: [Rochmady]
+github: [mokesano]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..14206b4
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,11 @@
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for all configuration options:
+# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
+
+version: 2
+updates:
+ - package-ecosystem: "composer" # See documentation for possible values
+ directory: "/" # Location of package manifests
+ schedule:
+ interval: "weekly"
diff --git a/.github/workflows/codacy.yml b/.github/workflows/codacy.yml
new file mode 100644
index 0000000..1cf1885
--- /dev/null
+++ b/.github/workflows/codacy.yml
@@ -0,0 +1,61 @@
+# This workflow uses actions that are not certified by GitHub.
+# They are provided by a third-party and are governed by
+# separate terms of service, privacy policy, and support
+# documentation.
+
+# This workflow checks out code, performs a Codacy security scan
+# and integrates the results with the
+# GitHub Advanced Security code scanning feature. For more information on
+# the Codacy security scan action usage and parameters, see
+# https://github.com/codacy/codacy-analysis-cli-action.
+# For more information on Codacy Analysis CLI in general, see
+# https://github.com/codacy/codacy-analysis-cli.
+
+name: Codacy Security Scan
+
+on:
+ push:
+ branches: [ "master" ]
+ pull_request:
+ # The branches below must be a subset of the branches above
+ branches: [ "master" ]
+ schedule:
+ - cron: '33 18 * * 1'
+
+permissions:
+ contents: read
+
+jobs:
+ codacy-security-scan:
+ permissions:
+ contents: read # for actions/checkout to fetch code
+ security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
+ actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status
+ name: Codacy Security Scan
+ runs-on: ubuntu-latest
+ steps:
+ # Checkout the repository to the GitHub Actions runner
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ # Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis
+ - name: Run Codacy Analysis CLI
+ uses: codacy/codacy-analysis-cli-action@d840f886c4bd4edc059706d09c6a1586111c540b
+ with:
+ # Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository
+ # You can also omit the token and run the tools that support default configurations
+ project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
+ verbose: true
+ output: results.sarif
+ format: sarif
+ # Adjust severity of non-security issues
+ gh-code-scanning-compat: true
+ # Force 0 exit code to allow SARIF file generation
+ # This will handover control about PR rejection to the GitHub side
+ max-allowed-issues: 2147483647
+
+ # Upload the SARIF file generated in the previous step
+ - name: Upload SARIF results file
+ uses: github/codeql-action/upload-sarif@v3
+ with:
+ sarif_file: results.sarif
diff --git a/.github/workflows/phpmd.yml b/.github/workflows/phpmd.yml
new file mode 100644
index 0000000..382178d
--- /dev/null
+++ b/.github/workflows/phpmd.yml
@@ -0,0 +1,57 @@
+# This workflow uses actions that are not certified by GitHub.
+# They are provided by a third-party and are governed by
+# separate terms of service, privacy policy, and support
+# documentation.
+# PHPMD is a spin-off project of PHP Depend and
+# aims to be a PHP equivalent of the well known Java tool PMD.
+# What PHPMD does is: It takes a given PHP source code base
+# and look for several potential problems within that source.
+# These problems can be things like:
+# Possible bugs
+# Suboptimal code
+# Overcomplicated expressions
+# Unused parameters, methods, properties
+# More details at https://phpmd.org/
+
+name: PHPMD
+
+on:
+ push:
+ branches: [ "master" ]
+ pull_request:
+ # The branches below must be a subset of the branches above
+ branches: [ "master" ]
+ schedule:
+ - cron: '33 17 * * 6'
+
+permissions:
+ contents: read
+
+jobs:
+ PHPMD:
+ name: Run PHPMD scanning
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read # for checkout to fetch code
+ security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
+ actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@aa1fe473f9c687b6fb896056d771232c0bc41161
+ with:
+ coverage: none
+ tools: phpmd
+
+ - name: Run PHPMD
+ run: phpmd . sarif codesize --reportfile phpmd-results.sarif
+ continue-on-error: true
+
+ - name: Upload analysis results to GitHub
+ uses: github/codeql-action/upload-sarif@v3
+ with:
+ sarif_file: phpmd-results.sarif
+ wait-for-processing: true
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..76140fa
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,55 @@
+name: Release Otomatis (Semantic Release)
+
+on:
+ push:
+ branches:
+ - main
+ - master
+ workflow_dispatch:
+
+permissions:
+ contents: write
+ issues: write
+ pull-requests: write
+
+jobs:
+ release:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '22'
+
+ - name: Install semantic-release dan plugin
+ run: |
+ npm install --no-save \
+ semantic-release \
+ @semantic-release/commit-analyzer \
+ @semantic-release/release-notes-generator \
+ @semantic-release/github
+
+ - name: Generate release config (GitHub release only)
+ run: |
+ cat > .releaserc.json <
+
+
+
+
+
---
-## Daftar Isi
-
-- [Fitur](#fitur)
-- [Persyaratan](#persyaratan)
-- [Instalasi](#instalasi)
-- [Struktur Direktori](#struktur-direktori)
-- [Cara Penggunaan](#cara-penggunaan)
- - [Inisialisasi Dasar](#inisialisasi-dasar)
- - [Integrasi OJS (Output Buffering)](#1-integrasi-ojs-output-buffering)
- - [Integrasi Database ADODB](#2-integrasi-database-adodb)
- - [Integrasi PSR-15 Middleware](#3-integrasi-psr-15-middleware)
-- [Collectors](#collectors)
-- [Adapters](#adapters)
-- [Konfigurasi](#konfigurasi)
-- [Menambah Collector Baru](#menambah-collector-baru)
-- [Kompatibilitas Framework](#kompatibilitas-framework)
-- [Troubleshooting](#troubleshooting)
-- [Atribusi & Lisensi](#atribusi--lisensi)
-
----
+## โจ Mengapa Debug Toolbar - Wizdam?
-## Fitur
-
-- **Framework-agnostic** โ tidak bergantung pada CI4, Laravel, Slim, atau framework apapun
-- **10 Collectors Built-in:**
- - โฑ๏ธ **Timers** โ Benchmark & timeline eksekusi kode
- - ๐พ **Database** โ Query logging dengan support ADODB, PDO, Doctrine
- - ๐ฃ๏ธ **Routes** โ Inspector route (page, op, parameters)
- - ๐ **Views** โ Template render tracker dengan durasi
- - ๐ **Files** โ Daftar file yang di-include/required
- - ๐ฅ **Events** โ Event listener & trigger tracker
- - ๐ **Logs** โ PSR-3 log viewer
- - โ๏ธ **Config** โ Informasi konfigurasi aplikasi
- - ๐ **History** โ Riwayat N request terakhir
-- **Dua mode integrasi:**
- - Output buffering (untuk OJS/legacy)
- - PSR-15 middleware (untuk aplikasi modern)
-- **Adapters built-in:**
- - `AdodbDatabaseAdapter` โ Integrasi native dengan ADODB
- - `WizdamRouterAdapter` โ Integrasi dengan Wizdam Router
-- **Interfaces untuk ekstensibilitas:**
- - `DatabaseAdapterInterface`, `RouterInterface`, `TemplateEngineInterface`, `CollectorInterface`
-- **UI Modern:**
- - Dark mode otomatis (mengikuti preferensi sistem)
- - Responsive design
- - AJAX-based history navigation
- - Real-time toolbar injection
-- **PHP 8.0โ8.4 compatible** โ diuji di PHP 8.4 dengan OJS 2.4.8.5
+| Situasi | Solusi |
+| :--- | :--- |
+| Anda ingin melihat query database yang lambat di aplikasi legacy OJS. | Aktifkan toolbar, dan *DatabaseCollector* akan mencatat semua query ADODB beserta durasinya. |
+| Anda penasaran *view* atau *template* mana yang paling lama dirender. | *ViewsCollector* menampilkan daftar view dan waktu rendering masing-masing. |
+| Anda perlu tahu rute mana yang *match* dengan request saat ini. | *RoutesCollector* akan menunjukkan rute, *controller*, dan parameter. |
+| Anda ingin memantau *event* yang di-*trigger* selama request. | *EventsCollector* menyediakan timeline event. |
+| Toolbar harus bekerja tanpa mengganggu framework utama. | Dirancang *framework-agnostic*, bisa diintegrasikan via *output buffering* atau *middleware*. |
---
-## Persyaratan
+## ๐ง Instalasi
-| Komponen | Versi Minimum |
-|:---|:---|
-| PHP | 8.0 |
-| Composer | 2.x |
-| Browser | Chrome 90+, Firefox 88+, Safari 14+ |
-
-Tidak ada dependensi Composer yang wajib. Dependensi opsional:
-- `psr/http-message` โ untuk integrasi PSR-7 request/response
-- `psr/simple-cache` โ untuk history storage berbasis PSR-16
-
----
-
-## Instalasi
-
-### Via Composer (direkomendasikan)
+### Via Composer (Direkomendasikan)
```bash
-composer require wizdamdebug/debug-toolbar
-```
-
-### Dari repository (development)
-
-```bash
-# Tambahkan repository ke composer.json proyek Anda
-composer config repositories.wizdam-debug-toolbar vcs https://github.com/mokesano/wizdam-debug-toolbar.git
-
-# Install versi development
-composer require wizdamdebug/debug-toolbar:@dev
-```
-
-### Manual (tanpa Composer)
-
-1. Download atau clone repository ini
-2. Salin folder `src/`, `config/`, `public/`, dan `views/` ke direktori library proyek Anda
-3. Daftarkan namespace `WizdamDebugToolbar\` ke autoloader Anda:
-
-```php
-// Di file bootstrap/autoload manual
-spl_autoload_register(function (string $class): void {
- $prefix = 'WizdamDebugToolbar\\';
- $base = __DIR__ . '/libs/wizdam-debug-toolbar/src/';
-
- if (str_starts_with($class, $prefix)) {
- $file = $base . str_replace('\\', '/', substr($class, strlen($prefix))) . '.php';
- if (file_exists($file)) {
- require $file;
- }
- }
-});
+composer require wizdam/debug-toolbar
```
----
+### Integrasi ke Proyek Development
-## Struktur Direktori
+Tambahkan repository ke `composer.json` proyek Anda:
-```
-wizdam-debug-toolbar/
-โโโ src/
-โ โโโ DebugToolbar.php # Engine utama (~20KB)
-โ โโโ Middleware/
-โ โ โโโ DebugToolbarMiddleware.php # PSR-15 + output buffering
-โ โโโ Collectors/ # 10 collectors
-โ โ โโโ BaseCollector.php
-โ โ โโโ TimersCollector.php
-โ โ โโโ DatabaseCollector.php
-โ โ โโโ RoutesCollector.php
-โ โ โโโ ViewsCollector.php
-โ โ โโโ FilesCollector.php
-โ โ โโโ EventsCollector.php
-โ โ โโโ LogsCollector.php
-โ โ โโโ ConfigCollector.php
-โ โ โโโ HistoryCollector.php
-โ โโโ Adapters/ # 2 adapters
-โ โ โโโ AdodbDatabaseAdapter.php # Untuk OJS / ADODB
-โ โ โโโ WizdamRouterAdapter.php # Untuk Wizdam Router
-โ โโโ Interfaces/ # 4 interfaces
-โ โโโ CollectorInterface.php
-โ โโโ DatabaseAdapterInterface.php
-โ โโโ RouterInterface.php
-โ โโโ TemplateEngineInterface.php
-โโโ config/
-โ โโโ wizdamtoolbar.php # File konfigurasi
-โโโ public/
-โ โโโ toolbar.css # ~19KB
-โ โโโ toolbar.js # ~29KB
-โ โโโ toolbarloader.js # ~4KB
-โ โโโ toolbarstandalone.js # ~2KB
-โโโ views/
-โ โโโ toolbar.tpl.php # Main template
-โ โโโ _config.tpl
-โ โโโ _database.tpl
-โ โโโ _events.tpl
-โ โโโ _files.tpl
-โ โโโ _history.tpl
-โ โโโ _logs.tpl
-โ โโโ _routes.tpl
-โโโ composer.json
-โโโ README.md
-โโโ LICENSE
-โโโ SECURITY.md
+```bash
+composer config repositories.wizdam-debug-toolbar vcs https://github.com/mokesano/DebugToolbar.git
+composer require wizdam/debug-toolbar:@dev
```
-**Catatan:** Folder `Wizdam_DEPRICATED/` dan `src_DEPRICATED/` tidak termasuk dalam distribusi package.
+> **Dependensi opsional**: `psr/http-message` (untuk integrasi PSR-7) dan `psr/simple-cache` (untuk *history storage*). Tidak ada dependensi wajib lainnya.
---
-## Cara Penggunaan
+## โก Contoh Penggunaan
### Inisialisasi Dasar
@@ -181,79 +56,24 @@ $toolbar = new DebugToolbar($config);
$toolbar->run();
```
-### 1. Integrasi OJS (Output Buffering)
+### Integrasi Aplikasi Legacy (Output Buffering)
-Cara paling sederhana untuk OJS 2.4.8.5 atau aplikasi PHP legacy apapun.
-Tambahkan tiga baris di file bootstrap utama aplikasi Anda (misalnya `index.php`):
+Cocok untuk aplikasi PHP tanpa *middleware stack*.
```php
-startBuffer(); // mulai menangkap output
-}
+$middleware = new \WizdamDebugToolbar\Middleware\DebugToolbarMiddleware($toolbar);
+$middleware->startBuffer();
-// ... sisa kode bootstrap OJS berjalan normal ...
+// ... logika aplikasi Anda berjalan normal ...
-// Di bagian PALING BAWAH index.php, setelah semua output selesai
-if (defined('WIZDAM_DEBUG') && WIZDAM_DEBUG === true) {
- $middleware->endBuffer(); // inject toolbar & flush output
-}
+$middleware->endBuffer(); // Toolbar otomatis di-inject di akhir HTML
```
-Definisikan konstanta di file konfigurasi environment Anda:
+### Integrasi Database ADODB
-```php
-// config/environment.php atau .env handler
-define('WIZDAM_DEBUG', true); // set false di production
-```
-
-> **Penting:** Jangan pernah mengaktifkan toolbar di environment production.
-> Toolbar menampilkan informasi sensitif seperti query database, konfigurasi server, dan path file.
-
----
-
-### 2. Integrasi Database ADODB
-
-`AdodbDatabaseAdapter` menggunakan pola static accumulator karena ADODB tidak memiliki event hook bawaan. Ada dua cara mengintegrasikannya:
-
-#### Cara A โ Subclass ADOConnection (direkomendasikan)
-
-Buat wrapper tipis di atas koneksi ADODB OJS:
-
-```php
-addCollector(new DatabaseCollector($dbAdapter));
```
-#### Cara B โ Logging manual (untuk kasus khusus)
-
-```php
-use WizdamDebugToolbar\Adapters\AdodbDatabaseAdapter;
-
-$start = microtime(true);
-$result = $dbconn->Execute($sql, $params);
-$ms = (microtime(true) - $start) * 1000;
-
-AdodbDatabaseAdapter::logQuery($sql, $ms, $params ?? []);
-```
-
----
-
-### 3. Integrasi PSR-15 Middleware
+### Integrasi PSR-15 Middleware
-Untuk aplikasi modern yang sudah memiliki stack middleware PSR-15:
+Untuk aplikasi modern yang sudah memiliki *middleware stack*:
```php
-use WizdamDebugToolbar\DebugToolbar;
-use WizdamDebugToolbar\Middleware\DebugToolbarMiddleware;
-
-// Inisialisasi
-$config = require 'config/wizdamtoolbar.php';
-$toolbar = new DebugToolbar($config);
-
-// Tambahkan ke stack middleware PSR-15
-$app->add(new DebugToolbarMiddleware($toolbar));
-```
-
-Atau gunakan mode `process()` manual:
-
-```php
-$config = require 'config/wizdamtoolbar.php';
-$toolbar = new DebugToolbar($config);
-$middleware = new DebugToolbarMiddleware($toolbar);
-
-$htmlOutput = $middleware->process($_REQUEST, function (array $request): string {
- // handler aplikasi Anda โ harus return string HTML
- return $myApp->handle($request);
-});
-
-echo $htmlOutput;
+$app->add(new \WizdamDebugToolbar\Middleware\DebugToolbarMiddleware($toolbar));
```
---
-## Collectors
-
-Collector adalah kelas yang mengumpulkan data tertentu untuk ditampilkan di toolbar.
-
-| Collector | Keterangan | Dependency |
-|:---|:---|:---|
-| `TimersCollector` | Benchmark / timeline eksekusi | Tidak ada |
-| `DatabaseCollector` | Query log, durasi, duplikat | `DatabaseAdapterInterface` |
-| `RoutesCollector` | Route saat ini, controller, params | `RouterInterface` |
-| `ViewsCollector` | Template yang di-render, durasi render | `TemplateEngineInterface` |
-| `FilesCollector` | File yang di-load, penggunaan memori | Tidak ada |
-| `EventsCollector` | Timeline event listener | Tidak ada |
-| `LogsCollector` | Output logger (PSR-3 compatible) | Tidak ada |
-| `ConfigCollector` | Nilai konfigurasi dan ENV vars | Tidak ada |
-| `HistoryCollector` | Riwayat N request terakhir | PSR-16 / file storage |
+## ๐งฉ Fitur Utama
-### Menambah atau menonaktifkan collector
-
-```php
-use WizdamDebugToolbar\DebugToolbar;
-use WizdamDebugToolbar\Collectors\TimersCollector;
-use WizdamDebugToolbar\Collectors\DatabaseCollector;
-
-$config = [
- 'collectors' => [
- TimersCollector::class,
- DatabaseCollector::class,
- // tambahkan hanya yang Anda butuhkan
- ],
-];
-
-$toolbar = new DebugToolbar($config);
-```
+| Kategori | Kolektor / Fitur |
+| :--- | :--- |
+| โฑ๏ธ **Timers** | Benchmark & timeline eksekusi kode |
+| ๐๏ธ **Database** | Query logging (ADODB, PDO, Doctrine) |
+| ๐ฃ๏ธ **Routes** | Inspector rute, *controller*, parameter |
+| ๐ **Views** | Template *render tracker* dengan durasi |
+| ๐ **Files** | Daftar file yang di-*include* / *require* |
+| ๐ก **Events** | *Event listener* & *trigger tracker* |
+| ๐ **Logs** | PSR-3 *log viewer* |
+| โ๏ธ **Config** | Informasi konfigurasi aplikasi |
+| ๐ **History** | Riwayat N *request* terakhir |
---
-## Adapters
-
-Adapter menghubungkan collector dengan implementasi spesifik framework atau library.
-
-### Database
-
-| Adapter | Target | Status |
-|:---|:---|:---|
-| `AdodbDatabaseAdapter` | OJS 2.4.8.5 / ADODB | โ
Tersedia |
-| `PdoDatabaseAdapter` | Aplikasi berbasis PDO | ๐ง Planned |
-| `DoctrineAdapter` | Symfony / Doctrine ORM | ๐ง Planned |
-
-### Router
+## โ๏ธ Konfigurasi
-| Adapter | Target | Status |
-|:---|:---|:---|
-| `WizdamRouterAdapter` | Wizdam Router | โ
Tersedia |
-| `SlimRouterAdapter` | Slim Framework 4 | ๐ง Planned |
-| `LaravelRouterAdapter` | Laravel 10+ | ๐ง Planned |
-
-### Membuat adapter sendiri
-
-Implementasikan interface yang sesuai:
+Salin dan sesuaikan `config/wizdamtoolbar.php`. Beberapa opsi penting:
```php
-getQueries());
- }
-
- public function getDuplicates(): array
- {
- // deteksi query yang dijalankan lebih dari satu kali
- $counts = array_count_values(
- array_column($this->getQueries(), 'sql')
- );
- return array_filter($counts, fn($c) => $c > 1);
- }
-}
-```
-
----
-
-## Konfigurasi
-
-Salin dan sesuaikan file `config/wizdamtoolbar.php`:
-
-```php
- [
- \WizdamDebugToolbar\Collectors\TimersCollector::class,
- \WizdamDebugToolbar\Collectors\DatabaseCollector::class,
- \WizdamDebugToolbar\Collectors\RoutesCollector::class,
- \WizdamDebugToolbar\Collectors\FilesCollector::class,
- \WizdamDebugToolbar\Collectors\EventsCollector::class,
- \WizdamDebugToolbar\Collectors\LogsCollector::class,
- \WizdamDebugToolbar\Collectors\ConfigCollector::class,
- \WizdamDebugToolbar\Collectors\HistoryCollector::class,
- ],
-
- // Jumlah maksimum riwayat request yang disimpan
- 'maxHistory' => 20,
-
- // Direktori penyimpanan file history (harus writable)
- 'historyPath' => sys_get_temp_dir() . '/wizdam-debug-toolbar/',
-
- // Route yang tidak di-inject toolbar (regex pattern)
- 'ignoredRoutes' => [
- '/_wizdam-debug-toolbar',
- '/api/',
- ],
-
- // Tampilan awal toolbar ('minimized' atau 'maximized')
- 'toolbarState' => 'minimized',
-
- // Tema toolbar ('light', 'dark', atau 'auto')
- 'theme' => 'auto',
-
- // Max query time untuk highlighting (ms)
- 'maxQueryTime' => 100,
+ 'collectors' => [ /* ... daftar collector class ... */ ],
+ 'maxHistory' => 20,
+ 'historyPath' => sys_get_temp_dir() . '/wizdam-debug-toolbar/',
+ 'ignoredRoutes'=> ['/_wizdam-debug-toolbar', '/api/'],
+ 'toolbarState' => 'minimized', // 'minimized' atau 'maximized'
+ 'theme' => 'auto', // 'light', 'dark', atau 'auto'
+ 'maxQueryTime' => 100, // Highlight query lambat (ms)
];
```
---
-## Menambah Collector Baru
+## ๐งช Menambah Collector Kustom
-Buat class yang mengimplementasikan `CollectorInterface`:
+Implementasikan `CollectorInterface`:
```php
- MyCacheDriver::getHits(),
- 'misses' => MyCacheDriver::getMisses(),
- ];
- }
-
- public function isEnabled(): bool
- {
- return class_exists('MyCacheDriver');
- }
-
- public function getBadgeValue(): string|int|null
- {
- return MyCacheDriver::getHits() . ' hits';
- }
-
- public function getIcon(): string
- {
- return 'cache'; // nama icon dari set toolbar
- }
+ public function getData(): array { /* ... */ }
+ public function isEnabled(): bool { /* ... */ }
+ public function getBadgeValue(): string|int|null { /* ... */ }
+ public function getIcon(): string { /* ... */ }
}
```
Daftarkan ke toolbar:
```php
-use WizdamDebugToolbar\DebugToolbar;
-use MyApp\Collectors\CacheCollector;
-
-$config = require 'config/wizdamtoolbar.php';
$config['collectors'][] = CacheCollector::class;
-
$toolbar = new DebugToolbar($config);
```
---
-## Troubleshooting
-
-### Toolbar tidak muncul
-
-1. **Pastikan `WIZDAM_DEBUG` didefinisikan sebagai `true`**
- ```php
- define('WIZDAM_DEBUG', true);
- ```
-
-2. **Cek apakah output buffering aktif**
- Pastikan `startBuffer()` dipanggil sebelum ada output HTML dan `endBuffer()` dipanggil setelah semua output selesai.
-
-3. **Periksa ignored routes**
- Jika URL Anda match dengan pattern di `ignoredRoutes`, toolbar tidak akan di-inject.
-
-4. **Cek browser console untuk error JavaScript**
- Tekan F12 > Console dan cari error terkait `toolbar.js`.
-
-### Query database tidak tercatat
-
-1. **Pastikan adapter sudah didaftarkan**
- ```php
- $dbAdapter = new AdodbDatabaseAdapter();
- $toolbar->addCollector(new DatabaseCollector($dbAdapter));
- ```
-
-2. **Untuk ADODB: Pastikan wrapper class digunakan**
- Gunakan `WizdamAdodbConnection` atau logging manual via `AdodbDatabaseAdapter::logQuery()`.
-
-### Error "Class not found"
-
-Pastikan autoloader sudah dikonfigurasi dengan benar untuk namespace `WizdamDebugToolbar\`:
-
-```php
-spl_autoload_register(function (string $class): void {
- $prefix = 'WizdamDebugToolbar\\';
- // ... sesuaikan path ke folder src/
-});
-```
-
-Atau gunakan Composer autoloading:
-```bash
-composer dump-autoload
-```
-
-### History tidak tersimpan
-
-1. **Pastikan direktori `historyPath` writable**
- ```php
- 'historyPath' => sys_get_temp_dir() . '/wizdam-debug-toolbar/',
- ```
-
-2. **Cek permission folder**
- ```bash
- chmod 755 /tmp/wizdam-debug-toolbar
- ```
-
----
-
-## Kompatibilitas Framework
-
-| Framework / Platform | Versi | Status | Adapter tersedia |
-|:---|:---|:---|:---|
-| OJS (Open Journal Systems) | 2.4.8.5 | โ
Diuji | Database, Router |
-| PHP Native / Custom | 8.0โ8.4 | โ
Diuji | โ |
-| Wizdam Router | Latest | โ
Diuji | Router |
-| Slim Framework | 4.x | ๐ง Planned | โ |
-| Laravel | 10, 11 | ๐ง Planned | โ |
-| Symfony | 6, 7 | ๐ง Planned | โ |
-| CodeIgniter 3 | 3.1.x | ๐ง Planned | โ |
-
----
-
-## Atribusi & Lisensi
-
-**Wizdam Debug Toolbar** diekstraksi dan direkayasa ulang dari **CodeIgniter4 v4.7.2 Debug Toolbar**, yang dikembangkan oleh [CodeIgniter Foundation](https://codeigniter.com) dan kontributornya.
-
-### File yang diadaptasi dari CodeIgniter4:
-- `src/Collectors/` โ berdasarkan `system/Debug/Toolbar/Collectors/`
-- `views/` โ berdasarkan `system/Debug/Toolbar/Views/`
-- `public/` โ berdasarkan `system/Debug/Toolbar/` (CSS, JS)
-
-Semua file tersebut telah dimodifikasi dengan:
-- Perubahan namespace dari `CodeIgniter\Debug\Toolbar` ke `WizdamDebugToolbar`
-- Penghapusan dependency framework CI4
-- Adaptasi untuk standalone usage
-
-### File yang dibuat baru (tidak berasal dari CodeIgniter4):
-- `src/Interfaces/` โ seluruh interface (`CollectorInterface`, `DatabaseAdapterInterface`, `RouterInterface`, `TemplateEngineInterface`)
-- `src/Adapters/` โ seluruh adapter (`AdodbDatabaseAdapter`, `WizdamRouterAdapter`)
-- `src/Middleware/DebugToolbarMiddleware.php`
-- `src/DebugToolbar.php` โ main engine yang direkayasa ulang
-- `config/wizdamtoolbar.php` โ konfigurasi standalone
-
----
-
-### Lisensi
+## ๐ Troubleshooting
-**MIT License**
-
-Copyright (c) 2025 [Sangia Publishing House](https://www.sangia.org)
-Copyright (c) 2014-2024 British Columbia Institute of Technology (CodeIgniter Foundation)
-
-Lihat file [LICENSE](LICENSE) untuk teks lisensi lengkap.
+| Masalah | Solusi |
+| :--- | :--- |
+| Toolbar tidak muncul | Pastikan `WIZDAM_DEBUG = true` dan *buffer* dimulai sebelum output HTML. |
+| Query database tidak tercatat | Pastikan adapter (mis. `AdodbDatabaseAdapter`) sudah didaftarkan ke `DatabaseCollector`. |
+| Error "Class not found" | Jalankan `composer dump-autoload` atau periksa konfigurasi *autoloader* manual. |
+| History tidak tersimpan | Pastikan direktori `historyPath` *writable* (chmod 755). |
---
-## Support & Kontribusi
+## ๐ Lisensi
-Untuk pertanyaan, bug report, atau feature request:
+**MIT License** โ Copyright (c) 2025 Sangia Publishing House. Lihat [LICENSE](https://github.com/mokesano/DebugToolbar/blob/master/LICENSE) untuk teks lengkap.
-- ๐ง Email: dev@sangia.org
-- ๐ Issue Tracker: https://github.com/mokesano/WizdamDebugToolbar/issues
-- ๐ Dokumentasi: README.md ini
-
-Kontribusi sangat diterima! Silakan fork repository dan buat pull request.
+> Toolbar ini diadaptasi dari [CodeIgniter 4 Debug Toolbar](https://github.com/codeigniter4/CodeIgniter4) (Copyright British Columbia Institute of Technology). Digunakan dengan modifikasi dan izin sesuai lisensi MIT asli.
---
-*Dikembangkan sebagai bagian dari ekosistem **Wizdam Frontedge** โ platform penerbitan ilmiah berbasis OJS dengan arsitektur modern.*
-
-**Happy Debugging! ๐๐**
+
+ Dibangun dengan โค๏ธ sebagai bagian dari ekosistem Wizdam Frontedge โ platform penerbitan ilmiah modern.
+
+
+
+
diff --git a/SECURITY.md b/SECURITY.md
index 87b894e..584b49d 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -6,7 +6,7 @@ The development team and community take all security issues seriously. **Please
Thank you for improving the security of our code! Any assistance in removing security flaws will be acknowledged.
-**Please report security flaws by emailing the development team directly: security@codeigniter.com**.
+**Please report security flaws by emailing the development team directly: security@sangia.org**.
The lead maintainer will acknowledge your email within 48 hours, and will send a more detailed response within 48 hours indicating
the next steps in handling your report. After the initial reply to your report, the security team will endeavor to keep you informed of the
@@ -20,7 +20,7 @@ This person will coordinate the fix and release process, involving the following
- Confirm the problem and determine the affected versions.
- Audit code to find any potential similar problems.
- Prepare fixes for all releases still under maintenance. These fixes will be released as fast as possible.
-- Publish security advisories at https://github.com/codeigniter4/CodeIgniter4/security/advisories
+- Publish security advisories at https://github.com/mokesano/WizdamDebugToolbar/security/advisories
## Comments on this Policy
diff --git a/Wizdam_DEPRICATED/DebugBar/Collectors/BaseCollector.php b/Wizdam_DEPRICATED/DebugBar/Collectors/BaseCollector.php
deleted file mode 100644
index 96fe549..0000000
--- a/Wizdam_DEPRICATED/DebugBar/Collectors/BaseCollector.php
+++ /dev/null
@@ -1,229 +0,0 @@
-title));
- }
-
- return $this->title;
- }
-
- /**
- * Returns any information that should be shown next to the title.
- */
- public function getTitleDetails(): string
- {
- return '';
- }
-
- /**
- * Does this collector need it's own tab?
- */
- public function hasTabContent(): bool
- {
- return (bool) $this->hasTabContent;
- }
-
- /**
- * Does this collector have a label?
- */
- public function hasLabel(): bool
- {
- return (bool) $this->hasLabel;
- }
-
- /**
- * Does this collector have information for the timeline?
- */
- public function hasTimelineData(): bool
- {
- return (bool) $this->hasTimeline;
- }
-
- /**
- * Grabs the data for the timeline, properly formatted,
- * or returns an empty array.
- */
- public function timelineData(): array
- {
- if (! $this->hasTimeline) {
- return [];
- }
-
- return $this->formatTimelineData();
- }
-
- /**
- * Does this Collector have data that should be shown in the
- * 'Vars' tab?
- */
- public function hasVarData(): bool
- {
- return (bool) $this->hasVarData;
- }
-
- /**
- * Gets a collection of data that should be shown in the 'Vars' tab.
- * The format is an array of sections, each with their own array
- * of key/value pairs:
- *
- * $data = [
- * 'section 1' => [
- * 'foo' => 'bar,
- * 'bar' => 'baz'
- * ],
- * 'section 2' => [
- * 'foo' => 'bar,
- * 'bar' => 'baz'
- * ],
- * ];
- *
- * @return array|null
- */
- public function getVarData()
- {
- return null;
- }
-
- /**
- * Child classes should implement this to return the timeline data
- * formatted for correct usage.
- *
- * Timeline data should be formatted into arrays that look like:
- *
- * [
- * 'name' => 'Database::Query',
- * 'component' => 'Database',
- * 'start' => 10 // milliseconds
- * 'duration' => 15 // milliseconds
- * ]
- */
- protected function formatTimelineData(): array
- {
- return [];
- }
-
- /**
- * Returns the data of this collector to be formatted in the toolbar
- *
- * @return array|string
- */
- public function display()
- {
- return [];
- }
-
- /**
- * This makes nicer looking paths for the error output.
- *
- * @deprecated Use the dedicated `clean_path()` function.
- */
- public function cleanPath(string $file): string
- {
- return clean_path($file);
- }
-
- /**
- * Gets the "badge" value for the button.
- *
- * @return int|null
- */
- public function getBadgeValue()
- {
- return null;
- }
-
- /**
- * Does this collector have any data collected?
- *
- * If not, then the toolbar button won't get shown.
- */
- public function isEmpty(): bool
- {
- return false;
- }
-
- /**
- * Returns the HTML to display the icon. Should either
- * be SVG, or a base-64 encoded.
- *
- * Recommended dimensions are 24px x 24px
- */
- public function icon(): string
- {
- return '';
- }
-
- /**
- * Return settings as an array.
- */
- public function getAsArray(): array
- {
- return [
- 'title' => $this->getTitle(),
- 'titleSafe' => $this->getTitle(true),
- 'titleDetails' => $this->getTitleDetails(),
- 'display' => $this->display(),
- 'badgeValue' => $this->getBadgeValue(),
- 'isEmpty' => $this->isEmpty(),
- 'hasTabContent' => $this->hasTabContent(),
- 'hasLabel' => $this->hasLabel(),
- 'icon' => $this->icon(),
- 'hasTimelineData' => $this->hasTimelineData(),
- 'timelineData' => $this->timelineData(),
- ];
- }
-}
diff --git a/Wizdam_DEPRICATED/DebugBar/Collectors/Config.php b/Wizdam_DEPRICATED/DebugBar/Collectors/Config.php
deleted file mode 100644
index 8a4fb20..0000000
--- a/Wizdam_DEPRICATED/DebugBar/Collectors/Config.php
+++ /dev/null
@@ -1,28 +0,0 @@
- '',
- 'phpVersion' => PHP_VERSION,
- 'phpSAPI' => PHP_SAPI,
- 'environment' => defined('ENVIRONMENT') ? ENVIRONMENT : 'production',
- 'baseURL' => $config['baseURL'] ?? '',
- 'timezone' => date_default_timezone_get(),
- 'locale' => $config['locale'] ?? 'en',
- 'cspEnabled' => $config['cspEnabled'] ?? false,
- ];
- }
-}
diff --git a/Wizdam_DEPRICATED/DebugBar/Collectors/Database.php b/Wizdam_DEPRICATED/DebugBar/Collectors/Database.php
deleted file mode 100644
index 20cee25..0000000
--- a/Wizdam_DEPRICATED/DebugBar/Collectors/Database.php
+++ /dev/null
@@ -1,258 +0,0 @@
-connections = $connections;
- }
-
- /**
- * The static method used to collect query data.
- *
- * @param array $query Query data array with keys: query, string, start_time, duration, trace
- */
- public static function collectQuery(array $query): void
- {
- $configMax = 100; // Default max queries
-
- if (count(static::$queries) < $configMax) {
- $queryString = $query['string'] ?? '';
-
- $backtrace = $query['trace'] ?? debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
-
- if (! empty($backtrace)) {
- // Simplify backtrace
- foreach ($backtrace as &$line) {
- if (isset($line['file'])) {
- $line['file'] = clean_path($line['file']) . ':' . ($line['line'] ?? '');
- unset($line['line']);
- }
- }
- }
-
- static::$queries[] = [
- 'query' => $query,
- 'string' => $queryString,
- 'duplicate' => in_array($queryString, array_column(static::$queries, 'string'), true),
- 'trace' => $backtrace,
- ];
- }
- }
-
- /**
- * Returns timeline data formatted for the toolbar.
- *
- * @return array The formatted data or an empty array.
- */
- protected function formatTimelineData(): array
- {
- $data = [];
-
- foreach ($this->connections as $alias => $connection) {
- // Connection Time
- $data[] = [
- 'name' => 'Connecting to Database: "' . $alias . '"',
- 'component' => 'Database',
- 'start' => $connection->getConnectStart(),
- 'duration' => $connection->getConnectDuration(),
- ];
- }
-
- foreach (static::$queries as $query) {
- $data[] = [
- 'name' => 'Query',
- 'component' => 'Database',
- 'start' => $query['query']->getStartTime(true),
- 'duration' => $query['query']->getDuration(),
- 'query' => $query['query']->debugToolbarDisplay(),
- ];
- }
-
- return $data;
- }
-
- /**
- * Returns the data of this collector to be formatted in the toolbar
- */
- public function display(): array
- {
- return ['queries' => array_map(static function (array $query): array {
- $isDuplicate = $query['duplicate'] === true;
-
- $firstNonSystemLine = '';
-
- foreach ($query['trace'] as $index => &$line) {
- // simplify file and line
- if (isset($line['file'])) {
- $line['file'] = clean_path($line['file']) . ':' . $line['line'];
- unset($line['line']);
- } else {
- $line['file'] = '[internal function]';
- }
-
- // find the first trace line that does not originate from `system/`
- if ($firstNonSystemLine === '' && ! str_contains($line['file'], 'SYSTEMPATH')) {
- $firstNonSystemLine = $line['file'];
- }
-
- // simplify function call
- if (isset($line['class'])) {
- $line['function'] = $line['class'] . $line['type'] . $line['function'];
- unset($line['class'], $line['type']);
- }
-
- if (strrpos($line['function'], '{closure}') === false) {
- $line['function'] .= '()';
- }
-
- $line['function'] = str_repeat(chr(0xC2) . chr(0xA0), 8) . $line['function'];
-
- // add index numbering padded with nonbreaking space
- $indexPadded = str_pad(sprintf('%d', $index + 1), 3, ' ', STR_PAD_LEFT);
- $indexPadded = preg_replace('/\s/', chr(0xC2) . chr(0xA0), $indexPadded);
-
- $line['index'] = $indexPadded . str_repeat(chr(0xC2) . chr(0xA0), 4);
- }
-
- return [
- 'hover' => $isDuplicate ? 'This query was called more than once.' : '',
- 'class' => $isDuplicate ? 'duplicate' : '',
- 'duration' => ((float) $query['query']->getDuration(5) * 1000) . ' ms',
- 'sql' => $query['query']->debugToolbarDisplay(),
- 'trace' => $query['trace'],
- 'trace-file' => $firstNonSystemLine,
- 'qid' => md5($query['query'] . Time::now()->format('0.u00 U')),
- ];
- }, static::$queries)];
- }
-
- /**
- * Gets the "badge" value for the button.
- */
- public function getBadgeValue(): int
- {
- return count(static::$queries);
- }
-
- /**
- * Information to be displayed next to the title.
- *
- * @return string The number of queries (in parentheses) or an empty string.
- */
- public function getTitleDetails(): string
- {
- $this->getConnections();
-
- $queryCount = count(static::$queries);
- $uniqueCount = count(array_filter(static::$queries, static fn ($query): bool => $query['duplicate'] === false));
- $connectionCount = count($this->connections);
-
- return sprintf(
- '(%d total Quer%s, %d %s unique across %d Connection%s)',
- $queryCount,
- $queryCount > 1 ? 'ies' : 'y',
- $uniqueCount,
- $uniqueCount > 1 ? 'of them' : '',
- $connectionCount,
- $connectionCount > 1 ? 's' : '',
- );
- }
-
- /**
- * Does this collector have any data collected?
- */
- public function isEmpty(): bool
- {
- return static::$queries === [];
- }
-
- /**
- * Display the icon.
- *
- * Icon from https://icons8.com - 1em package
- */
- public function icon(): string
- {
- return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADMSURBVEhLY6A3YExLSwsA4nIycQDIDIhRWEBqamo/UNF/SjDQjF6ocZgAKPkRiFeEhoYyQ4WIBiA9QAuWAPEHqBAmgLqgHcolGQD1V4DMgHIxwbCxYD+QBqcKINseKo6eWrBioPrtQBq/BcgY5ht0cUIYbBg2AJKkRxCNWkDQgtFUNJwtABr+F6igE8olGQD114HMgHIxAVDyAhA/AlpSA8RYUwoeXAPVex5qHCbIyMgwBCkAuQJIY00huDBUz/mUlBQDqHGjgBjAwAAACexpph6oHSQAAAAASUVORK5CYII=';
- }
-
- /**
- * Gets the connections from the database config
- */
- private function getConnections(): void
- {
- $this->connections = \Config\Database::getConnections();
- }
-
- /**
- * Reset collector state for worker mode.
- * Clears collected queries between requests.
- */
- public function reset(): void
- {
- static::$queries = [];
- }
-}
diff --git a/Wizdam_DEPRICATED/DebugBar/Collectors/Events.php b/Wizdam_DEPRICATED/DebugBar/Collectors/Events.php
deleted file mode 100644
index 38e8705..0000000
--- a/Wizdam_DEPRICATED/DebugBar/Collectors/Events.php
+++ /dev/null
@@ -1,114 +0,0 @@
- 'Event: ' . $info['event'],
- 'component' => 'Events',
- 'start' => $info['start'],
- 'duration' => $info['end'] - $info['start'],
- ];
- }
-
- return $data;
- }
-
- /**
- * Returns the data of this collector to be formatted in the toolbar
- */
- public function display(): array
- {
- $data = [
- 'events' => [],
- ];
-
- foreach (\CodeIgniter\Events\Events::getPerformanceLogs() as $row) {
- $key = $row['event'];
-
- if (! array_key_exists($key, $data['events'])) {
- $data['events'][$key] = [
- 'event' => $key,
- 'duration' => ($row['end'] - $row['start']) * 1000,
- 'count' => 1,
- ];
-
- continue;
- }
-
- $data['events'][$key]['duration'] += ($row['end'] - $row['start']) * 1000;
- $data['events'][$key]['count']++;
- }
-
- foreach ($data['events'] as &$row) {
- $row['duration'] = number_format($row['duration'], 2);
- }
-
- return $data;
- }
-
- /**
- * Gets the "badge" value for the button.
- */
- public function getBadgeValue(): int
- {
- return count(\CodeIgniter\Events\Events::getPerformanceLogs());
- }
-
- /**
- * Display the icon.
- *
- * Icon from https://icons8.com - 1em package
- */
- public function icon(): string
- {
- return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEASURBVEhL7ZXNDcIwDIVTsRBH1uDQDdquUA6IM1xgCA6MwJUN2hk6AQzAz0vl0ETUxC5VT3zSU5w81/mRMGZysixbFEVR0jSKNt8geQU9aRpFmp/keX6AbjZ5oB74vsaN5lSzA4tLSjpBFxsjeSuRy4d2mDdQTWU7YLbXTNN05mKyovj5KL6B7q3hoy3KwdZxBlT+Ipz+jPHrBqOIynZgcZonoukb/0ckiTHqNvDXtXEAaygRbaB9FvUTjRUHsIYS0QaSp+Dw6wT4hiTmYHOcYZsdLQ2CbXa4ftuuYR4x9vYZgdb4vsFYUdmABMYeukK9/SUme3KMFQ77+Yfzh8eYF8+orDuDWU5LAAAAAElFTkSuQmCC';
- }
-}
diff --git a/Wizdam_DEPRICATED/DebugBar/Collectors/Files.php b/Wizdam_DEPRICATED/DebugBar/Collectors/Files.php
deleted file mode 100644
index 3f72fc0..0000000
--- a/Wizdam_DEPRICATED/DebugBar/Collectors/Files.php
+++ /dev/null
@@ -1,93 +0,0 @@
- $path,
- 'name' => basename($file),
- ];
- } else {
- $userFiles[] = [
- 'path' => $path,
- 'name' => basename($file),
- ];
- }
- }
-
- sort($userFiles);
- sort($coreFiles);
-
- return [
- 'coreFiles' => $coreFiles,
- 'userFiles' => $userFiles,
- ];
- }
-
- /**
- * Displays the number of included files as a badge in the tab button.
- */
- public function getBadgeValue(): int
- {
- return count(get_included_files());
- }
-
- /**
- * Display the icon.
- *
- * Icon from https://icons8.com - 1em package
- */
- public function icon(): string
- {
- return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGBSURBVEhL7ZQ9S8NQGIVTBQUncfMfCO4uLgoKbuKQOWg+OkXERRE1IAXrIHbVDrqIDuLiJgj+gro7S3dnpfq88b1FMTE3VZx64HBzzvvZWxKnj15QCcPwCD5HUfSWR+JtzgmtsUcQBEva5IIm9SwSu+95CAWbUuy67qBa32ByZEDpIaZYZSZMjjQuPcQUq8yEyYEb8FSerYeQVGbAFzJkX1PyQWLhgCz0BxTCekC1Wp0hsa6yokzhed4oje6Iz6rlJEkyIKfUEFtITVtQdAibn5rMyaYsMS+a5wTv8qeXMhcU16QZbKgl3hbs+L4/pnpdc87MElZgq10p5DxGdq8I7xrvUWUKvG3NbSK7ubngYzdJwSsF7TiOh9VOgfcEz1UayNe3JUPM1RWC5GXYgTfc75B4NBmXJnAtTfpABX0iPvEd9ezALwkplCFXcr9styiNOKc1RRZpaPM9tcqBwlWzGY1qPL9wjqRBgF5BH6j8HWh2S7MHlX8PrmbK+k/8PzjOOzx1D3i1pKTTAAAAAElFTkSuQmCC';
- }
-}
diff --git a/Wizdam_DEPRICATED/DebugBar/Collectors/History.php b/Wizdam_DEPRICATED/DebugBar/Collectors/History.php
deleted file mode 100644
index a946042..0000000
--- a/Wizdam_DEPRICATED/DebugBar/Collectors/History.php
+++ /dev/null
@@ -1,132 +0,0 @@
-= 0 && $counter > $limit) {
- unlink($filename);
-
- continue;
- }
-
- // Get the contents of this specific history request
- $contents = file_get_contents($filename);
-
- $contents = @json_decode($contents);
- if (json_last_error() === JSON_ERROR_NONE) {
- preg_match('/debugbar_(.*)\.json$/s', $filename, $time);
- $time = sprintf('%.6F', $time[1] ?? 0);
-
- // Debugbar files shown in History Collector
- $files[] = [
- 'time' => $time,
- 'datetime' => DateTime::createFromFormat('U.u', $time)->format('Y-m-d H:i:s.u'),
- 'active' => $time === $current,
- 'status' => $contents->vars->response->statusCode,
- 'method' => $contents->method,
- 'url' => $contents->url,
- 'isAJAX' => $contents->isAJAX ? 'Yes' : 'No',
- 'contentType' => $contents->vars->response->contentType,
- ];
- }
- }
-
- $this->files = $files;
- }
-
- /**
- * Returns the data of this collector to be formatted in the toolbar
- */
- public function display(): array
- {
- return ['files' => $this->files];
- }
-
- /**
- * Displays the number of included files as a badge in the tab button.
- */
- public function getBadgeValue(): int
- {
- return count($this->files);
- }
-
- /**
- * Return true if there are no history files.
- */
- public function isEmpty(): bool
- {
- return $this->files === [];
- }
-
- /**
- * Display the icon.
- *
- * Icon from https://icons8.com - 1em package
- */
- public function icon(): string
- {
- return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAJySURBVEhL3ZU7aJNhGIVTpV6i4qCIgkIHxcXLErS4FBwUFNwiCKGhuTYJGaIgnRoo4qRu6iCiiIuIXXTTIkIpuqoFwaGgonUQlC5KafU5ycmNP0lTdPLA4fu+8573/a4/f6hXpFKpwUwmc9fDfweKbk+n07fgEv33TLSbtt/hvwNFT1PsG/zdTE0Gp+GFfD6/2fbVIxqNrqPIRbjg4t/hY8aztcngfDabHXbKyiiXy2vcrcPH8oDCry2FKDrA+Ar6L01E/ypyXzXaARjDGGcoeNxSDZXE0dHRA5VRE5LJ5CFy5jzJuOX2wHRHRnjbklZ6isQ3tIctBaAd4vlK3jLtkOVWqABBXd47jGHLmjTmSScttQV5J+SjfcUweFQEbsjAas5aqoCLXutJl7vtQsAzpRowYqkBinyCC8Vicb2lOih8zoldd0F8RD7qTFiqAnGrAy8stUAvi/hbqDM+YzkAFrLPdR5ZqoLXsd+Bh5YCIH7JniVdquUWxOPxDfboHhrI5XJ7HHhiqQXox+APe/Qk64+gGYVCYZs8cMpSFQj9JOoFzVqqo7k4HIvFYpscCoAjOmLffUsNUGRaQUwDlmofUa34ecsdgXdcXo4wbakBgiUFafXJV8A4DJ/2UrxUKm3E95H8RbjLcgOJRGILhnmCP+FBy5XvwN2uIPcy1AJvWgqC4xm2aU4Xb3lF4I+Tpyf8hRe5w3J7YLymSeA8Z3nSclv4WLRyFdfOjzrUFX0klJUEtZtntCNc+F69cz/FiDzEPtjzmcUMOr83kDQEX6pAJxJfpL3OX22n01YN7SZCoQnaSdoZ+Jz+PZihH3wt/xlCoT9M6nEtmRSPCQAAAABJRU5ErkJggg==';
- }
-}
diff --git a/Wizdam_DEPRICATED/DebugBar/Collectors/Logs.php b/Wizdam_DEPRICATED/DebugBar/Collectors/Logs.php
deleted file mode 100644
index 4b69f9b..0000000
--- a/Wizdam_DEPRICATED/DebugBar/Collectors/Logs.php
+++ /dev/null
@@ -1,82 +0,0 @@
-
- */
- protected $data = [];
-
- /**
- * Returns the data of this collector to be formatted in the toolbar.
- *
- * @return array{logs: list}
- */
- public function display(): array
- {
- return [
- 'logs' => $this->data,
- ];
- }
-
- /**
- * Does this collector actually have any data to display?
- */
- public function isEmpty(): bool
- {
- return $this->data === [];
- }
-
- /**
- * Display the icon.
- *
- * Icon from https://icons8.com - 1em package
- */
- public function icon(): string
- {
- return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACYSURBVEhLYxgFJIHU1FSjtLS0i0D8AYj7gEKMEBkqAaAFF4D4ERCvAFrwH4gDoFIMKSkpFkB+OTEYqgUTACXfA/GqjIwMQyD9H2hRHlQKJFcBEiMGQ7VgAqCBvUgK32dmZspCpagGGNPT0/1BLqeF4bQHQJePpiIwhmrBBEADR1MRfgB0+WgqAmOoFkwANHA0FY0CUgEDAwCQ0PUpNB3kqwAAAABJRU5ErkJggg==';
- }
-
- /**
- * Set logs data manually
- *
- * @param list $logs
- */
- public function setLogs(array $logs): void
- {
- $this->data = $logs;
- }
-}
diff --git a/Wizdam_DEPRICATED/DebugBar/Collectors/Routes.php b/Wizdam_DEPRICATED/DebugBar/Collectors/Routes.php
deleted file mode 100644
index fb98c44..0000000
--- a/Wizdam_DEPRICATED/DebugBar/Collectors/Routes.php
+++ /dev/null
@@ -1,158 +0,0 @@
-
- * }>,
- * routes: list
- * }
- *
- * @throws ReflectionException
- */
- public function display(): array
- {
- $rawRoutes = service('routes', true);
- $router = service('router', null, null, true);
-
- // Get our parameters
- // Closure routes
- if (is_callable($router->controllerName())) {
- $method = new ReflectionFunction($router->controllerName());
- } else {
- try {
- $method = new ReflectionMethod($router->controllerName(), $router->methodName());
- } catch (ReflectionException) {
- try {
- // If we're here, the method doesn't exist
- // and is likely calculated in _remap.
- $method = new ReflectionMethod($router->controllerName(), '_remap');
- } catch (ReflectionException) {
- // If we're here, page cache is returned. The router is not executed.
- return [
- 'matchedRoute' => [],
- 'routes' => [],
- ];
- }
- }
- }
-
- $rawParams = $method->getParameters();
-
- $params = [];
-
- foreach ($rawParams as $key => $param) {
- $params[] = [
- 'name' => '$' . $param->getName() . ' = ',
- 'value' => $router->params()[$key] ??
- ' | default: '
- . var_export(
- $param->isDefaultValueAvailable() ? $param->getDefaultValue() : null,
- true,
- ),
- ];
- }
-
- $matchedRoute = [
- [
- 'directory' => $router->directory(),
- 'controller' => $router->controllerName(),
- 'method' => $router->methodName(),
- 'paramCount' => count($router->params()),
- 'truePCount' => count($params),
- 'params' => $params,
- ],
- ];
-
- // Defined Routes
- $routes = [];
-
- $definedRouteCollector = new DefinedRouteCollector($rawRoutes);
-
- foreach ($definedRouteCollector->collect() as $route) {
- // filter for strings, as callbacks aren't displayable
- if ($route['handler'] !== '(Closure)') {
- $routes[] = [
- 'method' => strtoupper($route['method']),
- 'route' => $route['route'],
- 'handler' => $route['handler'],
- ];
- }
- }
-
- return [
- 'matchedRoute' => $matchedRoute,
- 'routes' => $routes,
- ];
- }
-
- /**
- * Returns a count of all the routes in the system.
- */
- public function getBadgeValue(): int
- {
- $rawRoutes = service('routes', true);
-
- return count($rawRoutes->getRoutes());
- }
-
- /**
- * Display the icon.
- *
- * Icon from https://icons8.com - 1em package
- */
- public function icon(): string
- {
- return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFDSURBVEhL7ZRNSsNQFIUjVXSiOFEcuQIHDpzpxC0IGYeE/BEInbWlCHEDLsSiuANdhKDjgm6ggtSJ+l25ldrmmTwIgtgDh/t37r1J+16cX0dRFMtpmu5pWAkrvYjjOB7AETzStBFW+inxu3KUJMmhludQpoflS1zXban4LYqiO224h6VLTHr8Z+z8EpIHFF9gG78nDVmW7UgTHKjsCyY98QP+pcq+g8Ku2s8G8X3f3/I8b038WZTp+bO38zxfFd+I6YY6sNUvFlSDk9CRhiAI1jX1I9Cfw7GG1UB8LAuwbU0ZwQnbRDeEN5qqBxZMLtE1ti9LtbREnMIuOXnyIf5rGIb7Wq8HmlZgwYBH7ORTcKH5E4mpjeGt9fBZcHE2GCQ3Vt7oTNPNg+FXLHnSsHkw/FR+Gg2bB8Ptzrst/v6C/wrH+QB+duli6MYJdQAAAABJRU5ErkJggg==';
- }
-}
diff --git a/Wizdam_DEPRICATED/DebugBar/Collectors/Timers.php b/Wizdam_DEPRICATED/DebugBar/Collectors/Timers.php
deleted file mode 100644
index f6a3b4d..0000000
--- a/Wizdam_DEPRICATED/DebugBar/Collectors/Timers.php
+++ /dev/null
@@ -1,76 +0,0 @@
-timers = $timers;
- }
-
- /**
- * Child classes should implement this to return the timeline data
- * formatted for correct usage.
- */
- protected function formatTimelineData(): array
- {
- $data = [];
-
- if ($this->timers === null) {
- return $data;
- }
-
- foreach ($this->timers as $name => $info) {
- if ($name === 'total_execution') {
- continue;
- }
-
- $data[] = [
- 'name' => ucwords(str_replace('_', ' ', $name)),
- 'component' => 'Timer',
- 'start' => $info['start'] ?? 0,
- 'duration' => ($info['end'] ?? 0) - ($info['start'] ?? 0),
- ];
- }
-
- return $data;
- }
-}
diff --git a/Wizdam_DEPRICATED/DebugBar/Collectors/Views.php b/Wizdam_DEPRICATED/DebugBar/Collectors/Views.php
deleted file mode 100644
index 6479a08..0000000
--- a/Wizdam_DEPRICATED/DebugBar/Collectors/Views.php
+++ /dev/null
@@ -1,139 +0,0 @@
-viewer ??= service('renderer');
- }
-
- /**
- * Child classes should implement this to return the timeline data
- * formatted for correct usage.
- */
- protected function formatTimelineData(): array
- {
- $this->initViewer();
-
- $data = [];
-
- $rows = $this->viewer->getPerformanceData();
-
- foreach ($rows as $info) {
- $data[] = [
- 'name' => 'View: ' . $info['view'],
- 'component' => 'Views',
- 'start' => $info['start'],
- 'duration' => $info['end'] - $info['start'],
- ];
- }
-
- return $data;
- }
-
- /**
- * Gets a collection of data that should be shown in the 'Vars' tab.
- * The format is an array of sections, each with their own array
- * of key/value pairs:
- *
- * $data = [
- * 'section 1' => [
- * 'foo' => 'bar,
- * 'bar' => 'baz'
- * ],
- * 'section 2' => [
- * 'foo' => 'bar,
- * 'bar' => 'baz'
- * ],
- * ];
- */
- public function getVarData(): array
- {
- $this->initViewer();
-
- return [
- 'View Data' => $this->viewer->getData(),
- ];
- }
-
- /**
- * Returns a count of all views.
- */
- public function getBadgeValue(): int
- {
- $this->initViewer();
-
- return count($this->viewer->getPerformanceData());
- }
-
- /**
- * Display the icon.
- *
- * Icon from https://icons8.com - 1em package
- */
- public function icon(): string
- {
- return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADeSURBVEhL7ZSxDcIwEEWNYA0YgGmgyAaJLTcUaaBzQQEVjMEabBQxAdw53zTHiThEovGTfnE/9rsoRUxhKLOmaa6Uh7X2+UvguLCzVxN1XW9x4EYHzik033Hp3X0LO+DaQG8MDQcuq6qao4qkHuMgQggLvkPLjqh00ZgFDBacMJYFkuwFlH1mshdkZ5JPJERA9JpI6xNCBESvibQ+IURA9JpI6xNCBESvibQ+IURA9DTsuHTOrVFFxixgB/eUFlU8uKJ0eDBFOu/9EvoeKnlJS2/08Tc8NOwQ8sIfMeYFjqKDjdU2sp4AAAAASUVORK5CYII=';
- }
-}
diff --git a/composer.json b/composer.json
index 36d54b6..edfae69 100644
--- a/composer.json
+++ b/composer.json
@@ -1,8 +1,12 @@
{
"name": "wizdamdebug/debug-toolbar",
"description": "Standalone Debug Toolbar based on CodeIgniter4 DebugBar - Framework Agnostic",
+ "version": "1.0.0.0",
"type": "library",
"license": "MIT",
+ "config": {
+ "vendor-dir": "library"
+ },
"keywords": [
"debugbar",
"debug",
@@ -14,8 +18,8 @@
],
"authors": [
{
- "name": "Sangia Publishing House",
- "email": "dev@sangia.org",
+ "name": "Rochmady",
+ "email": "srochmady@gmail.com",
"homepage": "https://www.sangia.org",
"role": "Developer"
}
@@ -49,8 +53,6 @@
},
"archive": {
"exclude": [
- "/Wizdam_DEPRICATED/",
- "/src_DEPRICATED/",
"/.github/",
"/tests/",
".editorconfig",
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..9dd99ee
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,1703 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "853cb7f8b7002b1a584a90ec75c1a82a",
+ "packages": [],
+ "packages-dev": [
+ {
+ "name": "myclabs/deep-copy",
+ "version": "1.13.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/DeepCopy.git",
+ "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a",
+ "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "conflict": {
+ "doctrine/collections": "<1.6.8",
+ "doctrine/common": "<2.13.3 || >=3 <3.2.2"
+ },
+ "require-dev": {
+ "doctrine/collections": "^1.6.8",
+ "doctrine/common": "^2.13.3 || ^3.2.2",
+ "phpspec/prophecy": "^1.10",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/DeepCopy/deep_copy.php"
+ ],
+ "psr-4": {
+ "DeepCopy\\": "src/DeepCopy/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Create deep copies (clones) of your objects",
+ "keywords": [
+ "clone",
+ "copy",
+ "duplicate",
+ "object",
+ "object graph"
+ ],
+ "support": {
+ "issues": "https://github.com/myclabs/DeepCopy/issues",
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4"
+ },
+ "funding": [
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-01T08:46:24+00:00"
+ },
+ {
+ "name": "nikic/php-parser",
+ "version": "v5.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nikic/PHP-Parser.git",
+ "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82",
+ "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "ext-json": "*",
+ "ext-tokenizer": "*",
+ "php": ">=7.4"
+ },
+ "require-dev": {
+ "ircmaxell/php-yacc": "^0.0.7",
+ "phpunit/phpunit": "^9.0"
+ },
+ "bin": [
+ "bin/php-parse"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PhpParser\\": "lib/PhpParser"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Nikita Popov"
+ }
+ ],
+ "description": "A PHP parser written in PHP",
+ "keywords": [
+ "parser",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/nikic/PHP-Parser/issues",
+ "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0"
+ },
+ "time": "2025-12-06T11:56:16+00:00"
+ },
+ {
+ "name": "phar-io/manifest",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/manifest.git",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-phar": "*",
+ "ext-xmlwriter": "*",
+ "phar-io/version": "^3.0.1",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+ "support": {
+ "issues": "https://github.com/phar-io/manifest/issues",
+ "source": "https://github.com/phar-io/manifest/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-03T12:33:53+00:00"
+ },
+ {
+ "name": "phar-io/version",
+ "version": "3.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/version.git",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Library for handling version information and constraints",
+ "support": {
+ "issues": "https://github.com/phar-io/version/issues",
+ "source": "https://github.com/phar-io/version/tree/3.2.1"
+ },
+ "time": "2022-02-21T01:04:05+00:00"
+ },
+ {
+ "name": "phpunit/php-code-coverage",
+ "version": "12.5.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "876099a072646c7745f673d7aeab5382c4439691"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/876099a072646c7745f673d7aeab5382c4439691",
+ "reference": "876099a072646c7745f673d7aeab5382c4439691",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-xmlwriter": "*",
+ "nikic/php-parser": "^5.7.0",
+ "php": ">=8.3",
+ "phpunit/php-text-template": "^5.0",
+ "sebastian/complexity": "^5.0",
+ "sebastian/environment": "^8.0.3",
+ "sebastian/lines-of-code": "^4.0",
+ "sebastian/version": "^6.0",
+ "theseer/tokenizer": "^2.0.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.5.1"
+ },
+ "suggest": {
+ "ext-pcov": "PHP extension that provides line coverage",
+ "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "12.5.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+ "keywords": [
+ "coverage",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+ "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.5.6"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-04-15T08:23:17+00:00"
+ },
+ {
+ "name": "phpunit/php-file-iterator",
+ "version": "6.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+ "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5",
+ "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+ "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+ "keywords": [
+ "filesystem",
+ "iterator"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+ "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/6.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpunit/php-file-iterator",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-02-02T14:04:18+00:00"
+ },
+ {
+ "name": "phpunit/php-invoker",
+ "version": "6.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-invoker.git",
+ "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406",
+ "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "ext-pcntl": "*",
+ "phpunit/phpunit": "^12.0"
+ },
+ "suggest": {
+ "ext-pcntl": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Invoke callables with a timeout",
+ "homepage": "https://github.com/sebastianbergmann/php-invoker/",
+ "keywords": [
+ "process"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
+ "security": "https://github.com/sebastianbergmann/php-invoker/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:58:58+00:00"
+ },
+ {
+ "name": "phpunit/php-text-template",
+ "version": "5.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
+ "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53",
+ "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Simple template engine.",
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+ "keywords": [
+ "template"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+ "security": "https://github.com/sebastianbergmann/php-text-template/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-text-template/tree/5.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:59:16+00:00"
+ },
+ {
+ "name": "phpunit/php-timer",
+ "version": "8.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
+ "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc",
+ "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "8.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Utility class for timing",
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
+ "keywords": [
+ "timer"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+ "security": "https://github.com/sebastianbergmann/php-timer/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:59:38+00:00"
+ },
+ {
+ "name": "phpunit/phpunit",
+ "version": "12.5.27",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "f37c01edaf3a0cd820331462506af93f1495696e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f37c01edaf3a0cd820331462506af93f1495696e",
+ "reference": "f37c01edaf3a0cd820331462506af93f1495696e",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-xml": "*",
+ "ext-xmlwriter": "*",
+ "myclabs/deep-copy": "^1.13.4",
+ "phar-io/manifest": "^2.0.4",
+ "phar-io/version": "^3.2.1",
+ "php": ">=8.3",
+ "phpunit/php-code-coverage": "^12.5.6",
+ "phpunit/php-file-iterator": "^6.0.1",
+ "phpunit/php-invoker": "^6.0.0",
+ "phpunit/php-text-template": "^5.0.0",
+ "phpunit/php-timer": "^8.0.0",
+ "sebastian/cli-parser": "^4.2.1",
+ "sebastian/comparator": "^7.1.8",
+ "sebastian/diff": "^7.0.0",
+ "sebastian/environment": "^8.1.2",
+ "sebastian/exporter": "^7.0.3",
+ "sebastian/global-state": "^8.0.2",
+ "sebastian/object-enumerator": "^7.0.0",
+ "sebastian/recursion-context": "^7.0.1",
+ "sebastian/type": "^6.0.4",
+ "sebastian/version": "^6.0.0",
+ "staabm/side-effects-detector": "^1.0.5"
+ },
+ "bin": [
+ "phpunit"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "12.5-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Framework/Assert/Functions.php"
+ ],
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "The PHP Unit Testing framework.",
+ "homepage": "https://phpunit.de/",
+ "keywords": [
+ "phpunit",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+ "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.27"
+ },
+ "funding": [
+ {
+ "url": "https://phpunit.de/sponsoring.html",
+ "type": "other"
+ }
+ ],
+ "time": "2026-05-25T15:35:34+00:00"
+ },
+ {
+ "name": "sebastian/cli-parser",
+ "version": "4.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/cli-parser.git",
+ "reference": "7d05781b13f7dec9043a629a21d086ed74582a15"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/7d05781b13f7dec9043a629a21d086ed74582a15",
+ "reference": "7d05781b13f7dec9043a629a21d086ed74582a15",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.5.25"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.2-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for parsing CLI options",
+ "homepage": "https://github.com/sebastianbergmann/cli-parser",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
+ "security": "https://github.com/sebastianbergmann/cli-parser/security/policy",
+ "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.2.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/cli-parser",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-05-17T05:29:34+00:00"
+ },
+ {
+ "name": "sebastian/comparator",
+ "version": "7.1.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/comparator.git",
+ "reference": "7c65c1e79836812819705b473a90c12399542485"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/7c65c1e79836812819705b473a90c12399542485",
+ "reference": "7c65c1e79836812819705b473a90c12399542485",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-mbstring": "*",
+ "php": ">=8.3",
+ "sebastian/diff": "^7.0",
+ "sebastian/exporter": "^7.0.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.5.25"
+ },
+ "suggest": {
+ "ext-bcmath": "For comparing BcMath\\Number objects"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@2bepublished.at"
+ }
+ ],
+ "description": "Provides the functionality to compare PHP values for equality",
+ "homepage": "https://github.com/sebastianbergmann/comparator",
+ "keywords": [
+ "comparator",
+ "compare",
+ "equality"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/comparator/issues",
+ "security": "https://github.com/sebastianbergmann/comparator/security/policy",
+ "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.8"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-05-21T04:45:25+00:00"
+ },
+ {
+ "name": "sebastian/complexity",
+ "version": "5.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/complexity.git",
+ "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb",
+ "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^5.0",
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for calculating the complexity of PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/complexity",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/complexity/issues",
+ "security": "https://github.com/sebastianbergmann/complexity/security/policy",
+ "source": "https://github.com/sebastianbergmann/complexity/tree/5.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:55:25+00:00"
+ },
+ {
+ "name": "sebastian/diff",
+ "version": "7.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/diff.git",
+ "reference": "7ab1ea946c012266ca32390913653d844ecd085f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f",
+ "reference": "7ab1ea946c012266ca32390913653d844ecd085f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0",
+ "symfony/process": "^7.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Kore Nordmann",
+ "email": "mail@kore-nordmann.de"
+ }
+ ],
+ "description": "Diff implementation",
+ "homepage": "https://github.com/sebastianbergmann/diff",
+ "keywords": [
+ "diff",
+ "udiff",
+ "unidiff",
+ "unified diff"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/diff/issues",
+ "security": "https://github.com/sebastianbergmann/diff/security/policy",
+ "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:55:46+00:00"
+ },
+ {
+ "name": "sebastian/environment",
+ "version": "8.1.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/environment.git",
+ "reference": "9d32c685773823b1983e256ae4ecd48a10d6e439"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/9d32c685773823b1983e256ae4ecd48a10d6e439",
+ "reference": "9d32c685773823b1983e256ae4ecd48a10d6e439",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.5.26"
+ },
+ "suggest": {
+ "ext-posix": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "8.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides functionality to handle HHVM/PHP environments",
+ "homepage": "https://github.com/sebastianbergmann/environment",
+ "keywords": [
+ "Xdebug",
+ "environment",
+ "hhvm"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/environment/issues",
+ "security": "https://github.com/sebastianbergmann/environment/security/policy",
+ "source": "https://github.com/sebastianbergmann/environment/tree/8.1.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/environment",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-05-25T13:40:20+00:00"
+ },
+ {
+ "name": "sebastian/exporter",
+ "version": "7.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/exporter.git",
+ "reference": "c5e21b5de653ce0a769fb36f5cdfcb5e7a32cf23"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c5e21b5de653ce0a769fb36f5cdfcb5e7a32cf23",
+ "reference": "c5e21b5de653ce0a769fb36f5cdfcb5e7a32cf23",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": ">=8.3",
+ "sebastian/recursion-context": "^7.0.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.5.25"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
+ }
+ ],
+ "description": "Provides the functionality to export PHP variables for visualization",
+ "homepage": "https://www.github.com/sebastianbergmann/exporter",
+ "keywords": [
+ "export",
+ "exporter"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/exporter/issues",
+ "security": "https://github.com/sebastianbergmann/exporter/security/policy",
+ "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-05-20T04:37:17+00:00"
+ },
+ {
+ "name": "sebastian/global-state",
+ "version": "8.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/global-state.git",
+ "reference": "ef1377171613d09edd25b7816f05be8313f9115d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/ef1377171613d09edd25b7816f05be8313f9115d",
+ "reference": "ef1377171613d09edd25b7816f05be8313f9115d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3",
+ "sebastian/object-reflector": "^5.0",
+ "sebastian/recursion-context": "^7.0"
+ },
+ "require-dev": {
+ "ext-dom": "*",
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "8.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Snapshotting of global state",
+ "homepage": "https://www.github.com/sebastianbergmann/global-state",
+ "keywords": [
+ "global state"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/global-state/issues",
+ "security": "https://github.com/sebastianbergmann/global-state/security/policy",
+ "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-29T11:29:25+00:00"
+ },
+ {
+ "name": "sebastian/lines-of-code",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+ "reference": "d543b8ef219dcd8da262cbb958639a96bedba10e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d543b8ef219dcd8da262cbb958639a96bedba10e",
+ "reference": "d543b8ef219dcd8da262cbb958639a96bedba10e",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^5.7.0",
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.5.25"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for counting the lines of code in PHP source code",
+ "homepage": "https://github.com/sebastianbergmann/lines-of-code",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
+ "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy",
+ "source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/lines-of-code",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-05-19T16:22:07+00:00"
+ },
+ {
+ "name": "sebastian/object-enumerator",
+ "version": "7.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+ "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894",
+ "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3",
+ "sebastian/object-reflector": "^5.0",
+ "sebastian/recursion-context": "^7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+ "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+ "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy",
+ "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:57:48+00:00"
+ },
+ {
+ "name": "sebastian/object-reflector",
+ "version": "5.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-reflector.git",
+ "reference": "4bfa827c969c98be1e527abd576533293c634f6a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a",
+ "reference": "4bfa827c969c98be1e527abd576533293c634f6a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Allows reflection of object attributes, including inherited and non-public ones",
+ "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+ "security": "https://github.com/sebastianbergmann/object-reflector/security/policy",
+ "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:58:17+00:00"
+ },
+ {
+ "name": "sebastian/recursion-context",
+ "version": "7.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
+ "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/0b01998a7d5b1f122911a66bebcb8d46f0c82d8c",
+ "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ }
+ ],
+ "description": "Provides functionality to recursively process PHP variables",
+ "homepage": "https://github.com/sebastianbergmann/recursion-context",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+ "security": "https://github.com/sebastianbergmann/recursion-context/security/policy",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-13T04:44:59+00:00"
+ },
+ {
+ "name": "sebastian/type",
+ "version": "6.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/type.git",
+ "reference": "82ff822c2edc46724be9f7411d3163021f602773"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/82ff822c2edc46724be9f7411d3163021f602773",
+ "reference": "82ff822c2edc46724be9f7411d3163021f602773",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.5.25"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the types of the PHP type system",
+ "homepage": "https://github.com/sebastianbergmann/type",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/type/issues",
+ "security": "https://github.com/sebastianbergmann/type/security/policy",
+ "source": "https://github.com/sebastianbergmann/type/tree/6.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/type",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-05-20T06:45:45+00:00"
+ },
+ {
+ "name": "sebastian/version",
+ "version": "6.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/version.git",
+ "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c",
+ "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+ "homepage": "https://github.com/sebastianbergmann/version",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/version/issues",
+ "security": "https://github.com/sebastianbergmann/version/security/policy",
+ "source": "https://github.com/sebastianbergmann/version/tree/6.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T05:00:38+00:00"
+ },
+ {
+ "name": "staabm/side-effects-detector",
+ "version": "1.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/staabm/side-effects-detector.git",
+ "reference": "d8334211a140ce329c13726d4a715adbddd0a163"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163",
+ "reference": "d8334211a140ce329c13726d4a715adbddd0a163",
+ "shasum": ""
+ },
+ "require": {
+ "ext-tokenizer": "*",
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/extension-installer": "^1.4.3",
+ "phpstan/phpstan": "^1.12.6",
+ "phpunit/phpunit": "^9.6.21",
+ "symfony/var-dumper": "^5.4.43",
+ "tomasvotruba/type-coverage": "1.0.0",
+ "tomasvotruba/unused-public": "1.0.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "lib/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A static analysis tool to detect side effects in PHP code",
+ "keywords": [
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/staabm/side-effects-detector/issues",
+ "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/staabm",
+ "type": "github"
+ }
+ ],
+ "time": "2024-10-20T05:08:20+00:00"
+ },
+ {
+ "name": "theseer/tokenizer",
+ "version": "2.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theseer/tokenizer.git",
+ "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/7989e43bf381af0eac72e4f0ca5bcbfa81658be4",
+ "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": "^8.1"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+ "support": {
+ "issues": "https://github.com/theseer/tokenizer/issues",
+ "source": "https://github.com/theseer/tokenizer/tree/2.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2025-12-08T11:19:18+00:00"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": {},
+ "prefer-stable": true,
+ "prefer-lowest": false,
+ "platform": {
+ "php": "^8.0"
+ },
+ "platform-dev": {},
+ "plugin-api-version": "2.9.0"
+}
diff --git a/phpunit.dist.xml b/phpunit.dist.xml
index 1de961c..9d9f0f9 100644
--- a/phpunit.dist.xml
+++ b/phpunit.dist.xml
@@ -2,64 +2,22 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- tests/system
+
+ tests
- system
+ src
-
- system/Commands/Generators/Views
- system/Debug/Toolbar/Views
- system/Pager/Views
- system/ThirdParty
- system/Validation/Views
- system/bootstrap.php
- system/ComposerScripts.php
- system/Config/Routes.php
- system/Test/bootstrap.php
- system/Test/ControllerTester.php
- system/Test/FeatureTestCase.php
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/psalm.xml b/psalm.xml
index d603331..919dfa0 100644
--- a/psalm.xml
+++ b/psalm.xml
@@ -13,18 +13,10 @@
findUnusedCode="false"
>
-
-
-
+
+
-
-
-
-
-
-
-
@@ -32,7 +24,7 @@
-
+
diff --git a/src/Adapters/AdodbDatabaseAdapter.php b/src/Adapters/AdodbDatabaseAdapter.php
index 9f9a635..a10b652 100644
--- a/src/Adapters/AdodbDatabaseAdapter.php
+++ b/src/Adapters/AdodbDatabaseAdapter.php
@@ -57,19 +57,21 @@ class AdodbDatabaseAdapter implements DatabaseAdapterInterface
* Catat satu query ke dalam log.
* Dipanggil dari kode aplikasi saat query dieksekusi.
*
- * @param string $sql Query SQL yang dieksekusi
- * @param float $duration Durasi dalam milidetik
- * @param array $params Bind parameter (opsional)
+ * @param string $sql Query SQL yang dieksekusi
+ * @param float $duration Durasi dalam milidetik
+ * @param array $params Bind parameter (opsional)
+ * @param float|null $startTime microtime(true) saat query dimulai (untuk timeline)
*/
- public static function logQuery(string $sql, float $duration, array $params = []): void
+ public static function logQuery(string $sql, float $duration, array $params = [], ?float $startTime = null): void
{
$sql = trim($sql);
self::$queries[] = [
- 'sql' => $sql,
- 'duration' => round($duration, 4),
- 'params' => $params,
- 'trace' => self::buildShortTrace(),
+ 'sql' => $sql,
+ 'duration' => round($duration, 4),
+ 'params' => $params,
+ 'trace' => self::buildShortTrace(),
+ 'startTime' => $startTime ?? (microtime(true) - $duration / 1000.0),
];
self::$totalTime += $duration;
diff --git a/src/Collectors/BaseCollector.php b/src/Collectors/BaseCollector.php
index 53cec33..ad7f117 100644
--- a/src/Collectors/BaseCollector.php
+++ b/src/Collectors/BaseCollector.php
@@ -186,13 +186,13 @@ public function display()
}
/**
- * This makes nicer looking paths for the error output.
+ * Normalize a file path for display (forward slashes, no trailing sep).
*
- * @deprecated Use the dedicated `clean_path()` function.
+ * @deprecated No longer needed โ paths are normalized per-collector.
*/
public function cleanPath(string $file): string
{
- return clean_path($file);
+ return str_replace('\\', '/', $file);
}
/**
diff --git a/src/Collectors/Config.php b/src/Collectors/Config.php
index 4592f2f..d1d7a6d 100644
--- a/src/Collectors/Config.php
+++ b/src/Collectors/Config.php
@@ -27,18 +27,20 @@
* Adapted from CodeIgniter 4 to be framework-agnostic.
* Returns basic PHP and environment information.
*/
-class Config extends BaseCollector
+class Config
{
/**
* Return toolbar config values as an array.
*/
- public static function display(): array
+ public static function display(array $config = []): array
{
return [
'phpVersion' => PHP_VERSION,
'phpSAPI' => PHP_SAPI,
'timezone' => date_default_timezone_get(),
'serverOS' => PHP_OS,
+ 'baseURL' => $config['baseURL'] ?? '',
+ 'environment' => $config['environment'] ?? '',
];
}
}
diff --git a/src/Collectors/Database.php b/src/Collectors/Database.php
index 8e12135..438608e 100644
--- a/src/Collectors/Database.php
+++ b/src/Collectors/Database.php
@@ -27,7 +27,11 @@
* Collector for the Database tab of the Debug Toolbar.
*
* Adapted from CodeIgniter 4 to be framework-agnostic.
- * Requires a DatabaseAdapterInterface implementation (e.g., AdodbDatabaseAdapter).
+ * Use setAdapter() to register a DatabaseAdapterInterface implementation
+ * (e.g., AdodbDatabaseAdapter) before the toolbar is rendered.
+ *
+ * Example:
+ * Database::setAdapter(new AdodbDatabaseAdapter());
*/
class Database extends BaseCollector
{
@@ -45,13 +49,6 @@ class Database extends BaseCollector
*/
protected $hasTabContent = true;
- /**
- * Whether this collector has data for the Vars tab.
- *
- * @var bool
- */
- protected $hasVarData = false;
-
/**
* The name used to reference this collector in the toolbar.
*
@@ -60,89 +57,40 @@ class Database extends BaseCollector
protected $title = 'Database';
/**
- * Array of database connections.
- *
- * @var array
- */
- protected $connections;
-
- /**
- * The query instances that have been collected
- * through the DBQuery Event.
- *
- * @var array
- */
- protected static $queries = [];
-
- /**
- * Constructor
+ * Registered database adapter, shared across all instances.
*/
- public function __construct()
- {
- $this->getConnections();
- }
+ private static ?DatabaseAdapterInterface $adapter = null;
/**
- * The static method used during Events to collect
- * data.
- *
- * @internal
- *
- * @return void
+ * Register the database adapter.
+ * Call this once at bootstrap before the toolbar is rendered.
*/
- public static function collect(Query $query)
+ public static function setAdapter(DatabaseAdapterInterface $adapter): void
{
- $config = config(Toolbar::class);
-
- // Provide default in case it's not set
- $max = $config->maxQueries ?: 100;
-
- if (count(static::$queries) < $max) {
- $queryString = $query->getQuery();
-
- $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
-
- if (! is_cli()) {
- // when called in the browser, the first two trace arrays
- // are from the DB event trigger, which are unneeded
- $backtrace = array_slice($backtrace, 2);
- }
-
- static::$queries[] = [
- 'query' => $query,
- 'string' => $queryString,
- 'duplicate' => in_array($queryString, array_column(static::$queries, 'string'), true),
- 'trace' => $backtrace,
- ];
- }
+ self::$adapter = $adapter;
}
/**
* Returns timeline data formatted for the toolbar.
- *
- * @return array The formatted data or an empty array.
*/
protected function formatTimelineData(): array
{
+ if (self::$adapter === null) {
+ return [];
+ }
+
$data = [];
- foreach ($this->connections as $alias => $connection) {
- // Connection Time
- $data[] = [
- 'name' => 'Connecting to Database: "' . $alias . '"',
- 'component' => 'Database',
- 'start' => $connection->getConnectStart(),
- 'duration' => $connection->getConnectDuration(),
- ];
- }
+ foreach (self::$adapter->getQueries() as $query) {
+ $startTime = (float) ($query['startTime'] ?? 0);
+ $duration = ((float) ($query['duration'] ?? 0)) / 1000; // ms โ seconds
- foreach (static::$queries as $query) {
$data[] = [
'name' => 'Query',
'component' => 'Database',
- 'start' => $query['query']->getStartTime(true),
- 'duration' => $query['query']->getDuration(),
- 'query' => $query['query']->debugToolbarDisplay(),
+ 'start' => $startTime,
+ 'duration' => $duration,
+ 'query' => htmlspecialchars($query['sql'] ?? '', ENT_QUOTES, 'UTF-8'),
];
}
@@ -150,58 +98,38 @@ protected function formatTimelineData(): array
}
/**
- * Returns the data of this collector to be formatted in the toolbar
+ * Returns the data of this collector to be formatted in the toolbar.
*/
public function display(): array
{
- return ['queries' => array_map(static function (array $query): array {
- $isDuplicate = $query['duplicate'] === true;
-
- $firstNonSystemLine = '';
-
- foreach ($query['trace'] as $index => &$line) {
- // simplify file and line
- if (isset($line['file'])) {
- $line['file'] = clean_path($line['file']) . ':' . $line['line'];
- unset($line['line']);
- } else {
- $line['file'] = '[internal function]';
- }
-
- // find the first trace line that does not originate from `system/`
- if ($firstNonSystemLine === '' && ! str_contains($line['file'], 'SYSTEMPATH')) {
- $firstNonSystemLine = $line['file'];
- }
-
- // simplify function call
- if (isset($line['class'])) {
- $line['function'] = $line['class'] . $line['type'] . $line['function'];
- unset($line['class'], $line['type']);
- }
-
- if (strrpos($line['function'], '{closure}') === false) {
- $line['function'] .= '()';
- }
+ if (self::$adapter === null) {
+ return ['queries' => []];
+ }
- $line['function'] = str_repeat(chr(0xC2) . chr(0xA0), 8) . $line['function'];
+ $rawQueries = self::$adapter->getQueries();
+ $sqlCounts = array_count_values(array_column($rawQueries, 'sql'));
- // add index numbering padded with nonbreaking space
- $indexPadded = str_pad(sprintf('%d', $index + 1), 3, ' ', STR_PAD_LEFT);
- $indexPadded = preg_replace('/\s/', chr(0xC2) . chr(0xA0), $indexPadded);
+ $queries = [];
+ $idx = 0;
- $line['index'] = $indexPadded . str_repeat(chr(0xC2) . chr(0xA0), 4);
- }
+ foreach ($rawQueries as $query) {
+ $sql = $query['sql'] ?? '';
+ $isDuplicate = ($sqlCounts[$sql] ?? 1) > 1;
- return [
+ $queries[] = [
'hover' => $isDuplicate ? 'This query was called more than once.' : '',
'class' => $isDuplicate ? 'duplicate' : '',
- 'duration' => ((float) $query['query']->getDuration(5) * 1000) . ' ms',
- 'sql' => $query['query']->debugToolbarDisplay(),
- 'trace' => $query['trace'],
- 'trace-file' => $firstNonSystemLine,
- 'qid' => md5($query['query'] . Time::now()->format('0.u00 U')),
+ 'duration' => number_format((float) ($query['duration'] ?? 0), 2) . ' ms',
+ 'sql' => htmlspecialchars($sql, ENT_QUOTES, 'UTF-8'),
+ 'trace' => $query['trace'] ?? '',
+ 'trace-file' => $query['trace'] ?? '',
+ 'qid' => md5($sql . $idx),
];
- }, static::$queries)];
+
+ $idx++;
+ }
+
+ return ['queries' => $queries];
}
/**
@@ -209,30 +137,26 @@ public function display(): array
*/
public function getBadgeValue(): int
{
- return count(static::$queries);
+ return self::$adapter !== null ? self::$adapter->getQueryCount() : 0;
}
/**
* Information to be displayed next to the title.
- *
- * @return string The number of queries (in parentheses) or an empty string.
*/
public function getTitleDetails(): string
{
- $this->getConnections();
+ if (self::$adapter === null) {
+ return '';
+ }
- $queryCount = count(static::$queries);
- $uniqueCount = count(array_filter(static::$queries, static fn ($query): bool => $query['duplicate'] === false));
- $connectionCount = count($this->connections);
+ $total = self::$adapter->getQueryCount();
+ $duplicates = count(self::$adapter->getDuplicates());
return sprintf(
- '(%d total Quer%s, %d %s unique across %d Connection%s)',
- $queryCount,
- $queryCount > 1 ? 'ies' : 'y',
- $uniqueCount,
- $uniqueCount > 1 ? 'of them' : '',
- $connectionCount,
- $connectionCount > 1 ? 's' : '',
+ '(%d total Quer%s, %d duplicate)',
+ $total,
+ $total === 1 ? 'y' : 'ies',
+ $duplicates,
);
}
@@ -241,7 +165,7 @@ public function getTitleDetails(): string
*/
public function isEmpty(): bool
{
- return static::$queries === [];
+ return self::$adapter === null || self::$adapter->getQueryCount() === 0;
}
/**
@@ -255,19 +179,10 @@ public function icon(): string
}
/**
- * Gets the connections from the database config
- */
- private function getConnections(): void
- {
- $this->connections = \Config\Database::getConnections();
- }
-
- /**
- * Reset collector state for worker mode.
- * Clears collected queries between requests.
+ * Reset collector state.
*/
- public function reset(): void
+ public static function reset(): void
{
- static::$queries = [];
+ self::$adapter = null;
}
}
diff --git a/src/Collectors/Events.php b/src/Collectors/Events.php
index c683d2b..aa55a9c 100644
--- a/src/Collectors/Events.php
+++ b/src/Collectors/Events.php
@@ -25,6 +25,11 @@
* Events collector
*
* Adapted from CodeIgniter 4 to be framework-agnostic.
+ *
+ * Usage:
+ * $start = microtime(true);
+ * // ... event handler runs ...
+ * Events::trigger('my_event', $start, microtime(true));
*/
class Events extends BaseCollector
{
@@ -60,6 +65,26 @@ class Events extends BaseCollector
*/
protected $title = 'Events';
+ /**
+ * @var list
+ */
+ private static array $logs = [];
+
+ /**
+ * Log a triggered event.
+ *
+ * @param float $start microtime(true) before the event handler ran
+ * @param float $end microtime(true) after the event handler returned
+ */
+ public static function trigger(string $event, float $start, float $end): void
+ {
+ self::$logs[] = [
+ 'event' => $event,
+ 'start' => $start,
+ 'end' => $end,
+ ];
+ }
+
/**
* Child classes should implement this to return the timeline data
* formatted for correct usage.
@@ -68,9 +93,7 @@ protected function formatTimelineData(): array
{
$data = [];
- $rows = \CodeIgniter\Events\Events::getPerformanceLogs();
-
- foreach ($rows as $info) {
+ foreach (self::$logs as $info) {
$data[] = [
'name' => 'Event: ' . $info['event'],
'component' => 'Events',
@@ -87,15 +110,13 @@ protected function formatTimelineData(): array
*/
public function display(): array
{
- $data = [
- 'events' => [],
- ];
+ $events = [];
- foreach (\CodeIgniter\Events\Events::getPerformanceLogs() as $row) {
+ foreach (self::$logs as $row) {
$key = $row['event'];
- if (! array_key_exists($key, $data['events'])) {
- $data['events'][$key] = [
+ if (! array_key_exists($key, $events)) {
+ $events[$key] = [
'event' => $key,
'duration' => ($row['end'] - $row['start']) * 1000,
'count' => 1,
@@ -104,15 +125,15 @@ public function display(): array
continue;
}
- $data['events'][$key]['duration'] += ($row['end'] - $row['start']) * 1000;
- $data['events'][$key]['count']++;
+ $events[$key]['duration'] += ($row['end'] - $row['start']) * 1000;
+ $events[$key]['count']++;
}
- foreach ($data['events'] as &$row) {
+ foreach ($events as &$row) {
$row['duration'] = number_format($row['duration'], 2);
}
- return $data;
+ return ['events' => array_values($events)];
}
/**
@@ -120,7 +141,15 @@ public function display(): array
*/
public function getBadgeValue(): int
{
- return count(\CodeIgniter\Events\Events::getPerformanceLogs());
+ return count(self::$logs);
+ }
+
+ /**
+ * Does this collector have any data collected?
+ */
+ public function isEmpty(): bool
+ {
+ return self::$logs === [];
}
/**
@@ -132,4 +161,12 @@ public function icon(): string
{
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEASURBVEhL7ZXNDcIwDIVTsRBH1uDQDdquUA6IM1xgCA6MwJUN2hk6AQzAz0vl0ETUxC5VT3zSU5w81/mRMGZysixbFEVR0jSKNt8geQU9aRpFmp/keX6AbjZ5oB74vsaN5lSzA4tLSjpBFxsjeSuRy4d2mDdQTWU7YLbXTNN05mKyovj5KL6B7q3hoy3KwdZxBlT+Ipz+jPHrBqOIynZgcZonoukb/0ckiTHqNvDXtXEAaygRbaB9FvUTjRUHsIYS0QaSp+Dw6wT4hiTmYHOcYZsdLQ2CbXa4ftuuYR4x9vYZgdb4vsFYUdmABMYeukK9/SUme3KMFQ77+Yfzh8eYF8+orDuDWU5LAAAAAElFTkSuQmCC';
}
+
+ /**
+ * Reset all logged events.
+ */
+ public static function reset(): void
+ {
+ self::$logs = [];
+ }
}
diff --git a/src/Collectors/Files.php b/src/Collectors/Files.php
index 89e3df3..c2f2321 100644
--- a/src/Collectors/Files.php
+++ b/src/Collectors/Files.php
@@ -25,6 +25,8 @@
* Files collector
*
* Adapted from CodeIgniter 4 to be framework-agnostic.
+ * Lists all PHP files loaded during this request, separated into
+ * "vendor / core" files and "user application" files.
*/
class Files extends BaseCollector
{
@@ -70,9 +72,9 @@ public function display(): array
$userFiles = [];
foreach ($rawFiles as $file) {
- $path = clean_path($file);
+ $path = $this->normalizePath($file);
- if (str_contains($path, 'SYSTEMPATH')) {
+ if ($this->isCoreFile($path)) {
$coreFiles[] = [
'path' => $path,
'name' => basename($file),
@@ -111,4 +113,21 @@ public function icon(): string
{
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGBSURBVEhL7ZQ9S8NQGIVTBQUncfMfCO4uLgoKbuKQOWg+OkXERRE1IAXrIHbVDrqIDuLiJgj+gro7S3dnpfq88b1FMTE3VZx64HBzzvvZWxKnj15QCcPwCD5HUfSWR+JtzgmtsUcQBEva5IIm9SwSu+95CAWbUuy67qBa32ByZEDpIaZYZSZMjjQuPcQUq8yEyYEb8FSerYeQVGbAFzJkX1PyQWLhgCz0BxTCekC1Wp0hsa6yokzhed4oje6Iz6rlJEkyIKfUEFtITVtQdAibn5rMyaYsMS+a5wTv8qeXMhcU16QZbKgl3hbs+L4/pnpdc87MElZgq10p5DxGdq8I7xrvUWUKvG3NbSK7ubngYzdJwSsF7TiOh9VOgfcEz1UayNe3JUPM1RWC5GXYgTfc75B4NBmXJnAtTfpABX0iPvEd9ezALwkplCFXcr9styiNOKc1RRZpaPM9tcqBwlWzGY1qPL9wjqRBgF5BH6j8HWh2S7MHlX8PrmbK+k/8PzjOOzx1D3i1pKTTAAAAAElFTkSuQmCC';
}
+
+ /**
+ * Convert backslashes to forward slashes for uniform display.
+ */
+ private function normalizePath(string $file): string
+ {
+ return str_replace('\\', '/', $file);
+ }
+
+ /**
+ * Heuristic: files inside a vendor/ directory are "core/library" files;
+ * everything else is considered a user application file.
+ */
+ private function isCoreFile(string $normalizedPath): bool
+ {
+ return str_contains($normalizedPath, '/vendor/');
+ }
}
diff --git a/src/Collectors/History.php b/src/Collectors/History.php
index 83a5713..b7fc034 100644
--- a/src/Collectors/History.php
+++ b/src/Collectors/History.php
@@ -65,6 +65,21 @@ class History extends BaseCollector
*/
protected $files = [];
+ /**
+ * Path to directory containing debugbar JSON history files.
+ *
+ * @var string
+ */
+ private string $historyPath;
+
+ public function __construct(array $config = [])
+ {
+ $this->historyPath = rtrim(
+ $config['historyPath'] ?? sys_get_temp_dir() . '/wizdam-debugbar/',
+ '/\\',
+ ) . DIRECTORY_SEPARATOR;
+ }
+
/**
* Specify time limit & file count for debug history.
*
@@ -75,7 +90,7 @@ class History extends BaseCollector
*/
public function setFiles(string $current, int $limit = 20)
{
- $filenames = glob(WRITEPATH . 'debugbar/debugbar_*.json');
+ $filenames = glob($this->historyPath . 'debugbar_*.json') ?: [];
$files = [];
$counter = 0;
@@ -99,15 +114,16 @@ public function setFiles(string $current, int $limit = 20)
$time = sprintf('%.6F', $time[1] ?? 0);
// Debugbar files shown in History Collector
+ $dt = date_create_from_format('U.u', $time);
$files[] = [
'time' => $time,
- 'datetime' => DateTime::createFromFormat('U.u', $time)->format('Y-m-d H:i:s.u'),
+ 'datetime' => $dt !== false ? $dt->format('Y-m-d H:i:s.u') : '',
'active' => $time === $current,
- 'status' => $contents->vars->response->statusCode,
- 'method' => $contents->method,
- 'url' => $contents->url,
- 'isAJAX' => $contents->isAJAX ? 'Yes' : 'No',
- 'contentType' => $contents->vars->response->contentType,
+ 'status' => $contents->vars->response->statusCode ?? 0,
+ 'method' => $contents->method ?? '',
+ 'url' => $contents->url ?? '',
+ 'isAJAX' => !empty($contents->isAJAX) ? 'Yes' : 'No',
+ 'contentType' => $contents->vars->response->contentType ?? '',
];
}
}
@@ -146,6 +162,6 @@ public function isEmpty(): bool
*/
public function icon(): string
{
- return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAJySURBVEhL3ZU7aJNhGIVTpV6i4qCIgkIHxcXLErS4FBwUFNwiCKGhuTYJGaIgnRoo4qRu6iCiiIuIXXTTIkIpuqoFwaGgonUQlC5KafU5ycmNP0lTdPLA4fu+8573/a4/f6hXpFKpwUwmc9fDfweKbk+n07fgEv33TLSbtt/hvwNFT1PsG/zdTE0Gp+GFfD6/2fbVIxqNrqPIRbjg4t/hY8aztcngfDabHXbKyiiXy2vcrcPH8oDCry2FKDrA+Ar6L01E/ypyXzXaARjDGGcoeNxSDZXE0dHRA5VRE5LJ5CFy5jzJuOX2wHRHRnjbklZ6isQ3tIctBaAd4vlK3jLtkOVWqABBXd47jGHLmjTmSScttQV5J+SjfcUweFQEbsjAas5aqoCLXutJl7vtQsAzpRowYqkBinyCC8Vicb2lOih8zoldd0F8RD7qTFiqAnGrAy8stUAvi/hbqDM+YzkAFrLPdR5ZqoLXsd+Bh5YCIH7JniVdquUWxOPxDfboHhrI5XJ7HHhiqQXox+APe/Qk64+gGYVCYZs8cMpSFQj9JOoFzVqqo7k4HIvFYpscCoAjOmLffUsNUGRaQUwDlmofUa34ecsdgXdcXo4wbakBgiUFafXJV8A4DJ/2UrxUKm3E95H8RbjLcgOJRGILhnmCP+FBy5XvwN2uIPcy1AJvWgqC4xm2aU4Xb3lF4I+Tpyf8hRe5w3J7YLymSeA8Z3nSclv4WLRyFdfOjzrUFX0klJUEtZtntCNc+F69cz/FiDzEPtjzmcUMOr83kDQEX6pAJxJfpL3OX22n01YN7SZCoQnaSdoZ+Jz+PZihH3wt/xlCoT9M6nEtmRSPCQAAAABJRU5ErkJggg==';
+ return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAJySURBVEhL3ZU7aJNhGIVTpV6i4qCIgkIHxcXLErS4FBwUFNwiCKGhuTYJGaIgnRoo4qRu6iCiiIuIXXTTIkIpuqoFwaGgonUQlC5KafU5ycmNP0lTdPLA4fs+c973va4/f6hXpFKpwUwmc9fDfweKbk+n07fgEv33TLSbtt/hvwNFT1PsG/zdTE0Gp+GFfD6/2fbVIxqNrqPIRbjg4t/hY8aztcngfDabHTrKyiiXy2vcrcPH8oDCry2FKDrA+Ar6L01E/ypyXzXaARjDGGcoeNxSDZXE0dHRA5VRE5LJ5CFy5jzJuOX2wHRHRnjbklZ6isQ3tIctBaAd4vlK3jLtkOVWqABBXd47jGHLmjTmSScttQV5J+SjfcUweFQEbsjAas5aqoCLXutJl7vtQsAzpRowYqkBinyCC8Vicb2lOih8zoldd0F8RD7qTFiqAnGrAy8stUAvi/hbqDM+YzkAFrLPdR5ZqoLXsd+Bh5YCIH7JniVdquUWxOPxDfboHhrI5XJ7HHhiqQXox+APe/Qk64+gGYVCYZs8cMpSFQj9JOoFzVqqo7k4HIvFYpscCoAjOmLffUsNUGRaQUwDlmofUa34ecsdgXdcXo4wbakBgiUFafXJV8A4DJ/2UrxUKm3E95H8RbjLcgOJRGILhnmCP+FBy5XvwN2uIPcy1AJvWgqC4xm2aU4Xb3lF4I+Tpyf8hRe5w3J7YLymSeA8Z3nSclv4WLRyFdfOjzrUFX0klJUEtZtntCNc+F69cz/FiDzEPtjzmcUMOr83kDQEX6pAJxJfpL3OX22n01YN7SZCoQnaSdoZ+Jz+PZihH3wt/xlCoT9M6nEtmRSPCQAAAABJRU5ErkJggg==';
}
}
diff --git a/src/Collectors/Logs.php b/src/Collectors/Logs.php
index 4a89de7..f457add 100644
--- a/src/Collectors/Logs.php
+++ b/src/Collectors/Logs.php
@@ -25,6 +25,10 @@
* Logs collector
*
* Adapted from CodeIgniter 4 to be framework-agnostic.
+ *
+ * Usage:
+ * Logs::addLog('error', 'Something went wrong');
+ * Logs::addLog('info', 'User logged in');
*/
class Logs extends BaseCollector
{
@@ -53,11 +57,23 @@ class Logs extends BaseCollector
protected $title = 'Logs';
/**
- * Our collected data.
- *
* @var list
*/
- protected $data = [];
+ private static array $logCache = [];
+
+ /**
+ * Add a log entry.
+ *
+ * @param string $level PSR-3 log level: emergency, alert, critical, error, warning, notice, info, debug
+ * @param string $msg Log message
+ */
+ public static function addLog(string $level, string $msg): void
+ {
+ self::$logCache[] = [
+ 'level' => $level,
+ 'msg' => $msg,
+ ];
+ }
/**
* Returns the data of this collector to be formatted in the toolbar.
@@ -66,9 +82,7 @@ class Logs extends BaseCollector
*/
public function display(): array
{
- return [
- 'logs' => $this->collectLogs(),
- ];
+ return ['logs' => self::$logCache];
}
/**
@@ -76,9 +90,7 @@ public function display(): array
*/
public function isEmpty(): bool
{
- $this->collectLogs();
-
- return $this->data === [];
+ return self::$logCache === [];
}
/**
@@ -92,20 +104,10 @@ public function icon(): string
}
/**
- * Ensures the data has been collected.
- *
- * @return list
+ * Reset all log entries.
*/
- protected function collectLogs()
+ public static function reset(): void
{
- if ($this->data !== []) {
- return $this->data;
- }
-
- $cache = service('logger')->logCache;
-
- $this->data = $cache ?? [];
-
- return $this->data;
+ self::$logCache = [];
}
}
diff --git a/src/Collectors/Routes.php b/src/Collectors/Routes.php
index 0e23333..3184324 100644
--- a/src/Collectors/Routes.php
+++ b/src/Collectors/Routes.php
@@ -27,7 +27,9 @@
* Routes collector
*
* Adapted from CodeIgniter 4 to be framework-agnostic.
- * Requires a RouterInterface implementation (e.g., WizdamRouterAdapter).
+ *
+ * Usage:
+ * Routes::setRouter(new WizdamRouterAdapter());
*/
class Routes extends BaseCollector
{
@@ -56,7 +58,21 @@ class Routes extends BaseCollector
protected $title = 'Routes';
/**
- * Returns the data of this collector to be formatted in the toolbar
+ * Registered router adapter, shared across all instances.
+ */
+ private static ?RouterInterface $router = null;
+
+ /**
+ * Register the router adapter.
+ * Call this once at bootstrap before the toolbar is rendered.
+ */
+ public static function setRouter(RouterInterface $router): void
+ {
+ self::$router = $router;
+ }
+
+ /**
+ * Returns the data of this collector to be formatted in the toolbar.
*
* @return array{
* matchedRoute: list
+ * params: list
* }>,
- * routes: list
+ * routes: list
* }
- *
- * @throws ReflectionException
*/
public function display(): array
{
- $rawRoutes = service('routes', true);
- $router = service('router', null, null, true);
-
- // Get our parameters
- // Closure routes
- if (is_callable($router->controllerName())) {
- $method = new ReflectionFunction($router->controllerName());
- } else {
- try {
- $method = new ReflectionMethod($router->controllerName(), $router->methodName());
- } catch (ReflectionException) {
- try {
- // If we're here, the method doesn't exist
- // and is likely calculated in _remap.
- $method = new ReflectionMethod($router->controllerName(), '_remap');
- } catch (ReflectionException) {
- // If we're here, page cache is returned. The router is not executed.
- return [
- 'matchedRoute' => [],
- 'routes' => [],
- ];
- }
- }
+ if (self::$router === null) {
+ return [
+ 'matchedRoute' => [],
+ 'routes' => [],
+ ];
}
- $rawParams = $method->getParameters();
-
- $params = [];
+ $rawParams = self::$router->getParams();
+ $params = [];
- foreach ($rawParams as $key => $param) {
+ foreach ($rawParams as $name => $value) {
$params[] = [
- 'name' => '$' . $param->getName() . ' = ',
- 'value' => $router->params()[$key] ??
- ' | default: '
- . var_export(
- $param->isDefaultValueAvailable() ? $param->getDefaultValue() : null,
- true,
- ),
+ 'name' => (string) $name,
+ 'value' => is_scalar($value) ? (string) $value : print_r($value, true),
];
}
$matchedRoute = [
[
- 'directory' => $router->directory(),
- 'controller' => $router->controllerName(),
- 'method' => $router->methodName(),
- 'paramCount' => count($router->params()),
+ 'directory' => '',
+ 'controller' => self::$router->getController(),
+ 'method' => self::$router->getMethod(),
+ 'paramCount' => count($rawParams),
'truePCount' => count($params),
'params' => $params,
],
];
- // Defined Routes
- $routes = [];
-
- $definedRouteCollector = new DefinedRouteCollector($rawRoutes);
-
- foreach ($definedRouteCollector->collect() as $route) {
- // filter for strings, as callbacks aren't displayable
- if ($route['handler'] !== '(Closure)') {
- $routes[] = [
- 'method' => strtoupper($route['method']),
- 'route' => $route['route'],
- 'handler' => $route['handler'],
- ];
- }
- }
-
return [
'matchedRoute' => $matchedRoute,
- 'routes' => $routes,
+ 'routes' => [],
];
}
/**
- * Returns a count of all the routes in the system.
+ * Returns the number of matched route entries as the badge value.
*/
public function getBadgeValue(): int
{
- $rawRoutes = service('routes', true);
+ return self::$router !== null ? 1 : 0;
+ }
- return count($rawRoutes->getRoutes());
+ /**
+ * Does this collector have any data collected?
+ */
+ public function isEmpty(): bool
+ {
+ return self::$router === null;
}
/**
@@ -174,4 +147,12 @@ public function icon(): string
{
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFDSURBVEhL7ZRNSsNQFIUjVXSiOFEcuQIHDpzpxC0IGYeE/BEInbWlCHEDLsSiuANdhKDjgm6ggtSJ+l25ldrmmTwIgtgDh/t37r1J+16cX0dRFMtpmu5pWAkrvYjjOB7AETzStBFW+inxu3KUJMmhludQpoflS1zXban4LYqiO224h6VLTHr8Z+z8EpIHFF9gG78nDVmW7UgTHKjsCyY98QP+pcq+g8Ku2s8G8X3f3/I8b038WZTp+bO38zxfFd+I6YY6sNUvFlSDk9CRhiAI1jX1I9Cfw7GG1UB8LAuwbU0ZwQnbRDeEN5qqBxZMLtE1ti9LtbREnMIuOXnyIf5rGIb7Wq8HmlZgwYBH7ORTcKH5E4mpjeGt9fBZcHE2GCQ3Vt7oTNPNg+FXLHnSsHkw/FR+Gg2bB8Ptzrst/v6C/wrH+QB+duli6MYJdQAAAABJRU5ErkJggg==';
}
+
+ /**
+ * Reset the registered router.
+ */
+ public static function reset(): void
+ {
+ self::$router = null;
+ }
}
diff --git a/src/Collectors/Timers.php b/src/Collectors/Timers.php
index 820e526..fdb8db2 100644
--- a/src/Collectors/Timers.php
+++ b/src/Collectors/Timers.php
@@ -25,6 +25,11 @@
* Timers collector
*
* Adapted from CodeIgniter 4 to be framework-agnostic.
+ *
+ * Usage:
+ * Timers::start('my_block');
+ * // ... code ...
+ * Timers::stop('my_block');
*/
class Timers extends BaseCollector
{
@@ -52,6 +57,39 @@ class Timers extends BaseCollector
*/
protected $title = 'Timers';
+ /**
+ * @var array
+ */
+ private static array $timers = [];
+
+ /**
+ * Start a named timer.
+ */
+ public static function start(string $name): void
+ {
+ self::$timers[$name] = ['start' => microtime(true), 'end' => null];
+ }
+
+ /**
+ * Stop a named timer.
+ */
+ public static function stop(string $name): void
+ {
+ if (isset(self::$timers[$name])) {
+ self::$timers[$name]['end'] = microtime(true);
+ }
+ }
+
+ /**
+ * Returns all recorded timers (read-only).
+ *
+ * @return array
+ */
+ public static function getTimers(): array
+ {
+ return self::$timers;
+ }
+
/**
* Child classes should implement this to return the timeline data
* formatted for correct usage.
@@ -60,22 +98,29 @@ protected function formatTimelineData(): array
{
$data = [];
- $benchmark = service('timer', true);
- $rows = $benchmark->getTimers(6);
-
- foreach ($rows as $name => $info) {
+ foreach (self::$timers as $name => $timer) {
if ($name === 'total_execution') {
continue;
}
+ $end = $timer['end'] ?? microtime(true);
+
$data[] = [
'name' => ucwords(str_replace('_', ' ', $name)),
'component' => 'Timer',
- 'start' => $info['start'],
- 'duration' => $info['end'] - $info['start'],
+ 'start' => $timer['start'],
+ 'duration' => $end - $timer['start'],
];
}
return $data;
}
+
+ /**
+ * Reset all timers (e.g. between requests in worker mode).
+ */
+ public static function reset(): void
+ {
+ self::$timers = [];
+ }
}
diff --git a/src/Collectors/Views.php b/src/Collectors/Views.php
index cefd99c..2b8c293 100644
--- a/src/Collectors/Views.php
+++ b/src/Collectors/Views.php
@@ -25,7 +25,11 @@
* Views collector
*
* Adapted from CodeIgniter 4 to be framework-agnostic.
- * Works with any template engine via manual logging.
+ *
+ * Usage:
+ * $start = microtime(true);
+ * // ... render template ...
+ * Views::logView('article/view.tpl', $start, microtime(true), $templateData);
*/
class Views extends BaseCollector
{
@@ -70,22 +74,26 @@ class Views extends BaseCollector
protected $title = 'Views';
/**
- * Instance of the shared Renderer service
- *
- * @var RendererInterface|null
+ * @var list
*/
- protected $viewer;
+ private static array $renderedViews = [];
/**
- * Views counter
+ * Log a rendered view.
*
- * @var array
+ * @param string $view Template name / path
+ * @param float $start microtime(true) before rendering
+ * @param float $end microtime(true) after rendering
+ * @param array $data Variables passed to the template (optional)
*/
- protected $views = [];
-
- private function initViewer(): void
+ public static function logView(string $view, float $start, float $end, array $data = []): void
{
- $this->viewer ??= service('renderer');
+ self::$renderedViews[] = [
+ 'view' => $view,
+ 'start' => $start,
+ 'end' => $end,
+ 'data' => $data,
+ ];
}
/**
@@ -94,13 +102,9 @@ private function initViewer(): void
*/
protected function formatTimelineData(): array
{
- $this->initViewer();
-
$data = [];
- $rows = $this->viewer->getPerformanceData();
-
- foreach ($rows as $info) {
+ foreach (self::$renderedViews as $info) {
$data[] = [
'name' => 'View: ' . $info['view'],
'component' => 'Views',
@@ -114,37 +118,34 @@ protected function formatTimelineData(): array
/**
* Gets a collection of data that should be shown in the 'Vars' tab.
- * The format is an array of sections, each with their own array
- * of key/value pairs:
- *
- * $data = [
- * 'section 1' => [
- * 'foo' => 'bar,
- * 'bar' => 'baz'
- * ],
- * 'section 2' => [
- * 'foo' => 'bar,
- * 'bar' => 'baz'
- * ],
- * ];
*/
public function getVarData(): array
{
- $this->initViewer();
+ $merged = [];
- return [
- 'View Data' => $this->viewer->getData(),
- ];
+ foreach (self::$renderedViews as $info) {
+ foreach ($info['data'] as $key => $value) {
+ $merged[(string) $key] = $value;
+ }
+ }
+
+ return ['View Data' => $merged];
}
/**
- * Returns a count of all views.
+ * Returns a count of all views rendered.
*/
public function getBadgeValue(): int
{
- $this->initViewer();
+ return count(self::$renderedViews);
+ }
- return count($this->viewer->getPerformanceData());
+ /**
+ * Does this collector have any data collected?
+ */
+ public function isEmpty(): bool
+ {
+ return self::$renderedViews === [];
}
/**
@@ -154,6 +155,14 @@ public function getBadgeValue(): int
*/
public function icon(): string
{
- return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADeSURBVEhL7ZSxDcIwEEWNYA0YgGmgyAaJLTcUaaBzQQEVjMEabBQxAdw53zTHiThEovGTfnE/9rsoRUxhKLOmaa6Uh7X2+UvguLCzVxN1XW9x4EYHzik033Hp3X0LO+DaQG8MDQcuq6qao4qkHuMgQggLvkPLjqh00ZgFDBacMJYFkuwFlH1mshdkZ5JPJERA9JpI6xNCBESvibQ+IURA9JpI6xNCBESvibQ+IURA9DTsuHTOrVFFxixgB/eUFlU8uKJ0eDBFOu/9EvoeKnlJS2/08Tc8NOwQ8sIfMeYFjqKDjdU2sp4AAAAASUVORK5CYII=';
+ return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADeSURBVEhL7ZSxDcIwEEWNYA0YgGmgyAaJLTcUaaBzQQEVjMEabBQxAdw53zTHiThEovGTfnE/9rsoRUxhKLOmaa6Uh7X2+UvguLCzVxN1XW9x4EYHzik033Hp3X0LO+DaQG8MDQcuq6qao4qkHuMgQggLvkPLjqh00ZgFDBacMJYFkuwFlH1mshdkZ5JPJURA9JpI6xNCBESvibQ+IURA9JpI6xNCBESvibQ+IURA9DTsuHTOrVFFxixgB/eUFlU8uKJ0eDBFOu/9EvoeKnlJS2/08Tc8NOwQ8sIfMeYFjqKDjdU2sp4AAAAASUVORK5CYII=';
+ }
+
+ /**
+ * Reset all logged views.
+ */
+ public static function reset(): void
+ {
+ self::$renderedViews = [];
}
}
diff --git a/src/Middleware/DebugToolbarMiddleware.php b/src/Middleware/DebugToolbarMiddleware.php
index d7f1b8c..e892428 100644
--- a/src/Middleware/DebugToolbarMiddleware.php
+++ b/src/Middleware/DebugToolbarMiddleware.php
@@ -108,19 +108,13 @@ public function process(array $request, callable $handler): string
// ---------------------------------------------------------------
/**
- * Inject toolbar HTML ke dalam response sebelum tag .
+ * Inject toolbar script loader ke dalam response.
+ * Menggunakan DebugToolbar::prepare() yang memodifikasi response by reference.
*/
private function inject(string $response): string
{
- $toolbarHtml = $this->debugBar->render();
-
- // Inject tepat sebelum