Completed
Push — master ( ecf490...584b8b )
by Tobias
02:05
created

ReplaceOperation::isArrayAssociative()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 3
cts 4
cp 0.75
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2.0625
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 7
    protected function processDomain($domain)
34
    {
35 7
        $this->messages[$domain] = [
36
            'all' => [],
37
            'new' => [],
38
            'obsolete' => [],
39
        ];
40 7
        if (defined(sprintf('%s::INTL_DOMAIN_SUFFIX', MessageCatalogueInterface::class))) {
41 7
            $intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX;
42
        } else {
43
            $intlDomain = $domain;
44
        }
45
46 7
        foreach ($this->source->all($domain) as $id => $message) {
47 6
            $messageDomain = $this->source->defines($id, $intlDomain) ? $intlDomain : $domain;
48
49 6
            if (!$this->target->has($id, $domain)) {
50
                // No merge required
51 6
                $translation = $message;
52 6
                $this->messages[$domain]['new'][$id] = $message;
53 6
                $resultMeta = $this->getMetadata($this->source, $messageDomain, $id);
54
            } else {
55
                // Merge required
56 6
                $translation = $message ?? $this->target->get($id, $domain);
57 6
                $resultMeta = null;
0 ignored issues
show
Unused Code introduced by
$resultMeta is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
58 6
                $sourceMeta = $this->getMetadata($this->source, $messageDomain, $id);
59 6
                $targetMeta = $this->getMetadata($this->target, $this->target->defines($id, $intlDomain) ? $intlDomain : $domain, $id);
60 6
                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 5
                } elseif (!empty($sourceMeta)) {
64 1
                    $resultMeta = $sourceMeta;
65
                } else {
66
                    // Assert: true === empty($sourceMeta);
67 4
                    $resultMeta = $targetMeta;
68
                }
69
            }
70
71 6
            $this->messages[$domain]['all'][$id] = $translation;
72 6
            $this->result->add([$id => $translation], $messageDomain);
73
74 6
            if (!empty($resultMeta)) {
75 2
                $this->result->setMetadata($id, $resultMeta, $messageDomain);
76
            }
77
        }
78
79 7
        foreach ($this->target->all($domain) as $id => $message) {
80 6
            if ($this->result->has($id, $domain)) {
81
                // We've already merged this
82
                // That message was in source
83 6
                continue;
84
            }
85
86 6
            $messageDomain = $this->target->defines($id, $intlDomain) ? $intlDomain : $domain;
87 6
            $this->messages[$domain]['all'][$id] = $message;
88 6
            $this->messages[$domain]['obsolete'][$id] = $message;
89 6
            $this->result->add([$id => $message], $messageDomain);
90
91 6
            $resultMeta = $this->getMetadata($this->target, $messageDomain, $id);
92 6
            if (!empty($resultMeta)) {
93 2
                $this->result->setMetadata($id, $resultMeta, $messageDomain);
94
            }
95
        }
96 7
    }
97
98
    /**
99
     * @return array|null|string|mixed Can return anything..
100
     */
101 6
    private function getMetadata(MessageCatalogueInterface $catalogue, string $domain, string $key = '')
102
    {
103 6
        if (!$this->target instanceof MetadataAwareInterface) {
104
            return [];
105
        }
106
107
        /* @var MetadataAwareInterface $catalogue */
108 6
        return $catalogue->getMetadata($key, $domain);
109
    }
110
111
    /**
112
     * @param array|null $source
113
     * @param array|null $target
114
     *
115
     * @return array
116
     */
117 1
    private function mergeMetadata($source, $target)
118
    {
119 1
        if (empty($source) && empty($target)) {
120
            return [];
121
        }
122
123 1
        if (empty($source)) {
124
            return $target;
125
        }
126
127 1
        if (empty($target)) {
128
            return $source;
129
        }
130
131 1
        if (!is_array($source) || !is_array($target)) {
132
            return $source;
133
        }
134
135 1
        $result = $this->doMergeMetadata($source, $target);
136
137 1
        return $result;
138
    }
139
140 1
    private function doMergeMetadata(array $source, array $target)
141
    {
142 1
        $isTargetArrayAssociative = $this->isArrayAssociative($target);
143 1
        foreach ($target as $key => $value) {
144 1
            if ($isTargetArrayAssociative) {
145 1
                if (isset($source[$key]) && $source[$key] !== $value) {
146 1
                    if (is_array($source[$key]) && is_array($value)) {
147
                        // If both arrays, do recursive call
148 1
                        $source[$key] = $this->doMergeMetadata($source[$key], $value);
149
                    }
150
                    // Else, use value form $source
151
                } else {
152
                    // Add new value
153 1
                    $source[$key] = $value;
154
                }
155
                // if sequential
156 1
            } elseif (!in_array($value, $source)) {
157 1
                $source[] = $value;
158
            }
159
        }
160
161 1
        return $source;
162
    }
163
164 1
    public function isArrayAssociative(array $arr)
165
    {
166 1
        if ([] === $arr) {
167
            return false;
168
        }
169
170 1
        return array_keys($arr) !== range(0, count($arr) - 1);
171
    }
172
}
173