ReplaceOperation::doMergeMetadata()   B
last analyzed

Complexity

Conditions 8
Paths 6

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 8

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 11
c 1
b 0
f 0
nc 6
nop 2
dl 0
loc 22
ccs 11
cts 11
cp 1
crap 8
rs 8.4444
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\Catalogue\Operation;
13
14
use Symfony\Component\Translation\Catalogue\AbstractOperation;
15
use Symfony\Component\Translation\MessageCatalogueInterface;
16
use Symfony\Component\Translation\MetadataAwareInterface;
17
18
/**
19
 * This will merge and replace all values in $target with values from $source.
20
 * It is the equivalent of running array_merge($target, $source). When in conflict,
21
 * always take values from $source.
22
 *
23
 * This operation is metadata aware. It will do the same recursive merge on metadata.
24
 *
25
 * all = source ∪ target = {x: x ∈ source ∨ x ∈ target}
26
 * new = all ∖ target = {x: x ∈ source ∧ x ∉ target}
27
 * obsolete = target ∖ all = {x: x ∈ target ∧ x ∉ source}
28
 *
29
 * @author Tobias Nyholm <[email protected]>
30
 */
31
final class ReplaceOperation extends AbstractOperation
32
{
33 5
    protected function processDomain($domain): void
34
    {
35 5
        $this->messages[$domain] = [
36
            'all' => [],
37
            'new' => [],
38
            'obsolete' => [],
39
        ];
40 5
        if (\defined(\sprintf('%s::INTL_DOMAIN_SUFFIX', MessageCatalogueInterface::class))) {
41 5
            $intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX;
42
        } else {
43
            $intlDomain = $domain;
44
        }
45
46 5
        foreach ($this->source->all($domain) as $id => $message) {
47 5
            $messageDomain = $this->source->defines($id, $intlDomain) ? $intlDomain : $domain;
48
49 5
            if (!$this->target->has($id, $domain)) {
50
                // No merge required
51 5
                $translation = $message;
52 5
                $this->messages[$domain]['new'][$id] = $message;
53 5
                $resultMeta = $this->getMetadata($this->source, $messageDomain, $id);
54
            } else {
55
                // Merge required
56 5
                $translation = $message ?? $this->target->get($id, $domain);
57 5
                $resultMeta = null;
58 5
                $sourceMeta = $this->getMetadata($this->source, $messageDomain, $id);
59 5
                $targetMeta = $this->getMetadata($this->target, $this->target->defines($id, $intlDomain) ? $intlDomain : $domain, $id);
60 5
                if (\is_array($sourceMeta) && \is_array($targetMeta)) {
61
                    // We can only merge meta if both is an array
62 1
                    $resultMeta = $this->mergeMetadata($sourceMeta, $targetMeta);
63 4
                } elseif (!empty($sourceMeta)) {
64 1
                    $resultMeta = $sourceMeta;
65
                } else {
66
                    // Assert: true === empty($sourceMeta);
67 3
                    $resultMeta = $targetMeta;
68
                }
69
            }
70
71 5
            $this->messages[$domain]['all'][$id] = $translation;
72 5
            $this->result->add([$id => $translation], $messageDomain);
73
74 5
            if (!empty($resultMeta)) {
75 2
                $this->result->setMetadata($id, $resultMeta, $messageDomain);
76
            }
77
        }
78
79 5
        foreach ($this->target->all($domain) as $id => $message) {
80 5
            if ($this->result->has($id, $domain)) {
81
                // We've already merged this
82
                // That message was in source
83 5
                continue;
84
            }
85
86 5
            $messageDomain = $this->target->defines($id, $intlDomain) ? $intlDomain : $domain;
87 5
            $this->messages[$domain]['all'][$id] = $message;
88 5
            $this->messages[$domain]['obsolete'][$id] = $message;
89 5
            $this->result->add([$id => $message], $messageDomain);
90
91 5
            $resultMeta = $this->getMetadata($this->target, $messageDomain, $id);
92 5
            if (!empty($resultMeta)) {
93 2
                $this->result->setMetadata($id, $resultMeta, $messageDomain);
94
            }
95
        }
96 5
    }
97
98
    /**
99
     * @param MessageCatalogueInterface|MetadataAwareInterface $catalogue
100
     *
101
     * @return array|string|mixed|null Can return anything..
102
     */
103 5
    private function getMetadata($catalogue, string $domain, string $key = '')
104
    {
105 5
        if (!$this->target instanceof MetadataAwareInterface) {
106
            return [];
107
        }
108
109 5
        return $catalogue->getMetadata($key, $domain);
110
    }
111
112 1
    private function mergeMetadata(?array $source, ?array $target): array
113
    {
114 1
        if (empty($source) && empty($target)) {
115
            return [];
116
        }
117
118 1
        if (empty($source)) {
119
            return $target;
120
        }
121
122 1
        if (empty($target)) {
123
            return $source;
124
        }
125
126 1
        if (!\is_array($source) || !\is_array($target)) {
127
            return $source;
128
        }
129
130 1
        return $this->doMergeMetadata($source, $target);
131
    }
132
133 1
    private function doMergeMetadata(array $source, array $target): array
134
    {
135 1
        $isTargetArrayAssociative = $this->isArrayAssociative($target);
136 1
        foreach ($target as $key => $value) {
137 1
            if ($isTargetArrayAssociative) {
138 1
                if (isset($source[$key]) && $source[$key] !== $value) {
139 1
                    if (\is_array($source[$key]) && \is_array($value)) {
140
                        // If both arrays, do recursive call
141 1
                        $source[$key] = $this->doMergeMetadata($source[$key], $value);
142
                    }
143
                    // Else, use value form $source
144
                } else {
145
                    // Add new value
146 1
                    $source[$key] = $value;
147
                }
148
                // if sequential
149 1
            } elseif (!\in_array($value, $source, true)) {
150 1
                $source[] = $value;
151
            }
152
        }
153
154 1
        return $source;
155
    }
156
157 1
    public function isArrayAssociative(array $arr): bool
158
    {
159 1
        if ([] === $arr) {
160
            return false;
161
        }
162
163 1
        return \array_keys($arr) !== \range(0, \count($arr) - 1);
164
    }
165
}
166