Configuration
PhpSpec can be configured via a config file in the project root. The following formats are supported, checked in priority order:
phpspec.yamlphpspec.ymlphpspec.jsonphpspec.php
The first file found wins. If none exists, defaults are used, and whatever
phpspec writes on your behalf, an ai section or the guard switch, goes to
phpspec.yml. Both YAML extensions are read; the examples here use .yml.
Alternatively, pass an explicit path with --config (or -c) to load exactly
that file and skip the working directory lookup entirely:
$ vendor/bin/phpspec run --config custom/phpspec.ci.yml
The format is resolved from the file extension, and the command fails if the file does not exist. Because the path is explicit, several processes can run side by side in the same directory with different configurations.
phpspec.yml
spec_path: spec src_path: src format: pretty bootstrap: bootstrap.php stop_on_failure: false autoload: App\: src/App Acme\: lib/Acme
phpspec.yaml
Same syntax as phpspec.yml, an alternative extension.
phpspec.json
{
"spec_path": "spec",
"src_path": "src",
"format": "pretty",
"bootstrap": "bootstrap.php",
"stop_on_failure": false,
"autoload": {
"App\\": "src/App",
"Acme\\": "lib/Acme"
}
}
phpspec.php
A PHP file that returns an array:
<?php return [ 'spec_path' => 'spec', 'src_path' => 'src', 'format' => 'dot', 'stop_on_failure' => true, ];
Configuration Keys
spec_path
Directory containing spec files. Default: spec.
spec_path: tests/spec
spec_suffix
File suffix used to identify spec files. Default: .spec.php. Change this if you prefer a different naming convention such as Spec.php or Test.php.
spec_suffix: Spec.php
This affects file discovery, spec generation (describe command), and title derivation.
src_path
Directory containing source files. Used for code generation and coverage filtering. Default: src.
src_path: lib
features_path
Directory containing Gherkin .feature files, used by --story, --all and
step discovery. A path given to run runs as features when it is a .feature
file or lies inside this directory, and as specs otherwise, whatever words it
holds. Default: features/.
features_path: acceptance
steps_path
An additional directory to search for step definitions (steps.php and
*.steps.php), and the one generated steps go to. Step definitions are always
discoverable anywhere inside the features folder; when steps_path is set,
that directory is searched as well, and steps.php there becomes the default
steps file. Default: unset, so generated steps go to features/steps/steps.php.
steps_path: acceptance_steps
format
Default output formatter. Overridden by --format CLI flag.
| Value | Formatter |
|---|---|
pretty | Full output with context hierarchy (default) |
dot | One character per example |
tap | TAP (Test Anything Protocol) |
junit | JUnit XML for CI integration |
html | Self-contained HTML report |
agent | Machine-readable JSON for coding agents (see Coding Agents) |
bootstrap
PHP file to require before running specs. Useful for defining constants, setting up autoloaders, or configuring the environment. Overridden by --bootstrap CLI flag.
bootstrap: tests/bootstrap.php
stop_on_failure
Stop execution after the first failing or erroring spec. Default: false. Overridden by --stop-on-failure CLI flag.
stop_on_failure: true
stop_on_error
Stop execution after the first erroring spec. Default: false. Overridden by --stop-on-error CLI flag.
stop_on_error: true
stop_on_warning
Stop execution after the first spec that triggers a warning. Default: false. Overridden by --stop-on-warning CLI flag.
stop_on_warning: true
stop_on_deprecation
Stop execution after the first spec that triggers a deprecation notice. Default: false. Overridden by --stop-on-deprecation CLI flag.
stop_on_deprecation: true
stop_on_notice
Stop execution after the first spec that triggers a notice. Default: false. Overridden by --stop-on-notice CLI flag.
stop_on_notice: true
stop_on_pending
Stop execution after the first pending spec. Default: false. Overridden by --stop-on-pending CLI flag.
stop_on_pending: true
stop_on_skipped
Stop execution after the first skipped spec. Default: false. Overridden by --stop-on-skipped CLI flag.
stop_on_skipped: true
autoload
PSR-4 namespace-to-directory mappings, used to autoload classes while specs run and to place the classes PhpSpec generates: a class goes to the directory its own namespace is mapped to, the longest matching prefix winning. A config that states none reads the mappings from composer.json; see Code Generation.
autoload: App\: src/App Domain\: src/Domain Infrastructure\: src/Infrastructure
Each key is a namespace prefix (with trailing \), and each value is the directory path relative to the project root.
Classes under the source directory are loaded by PhpSpec itself as well, at
the path the layout maps their namespace to (the same place generated classes
are written), so a project with no Composer autoloader, or one whose mapping
changed without a composer dump-autoload, still runs.
default_namespace
The namespace a describe or exemplify name is put under when it is under
none of the mapped namespaces, instead of asking which one. A name already
under a mapping is left as written.
default_namespace: Brew\Acme autoload: Brew\Acme\: src/Brew/Acme Another\Acme\: src/Another
With this, describe Thing describes Brew\Acme\Thing in
spec/Brew/Acme/Thing.spec.php, and describe Another/Acme/Thing stays as it is.
base_url
Base URL for browser testing. Default: null.
base_url: http://localhost:8080
coverage_adapter
The engine that collects coverage: pcov or xdebug. Default: whichever is
on, PCOV first.
coverage_adapter: pcov
PCOV traces the files under pcov.directory, which it picks itself when it
is not set: src, lib or app under the working directory, else all of it.
A coverage run whose source directory lies outside it is refused, naming the
directory to set. Xdebug needs xdebug.mode=coverage.
coverage_exclude
What coverage leaves out: the lines neither count towards the percentage nor need an example, and guard never asks for one. Each entry is read by its shape:
coverage_exclude: - src/Kernel.php # a file, by its path from the project root - src/Migrations/ # a directory - src/**/Generated/*.php # a glob: * within a directory, ** across them - App\Legacy\* # every class, trait or enum in a namespace - App\Http\Kernel # one class, trait or enum - App\Clock::now # a method (App\Clock::* for all of them) - App\dump_and_die() # a function
Names match whatever their case, as PHP's do. Code can also leave itself out; see Excluding code from coverage.
Named Suites
Group specs into named suites, each with its own paths and source directory. When no explicit paths are passed to vendor/bin/phpspec run, all suite paths are loaded.
suites: unit: paths: - spec/Unit src: src integration: paths: - spec/Integration src: src acceptance: paths: - features steps: - features/steps
Each suite supports:
| Key | Description |
|---|---|
paths | Array of directories to scan for spec/feature files |
src | Source directory for code generation and coverage |
steps | Array of directories containing step definition files (reserved for future use) |
When no suites key is present, PhpSpec synthesises a default suite from the flat spec_path and src_path values:
# This: spec_path: spec src_path: src # Is equivalent to: suites: default: paths: - spec src: src
Running a specific suite's paths
Pass paths directly to vendor/bin/phpspec run:
$ vendor/bin/phpspec run spec/Unit $ vendor/bin/phpspec run spec/Unit spec/Integration
Auto-Detection
vendor/autoload.php
PhpSpec automatically detects and requires vendor/autoload.php if it exists in the project root. This means Composer-managed projects work out of the box without configuring autoloading.
Config File Discovery
PhpSpec looks for configuration files in this order:
phpspec.yamlphpspec.ymlphpspec.jsonphpspec.php
The first file found wins. If none exists, defaults are used.
CLI Overrides
CLI flags take priority over configuration file values:
| Config Key | CLI Flag |
|---|---|
format | --format / -f |
bootstrap | --bootstrap / -b |
stop_on_failure | --stop-on-failure |
stop_on_error | --stop-on-error |
stop_on_warning | --stop-on-warning |
stop_on_deprecation | --stop-on-deprecation |
stop_on_notice | --stop-on-notice |
stop_on_skipped | --stop-on-skipped |
spec_path | Pass path as argument to run |
Config and CLI flags are merged: if either is enabled, the condition is active. For example, stop_on_error: true in config combined with --stop-on-warning on the CLI will stop on both errors and warnings.
AI Assistant
The ai section enables the AI assistant in pair mode (vendor/bin/phpspec pair) and the refactor command (vendor/bin/phpspec refactor). When configured, any input that doesn't match a built-in command is sent to the LLM, which can generate specs, features, step definitions, run specs, and answer questions about your codebase.
ai: provider: anthropic api_key: YOUR_API_KEY
| Key | Required | Description |
|---|---|---|
provider | Yes | LLM provider: google, anthropic, openai, grok, deepseek, or ollama |
model | No | Model identifier; your setting always beats the shipped default |
max_tokens | No | Output-token ceiling per call; beats the per-command defaults |
effort | No | Reasoning effort passed to the provider (e.g. low, medium, high), applied once the provider supports it |
api_key | Yes* | API key for the provider. Required for every provider except ollama. |
base_url | No | The local ollama endpoint (defaults to http://localhost:11434); only ollama reads it |
Default models per provider: gemini-3.1-pro-preview (google), claude-sonnet-5 (anthropic), gpt-5.1 (openai), grok-4 (grok), deepseek-chat (deepseek), llama3.1 (ollama).
Key names are snake_case; hyphenated spellings (api-key) are accepted. A misspelled or unknown key, a missing required one, or a wrong-typed value is reported precisely, naming the key and the fix.
Customising the prompts
Every prompt the AI commands use is a text file. The shipped ones live inside the package; a project overrides any of them by placing a file of the same name under .phpspec/prompts/, which is committable and survives composer updates. A project file fully replaces the shipped one.
.phpspec/prompts/ commands/next.txt the next advisor's manifest and voice commands/generate.txt the generate command commands/refactor.txt the refactor rules: a plan, then baby steps commands/navigator.txt the pair navigator's manifest: its tools, params, and voice (also: driver.txt) instructions/write-spec.txt step guidance (also: write-feature, write-steps, write-code, refactor, tdd-cycle) instructions/spec-syntax.txt syntax primers (also: steps-syntax, gherkin-syntax, pair-guidance) tools/offer_change.txt any tool description, by tool name (write tools; the live ones run_specs, inspect_symbol, ask_user, read_file, list_files; refactor's propose_plan, propose_step, decline_refactoring) navigator.txt the pair navigator role contract (also: driver.txt, next.txt)
Command files under commands/ may start with YAML frontmatter (tools, answer channel, grounding, temperature, max_tokens, max_turns). Overrides inherit the shipped frontmatter per key: a prose-only file keeps the shipped machine contract, and declaring only temperature: 0.2 keeps the shipped tools. An @include <name> line inside any prompt resolves project-first.
.phpspec/ai/last-request.json marks overridden prompts in composed_from (e.g. "commands/next (project)"), so you can always see whose words the model heard.
See Pair Programming & AI for full documentation on pair mode, the AI assistant, and the refactor command.