DebugAdvisorCommand   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 131
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 16
lcom 1
cbo 5
dl 0
loc 131
c 0
b 0
f 0
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A configure() 0 14 1
A execute() 0 16 2
A showAdvisorsList() 0 30 3
A showAdvisorInformation() 0 31 5
A writeInfoAboutAdvices() 0 9 3
A loadAdvisorsList() 0 12 2
1
<?php
2
3
declare(strict_types=1);
4
/*
5
 * Go! AOP framework
6
 *
7
 * @copyright Copyright 2015, Lisachenko Alexander <[email protected]>
8
 *
9
 * This source file is subject to the license that is bundled
10
 * with this source code in the file LICENSE.
11
 */
12
13
namespace Go\Console\Command;
14
15
use Go\Aop\Advisor;
16
use Go\Core\AdviceMatcher;
17
use Go\Core\AdviceMatcherInterface;
18
use Go\Core\AspectContainer;
19
use Go\Core\AspectLoader;
20
use Go\Instrument\FileSystem\Enumerator;
21
use Go\ParserReflection\ReflectionFile;
22
use ReflectionClass;
23
use ReflectionException;
24
use ReflectionProperty;
25
use Symfony\Component\Console\Input\InputInterface;
26
use Symfony\Component\Console\Input\InputOption;
27
use Symfony\Component\Console\Output\OutputInterface;
28
use Symfony\Component\Console\Style\SymfonyStyle;
29
30
/**
31
 * Console command to debug an advisors
32
 *
33
 * @codeCoverageIgnore
34
 */
35
class DebugAdvisorCommand extends BaseAspectCommand
36
{
37
    /**
38
     * {@inheritDoc}
39
     */
40
    protected function configure(): void
41
    {
42
        parent::configure();
43
        $this
44
            ->setName('debug:advisor')
45
            ->addOption('advisor', null, InputOption::VALUE_OPTIONAL, 'Identifier of advisor')
46
            ->setDescription('Provides an interface for checking and debugging advisors')
47
            ->setHelp(
48
                <<<EOT
49
Allows to query an information about matching joinpoints for specified advisor.
50
EOT
51
            )
52
        ;
53
    }
54
55
    /**
56
     * {@inheritDoc}
57
     */
58
    protected function execute(InputInterface $input, OutputInterface $output): int
59
    {
60
        $this->loadAspectKernel($input, $output);
61
62
        $io = new SymfonyStyle($input, $output);
63
        $io->title('Advisor debug information');
64
65
        $advisorId = $input->getOption('advisor');
66
        if (!$advisorId) {
67
            $this->showAdvisorsList($io);
68
        } else {
69
            $this->showAdvisorInformation($io, $advisorId);
70
        }
71
72
        return 0;
73
    }
74
75
    private function showAdvisorsList(SymfonyStyle $io): void
76
    {
77
        $io->writeln('List of registered advisors in the container');
78
79
        $aspectContainer = $this->aspectKernel->getContainer();
80
        $advisors        = $this->loadAdvisorsList($aspectContainer);
81
82
        $tableRows = [];
83
        foreach ($advisors as $id => $advisor) {
84
            [, $id] = explode('.', $id, 2);
85
            $advice     = $advisor->getAdvice();
86
            $expression = '';
87
            try {
88
                $pointcutExpression = new ReflectionProperty($advice, 'pointcutExpression');
89
                $pointcutExpression->setAccessible(true);
90
                $expression = $pointcutExpression->getValue($advice);
91
            } catch (ReflectionException $e) {
92
                // nothing here, just ignore
93
            }
94
            $tableRows[] = [$id, $expression];
95
        }
96
        $io->table(['Id', 'Expression'], $tableRows);
97
98
        $io->writeln(
99
            [
0 ignored issues
show
Documentation introduced by
array('If you want to qu.../info> to the command') is of type array<integer,string,{"0":"string","1":"string"}>, but the function expects a string|object<Symfony\Co...onsole\Output\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
100
                'If you want to query an information about concrete advisor, then just query it',
101
                'by adding <info>--advisor="Advisor\\Name"</info> to the command'
102
            ]
103
        );
104
    }
105
106
    private function showAdvisorInformation(SymfonyStyle $io, string $advisorId): void
107
    {
108
        $aspectContainer = $this->aspectKernel->getContainer();
109
110
        /** @var AdviceMatcherInterface $adviceMatcher */
111
        $adviceMatcher = $aspectContainer->get('aspect.advice_matcher');
112
        $this->loadAdvisorsList($aspectContainer);
113
114
        $advisor = $aspectContainer->getAdvisor($advisorId);
115
        $options = $this->aspectKernel->getOptions();
116
117
        $enumerator = new Enumerator($options['appDir'], $options['includePaths'], $options['excludePaths']);
118
119
        $iterator   = $enumerator->enumerate();
120
        $totalFiles = iterator_count($iterator);
121
        $io->writeln("Total <info>{$totalFiles}</info> files to analyze.");
122
        $iterator->rewind();
123
124
        foreach ($iterator as $file) {
125
            $reflectionFile       = new ReflectionFile((string)$file);
126
            $reflectionNamespaces = $reflectionFile->getFileNamespaces();
127
            foreach ($reflectionNamespaces as $reflectionNamespace) {
128
                foreach ($reflectionNamespace->getClasses() as $reflectionClass) {
129
                    $advices = $adviceMatcher->getAdvicesForClass($reflectionClass, [$advisorId => $advisor]);
130
                    if (!empty($advices)) {
131
                        $this->writeInfoAboutAdvices($io, $reflectionClass, $advices);
132
                    }
133
                }
134
            }
135
        }
136
    }
137
138
    private function writeInfoAboutAdvices(SymfonyStyle $io, ReflectionClass $reflectionClass, array $advices): void
139
    {
140
        $className = $reflectionClass->getName();
141
        foreach ($advices as $type => $typedAdvices) {
142
            foreach ($typedAdvices as $pointName => $advice) {
143
                $io->writeln("  -> matching <comment>{$type} {$className}->{$pointName}</comment>");
144
            }
145
        }
146
    }
147
148
    /**
149
     * Collects list of advisors from the given aspect container
150
     *
151
     * @return Advisor[] List of advisors in the container
152
     */
153
    private function loadAdvisorsList(AspectContainer $aspectContainer): array
154
    {
155
        /** @var AspectLoader $aspectLoader */
156
        $aspectLoader = $aspectContainer->get('aspect.cached.loader');
157
        $aspects      = $aspectLoader->getUnloadedAspects();
158
        foreach ($aspects as $aspect) {
159
            $aspectLoader->loadAndRegister($aspect);
160
        }
161
        $advisors = $aspectContainer->getByTag('advisor');
162
163
        return $advisors;
164
    }
165
}
166