LoadFeatureCommand::execute()   B
last analyzed

Complexity

Conditions 7
Paths 21

Size

Total Lines 48
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 7.1284

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 7
eloc 29
c 3
b 0
f 0
nc 21
nop 2
dl 0
loc 48
ccs 25
cts 29
cp 0.8621
crap 7.1284
rs 8.5226
1
<?php
2
3
namespace Ae\FeatureBundle\Command;
4
5
use Ae\FeatureBundle\Twig\Node\FeatureNode;
6
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
7
use Symfony\Bundle\FrameworkBundle\Console\Application;
8
use Symfony\Component\Console\Input\InputArgument;
9
use Symfony\Component\Console\Input\InputInterface;
10
use Symfony\Component\Console\Input\InputOption;
11
use Symfony\Component\Console\Output\OutputInterface;
12
use Symfony\Component\Finder\Finder;
13
use Twig_Node;
14
use Twig_Source;
15
16
/**
17
 * @author Carlo Forghieri <[email protected]>
18
 */
19
class LoadFeatureCommand extends ContainerAwareCommand
20
{
21
    /**
22
     * {@inheritdoc}
23
     */
24 3
    protected function configure()
25
    {
26
        $this
27 3
            ->setName('features:load')
28 3
            ->setDescription('Persist new features found in templates')
29 3
            ->addArgument(
30 3
                'path',
31 3
                InputArgument::REQUIRED | InputArgument::IS_ARRAY,
32 3
                'The path or bundle where to load the features'
33
            )
34 3
            ->addOption(
35 3
                'dry-run',
36 3
                null,
37 3
                InputOption::VALUE_NONE,
38 3
                'Do not persist new features'
39
            );
40 3
    }
41
42
    /**
43
     * {@inheritdoc}
44
     */
45 3
    public function execute(InputInterface $input, OutputInterface $output)
46
    {
47 3
        $container = $this->getContainer();
48 3
        $twig = $container->get('twig');
49 3
        $files = $this->getFinderInstance($input->getArgument('path'));
0 ignored issues
show
Bug introduced by
It seems like $input->getArgument('path') can also be of type null and string; however, parameter $dirsOrBundles of Ae\FeatureBundle\Command...nd::getFinderInstance() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

49
        $files = $this->getFinderInstance(/** @scrutinizer ignore-type */ $input->getArgument('path'));
Loading history...
50
51 3
        $found = [];
52 3
        foreach ($files as $file) {
53 1
            if (class_exists(Twig_Source::class)) {
54 1
                $tree = $twig->parse($twig->tokenize(new Twig_Source(
55 1
                    file_get_contents($file->getPathname()),
56 1
                    $file->getFilename(),
57 1
                    $file->getPathname()
58
                )));
59
            } else {
60
                $tree = $twig->parse(
61
                    $twig->tokenize(file_get_contents($file->getPathname()))
62
                );
63
            }
64
65 1
            $tags = $this->findFeatureNodes($tree);
66
67 1
            if (empty($tags)) {
68
                continue;
69
            }
70
71 1
            $found = array_merge($found, $tags);
72
73 1
            foreach ($tags as $tag) {
74 1
                $output->writeln(sprintf(
75 1
                    'Found <info>%s</info>.<info>%s</info> in <info>%s</info>',
76 1
                    $tag['parent'],
77 1
                    $tag['name'],
78 1
                    $file->getFilename()
79
                ));
80
            }
81
        }
82
83 2
        if ($input->getOption('dry-run')) {
84 1
            return 0;
85
        }
86
87 1
        $manager = $container->get('ae_feature.manager');
88 1
        foreach ($found as $tag) {
89
            $manager->findOrCreate($tag['name'], $tag['parent']);
90
        }
91
92 1
        return 0;
93
    }
94
95
    /**
96
     * Find feature nodes.
97
     *
98
     * @return array
99
     */
100 1
    private function findFeatureNodes(Twig_Node $node)
101
    {
102 1
        $found = [];
103 1
        $stack = [$node];
104 1
        while ($stack) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $stack of type array<integer,Twig_Node> 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...
105 1
            $node = array_pop($stack);
106 1
            if ($node instanceof FeatureNode) {
107
                $arguments = $node
108 1
                    ->getNode('tests')
109 1
                    ->getNode(0)
110 1
                    ->getNode('arguments')
111 1
                    ->getKeyValuePairs();
0 ignored issues
show
Bug introduced by
The method getKeyValuePairs() does not exist on Twig\Node\Node. It seems like you code against a sub-type of Twig\Node\Node such as Twig\Node\Expression\ArrayExpression. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

111
                    ->/** @scrutinizer ignore-call */ getKeyValuePairs();
Loading history...
112
113 1
                $tag = [];
114 1
                foreach ($arguments as $argument) {
115 1
                    $keyAttr = $argument['key']->getAttribute('value');
116 1
                    $valueAttr = $argument['value']->getAttribute('value');
117
118 1
                    $tag[$keyAttr] = $valueAttr;
119
                }
120 1
                $key = md5(serialize($tag));
121 1
                $found[$key] = $tag;
122
            } else {
123 1
                foreach ($node as $child) {
124 1
                    if (null !== $child) {
125 1
                        $stack[] = $child;
126
                    }
127
                }
128
            }
129
        }
130
131 1
        return array_values($found);
132
    }
133
134
    /**
135
     * Gets a Finder instance with required paths.
136
     *
137
     * @param array $dirsOrBundles Required directories or bundles
138
     *
139
     * @return Finder
140
     */
141 3
    private function getFinderInstance(array $dirsOrBundles)
142
    {
143 3
        $finder = new Finder();
144 3
        $application = $this->getApplication();
145
146 3
        $kernel = null;
147 3
        $bundles = [];
148 3
        if ($application instanceof Application) {
149 3
            $kernel = $application->getKernel();
150 3
            $bundles = $kernel->getBundles();
151
        }
152
153 3
        foreach ($dirsOrBundles as $dirOrBundle) {
154 2
            if (null !== $kernel && isset($bundles[$dirOrBundle])) {
155
                $bundle = $kernel->getBundle($dirOrBundle);
156
                $dirOrBundle = $bundle->getPath().'/Resources/views/';
157
            }
158
159 2
            $finder->in($dirOrBundle);
160
        }
161
162
        return $finder
163 3
            ->files()
164 3
            ->name('*.twig');
165
    }
166
}
167