Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 175 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
name: Tests

on:
push:
branches: [ main, develop ]
pull_request:
workflow_dispatch:

jobs:
tests:
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
php: [8.3, 8.4]

name: PHP ${{ matrix.php }}

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: dom, curl, libxml, mbstring, zip, pdo_sqlite
coverage: none

- name: Install dependencies
run: composer update --prefer-stable --prefer-dist --no-interaction

- name: Execute tests
run: vendor/bin/pest

static-analysis:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.3
extensions: dom, curl, libxml, mbstring, zip
coverage: none

- name: Install dependencies
run: composer update --prefer-stable --prefer-dist --no-interaction

- name: Run PHPStan
run: composer phpstan:ci

- name: Check coding standards with Pint
run: composer lint:check

e2e:
name: E2E
runs-on: ubuntu-latest
needs: tests

steps:
- name: Checkout skeleton
uses: actions/checkout@v4
with:
repository: Pollora/pollora

- name: Checkout this package
uses: actions/checkout@v4
with:
path: packages/debugbar

- name: Setup DDEV
uses: ddev/github-action-setup-ddev@v1
with:
autostart: false

- name: Configure DDEV
run: |
ddev config \
--project-name=pollora-debugbar \
--project-type=wordpress \
--docroot=public \
--php-version=8.4 \
--webserver-type=apache-fpm \
--database=mariadb:10.11 \
--nodejs-version=22 \
--disable-settings-management

- name: Start DDEV
run: ddev start

- name: Prepare .env
run: |
cp .env.example .env
sed -i \
-e 's#^APP_URL=.*#APP_URL=https://pollora-debugbar.ddev.site#' \
-e 's#^APP_ENV=.*#APP_ENV=local#' \
-e 's#^APP_DEBUG=.*#APP_DEBUG=true#' \
-e 's#^DB_CONNECTION=.*#DB_CONNECTION=mysql#' \
-e 's/^# DB_HOST=.*/DB_HOST=db/' \
-e 's/^# DB_PORT=.*/DB_PORT=3306/' \
-e 's/^# DB_DATABASE=.*/DB_DATABASE=db/' \
-e 's/^# DB_USERNAME=.*/DB_USERNAME=db/' \
-e 's/^# DB_PASSWORD=.*/DB_PASSWORD=db/' \
.env

- name: Install the skeleton with this package checkout
run: |
ddev composer config repositories.debugbar '{"type": "path", "url": "packages/debugbar", "options": {"symlink": false, "versions": {"pollora/debugbar": "1.0.0"}}}'
ddev composer require --dev pollora/debugbar:1.0.0 --with-all-dependencies --no-interaction --no-progress

- name: Generate application key
run: ddev exec php artisan key:generate --no-interaction

- name: Install WordPress
run: |
ddev exec php artisan pollora:install --install \
--title="Pollora" \
--description="Pollora Debugbar CI" \
--admin-user=admin \
--admin-email=admin@example.com \
--admin-password=pollora-ci-password \
--locale=en_US \
--public=false \
--no-interaction

- name: Add the plugin and package fixtures
run: |
mkdir -p public/content/mu-plugins
cp packages/debugbar/tests/e2e/fixtures/acme-debugbar.php public/content/mu-plugins/

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
cache-dependency-path: packages/debugbar/tests/e2e/package-lock.json

- name: Install the browser tests
working-directory: packages/debugbar/tests/e2e
run: |
npm ci
npx playwright install --with-deps chromium

- name: Trust the site's certificate
run: |
if command -v mkcert >/dev/null && [ -f "$(mkcert -CAROOT)/rootCA.pem" ]; then
echo "NODE_EXTRA_CA_CERTS=$(mkcert -CAROOT)/rootCA.pem" >> "$GITHUB_ENV"
else
echo "::warning::No mkcert authority on the runner; certificate checks are off for this disposable site"
echo "NODE_TLS_REJECT_UNAUTHORIZED=0" >> "$GITHUB_ENV"
fi

