Completed
Pull Request — master (#257)
by Alexander
02:36
created

DebugAdvisorCommand   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 127
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 0%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 16
c 1
b 0
f 0
lcom 1
cbo 9
dl 0
loc 127
ccs 0
cts 92
cp 0
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A configure() 0 12 1
A execute() 0 13 2
B showAdvisorsList() 0 28 3
B showAdvisorInformation() 0 31 5
A writeInfoAboutAdvices() 0 9 3
A loadAdvisorsList() 0 12 2
1
<?php
2
/*
3
 * Go! AOP framework
4
 *
5
 * @copyright Copyright 2015, Lisachenko Alexander <[email protected]>
6
 *
7
 * This source file is subject to the license that is bundled
8
 * with this source code in the file LICENSE.
9
 */
10
11
namespace Go\Console\Command;
12
13
use Go\Aop\Advisor;
14
use Go\Aop\Pointcut;
15
use Go\Core\AdviceMatcher;
16
use Go\Core\AspectContainer;
17
use Go\Core\AspectLoader;
18
use Go\Instrument\FileSystem\Enumerator;
19
use Go\ParserReflection\ReflectionFile;
20
use ReflectionClass;
21
use Symfony\Component\Console\Input\InputInterface;
22
use Symfony\Component\Console\Input\InputOption;
23
use Symfony\Component\Console\Output\OutputInterface;
24
use Symfony\Component\Console\Style\SymfonyStyle;
25
26
/**
27
 * Console command to debug an advisors
28
 */
29
class DebugAdvisorCommand extends BaseAspectCommand
30
{
31
32
    /**
33
     * {@inheritDoc}
34
     */
35
    protected function configure()
36
    {
37
        parent::configure();
38
        $this
39
            ->setName('debug:advisor')
40
            ->addOption('advisor', null, InputOption::VALUE_OPTIONAL, "Identifier of advisor")
41
            ->setDescription("Provides an interface for checking and debugging advisors")
42
            ->setHelp(<<<EOT
43
Allows to query an information about matching joinpoints for specified advisor.
44
EOT
45
            );
46
    }
47
48
    /**
49
     * {@inheritDoc}
50
     */
51
    protected function execute(InputInterface $input, OutputInterface $output)
52
    {
53
        parent::execute($input, $output);
54
        $io = new SymfonyStyle($input, $output);
55
        $io->title('Advisor debug information');
56
57
        $advisorId = $input->getOption('advisor');
58
        if (!$advisorId) {
59
            $this->showAdvisorsList($io);
60
        } else {
61
            $this->showAdvisorInformation($io, $advisorId);
62
        }
63
    }
64
65
    private function showAdvisorsList(SymfonyStyle $io)
66
    {
67
        $io->writeln('List of registered advisors in the container');
68
69
        $aspectContainer = $this->aspectKernel->getContainer();
70
        $advisors        = $this->loadAdvisorsList($aspectContainer);
71
72
        $tableRows = [];
73
        foreach ($advisors as $id => $advisor) {
74
            list(,$id) = explode('.', $id, 2);
75
            $advice     = $advisor->getAdvice();
76
            $expression = '';
77
            try {
78
                $pointcutExpression = new \ReflectionProperty($advice, 'pointcutExpression');
79
                $pointcutExpression->setAccessible('true');
80
                $expression = $pointcutExpression->getValue($advice);
81
            } catch (\ReflectionException $e) {
82
                // nothing here, just ignore
83
            };
84
            $tableRows[] = [$id, $expression];
85
        }
86
        $io->table(['Id', 'Expression'], $tableRows);
87
88
        $io->writeln([
89
            'If you want to query an information about concrete advisor, then just query it',
90
            'by adding <info>--advisor="Advisor\\Name"</info> to the command'
91
        ]);
92
    }
93
94
    private function showAdvisorInformation(SymfonyStyle $io, $advisorId)
95
    {
96
        $aspectContainer = $this->aspectKernel->getContainer();
97
98
        /** @var AdviceMatcher $adviceMatcher */
99
        $adviceMatcher = $aspectContainer->get('aspect.advice_matcher');
100
        $this->loadAdvisorsList($aspectContainer);
101
102
        $advisor = $aspectContainer->getAdvisor($advisorId);
103
        $options = $this->aspectKernel->getOptions();
104
105
        $enumerator = new Enumerator($options['appDir'], $options['includePaths'], $options['excludePaths']);
106
107
        $iterator   = $enumerator->enumerate();
108
        $totalFiles = iterator_count($iterator);
109
        $io->writeln("Total <info>{$totalFiles}</info> files to analyze.");
110
        $iterator->rewind();
111
112
        foreach ($iterator as $file) {
113
            $reflectionFile       = new ReflectionFile((string)$file);
114
            $reflectionNamespaces = $reflectionFile->getFileNamespaces();
115
            foreach ($reflectionNamespaces as $reflectionNamespace) {
116
                foreach ($reflectionNamespace->getClasses() as $reflectionClass) {
117
                    $advices = $adviceMatcher->getAdvicesForClass($reflectionClass, array($advisor));
118
                    if ($advices) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $advices of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
119
                        $this->writeInfoAboutAdvices($io, $reflectionClass, $advices);
120
                    }
121
                }
122
            }
123
        }
124
    }
125
126
    private function writeInfoAboutAdvices(SymfonyStyle $io, ReflectionClass $reflectionClass, array $advices)
127
    {
128
        $className = $reflectionClass->getName();
1 ignored issue
show
Bug introduced by
Consider using $reflectionClass->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
129
        foreach ($advices as $type=>$typedAdvices) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space before "=>"; 0 found
Loading history...
Coding Style introduced by
Expected 1 space after "=>"; 0 found
Loading history...
130
            foreach ($typedAdvices as $pointName=>$advice) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space before "=>"; 0 found
Loading history...
Coding Style introduced by
Expected 1 space after "=>"; 0 found
Loading history...
131
                $io->writeln("  -> matching <comment>{$type} {$className}->{$pointName}</comment>");
132
            }
133
        }
134
    }
135
136
    /**
137
     * Collects list of advisors from the container
138
     *
139
     * @param AspectContainer $aspectContainer Container instance
140
     *
141
     * @return Advisor[] List of advisors in the container
142
     */
143
    private function loadAdvisorsList(AspectContainer $aspectContainer)
144
    {
145
        /** @var AspectLoader $aspectLoader */
146
        $aspectLoader   = $aspectContainer->get('aspect.cached.loader');
147
        $aspects        = $aspectLoader->getUnloadedAspects();
148
        foreach ($aspects as $aspect) {
149
            $aspectLoader->loadAndRegister($aspect);
150
        }
151
        $advisors = $aspectContainer->getByTag('advisor');
152
153
        return $advisors;
154
    }
155
}
156