Completed
Pull Request — master (#64)
by Emanuele
03:40 queued 12s
created

LoadFeatureCommand::execute()   B

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
     * @param Twig_Node $node
99
     *
100
     * @return array
101
     */
102 1
    private function findFeatureNodes(Twig_Node $node)
103
    {
104 1
        $found = [];
105 1
        $stack = [$node];
106 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...
107 1
            $node = array_pop($stack);
108 1
            if ($node instanceof FeatureNode) {
109
                $arguments = $node
110 1
                    ->getNode('tests')
111 1
                    ->getNode(0)
112 1
                    ->getNode('arguments')
113 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

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