mihaeu / dephpend

Detect flaws in your architecture, before they drag you down into the depths of dependency hell ...

Home Page:https://dephpend.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Optionally mark visibility of a dependency in output

brightbyte opened this issue · comments

It would be useful to me to be able to distinguish between dependencies of different visibility, in particular "public" dependencies (subclassing, implemented interfaces, public method signatures) and "private" dependencies (private method signatures, use in method bodies). For a large and messy code base like MediaWiki, this would allow further analysis to distinguish between that are hard to fix (public dependencies) and things that are easy to fix (private dependencies).

This is a feature I so far have not seen in any static analysis tool. I'm wondering why, I can't be the only one who'd find this useful. Perhaps it's because static analysis tools are mostly used by design purists, and options to filter out some kinds of issues as "less bad" does not appeal to them?... When working with clean code, this kind of thing isn't needed. When trying to clean up an organically grown tangle of half a million lines, it would be great to have...

Anyway. In addition to "private" and "public", there are a few in-between visibilities to be consider:

  • "constructor" (use in the constructor signature). Technically public, but constructor calls may be restricted to wiring code by convention, making it "internal" in a sense. For this to me useful, constructor usage should not be considered "public".
  • "protected" - may have to be treated as private or public, depending on context.
  • "internal" if the class or method has the @internal tag
  • "deprecated" if the class or method has the @deprecated tag. This essentially makes this an extension of or alternative to issue #42.

In "text" output, with --visibility enabled, this flags could form a third column, perhaps using the form
Foo --> Bar @Private
FooBar --> Foo @public
FooBar --> Frob @Protected @deprecated

In GML (as proposed in #41) this could trivially be represented as additional attributes on the edges.

Hi mihaeu, I'm glad you agree that this is would be a helpful feature!
I may be able to help with implementing this. This functionality would help me a lot!

Well, help is always appreciated :)

I have a long flight today, so I have some time to work on all your ideas :)

But dePHPend's architecture is quite simple actually.

All selected PHP files are parsed and then analyzed. After that, you've got a map of dependency tuples. Prefilters are run on every command and reduce that map depending on which filter was used (mostly selectors like regex etc.). Post processors are passed as callables depending on the command because those change how dependencies are displayed (e.g. you cannot strip classes from a namespace before counting how many classes are in a namespace, even though afterward you're only interested in the namespace).

Long story short what needs to happen for detecting visibility:

  • add the property to the Dependency value object
  • extend the src/Analyser/DependencyInspectionVisitor.php to add this information when encountering a property node
  • adjust output accordingly (probably best done by implementing a post-processor option --scope or --visibility)

Similarly, if you want to track deprecated methods you want to extend the inspector and (in this case) write a prefilter for it.

This might actually be a point where it could make sense to split up the inspector and inject which visitors are supposed to be used. It was mostly a performance and laziness decision to not do that in the first place. It might be worth benchmarking if the DependencyInspector can be split up into separate visitors, but honestly, I'm fine with the mess in there ... maybe one day I'll get around to fixing that.

I still see this as a very interesting feature, but also one that requires keeping track of state and doing multiple passes over all dependencies.

I'm guessing you already solved this issue, since it's 2 years old now 🙉 but if there's still a need, feel free to re-open, and I'll try to help in some way or another.