Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce config builders for deptrac #978

Closed
dbrumann opened this issue Sep 9, 2022 · 8 comments
Closed

Introduce config builders for deptrac #978

dbrumann opened this issue Sep 9, 2022 · 8 comments

Comments

@dbrumann
Copy link
Collaborator

dbrumann commented Sep 9, 2022

Background

In theory, it is already possible to use php instead of yaml for configuring deptrac. Unfortunately, because a lot of the configuration contains complex array structures, e.g. for defining layers and the collectors, this can be rather unwieldy.

Before, switching to PHP as default format, I would like to make configuration with php a bit easier. Symfony introduced config builder classes.

Task

Notes

  • It might make sense to refactor the LayerResolver and move initializeLayers into the container, i.e. each layer represents its own service in the container. There is one case that will be hard to handle, when layers need to access other layers (e.g. layer resolver or bool resolver) and their ordering is important. If we can do it, it might make it easier to configure them, because we could replace the extension logic with simple service instantiation instead. This is just a vague idea, so it definitely needs some investigation and might be a follow up instead.
@dbrumann
Copy link
Collaborator Author

dbrumann commented Sep 9, 2022

To give an example, this is what the current deptrac.yaml would likely look like (I get differing output, so I might have made some mistakes, please double check) with PHP config without config builders:

<?php

use Internal\Qossmic\Deptrac\IgnoreDependenciesOnContract;
use Internal\Qossmic\Deptrac\IgnoreDependenciesOnShouldNotHappenException;
use Qossmic\Deptrac\Contract\Analyser\ProcessEvent;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $container) {
    $container->import('deptrac.baseline.yaml');

    $container->services()
        ->set(IgnoreDependenciesOnContract::class)
        ->tag('kernel.event_listener', ['event' => ProcessEvent::class]);

    $container->services()
        ->set(IgnoreDependenciesOnShouldNotHappenException::class)
        ->tag('kernel.event_listener', ['event' => ProcessEvent::class]);

    $container->extension('deptrac', [
        'paths' => ['./src/'],
        'layers' => [
            [
                'name' => 'Analyser',
                'collectors' => [
                    [
                        'type' => 'directory',
                        'value' => 'src/Core/Analyser/.*',
                    ],
                    [
                        'type' => 'className',
                        'value' => '^JetBrains\\\\PHPStormStub\\\\.*',
                        'private' => true,
                    ],
                ],
            ],
            [
                'name' => 'Ast',
                'collectors' => [
                    [
                        'type' => 'directory',
                        'value' => 'src/Core/Ast/.*',
                    ],
                    [
                        'type' => 'className',
                        'value' => '^PHPStan\\\\PhpDocParser\\\\.*',
                        'private' => true,
                    ],
                    [
                        'type' => 'className',
                        'value' => '^PhpParser\\\\.*',
                        'private' => true,
                    ],
                    [
                        'type' => 'className',
                        'value' => '^phpDocumentor\\\\Reflection\\\\.*',
                        'private' => true,
                    ],
                ],
            ],
            [
                'name' => 'Console',
                'collectors' => [
                    [
                        'type' => 'directory',
                        'value' => 'src/Supportive/Console/.*',
                    ],
                ],
            ],
            [
                'name' => 'Dependency',
                'collectors' => [
                    [
                        'type' => 'directory',
                        'value' => 'src/Core/Dependency/.*',
                    ],
                ],
            ],
            [
                'name' => 'DependencyInjection',
                'collectors' => [
                    [
                        'type' => 'directory',
                        'value' => 'src/Supportive/DependencyInjection/.*',
                    ],
                ],
            ],
            [
                'name' => 'Contract',
                'collectors' => [
                    [
                        'type' => 'directory',
                        'value' => 'src/Contract/.*',
                    ],
                ],
            ],
            [
                'name' => 'InputCollector',
                'collectors' => [
                    [
                        'type' => 'directory',
                        'value' => 'src/Core/InputCollector/.*',
                    ],
                ],
            ],
            [
                'name' => 'Layer',
                'collectors' => [
                    [
                        'type' => 'directory',
                        'value' => 'src/Core/Layer/.*',
                    ],
                ],
            ],
            [
                'name' => 'OutputFormatter',
                'collectors' => [
                    [
                        'type' => 'directory',
                        'value' => 'src/Supportive/OutputFormatter/.*',
                    ],
                    [
                        'type' => 'className',
                        'value' => '^phpDocumentor\\\\GraphViz\\\\.*',
                        'private' => true,
                    ],
                ],
            ],
            [
                'name' => 'File',
                'collectors' => [
                    [
                        'type' => 'directory',
                        'value' => 'src/Supportive/File/.*',
                    ],
                ],
            ],
            [
                'name' => 'Supportive',
                'collectors' => [
                    [
                        'type' => 'bool',
                        'must_not' => [
                            [
                                'type' => 'directory',
                                'value' => 'src/Supportive/.*',
                            ],
                        ],
                        'must' => [
                            [
                                'type' => 'directory',
                                'value' => 'src/Supportive/.*/.*',
                            ],
                        ],
                    ],
                ],
            ],
        ],
        'ruleset' => [
            'Layer' => ['Ast'],
            'Console' => ['Analyser', 'OutputFormatter', 'DependencyInjection', 'File'],
            'Dependency' => ['Ast'],
            'Result' => ['Dependency'],
            'Analyser' => ['Result', 'Layer', 'Dependency', 'InputCollector', 'Ast'],
            'OutputFormatter' => ['Result', 'Console', 'DependencyInjection'],
            'Events' => ['Result'],
            'Ast' => ['File'],
            'InputCollector' => ['File'],
        ],
        'formatters' => [
            'graphviz' => [
                'pointToGroups' => true,
                'groups' => [
                    'Contract' => ['Contract'],
                    'Supportive' => ['Supportive', 'File'],
                    'Symfony' => ['Console', 'DependencyInjection', 'OutputFormatter'],
                    'Core' => ['Analyser', 'Ast', 'Dependency', 'InputCollector', 'Layer'],
                ],
            ],
        ],
    ]);
};

