degraciamathieu / php-dep
PHP dependency analyzer - extract and visualize class relationships
Requires
- php: >=8.2
- nikic/php-parser: ^5.0
- phpstan/phpdoc-parser: ^1.0
- symfony/console: ^7.0
Requires (Dev)
- humbug/box: ^4.6
- phpunit/phpunit: ^11.0
README
PHP command-line dependency analyzer. Extracts and visualizes all class relationships in a project: inheritance, interfaces, traits, injections, instantiations, static calls, docblocks.
Installation
git clone <repo> cd php-dep composer install chmod +x bin/php-dep
For global access:
ln -s /absolute/path/to/bin/php-dep /usr/local/bin/php-dep
Quick start
# Analyze the current directory ./bin/php-dep analyze # Analyze a specific project ./bin/php-dep analyze /path/to/project/src # JSON output (for scripts, CI, jq…) ./bin/php-dep analyze src/ --format=json -q # Zoom in on a class ./bin/php-dep analyze src/ --class='App\Service\UserService'
analyze command
php-dep analyze [<path>] [options]
<path> is optional, defaults to .
Options
| Option | Shortcut | Description |
|---|---|---|
--format=text|json |
-f |
Output format (default: text) |
--class=FQCN |
-c |
Focus on a class (full FQCN) |
--sort=alpha|deps|fanin |
-s |
Table sort: alphabetical, number of outgoing dependencies, number of incoming dependencies (default: alpha) |
--limit=N |
-l |
Limit output to N classes |
--exclude=dir |
Exclude a directory (can be used multiple times) | |
--include-vendor |
Include the vendor/ directory in the analysis |
|
--skip-docblocks |
Ignore @param/@return/@throws annotations |
|
--quiet |
-q |
Suppresses the progress bar and warnings (stdout preserved) |
--verbose |
-v |
Show warning details |
Exit codes
| Code | Meaning |
|---|---|
0 |
Success |
1 |
Parsing error(s) |
2 |
Internal error |
3 |
Invalid arguments |
Examples
Project overview
./bin/php-dep analyze src/
Displays a table with, for each class: type, number of outgoing dependencies (what it uses), incoming dependencies (what uses it), and source file.
Find the most coupled classes
# Highest dependency consumers ./bin/php-dep analyze src/ --sort=deps --limit=10 # Most used by others (high fan-in) ./bin/php-dep analyze src/ --sort=fanin --limit=10
Inspect a class
./bin/php-dep analyze src/ --class='PhpDep\Parser\PhpFileParser' -v
Displays two tables: what the class uses (outgoing dependencies) and who uses it (incoming dependencies), with the relationship type and source line.
JSON pipeline with jq
# Count classes ./bin/php-dep analyze src/ -f json -q | jq '.meta.class_count' # List all inheritance relationships ./bin/php-dep analyze src/ -f json -q | jq '[.edges[] | select(.type == "extends")]' # Find classes with no dependants (leaves) ./bin/php-dep analyze src/ -f json -q | jq '[.classes[] | select(.dependants | length == 0) | .fqcn]' # Find dependencies of a specific class ./bin/php-dep analyze src/ -f json -q | jq '.classes[] | select(.fqcn == "App\\Service\\UserService") | .dependencies'
Exclude directories
./bin/php-dep analyze . --exclude=tests --exclude=fixtures --exclude=migrations
Analysis without vendor
By default, vendor/ is excluded from the analysis but third-party classes appear as external nodes in the graph. No additional option is needed to ignore them entirely.
To analyze the vendor itself:
./bin/php-dep analyze . --include-vendor
JSON output structure
{
"meta": {
"version": "1.0",
"generated_at": "2026-02-26T10:00:00+00:00",
"analyzed_path": "/path/to/project",
"file_count": 42,
"class_count": 38, // internal nodes only
"node_count": 95, // internal + external (vendor, built-in)
"edge_count": 312,
"warning_count": 2
},
"classes": [
{
"fqcn": "App\\Service\\UserService",
"type": "class", // class | interface | trait | enum
"file": "/path/to/UserService.php",
"line": 12,
"dependencies": ["App\\Repository\\UserRepository", "Psr\\Log\\LoggerInterface"],
"dependants": ["App\\Controller\\UserController"]
}
],
"edges": [
{
"source": "App\\Service\\UserService",
"target": "App\\Repository\\UserRepository",
"type": "param_type", // see relationship types below
"confidence": "certain", // certain | high | medium | low
"file": "/path/to/UserService.php",
"line": 23,
"metadata": {}
}
],
"warnings": [
{
"type": "dynamic_instantiation",
"file": "/path/to/Factory.php",
"line": 45,
"message": "Dynamic instantiation: new $variable()"
}
]
}
Relationship types (edge.type)
Tier 1 — Maximum certainty (AST)
| Type | Description |
|---|---|
extends |
Class or interface inheritance |
implements |
Interface implementation |
uses_trait |
Trait usage |
param_type |
Type hint on a method parameter |
return_type |
Method return type |
property_type |
Class property type |
instantiates |
new Foo() |
static_call |
Foo::method() |
static_property |
Foo::$prop |
const_access |
Foo::CONST |
instanceof |
$x instanceof Foo |
catches |
catch (FooException $e) |
Tier 2 — High confidence (docblocks)
Extracted from @param, @return, @var, @throws. Confidence: high.
| Type | Source |
|---|---|
docblock_param |
@param FooType $x |
docblock_return |
@return FooType |
docblock_var |
@var FooType |
docblock_throws |
@throws FooException |
Confidence levels
| Value | Meaning |
|---|---|
certain |
Structural relationship guaranteed by the AST |
high |
Docblock, highly probable |
medium |
Ambiguous pattern (instanceof) |
low |
Dynamic pattern |
Warnings
| Type | Trigger |
|---|---|
dynamic_instantiation |
new $variable() — class unknown at static analysis time |
dynamic_call |
Unresolvable dynamic call |
parse_error |
Invalid or unreadable PHP file |
Warnings are printed to stderr. The JSON output includes them in warnings[]. With -v, the text output lists them at the end of the report.
How it works
- Discovery:
git ls-filesif inside a git repository, otherwiseRecursiveDirectoryIterator. Thevendor/,node_modules/, and.git/directories are excluded by default. - Parsing: nikic/PHP-Parser v5. The
NameResolverruns first in the traverser: all downstream names are resolved FQCNs. - Docblocks: phpstan/phpdoc-parser for
@param,@return,@var,@throws. - Memory: each file's AST is freed immediately after extraction (streaming). Only one AST in memory at a time.
- Vendor: in
boundarymode (default), vendor classes areexternalnodes (leaves) — their files are not analyzed.
Requirements
- PHP >= 8.2
- Composer