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

ReplaceOperation::isArrayAssociative()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

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 9.4285
c 0
b 0
f 0
cc 2
eloc 4
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\MetadataAwareInterface;
16
17
/**
18
 * This will merge and replace all values in $target with values from $source.
19
 * It is the equivalent of running array_merge($target, $source). When in conflict,
20
 * always take values from $source.
21
 *
22
 * This operation is metadata aware. It will do the same recursive merge on metadata.
23
 *
24
 * all = source ∪ target = {x: x ∈ source ∨ x ∈ target}
25
 * new = all ∖ target = {x: x ∈ source ∧ x ∉ target}
26
 * obsolete = target ∖ all = {x: x ∈ target ∧ x ∉ source}
27
 *
28
 * @author Tobias Nyholm <[email protected]>
29
 */
30
final class ReplaceOperation extends AbstractOperation
31
{
32 5
    protected function processDomain($domain)
33
    {
34 5
        $this->messages[$domain] = [
35 5
            'all' => [],
36 5
            'new' => [],
37 5
            'obsolete' => [],
38
        ];
39 5
        $sourceMessages = $this->source->all($domain);
40
41 5 View Code Duplication
        foreach ($this->target->all($domain) as $id => $message) {
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...
42 4
            $this->messages[$domain]['all'][$id] = $message;
43
44
            // If $id is NOT defined in source.
45 4
            if (!array_key_exists($id, $sourceMessages)) {
46 4
                $this->messages[$domain]['obsolete'][$id] = $message;
47 4
            }
48 5
        }
49
50 5 View Code Duplication
        foreach ($sourceMessages as $id => $message) {
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...
51 4
            $this->messages[$domain]['all'][$id] = $message;
52 4
            if (!$this->target->has($id, $domain)) {
53 4
                $this->messages[$domain]['new'][$id] = $message;
54 4
            }
55 5
        }
56
57 5
        $this->result->add($this->messages[$domain]['all'], $domain);
58
59 5
        $targetMetadata = $this->target instanceof MetadataAwareInterface ? $this->target->getMetadata('', $domain) : [];
60 5
        $sourceMetadata = $this->source instanceof MetadataAwareInterface ? $this->source->getMetadata('', $domain) : [];
61 5
        $resultMetadata = $this->mergeMetaData($sourceMetadata, $targetMetadata);
62
63
        // Write back metadata
64 5
        foreach ($resultMetadata as $id => $data) {
0 ignored issues
show
Bug introduced by
The expression $resultMetadata of type array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
65 2
            $this->result->setMetadata($id, $data, $domain);
66 5
        }
67 5
    }
68
69
    /**
70
     * @param array|null $source
71
     * @param array|null $target
72
     *
73
     * @return array
74
     */
75 5
    private function mergeMetadata($source, $target)
76
    {
77 5
        if (empty($source) && empty($target)) {
78 3
            return [];
79
        }
80
81 2
        if (empty($source)) {
82
            return $target;
83
        }
84
85 2
        if (empty($target)) {
86
            return $source;
87
        }
88
89 2
        if (!is_array($source) || !is_array($target)) {
90
            return $source;
91
        }
92
93 2
        $result = $this->doMergeMetadata($source, $target);
94
95 2
        return $result;
96
    }
97
98 2
    private function doMergeMetadata(array $source, array $target)
99
    {
100 2
        $isTargetArrayAssociative = $this->isArrayAssociative($target);
101 2
        foreach ($target as $key => $value) {
102 2
            if ($isTargetArrayAssociative) {
103 2
                if (isset($source[$key]) && $source[$key] !== $value) {
104 2
                    if (is_array($source[$key]) && is_array($value)) {
105
                        // If both arrays, do recursive call
106 1
                        $source[$key] = $this->doMergeMetadata($source[$key], $value);
107 1
                    }
108
                    // Else, use value form $source
1 ignored issue
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
109 2
                } else {
110
                    // Add new value
111 2
                    $source[$key] = $value;
112
                }
113
                // if sequential
114 2
            } elseif (!in_array($value, $source)) {
115 1
                $source[] = $value;
116 1
            }
117 2
        }
118
119 2
        return $source;
120
    }
121
122 2
    public function isArrayAssociative(array $arr)
123
    {
124 2
        if ([] === $arr) {
125
            return false;
126
        }
127
128 2
        return array_keys($arr) !== range(0, count($arr) - 1);
129
    }
130
}
131