Especially for layers it would make sense to have something that is easier to manage and having autocomplete for the keys and ideally for the type values as well.

@gennadigennadigennadi
Copy link
Member

If no one else likes to start to work on this one, I would give it a try in the next few days.

@BafS
Copy link

BafS commented Mar 16, 2023

With this change it will be much more easy to work with big monorepos 👍

@ilnytskyi
Copy link

ilnytskyi commented May 3, 2023

Could we just have possibility to inject custom layers resolver? Or get layers from own services?
Now we could instantiate the LayerResolver via factory

  Qossmic\Deptrac\Core\Layer\LayerResolver:
    factory:   ['@My\Layer\Factory', 'create']

But this requires to know about all its dependencies inside a factory. So my method looks like

public function create(CollectorResolverInterface $collectorResolver): LayerResolverInterface
    {
        $layers = //my logic to automatically build layers

        return new LayerResolver($collectorResolver, $layers);
    }

I still pass CollectorResolverInterface $collectorResolver as dependency.

Maybe it would be better to grab merge layers from config and or custom layer provider in Qossmic\Deptrac\Core\Layer\LayerResolver::initializeLayers

something like

$layers = [];

foreach ($this->layerProviders as $provider) {
    $layers = array_merge($layers, $provider->getLayers());
}

The default providers would read config from deptrac.layers. Additional providers would be registered by user without digging in core code and symfony di.
e.g.

deptrac:
    layer_providers:
       - 'My\Layer\Provider'

@patrickkusebauch
Copy link
Collaborator

@ilnytskyi can you explain how the current ConfigBuilder is not sufficient? You can define layers dynamically in PHP now:

->layers(
. Can you outline your use case where this is not enough?

@patrickkusebauch
Copy link
Collaborator

Implemented in #1002.

@ilnytskyi
Copy link

@patrickkusebauch thanks for info! That should be sufficient. I will take a look.
Probably due to lack of info about PHP approach at https://qossmic.github.io/deptrac/configuration/ and deptrack always generating and requiring deptrac.yaml I got stuck with YAML file.

@patrickkusebauch
Copy link
Collaborator

Yeah, we are lagging behind on the docs. It is a very recent addition.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants