From 605fff5324ff651599a1568cd5602bbed3c48856 Mon Sep 17 00:00:00 2001 From: Michael Kramer Date: Sun, 2 Aug 2026 16:47:04 +0200 Subject: [PATCH 1/2] Add AI instructions for contributors --- .gitignore | 1 + CLAUDE.md | 59 ++++++++++++++++++++++++++++++++++++++++++ contribution/CLAUDE.md | 29 +++++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 CLAUDE.md create mode 100644 contribution/CLAUDE.md diff --git a/.gitignore b/.gitignore index 8562fb1c7..ec0c66ddc 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ bin/configlet.exe /vendor/ composer.lock +CLAUDE.local.md # IDE Files .idea diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..30d356368 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,59 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What this repo is + +This is the Exercism PHP track: the source of truth for every PHP exercise served on exercism.org. It is not an application — it's a curated set of independent exercise directories (practice + concept exercises), each containing a problem statement, a test suite, and a reference solution, plus tooling to keep them in sync with Exercism's cross-language `problem-specifications` and CI-verify that every reference solution actually passes its own tests. + +## Common commands + +```shell +composer install # install dependencies, fetches bin/configlet +composer ci # full local CI: configlet fmt + lint:check + tests:run — run before pushing +composer test:run # run every exercise's tests against its reference solution +composer test:run -- book-store # run tests for a single exercise (glob supported, e.g. "b*") +composer lint:check # phpcs — check PSR-12-derived style +composer lint:fix # phpcbf — autofix style issues +composer configlet:fmt # normalize exercise metadata files via configlet +``` + +`composer test:run` works by copying an exercise directory to a temp dir, overlaying the reference solution (`.meta/example.php` for practice exercises, `.meta/exemplar.php` for concept exercises) on top of the stub, stripping `markTestSkipped()` calls from the test file, and running PHPUnit directly (`bin/test.sh`). Always use `composer test:run -- ` to test a single exercise rather than invoking `phpunit` directly — running the test file in place tests the stub, not the reference solution. + +## Repository layout + +- `exercises/practice//` — one dir per practice exercise: + - `.php` — stub the student fills in + - `Test.php` — PHPUnit test suite (student-facing) + - `.docs/introduction.md`, `.docs/instructions.md` — problem statement (often generated, see below) + - `.meta/example.php` — reference solution used by CI (may instead be a `.meta/example/` directory when the solution needs multiple files) + - `.meta/tests.toml` — auto-generated by `configlet sync`; controls which canonical test cases are included/excluded (`include = false`) and lets you attach a `comment` explaining a deviation. Hand edits other than `include`/`comment` get wiped on regeneration. + - `.meta/config.json` — per-exercise metadata (authors, files, etc.) +- `exercises/concept//` — same idea but for concept exercises, which teach one specific language concept: + - reference solution is `.meta/exemplar.php` instead of `example.php` + - additional `.docs/hints.md` and `.meta/design.md` explaining pedagogical intent + - `.docs/introduction.md.tpl` may exist as the templated source for the generated `introduction.md`. A template pulls in one or more concepts' own `introduction.md` via `%{concept:}` placeholders (see `concepts//introduction.md`), and can add exercise-specific prose around those placeholders. **When a `.docs/introduction.md.tpl` exists, never hand-edit `.docs/introduction.md` directly — edit the `.tpl` and regenerate with `bin/configlet generate` (not `configlet fmt`, which will fail/overwrite it).** +- `concepts//` — the concept glossary (`about.md`, `links.md`) that concept exercises reference by slug (e.g. `basic-syntax`, `arrays`); a concept exercise's `.meta/config.json` lists which concepts it teaches and which are prerequisites. +- `config.json` (repo root) — the master Exercism track manifest: registers every exercise, its UUID, concepts/prerequisites, difficulty, and the file-role patterns (`%{pascal_slug}.php`, etc.) `configlet` uses to generate per-exercise scaffolding. +- `bin/configlet` — the official Exercism tool (fetched via `bin/fetch-configlet`, run through `composer` scripts) that validates `config.json` against the exercise directories and formats metadata (`configlet fmt`, `configlet sync`, `configlet create`). +- `src/Exercism/Sniffs/` — a custom PHP_CodeSniffer sniff (`ExplainStrictTypesSniff`) enforced by `phpcs.xml` on top of PSR-12. +- `contribution/` — auxiliary, not-fully-maintained tooling (e.g. a Symfony-based test generator, a deprecated-exercise checker); treat as separate from the main track content. + +## Coding standard specifics (phpcs.xml) + +Style is PSR-12 with these deviations: + +- Namespace/multiple-class-per-file rules are relaxed (exercises are single-file, namespace-free by convention). +- `declare(strict_types=1)` is required on practice-exercise solution files but is explicitly excluded on `.meta/*.php` reference solutions, all `concept/*` exercises, and `hello-world`. +- The custom `ExplainStrictTypesSniff` requires strict-types declarations to carry an explanatory comment; it's excluded on test files, `.meta/*.php`, `src/*`, and `contribution/*.php`. +- `Squiz.Scope.MethodScope.Missing` is excluded for `concept/city-office` and `concept/windowing-system` (these intentionally use non-method function scope for teaching purposes). +- `use` statements must be alphabetically sorted. + +## Adding/modifying exercises + +1. Scaffold a new practice exercise: `bin/configlet create --practice-exercise ` (creates `exercises/practice//`). +2. Write/edit `.meta/example.php` (or `exemplar.php` for concept exercises) and the test file; mark canonical test cases you deliberately skip in `.meta/tests.toml` with `include = false` (and a `comment` explaining why). +3. There is a WIP test generator under `contribution/generator` (Symfony console app) usable via `composer -d contribution/generator install && contribution/generator/bin/console app:create-tests ''`, followed by `composer lint:fix`. +4. If you change an exercise's difficulty or add a practice exercise, run `bin/order-exercises.sh` to reorder `config.json` accordingly (requires `jq`). +5. If you add a new practice exercise that should stay in sync with `problem-specifications`, add its slug to `bin/auto-sync.txt` — `bin/auto-sync.sh` reads that list and runs `configlet sync` (update mode) only for the exercises named in it, so exercises left off the list are never auto-synced. +6. Run `composer ci` before opening a PR — this is what GitHub Actions enforces (PHP 8.2–8.4 across Linux/Windows/macOS). diff --git a/contribution/CLAUDE.md b/contribution/CLAUDE.md new file mode 100644 index 000000000..d7e4a8c2a --- /dev/null +++ b/contribution/CLAUDE.md @@ -0,0 +1,29 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this directory. + +## Scope + +This applies to `contribution/` only. It's a grab-bag of standalone maintainer tooling for the Exercism PHP track (see the parent `../CLAUDE.md` for the track itself) — not part of the track content shipped to students, not covered by the root `composer.json`/`phpcs.xml`, and not exercised by `composer ci`. + +- `generator/` — a proof-of-concept Symfony console app that auto-generates exercise test files from `problem-specifications` canonical data, using `nikic/php-parser`. Has its own `composer.json` (PHP >=8.2, Symfony 7.0, PHPUnit 11) independent of the root project's dependencies. +- `checkDeprecatedExercises.php` — a standalone PHP script (no dependencies) with a hardcoded list of exercise slugs; it queries GitHub for a `.deprecated` marker on each in `exercism/problem-specifications` and reports which ones should be marked deprecated in the root `config.json` and then removed from the script's own list. + +If a directory in here isn't listed above but shows up on disk anyway, check for a `CLAUDE.local.md` in this folder (gitignored, machine-specific) before assuming it's part of the project. + +## Commands + +```shell +composer -d contribution/generator install # install the generator's own dependencies +contribution/generator/bin/console app:create-tests '' # generate a test file for an exercise +composer lint:fix # fix style on the generated file, from repo root +``` + +```shell +php contribution/checkDeprecatedExercises.php # list exercises that problem-specifications has deprecated +``` + +## Notes + +- The generator is explicitly a PoC ("Let me know what you think" in `generator/README.md`) — treat it as unmaintained/experimental, not a polished tool with guaranteed correctness. +- Generated test files still need to go through the normal track workflow (write/adjust `.meta/example.php`, `.meta/tests.toml`, `composer lint:fix`, `composer test:run -- `) described in the root `CLAUDE.md`. From bfdf74e8c7f8fb239f0adc0475ac9505dbfb7658 Mon Sep 17 00:00:00 2001 From: Michael Kramer Date: Sun, 2 Aug 2026 16:47:31 +0200 Subject: [PATCH 2/2] Fix README's PHP version range --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 73d530b65..25fc18608 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ The `lint:check` is included in `composer ci` to run the CI checks locally. - Follow the [PSR-12] coding style (Exercisms PHP track uses a slightly [modified][local-file-phpcs-config] version of [PSR-12]). - Run `composer ci` to run CI checks locally before pushing. - CI is run on all pull requests, it must pass the required checks for merge. -- CI is running all tests on PHP 8.1 to PHP 8.4 for Linux, Windows and MacOS. +- CI is running all tests on PHP 8.2 to PHP 8.4 for Linux, Windows and MacOS. ## Generating new practice exercises