Completed
Push — master ( 04c37a...3d7f4d )
by Tobias
10:27 queued 08:42
created

Importer::extractToCatalogues()   C

Complexity

Conditions 7
Paths 16

Size

Total Lines 41
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
dl 0
loc 41
ccs 0
cts 35
cp 0
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 27
nc 16
nop 3
crap 56
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\Extractor\Extractor;
19
use Translation\Extractor\Model\SourceCollection;
20
use Translation\Extractor\Model\SourceLocation;
21
use Translation\Bundle\Catalogue\Operation\ReplaceOperation;
22
23
/**
24
 * Use extractors to import translations to message catalogues.
25
 *
26
 * @author Tobias Nyholm <[email protected]>
27
 */
28
final class Importer
29
{
30
    /**
31
     * @var Extractor
32
     */
33
    private $extractor;
34
35
    /**
36
     * @var array
37
     */
38
    private $config;
39
40
    /**
41
     * @param Extractor $extractor
42
     */
43
    public function __construct(Extractor $extractor)
44
    {
45
        $this->extractor = $extractor;
46
    }
47
48
    /**
49
     * @param Finder             $finder
50
     * @param MessageCatalogue[] $catalogues
51
     * @param array              $config     {
52
     *
53
     *     @var array $blacklist_domains Blacklist the domains we should exclude. Cannot be used with whitelist.
54
     *     @var array $whitelist_domains Whitelist the domains we should include. Cannot be used with blacklist.
55
     *     @var string $project_root The project root will be removed from the source location.
56
     * }
57
     *
58
     * @return ImportResult
59
     */
60
    public function extractToCatalogues(Finder $finder, array $catalogues, array $config = [])
61
    {
62
        $this->processConfig($config);
63
        $sourceCollection = $this->extractor->extract($finder);
64
        $results = [];
65
        foreach ($catalogues as $catalogue) {
66
            $target = new MessageCatalogue($catalogue->getLocale());
67
            $this->convertSourceLocationsToMessages($target, $sourceCollection);
68
69
            // Remove all SourceLocation and State form catalogue.
70
            foreach ($catalogue->getDomains() as $domain) {
71
                foreach ($catalogue->all($domain) as $key => $translation) {
72
                    $meta = $this->getMetadata($catalogue, $key, $domain);
73
                    $meta->removeAllInCategory('file-source');
74
                    $meta->removeAllInCategory('state');
75
                    $this->setMetadata($catalogue, $key, $domain, $meta);
76
                }
77
            }
78
79
            $merge = new ReplaceOperation($target, $catalogue);
80
            $result = $merge->getResult();
81
            $domains = $merge->getDomains();
82
83
            // Mark new messages as new/obsolete
84
            foreach ($domains as $domain) {
85
                foreach ($merge->getNewMessages($domain) as $key => $translation) {
86
                    $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...
87
                    $meta->setState('new');
88
                    $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...
89
                }
90
                foreach ($merge->getObsoleteMessages($domain) as $key => $translation) {
91
                    $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...
92
                    $meta->setState('obsolete');
93
                    $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...
94
                }
95
            }
96
            $results[] = $result;
97
        }
98
99
        return new ImportResult($results, $sourceCollection->getErrors());
100
    }
101
102
    /**
103
     * @param MessageCatalogue $catalogue
104
     * @param SourceCollection $collection
105
     */
106
    private function convertSourceLocationsToMessages(MessageCatalogue $catalogue, SourceCollection $collection)
107
    {
108
        /** @var SourceLocation $sourceLocation */
109
        foreach ($collection as $sourceLocation) {
110
            $context = $sourceLocation->getContext();
111
            $domain = isset($context['domain']) ? $context['domain'] : 'messages';
112
            // Check with white/black list
113
            if (!$this->isValidDomain($domain)) {
114
                continue;
115
            }
116
117
            $key = $sourceLocation->getMessage();
118
            $catalogue->set($key, null, $domain);
119
            $trimLength = 1 + strlen($this->config['project_root']);
120
121
            $meta = $this->getMetadata($catalogue, $key, $domain);
122
            $meta->addCategory('file-source', sprintf('%s:%s', substr($sourceLocation->getPath(), $trimLength), $sourceLocation->getLine()));
123
            $this->setMetadata($catalogue, $key, $domain, $meta);
124
        }
125
    }
126
127
    /**
128
     * @param MessageCatalogue $catalogue
129
     * @param $key
130
     * @param $domain
131
     *
132
     * @return Metadata
133
     */
134
    private function getMetadata(MessageCatalogue $catalogue, $key, $domain)
135
    {
136
        return new Metadata($catalogue->getMetadata($key, $domain));
137
    }
138
139
    /**
140
     * @param MessageCatalogue $catalogue
141
     * @param $key
142
     * @param $domain
143
     * @param Metadata $metadata
144
     */
145
    private function setMetadata(MessageCatalogue $catalogue, $key, $domain, Metadata $metadata)
146
    {
147
        $catalogue->setMetadata($key, $metadata->toArray(), $domain);
148
    }
149
150
    /**
151
     * @param string $domain
152
     *
153
     * @return bool
154
     */
155
    private function isValidDomain($domain)
156
    {
157 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...
158
            return false;
159
        }
160 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...
161
            return false;
162
        }
163
164
        return true;
165
    }
166
167
    /**
168
     * Make sure the configuration is valid.
169
     *
170
     * @param array $config
171
     */
172
    private function processConfig($config)
173
    {
174
        $default = [
175
            'project_root' => '',
176
            'blacklist_domains' => [],
177
            'whitelist_domains' => [],
178
        ];
179
180
        $config = array_merge($default, $config);
181
182 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...
183
            throw new \InvalidArgumentException('Cannot use "blacklist_domains" and "whitelist_domains" at the same time');
184
        }
185
186 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...
187
            throw new \InvalidArgumentException('Config parameter "blacklist_domains" must be an array');
188
        }
189
190 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...
191
            throw new \InvalidArgumentException('Config parameter "whitelist_domains" must be an array');
192
        }
193
194
        $this->config = $config;
195
    }
196
}
197