5

GOAL: I'm interested in generating a DOT Format description of the class dependencies in a PHP program.

IDEA: It shouldn't be hard to write a CodeSniffer "sniff" that can detect (and emit DOT records for) the following patterns in PHP source:

class SomeClassName extends BasicClassName {  // SomeClassName refers to BasicClassName
...
    new OtherClassName();            // SomeClassName refers to OtherClassName
    ThisClassName::some_method();    // SomeClassName refers to ThisClassName
    ThatClassName::$some_member;     // SomeClassName refers to ThatClassName
    RandomClassName::some_constant;  // SomeClassName refers to RandomClassName
...
}

But I haven't found any published sniffs to emit this information (and any other patterns indicating a "real" class dependency relationship that I may have missed).

NOTE: I specifically do not care about PHP's include() and require() statements (whose behavior I'm not convinced is even well-defined). For the purposes of this question let's assume that all PHP class resolution is handled via autoloading, and that we're looking to use only static code analysis to build the class dependency diagram.

EDIT: Unfortunately, I see no general way to deal with the following:

class ThatClassName {
...
    function generateClassName() {
        // something too complicated to analyze statically...
    }

    function foo() {
        $name = $this->generateClassName();
        $instance = new $name; // ThatClassName refers to ... what?
        ...
    }
...
}

But of course it would be possible to represent this scenario in a dependency graph by showing ThatClassName with a dependency on the generateClassName() method - perhaps shown with parens to make the method name easily distinguishable from a class name. And it probably wouldn't be a bad idea to establish a convention whereby any method which generates a class name dynamically must contain an annotation (in the associated comment) which indicates every class name which might possibly be generated - these "documented dynamic dependencies" could then be automatically included in the dependency graph.

Peter
  • 2,407
  • 22
  • 31

2 Answers2

2

This isn't really what PHP_CodeSniffer is designed to do; specifically because the sniffs are not supposed to output data or write to files. But, there is certainly nothing stopping you from doing this inside a sniff. It's just PHP code after all and it doesn't need to report any errors or warnings.

I haven't come across any sniffs that are doing anything like you describe, so I think you'd have to write a new one.

If you want to create a new sniff, I'd recommend starting with an abstract scope sniff. This allows you to look for T_NEW and T_DOUBLE_COLON tokens inside T_CLASS tokens. Here is an example.

Or, if you also want to look into global functions and other code outside classes, you can just look for T_NEW and T_DOUBLE_COLON tokens inside a regular sniff

If you're not sure how to get started or you just want some help writing the sniff, contact me and I can help write this with you. I'd just need to know what output you'd want for each case found, or I can just use something basic. If you want a hand, my email is: gsherwood at squiz dot net

Greg Sherwood
  • 6,083
  • 1
  • 23
  • 22
  • 1
    Hello Greg, I recognize you as the author of many "standard" CodeSniffer sniffs, and your offer to help is much appreciated (and thanks for the reference). Of course I recognize that Codesniffer was designed to check conformance to coding style standards - however, CodeSniffer's ability to parse an entire codebase **with syntactic intelligence** makes it an ideal platform for performing all sorts of static analysis chores. For example, I modified another "Generic" sniff (yours?) for finding duplicate class names) to generate a file->class map for use in an autoloader. – Peter Nov 12 '12 at 15:37
  • 1
    I'll have a go at writing a sniff as per your recommendation. If I run into trouble I'll take advantage of your offer to help, and when I get something working I'll propose adding it to the CodeSniffer distribution (since I'm sure somebody else will find it useful someday). – Peter Nov 12 '12 at 15:39
  • That sounds great Peter. I also maintain PHP_CodeSniffer itself, so if you run into any problems with tokenizing, or the core code getting in your way, please get in touch and let me know and I'll see if I can make any changes to help you. While it's not what PHPCS was designed for, I'm really interested to see how it goes and would love for it to be contributed back once working. – Greg Sherwood Nov 12 '12 at 21:15
  • With respect this is an older question, did you ever come up with a solution. I am looking to do something very similar and would appreciate not having to re-invent the wheel. – Jason McCreary Dec 06 '15 at 16:41
2

I wrote a tool for this: PhpDependencyAnalysis.

This is an extandable static code analysis for object-oriented PHP-Projects (>= 5.3.3) based on namespaces. It creates dependency graphs on customizable levels, e.g. on package-level or on class-level. Thus, it's usable to declare dependencies in general, but it's also usable to perform a detection of violations between layers in a tiered architecture according to compliance with Separation of Concerns, Law of Demeter and Acyclic Dependencies Principle. You can also change the output format to DOT.

Just check PhpDependencyAnalysis on GitHub.

Mamuz
  • 1,692
  • 12
  • 14
  • Thanks for the link! This looks interesting, and potentially very useful. In your (very nice) documentation for Call Graphs you write "A call can be a method parameter, a method return-value or an instance creation." Is your tool smart enough to track object instance creation via factory methods, or does it only recognize creation via "new"? – Peter Dec 08 '15 at 17:36