Files
unsupervised-scheduler/CLAUDE.md
T
thatguygriff 1d6ac46ba3
CI / No Debug Code (pull_request) Successful in 3s
CI / Tests (PHP 8.2) (pull_request) Successful in 48s
CI / Tests (PHP 8.3) (pull_request) Successful in 52s
CI / Coding Standards (pull_request) Successful in 57s
CI / Tests (PHP 8.1) (pull_request) Successful in 1m1s
CI / PHPStan (pull_request) Successful in 1m11s
CI / Build Plugin Zip (pull_request) Has been skipped
Upgrade PHPStan to 2.x and raise analysis level from 6 to 10
- Bump phpstan/phpstan ^2.0 and szepeviktor/phpstan-wordpress ^2.0
- Move the analysis level into phpstan.neon (single source) and raise it to 10
- Add Val, a runtime coercion helper that narrows untyped WordPress boundary
  values (wpdb rows, REST params, superglobals, options) with explicit checks
  instead of blind casts, plus unit tests
- Type value-object fromRow() params as stdClass (what wpdb returns) and map
  columns through Val so unexpected shapes degrade safely
- Use %i identifier placeholders for table names in all wpdb::prepare() calls
  so every query string is a literal and identifiers are escaped by WordPress;
  raises the minimum WordPress version to 6.2 where %i was introduced
- Guard wpdb::prepare() null result before wpdb::query() in updateTax()
- Fix nullable get_permalink()/strtotime() handling, list types at REST and
  capability call sites, dead null-coalescing on checked superglobals, and
  narrow get_users() results before mapping
- Register Val method names with the ValidatedSanitizedInput sniff so it
  validates the real sanitizer around each superglobal read
- Update repository unit tests for the %i placeholder arguments

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 13:42:50 -03:00

6.5 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Commands

composer install                  # Install all dependencies

composer test                     # Run the full test suite (required after every change)
composer lint                     # PHPStan static analysis
composer cs                       # PHPCS coding standards check
composer cs:fix                   # Auto-fix coding standards

# Run a single test file
./vendor/bin/phpunit tests/Unit/Availability/AvailabilityRepositoryTest.php

# Run a single test by name
./vendor/bin/phpunit --filter testInsertCallsWpdbInsertAndReturnsId

Run composer test after every code change before considering a task complete.

Architecture

Plugin Bootstrap

unsupervised-schedular.php defines constants (USC_VERSION, USC_PLUGIN_DIR, USC_PLUGIN_URL), registers activation/deactivation hooks, then calls Plugin::boot() on plugins_loaded. No logic lives in the root file.

Directory Structure

src/                  — All plugin PHP (PSR-4 namespace: Unsupervised\Schedular\)
  Availability/       — Availability slots: value object, repository, controller, REST endpoint
  Booking/            — Lessons/bookings: value object, repository, controller, REST endpoint, shortcode page
  Auth/               — Roles, capabilities, login page
  Plugin.php          — Wires all components together on plugins_loaded
  Installer.php       — Creates DB tables and roles on activation
  Schema.php          — CREATE TABLE SQL for dbDelta
  AdminMenu.php       — Registers wp-admin menu pages
  RestRegistrar.php   — Registers all REST routes under us-scheduler/v1
  ShortcodeRegistrar.php — Registers [us_booking] and [us_student_login] shortcodes
  BlockRegistrar.php  — Registers Gutenberg dynamic-block wrappers for the shortcodes
  BlockPreview.php    — Static editor-preview markup for the blocks
templates/            — PHP view files included by controllers/shortcodes
assets/               — CSS and JS (vanilla JS, no build step)
tests/Unit/           — PHPUnit unit tests (PSR-4: Unsupervised\Schedular\Tests\)
  Availability/       — Tests for src/Availability/
  Booking/            — Tests for src/Booking/
  Auth/               — Tests for src/Auth/
docs/features/        — One markdown file per feature describing data model, API, and test locations

Code is organised package-by-domain (Availability, Booking, Auth). Each domain package contains everything related to that domain: value objects, repositories, controllers, REST endpoints, and shortcode pages. Cross-cutting wiring classes (Plugin, AdminMenu, RestRegistrar, ShortcodeRegistrar, Schema) live directly under src/.

Data Storage

Two custom database tables (created via dbDelta on activation):

  • {prefix}us_availability — instructor availability windows
  • {prefix}us_lessons — booked lessons

All database access goes through repository classes within their domain package. No direct $wpdb calls outside repositories.

Key Classes

Class Responsibility
Plugin Wires all components together on plugins_loaded
Installer Creates DB tables and roles on activation
Schema CREATE TABLE SQL strings for dbDelta
AdminMenu Registers wp-admin menu pages
RestRegistrar Registers all REST routes under us-scheduler/v1
ShortcodeRegistrar Registers [us_booking] and [us_student_login] shortcodes
BlockRegistrar Registers Gutenberg dynamic-block wrappers for the shortcodes
BlockPreview Static editor-preview markup for the blocks
Val Runtime coercion of untyped WP boundary values (wpdb rows, REST params, superglobals)
Auth\RoleManager Registers us_instructor and us_student roles with custom caps
Auth\LoginPage Renders front-end student login form
Availability\AvailabilitySlot Immutable value object for a slot row
Availability\AvailabilityRepository CRUD for availability slots
Availability\AvailabilityController Instructor availability management page
Availability\AvailabilityEndpoint REST handlers for availability CRUD
Booking\Lesson Immutable value object for a lesson row
Booking\BookingRepository CRUD for lesson bookings
Booking\BookingEndpoint REST handlers for booking and status updates
Booking\BookingPage Renders student booking UI shell (JS takes over)
Booking\LessonController Admin and instructor lesson list pages

REST API Namespace

All endpoints live under /wp-json/us-scheduler/v1/. Permissions are enforced via permission_callback using capability checks (manage_availability, book_lesson), never role name checks.

Testing Approach

Tests use Brain\Monkey to stub WordPress functions without a full WP installation, and Mockery to mock $wpdb and other dependencies.

All test classes extend tests/Unit/TestCase.php, which handles Monkey\setUp() / Monkey\tearDown() and stubs all WP translation/escape functions automatically.

Brain\Monkey API notes:

  • Functions\when('fn')->alias(fn() => ...) — stub with a closure (NOT returnUsing())
  • Functions\when('fn')->justReturn($val) — stub returning a fixed value
  • Functions\expect('fn')->once()->with(...) — assert call count and arguments
  • Use Functions\when() (not Functions\expect()) when you need argument-routing (e.g. get_role returning different values per argument) to avoid chaining ambiguity
  • Mockery matchers (e.g. \Mockery::type()) inside plain PHP arrays do not work with with() — use \Mockery::on(fn($arr) => ...) or \Mockery::any() instead
  • When mocking $wpdb, set $mock->prefix = 'wp_' explicitly — it is a public property, not a method

Adding a Feature

  1. Write the feature doc in docs/features/<feature-name>.md (data model, API, classes, test paths).
  2. Create a domain package under src/<Domain>/ containing all classes for that feature.
  3. Add template(s) under templates/ if needed.
  4. Write unit tests under tests/Unit/<Domain>/ mirroring the src/<Domain>/ structure.
  5. Run composer test — all tests must pass before the feature is complete.

CI

Gitea Actions (.gitea/workflows/ci.yml) runs on every push and pull request:

  • lint — PHPCS WordPress coding standards
  • static-analysis — PHPStan level 10
  • test — PHPUnit on PHP 8.1, 8.2, 8.3
  • no-debug — rejects commits with var_dump, error_log, etc. in src/