zoliszabo / php-ambit
Ambit: shared project file scopes and Git change selection.
Requires
- php: >=7.4
- symfony/process: ^5.4
- symfony/yaml: ^5.4
Requires (Dev)
- phpunit/phpunit: ^9.6.38
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Define which files your PHP tools should process in one place, and select files from those scopes or Git changes.
Compatible with PHP 7.4+.
Configuration and usage
Create an ambit.yaml in your project root. A single php set is enough;
additional sets are optional:
php: paths: - src - tests exclude: - '*/generated/*'
use Ambit\Scope; $php = Scope::fromProject(__DIR__); // Selects the php set by default.
If tools need different scopes, add dedicated sets to the same file. They can inherit
paths and exclusions from php:
rector: extends: php exclude: - tests/Fixtures lint: extends: php paths: - bin - config exclude: - config/legacy.php
use Ambit\Scope; $rector = Scope::fromProject(__DIR__, 'rector'); $lint = Scope::fromProject(__DIR__, 'lint');
The lint set includes src, tests, bin, and config, and excludes both
*/generated/* and config/legacy.php.
extendsaccepts a set name or a list, such asextends: [php, shared]. Parents may themselves extend other sets and may be declared in any order.- Paths and exclusions are additive: parent entries come first, in parent declaration order, followed by the child's entries. Duplicates are removed, keeping their first occurrence.
- Named sets may omit
pathsandexclude; both default to empty lists. Empty lists keep inherited entries. Inherited paths and exclusions cannot be removed. - All relative paths and patterns are anchored to the directory containing
ambit.yaml. Absolute paths remain absolute. Loading aConfigdoes not check whether paths exist; creating aScopechecks literal inclusion paths in the selected set. - Omitting the set name selects
phpby default. If it is missing, loading fails just as it does for any other unknown set name. - Unknown keys, unknown set names, malformed definitions, and inheritance cycles are rejected. All set definitions are validated, including unused sets.
For direct configuration loading, use
(new Ambit\YamlConfigLoader())->load($file, 'lint'), which returns an immutable
Ambit\Config with absolute paths and exclusion patterns.
Use loadAll($file) to obtain every named set with inheritance resolved.
Using Ambit with Rector
In rector.php, use the rector set for paths and exclusions alongside your rule
configuration:
use Ambit\Scope; use Rector\Config\RectorConfig; $scope = Scope::fromProject(__DIR__, 'rector'); return RectorConfig::configure() ->withPaths($scope->paths()) ->withSkip($scope->exclusions());
Path matching
Inclusion patterns containing * use PHP's glob(); ** is not a recursive
globstar. Unmatched inclusion patterns select no files. Exclusions follow
Rector-style prefix/suffix and fnmatch() matching. Use a trailing / on literal
directory exclusions to avoid matching sibling directories with the same prefix.
Validation
Ambit provides two types of validation:
- Schema validation checks the parsed configuration's set definitions, keys, path lists, parent references, and inheritance cycles. It reports the first error without reading files or requiring configured paths to exist.
- Filesystem validation checks whether inclusions and exclusions match existing files or directories. Each unmatched entry produces a warning, including missing literal inclusions. Inclusion checks also warn when a leaf set (one that no other set extends) has no paths after inheritance and therefore selects no files. Empty parent sets can serve as shared exclusion definitions without this warning.
YamlConfigLoader parses YAML, validates its schema, and resolves paths and
inheritance into Config objects. Invalid YAML raises
Symfony\Component\Yaml\Exception\ParseException; schema violations raise
InvalidArgumentException. Loading an unknown set raises
Ambit\Exception\UnknownSetException, an InvalidArgumentException.
CLI
After installing Ambit as a dependency, run:
vendor/bin/ambit validate vendor/bin/ambit validate path/to/ambit.yaml --set=rector vendor/bin/ambit validate --schema-only vendor/bin/ambit validate --paths-only vendor/bin/ambit validate --exclusions-only
The configuration file defaults to ambit.yaml in the current working directory.
By default, filesystem checks cover every set; --set=NAME limits filesystem checks
to that set. Schema checks always cover every set. Only one of --schema-only,
--paths-only, and --exclusions-only may be supplied.
Example output:
Validating "ambit.yaml".
WARNING [filesystem] php.paths "modules/*/src" (resolved to "/project/modules/*/src"): This does not match any real path.
WARNING [filesystem] php.exclude "cache/" (resolved to "/project/cache/"): This does not match any real path.
0 error(s), 2 warning(s).
Exit status is 0 when there are no errors or warnings, 1 when there are errors
(including runs that also have warnings), 2 for incorrect CLI arguments, and 3
for warnings only.
PHP API
SchemaValidator takes already parsed configuration data:
use Ambit\Validation\SchemaValidator; $data = ['php' => ['paths' => ['src'], 'exclude' => ['cache/']]]; $result = (new SchemaValidator())->validate($data); // Or throw InvalidArgumentException on a schema violation: (new SchemaValidator())->assertValid($data);
FilesystemValidator checks an existing or programmatically created Config,
independently of YAML loading:
use Ambit\Validation\FilesystemValidator; use Ambit\YamlConfigLoader; $config = (new YamlConfigLoader())->load(__DIR__ . '/ambit.yaml'); $result = (new FilesystemValidator())->validate($config, __DIR__);
Both validators return a result with issues, errors, and warnings:
foreach ($result->issues() as $issue) { echo $issue->severity() . ': ' . $issue->path() . ' ' . $issue->message() . PHP_EOL; } $result->isValid(); // True when there are no errors; warnings are allowed. $result->errors(); $result->warnings();
Inclusions use the same glob() rules as Scope. Exclusions use PathMatcher
against the project directory tree, including hidden files and empty directories,
and resolved inclusion paths outside that tree. Exclusions with external absolute
prefixes are also checked at those locations. They need not overlap inclusions to
count as matching. Directory symlinks are not traversed recursively, preventing
cycles. An unreadable tree produces a filesystem error rather than potentially false
unmatched-exclusion warnings. Large trees or broad external patterns can make
filesystem validation slower.
Editor support
ambit.schema.json provides JSON Schema validation and
completion for YAML editors. Run ambit validate for inheritance and filesystem
checks.
Add this hint on the first line of your project-root ambit.yaml:
# yaml-language-server: $schema=vendor/zoliszabo/php-ambit/ambit.schema.json