- name: Browser tests
working-directory: packages/debugbar/tests/e2e
env:
E2E_HOME_URL: https://pollora-debugbar.ddev.site
run: npx playwright test

- name: Upload the browser test report
if: failure()
uses: actions/upload-artifact@v4
with:
name: e2e-report
path: |
packages/debugbar/tests/e2e/playwright-report
packages/debugbar/tests/e2e/test-results
retention-days: 14

- name: Laravel log
if: failure()
run: tail -n 200 storage/logs/laravel.log || true
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ composer.lock
patches.lock.json
.idea/
.phpunit.result.cache
tests/e2e/node_modules/
tests/e2e/playwright-report/
tests/e2e/test-results/
82 changes: 82 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<p align="center">
<a href="https://packagist.org/packages/pollora/debugbar"><img src="https://img.shields.io/packagist/v/pollora/debugbar" alt="Latest version"></a>
<a href="https://github.com/Pollora/debugbar/actions/workflows/tests.yml"><img src="https://github.com/Pollora/debugbar/actions/workflows/tests.yml/badge.svg" alt="Tests"></a>
<a href="LICENSE"><img src="https://img.shields.io/github/license/Pollora/debugbar" alt="License"></a>
</p>

# Pollora Debugbar

Puts WordPress and [Pollora](https://pollora.dev) in [Laravel Debugbar](https://github.com/fruitcake/laravel-debugbar): the `$wpdb` queries next to Eloquent's, the hooks that ran, how WordPress parsed the request, which route or template answered it, and WordPress's phases on the timeline. The long-term goal is to make Query Monitor unnecessary in a Pollora project.

## Installation

```bash
composer require --dev pollora/debugbar
```

It brings `fruitcake/laravel-debugbar` with it. Nothing runs unless Laravel Debugbar is enabled for the request (`DEBUGBAR_ENABLED`, or `APP_DEBUG`; never in production or testing), and as a dev dependency it is not installed by `composer install --no-dev`.

## Tabs

| Tab | Origin | Shows |
| --- | --- | --- |
| Pollora | Pollora | What answered (`Route::wp()`, the template hierarchy and its view, a Laravel route, or WordPress alone), versions, discovery, modules, theme, async actions registered and queued, WordPress constants and drop-ins |
| Doctor | Pollora | A **Run doctor** button: `pollora:doctor`'s web checks on demand, errors first |
| WP Request | WordPress | Rewrite rule, query vars, queried object, main query, true conditionals, template and hierarchy candidates |
| WP Queries | WordPress | `$wpdb` queries with time, full backtrace, rows, errors, duplicates, slow ones and the main query, grouped by component (core, plugin, theme…) |
| WP Hooks | WordPress | Hooks that ran, their callbacks, and those Pollora registered |
| WP HTTP | WordPress | `wp_remote_*` calls: result, time, transport, who made them |
| WP Cache | WordPress | Object cache hits and misses, transients set, OPcache |
| WP Capabilities | WordPress | `current_user_can()` checks, each distinct check once with its count |
| WP Blocks | WordPress | Blocks rendered by type with their time, Pollora's Blade blocks, block bindings |
| WP Assets | WordPress | Scripts, styles and script modules, header or footer, missing dependencies, Vite builds |
| WP Languages | WordPress | Locale and the translation files looked for |
| Timeline | WordPress | `muplugins_loaded` to `shutdown`, beside Debugbar's own measures |

REST and admin-ajax requests end with `exit`, which Laravel Debugbar never sees: this package stores them and sends the `phpdebugbar-id` header, so they appear in the bar's request list of the page that made the call. A `wp_redirect()` keeps its request for the page it leads to.

WordPress queries are traced through core's `log_query_custom_data` and `query` filters, so this works with any `db.php` drop-in, Pollora's included.

Configuration: `php artisan vendor:publish --tag=debugbar-pollora-config`.

## Adding your own data

Tabs from Pollora, WordPress and third parties are told apart: Pollora's tab comes first, WordPress's are prefixed `WP`, and everyone else's come last. Names starting with `wp_` or `pollora` are reserved.

**From a WordPress plugin or theme**, with no dependency on this package (without it, nothing fires the action):

```php
add_action('pollora/debugbar/register', function ($bar): void {
$bar->table('acme_cart', 'Acme cart', fn (): array => acme_cart_rows(), origin: 'acme-shop');
$bar->variables('acme_info', 'Acme', fn (): array => ['mode' => 'test'], origin: 'acme-shop');
$bar->section('wp_request', 'Acme', fn (): array => ['Cart' => acme_cart_id()]);
});

do_action('pollora/debugbar/message', 'Cart rebuilt', 'info', ['items' => 3]);
do_action('pollora/debugbar/start', 'acme-sync');
do_action('pollora/debugbar/stop', 'acme-sync');
```

Query Monitor's `qm/debug` … `qm/emergency`, `qm/start` and `qm/stop` actions keep working too.

**From a package or module**, extend `Pollora\Debugbar\Collector` and tag it:

```php
final class CartCollector extends \Pollora\Debugbar\Collector
{
public function getName(): string { return 'acme_cart'; }
public function title(): string { return 'Acme cart'; }
public function origin(): string { return 'acme-shop'; }
protected function data(): array { return ['items' => 3]; }
}

$this->app->tag([CartCollector::class], \Pollora\Debugbar\CollectorRegistrar::COLLECTORS_TAG);
```

`widget()` picks `Widget::Variables`, `Widget::Table` (with `columns()`) or `Widget::Queries`. A `Pollora\Debugbar\Contracts\SectionProvider` tagged `pollora.debugbar.sections` adds a section to an existing tab.

**With Laravel Debugbar alone**, `Debugbar::addCollector()` and `debugbar.custom_collectors` work as usual.

## License

MIT. See [LICENSE](LICENSE).
82 changes: 82 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{
"name": "pollora/debugbar",
"description": "Laravel Debugbar for Pollora: WordPress queries, hooks, the template hierarchy and Pollora's internals in the debug bar",
"type": "library",
"license": "MIT",
"keywords": [
"wordpress",
"laravel",
"debugbar",
"pollora",
"query-monitor"
],
"authors": [
{
"name": "Amphibee",
"email": "contact@amphibee.fr"
}
],
"require": {
"php": "^8.3",
"fruitcake/laravel-debugbar": "^4.4",
"pollora/framework": "^13.35.3"
},
"require-dev": {
"brain/monkey": "^2.7",
"laravel/pint": "^1.22",
"mockery/mockery": "^1.6",
"orchestra/testbench": "^11.1",
"pestphp/pest": "^3.8",
"php-stubs/wordpress-stubs": "^7.1",
"phpstan/phpstan": "^2.1",
"szepeviktor/phpstan-wordpress": "^2.0"
},
"autoload": {
"psr-4": {
"Pollora\\Debugbar\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Pollora\\Debugbar\\Tests\\": "tests/"
}
},
"extra": {
"laravel": {
"providers": [
"Pollora\\Debugbar\\DebugbarServiceProvider"
]
}
},
"config": {
"sort-packages": true,
"allow-plugins": {
"cweagans/composer-patches": true,
"pestphp/pest-plugin": true,
"phpstan/extension-installer": true,
"pollora/helper-overrider": true,
"wikimedia/composer-merge-plugin": true
}
},
"scripts": {
"test": [
"@test:unit",
"@phpstan",
"@lint:check"
],
"test:unit": "pest",
"phpstan": "phpstan analyse --memory-limit=1G",
"phpstan:ci": "phpstan analyse --memory-limit=1G --no-progress",
"lint": "pint",
"lint:check": "pint --test"
},
"scripts-descriptions": {
"test": "Run the whole quality gate as CI does",
"test:unit": "Run the Pest test suite",
"phpstan": "Run PHPStan static analysis",
"lint": "Apply Laravel Pint code style fixes",
"lint:check": "Verify code style without writing"
},
"minimum-stability": "dev",
"prefer-stable": true
}
Loading
Loading