mamuz / PhpDependencyAnalysis

Static code analysis to find violations in a dependency graph
http://mamuz.github.io/PhpDependencyAnalysis/
MIT License
561 stars 45 forks source link

Filter to leave only dependencies that are incoming for a specified namespace #7

Closed fankandin closed 8 years ago

fankandin commented 8 years ago

Hey Marco, thanks for a great tool! Really well done!

I have a situation of a project with many packages (grouped by folders) that mutually depend variously. The idea is to take one namespace and to find all other namespaces depend on it (let's name this specific namespace AnalysedFoo). If run the tool by folder it shows me only outgoing connection (or at least it doesn't show incoming dependencies that are located in other folders). If I run the tool for the entire project folder it takes way too long and the result will be probably anyway too complex and unacceptable. The excludePattern doesn't seem to work here as he can only filter out AnalysedFoo and leave all the rest (excludePattern: '/AnalysedFoo/'), or if I negate the pattern (excludePattern: '/^(?!.*AnalysedFoo).*$/') all incoming dependencies are dropped off.

To make it more clear:

So I want to see a diagram like

 +---------------------+
 |MyFolder\MySubFolderB|--+
 +---------------------+  |
                          |
 +---------------------+  |   +------------------------+
 |MyFolder\MySubFolderC|--+-->|MySubFolderA\AnalysedFoo|
 +---------------------+      +------------------------+

and nothing more.

Do you have any ideas how to approach a solution?

Thanks in advance!

mamuz commented 8 years ago

Hi @vintage-dreamer,

thx for your feedback.

This Tool is using at least three required visitors, which are configurable independently. DeclaredNamespaceCollector is for fetching a namespace which can have dependencies. MetaNamespaceCollector is for fetching possible dependecies by inheritence. UsedNamespaceCollector is for fetching possible dependecies by call.

That means you have to use your negated exclude pattern only for MetaNamespaceCollector and UsedNamespaceCollector to meet your requirement.

For instance based on the standard configuration:

# ...
visitorOptions:
  PhpDA\Parser\Visitor\Required\DeclaredNamespaceCollector: {minDepth: 2, sliceLength: 2}
  PhpDA\Parser\Visitor\Required\MetaNamespaceCollector: {minDepth: 2, sliceLength: 2, excludePattern: '/^(?!.*AnalysedFoo).*$/'}
  PhpDA\Parser\Visitor\Required\UsedNamespaceCollector: {minDepth: 2, sliceLength: 2, excludePattern: '/^(?!.*AnalysedFoo).*$/'}
# ...

In this case you are getting your desired graph, but you will also have edgeless nodes which are not depended to AnalysedFoo. I think this solution is working for you, due the fact that edgeless nodes are not in focus.

fankandin commented 8 years ago

Thank you for your reach in content reply! Unfortunately it doesn't seem to work. And I'm afraid it's pretty hard to provide a better example of my results. But maybe it would be more clear if show you this hack I applied to the method

\PhpDA\Layout\Builder::createEdgesFor():

    private function createEdgesFor(array $dependencies, array $edgeLayout, array $vertexLayout = array())
    {
        $dep = 'AnalysedFoo';
        foreach ($dependencies as $dependency) {
           if (
                (strpos($dependency->toString(), $dep) !== 0 && strpos($this->adtRootVertex->getId(), $dep) !== 0)
                || strpos($this->adtRootVertex->getId(), $dep) === 0
           ) {
               continue;
           }

with no exclusion patterns in the config. And this seems to help me a lot. The only problem left is edgeless nodes which are not removed with this hack. Does it ring you a bell, what exactly I did? :smile:

bishopb commented 8 years ago

@vintage-dreamer The configuration suggestion provided by @mamuz works in my case.

The following config shows all nodes connected to classes ending in "Peer", and uses a custom formatter to remove edgeless nodes in SVG output:

mode: 'usage'
source: 'path/to/source'
filePattern: '*.php'
formatter: 'GraphFormatter'
target: 'path/to/output/dependencies.svg'
visitorOptions:
  PhpDA\Parser\Visitor\Required\DeclaredNamespaceCollector: {minDepth: 1, sliceLength: 2}
  PhpDA\Parser\Visitor\Required\MetaNamespaceCollector: {minDepth: 1, sliceLength: 2, excludePattern: '/^(?!.*Peer).*$/'}
  PhpDA\Parser\Visitor\Required\UsedNamespaceCollector: {minDepth: 1, sliceLength: 2, excludePattern: '/^(?!.*Peer).*$/'}
<?php
use PhpDA\Writer\Strategy\StrategyInterface;
use PhpDA\Writer\Strategy\Svg;
use Fhaculty\Graph\Graph;

class GraphFormatter implements StrategyInterface
{
    public function filter(Graph $graph)
    {
        foreach ($graph->getVertices() as $vertex) {
            if (0 === count($vertex->getEdges())) {
                $vertex->destroy();
            }
        }

        $svg = new Svg();
        return $svg->filter($graph);
    }
}