Completed
Push — master ( 3674b7...0e13cf )
by Tobias
33:57
created

Importer::disableTwigVisitors()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 0
cts 0
cp 0
rs 9.2
c 0
b 0
f 0
cc 4
eloc 6
nc 5
nop 0
crap 20
1
<?php
2
3
/*
4
 * This file is part of the PHP Translation package.
5
 *
6
 * (c) PHP Translation team <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Translation\Bundle\Service;
13
14
use Symfony\Component\Finder\Finder;
15
use Symfony\Component\Translation\MessageCatalogue;
16
use Translation\Bundle\Model\ImportResult;
17
use Translation\Bundle\Model\Metadata;
18
use Translation\Bundle\Twig\Visitor\DefaultApplyingNodeVisitor;
19
use Translation\Bundle\Twig\Visitor\RemovingNodeVisitor;
20
use Translation\Extractor\Extractor;
21
use Translation\Extractor\Model\SourceCollection;
22
use Translation\Extractor\Model\SourceLocation;
23
use Translation\Bundle\Catalogue\Operation\ReplaceOperation;
24
25
/**
26
 * Use extractors to import translations to message catalogues.
27
 *
28
 * @author Tobias Nyholm <[email protected]>
29
 */
30
final class Importer
31
{
32
    /**
33
     * @var Extractor
34
     */
35
    private $extractor;
36
37
    /**
38
     * @var array
39
     */
40
    private $config;
41
42
    /**
43 1
     * @var \Twig_Environment
44
     */
45 1
    private $twig;
46 1
47
    /**
48
     * @param Extractor         $extractor
49
     * @param \Twig_Environment $twig
50
     */
51
    public function __construct(Extractor $extractor, \Twig_Environment $twig)
52
    {
53
        $this->extractor = $extractor;
54
        $this->twig = $twig;
55
    }
56
57
    /**
58
     * @param Finder             $finder
59
     * @param MessageCatalogue[] $catalogues
60 1
     * @param array              $config     {
61
     *
62 1
     *     @var array $blacklist_domains Blacklist the domains we should exclude. Cannot be used with whitelist.
63 1
     *     @var array $whitelist_domains Whitelist the domains we should include. Cannot be used with blacklist.
64 1
     *     @var string $project_root The project root will be removed from the source location.
65 1
     * }
66 1
     *
67 1
     * @return ImportResult
68
     */
69
    public function extractToCatalogues(Finder $finder, array $catalogues, array $config = [])
70 1
    {
71 1
        $this->processConfig($config);
72 1
        $this->disableTwigVisitors();
73 1
        $sourceCollection = $this->extractor->extract($finder);
74 1
        $results = [];
75 1
        foreach ($catalogues as $catalogue) {
76
            $target = new MessageCatalogue($catalogue->getLocale());
77
            $this->convertSourceLocationsToMessages($target, $sourceCollection);
78
79 1
            // Remove all SourceLocation and State form catalogue.
80 1
            foreach ($catalogue->getDomains() as $domain) {
81 1
                foreach ($catalogue->all($domain) as $key => $translation) {
82
                    $meta = $this->getMetadata($catalogue, $key, $domain);
83
                    $meta->removeAllInCategory('file-source');
84 1
                    $meta->removeAllInCategory('state');
85 1
                    $this->setMetadata($catalogue, $key, $domain, $meta);
86 1
                }
87 1
            }
88 1
89
            $merge = new ReplaceOperation($target, $catalogue);
90 1
            $result = $merge->getResult();
91 1
            $domains = $merge->getDomains();
92 1
93 1
            // Mark new messages as new/obsolete
94
            foreach ($domains as $domain) {
95
                foreach ($merge->getNewMessages($domain) as $key => $translation) {
96 1
                    $meta = $this->getMetadata($result, $key, $domain);
0 ignored issues
show
Compatibility introduced by
$result of type object<Symfony\Component...sageCatalogueInterface> is not a sub-type of object<Symfony\Component...ation\MessageCatalogue>. It seems like you assume a concrete implementation of the interface Symfony\Component\Transl...ssageCatalogueInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
97
                    $meta->setState('new');
98
                    $this->setMetadata($result, $key, $domain, $meta);
0 ignored issues
show
Compatibility introduced by
$result of type object<Symfony\Component...sageCatalogueInterface> is not a sub-type of object<Symfony\Component...ation\MessageCatalogue>. It seems like you assume a concrete implementation of the interface Symfony\Component\Transl...ssageCatalogueInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
99 1
100
                    // Add "desc" as translation
101
                    if (null === $translation && null !== $desc = $meta->getDesc()) {
102
                        $result->set($key, $desc, $domain);
103
                    }
104
                }
105
                foreach ($merge->getObsoleteMessages($domain) as $key => $translation) {
106 1
                    $meta = $this->getMetadata($result, $key, $domain);
0 ignored issues
show
Compatibility introduced by
$result of type object<Symfony\Component...sageCatalogueInterface> is not a sub-type of object<Symfony\Component...ation\MessageCatalogue>. It seems like you assume a concrete implementation of the interface Symfony\Component\Transl...ssageCatalogueInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
107
                    $meta->setState('obsolete');
108
                    $this->setMetadata($result, $key, $domain, $meta);
0 ignored issues
show
Compatibility introduced by
$result of type object<Symfony\Component...sageCatalogueInterface> is not a sub-type of object<Symfony\Component...ation\MessageCatalogue>. It seems like you assume a concrete implementation of the interface Symfony\Component\Transl...ssageCatalogueInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
109 1
                }
110 1
            }
111 1
            $results[] = $result;
112
        }
113 1
114
        return new ImportResult($results, $sourceCollection->getErrors());
115
    }
116
117 1
    /**
118 1
     * @param MessageCatalogue $catalogue
119 1
     * @param SourceCollection $collection
120
     */
121 1
    private function convertSourceLocationsToMessages(MessageCatalogue $catalogue, SourceCollection $collection)
122 1
    {
123 1
        /** @var SourceLocation $sourceLocation */
124
        foreach ($collection as $sourceLocation) {
125
            $context = $sourceLocation->getContext();
126 1
            $domain = isset($context['domain']) ? $context['domain'] : 'messages';
127
            // Check with white/black list
128 1
            if (!$this->isValidDomain($domain)) {
129
                continue;
130
            }
131
132
            $key = $sourceLocation->getMessage();
133
            $catalogue->set($key, null, $domain);
134
            $trimLength = 1 + strlen($this->config['project_root']);
135
136
            $meta = $this->getMetadata($catalogue, $key, $domain);
137 1
            $meta->addCategory('file-source', sprintf('%s:%s', substr($sourceLocation->getPath(), $trimLength), $sourceLocation->getLine()));
138
            if (isset($sourceLocation->getContext()['desc'])) {
139 1
                $meta->addCategory('desc', $sourceLocation->getContext()['desc']);
140
            }
141
            $this->setMetadata($catalogue, $key, $domain, $meta);
142
        }
143
    }
144
145
    /**
146
     * @param MessageCatalogue $catalogue
147
     * @param $key
148 1
     * @param $domain
149
     *
150 1
     * @return Metadata
151 1
     */
152
    private function getMetadata(MessageCatalogue $catalogue, $key, $domain)
153
    {
154
        return new Metadata($catalogue->getMetadata($key, $domain));
155
    }
156
157
    /**
158 1
     * @param MessageCatalogue $catalogue
159
     * @param $key
160 1
     * @param $domain
161
     * @param Metadata $metadata
162
     */
163 1
    private function setMetadata(MessageCatalogue $catalogue, $key, $domain, Metadata $metadata)
164
    {
165
        $catalogue->setMetadata($key, $metadata->toArray(), $domain);
166
    }
167 1
168
    /**
169
     * @param string $domain
170
     *
171
     * @return bool
172
     */
173
    private function isValidDomain($domain)
174
    {
175 1 View Code Duplication
        if (!empty($this->config['blacklist_domains']) && in_array($domain, $this->config['blacklist_domains'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
176
            return false;
177
        }
178 1 View Code Duplication
        if (!empty($this->config['whitelist_domains']) && !in_array($domain, $this->config['whitelist_domains'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
179
            return false;
180
        }
181
182
        return true;
183 1
    }
184
185 1
    /**
186
     * Make sure the configuration is valid.
187
     *
188
     * @param array $config
189 1
     */
190
    private function processConfig($config)
191
    {
192
        $default = [
193 1
            'project_root' => '',
194
            'blacklist_domains' => [],
195
            'whitelist_domains' => [],
196
        ];
197 1
198 1
        $config = array_merge($default, $config);
199
200 View Code Duplication
        if (!empty($config['blacklist_domains']) && !empty($config['whitelist_domains'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
201
            throw new \InvalidArgumentException('Cannot use "blacklist_domains" and "whitelist_domains" at the same time');
202
        }
203
204 View Code Duplication
        if (!empty($config['blacklist_domains']) && !is_array($config['blacklist_domains'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
205
            throw new \InvalidArgumentException('Config parameter "blacklist_domains" must be an array');
206
        }
207
208 View Code Duplication
        if (!empty($config['whitelist_domains']) && !is_array($config['whitelist_domains'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
209
            throw new \InvalidArgumentException('Config parameter "whitelist_domains" must be an array');
210
        }
211
212
        $this->config = $config;
213
    }
214
215
    private function disableTwigVisitors()
216
    {
217
        foreach ($this->twig->getNodeVisitors() as $visitor) {
218
            if ($visitor instanceof DefaultApplyingNodeVisitor) {
219
                $visitor->setEnabled(false);
220
            }
221
            if ($visitor instanceof RemovingNodeVisitor) {
222
                $visitor->setEnabled(false);
223
            }
224
        }
225
    }
226
}
227