Configuration

PhpSpec can be configured via a config file in the project root. The following formats are supported, checked in priority order:

  1. phpspec.yaml
  2. phpspec.yml
  3. phpspec.json
  4. phpspec.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.

ValueFormatter
prettyFull output with context hierarchy (default)
dotOne character per example
tapTAP (Test Anything Protocol)
junitJUnit XML for CI integration
htmlSelf-contained HTML report
agentMachine-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:

KeyDescription
pathsArray of directories to scan for spec/feature files
srcSource directory for code generation and coverage
stepsArray 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:

  1. phpspec.yaml
  2. phpspec.yml
  3. phpspec.json
  4. phpspec.php

The first file found wins. If none exists, defaults are used.

CLI Overrides

CLI flags take priority over configuration file values:

Config KeyCLI 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_pathPass 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
KeyRequiredDescription
providerYesLLM provider: google, anthropic, openai, grok, deepseek, or ollama
modelNoModel identifier; your setting always beats the shipped default
max_tokensNoOutput-token ceiling per call; beats the per-command defaults
effortNoReasoning effort passed to the provider (e.g. low, medium, high), applied once the provider supports it
api_keyYes*API key for the provider. Required for every provider except ollama.
base_urlNoThe 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.