PhpDependencyAnalysis - Manage PHP Dependencies
PhpDependencyAnalysis is an extendable open source static code analysis tool for object-oriented PHP projects. It builds a dependency graph for abstract datatypes (classes, interfaces and traits) based on their namespaces. Abstract datatypes are aggregatable to units on a higher level, like layers or packages. It detects cycle dependencies and verifies the graph against a custom architecture definition.
Web Site: https://github.com/mamuz/PhpDependencyAnalysis
Version tested: 0.*
System requirements: PHP>=5.3, GraphViz
License & Pricing: Open Source, MIT license, free
Why I needed it?
It is essential in mid-size or large-scale software projects to explicitly manage the dependencies over the complete software lifecycle. Ignoring this approach will lead to a maintenance nightmare (Big Ball of Mud).
Characteristics like testability, expandability, maintainability and comprehensibility are achieved using an active dependency management. Technical quality cannot be achieved only with testing. The architects and developers must design the software right from the start to meet some quality criteria. This design is mostly described in an architecture definition based on principles like Separation of Concerns, Law of Demeter and Acyclic Dependencies Principle by cutting the system into horizontally layers and vertically packages and a ruleset of engagements.
After writing and testing code, you have to verify that the implementation meets the architecture definition. Violations have to be solved to ensure a high maintainability for a long software life. For some programming languages you can find tools for this, but not for PHP. This is why I created PhpDependencyAnalysis.
How it works
PhpDependencyAnalysis parses PHP-files to extract all containing Abstract Datatypes (ADTs).
An ADT can be an interface, a trait or a class (concrete, abstract or final). Afterwards each ADT is converted to an abstract syntax tree and all nodes of this tree will be traversed. Comments like PHPDocBlocks are also abstracted as a node. During the traversing process, each node is visited by registered collectors (visitor objects). A collector inspects the node to collect a dependency to current ADT.
A dependency can be type of inheritance or type of call. Inheritance is defined by implementation, class-extending or a trait-use. Call is defined by a method parameter, by a method return-value or by an instance creation. A dependency is identified by their FQCN (Fully-Qualified Class Name). Those namespaces are sliceable to their namespace parts. This makes possible an aggregation of dependencies to a specific level, such as package-level, layer-level or component-level.
PhpDependencyAnalysis will create a directed graph of all ADTs (nodes) and their dependencies (edges). Cycles (detected automatically) and violations detected by your defined reference validator (Architecture Definition) are highlighted in red.
Example with detected cycle
Example with detected violations by using custom architecture definition
In most cases, large PHP projects generate a big dependency graph that is not readable for humans. To address this issue, PhpDependencyAnalysis is customizable to change the scope of the inspection. For instance you can filter namespaces to have a view on package level or you can parse a specific package only or a specific PHP file. You can also exclude namespaces from inspection.
Most PHP projects are using IoC-Containers for Dependecy-Injection, like ZF2-ServiceManager or Pimple. All of them provide objects by a string representation, which are not resolvable to dependencies in general. For this reason this tool is extendable with user-defined plugins to meet this requirement. Another useful extension would be a user-defined plugin to detect database tables or REST services as a dependency by parsing string nodes.
List of features:
- Providing high customizing level
- Dependency graph creation on customized levels respectively different scopes and layers
- Supports Usage-Graph, Call-Graph and Inheritance-Graph
- Dependencies can be aggregated to a package, a module or a layer
- Detecting cycles and violations between layers in a tiered architecture
- Verifying dependency graph against a user-defined reference architecture
- Collected Namespaces of dependencies are modifiable to meet custom use cases
- Printing graphs in several formats (HTML, SVG, DOT, JSON)
- Extendable by adding user-defined plugins for collecting and displaying dependencies
- Compatible to PHP7 Features, like Return Type Declarations and Anonymous Classes
This tool requires PHP >= 5.3.3. Besides this you need GraphViz (http://www.graphviz.org/) for graph creation on your machine. This is an open source graph visualization software and is available for the most platforms.
PHP is a dynamic language with a weak type system. It also contains a lot of magic statements, which resolves only during runtime. This tool perform static code analysis and thus has some limitations.
Here is a non-exhaustive list of unsupported PHP features:
- Dynamic features such as `eval` and `$$x`
- Globals such as `global $x;`
- Dynamic funcs such as `call_user_func`, `call_user_func_array`, `create_function`
The cleaner your project is, the more dependencies can be detected. Or in other words, it is highly recommend to have a clean project before using this tool.
You should apply naming conventions to have a greater chance for a generic detection of violations between packages and layers. Each package should have its own namespace and this is also true for each layer.
Example for a naming convention in an User-Package
- Namespace for Controllers: `User\Controller\..`
- Namespace for Listeners: `User\Listener\..`
- Namespace for Services: `User\Service\..`
- Namespace for Mappers: `User\Mapper\..`
In this case, you can declare classes, which use `User\Controller`- or `User\Listener`-Namespace, belongs to Application-Layer. Application-Layer is permitted access to Service-Layer (`User\Service`-Namespace) and is not permitted access to the Data-Source-Layer (`User\Mapper`-Namespace).
Project and Install Guide: https://github.com/mamuz/PhpDependencyAnalysis
Documentation and Examples: https://github.com/mamuz/PhpDependencyAnalysis/wiki