Completed
Push — master ( 963bb3...027522 )
by Eric
04:49 queued 01:57
created

CsvDeserializationVisitor::expand()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 9.2
c 0
b 0
f 0
ccs 10
cts 10
cp 1
cc 4
eloc 8
nc 4
nop 3
crap 4
1
<?php
2
3
/*
4
 * This file is part of the Ivory Serializer package.
5
 *
6
 * (c) Eric GELOEN <[email protected]>
7
 *
8
 * For the full copyright and license information, please read the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Ivory\Serializer\Visitor\Csv;
13
14
use Ivory\Serializer\Context\ContextInterface;
15
use Ivory\Serializer\Instantiator\InstantiatorInterface;
16
use Ivory\Serializer\Mapping\TypeMetadataInterface;
17
use Ivory\Serializer\Mutator\MutatorInterface;
18
use Ivory\Serializer\Visitor\AbstractDeserializationVisitor;
19
20
/**
21
 * @author GeLo <[email protected]>
22
 */
23
class CsvDeserializationVisitor extends AbstractDeserializationVisitor
24
{
25
    /**
26
     * @var string
27
     */
28
    private $delimiter;
29
30
    /**
31
     * @var string
32
     */
33
    private $enclosure;
34
35
    /**
36
     * @var string
37
     */
38
    private $escapeChar;
39
40
    /**
41
     * @var string
42
     */
43
    private $keySeparator;
44
45
    /**
46
     * @param InstantiatorInterface $instantiator
47
     * @param MutatorInterface      $mutator
48
     * @param string                $delimiter
49
     * @param string                $enclosure
50
     * @param string                $escapeChar
51
     * @param string                $keySeparator
52
     */
53 984
    public function __construct(
54
        InstantiatorInterface $instantiator,
55
        MutatorInterface $mutator,
56
        $delimiter = ',',
57
        $enclosure = '"',
58
        $escapeChar = '\\',
59
        $keySeparator = '.'
60
    ) {
61 984
        parent::__construct($instantiator, $mutator);
62
63 984
        $this->delimiter = $delimiter;
64 984
        $this->enclosure = $enclosure;
65 984
        $this->escapeChar = $escapeChar;
66 984
        $this->keySeparator = $keySeparator;
67 984
    }
68
69
    /**
70
     * {@inheritdoc}
71
     */
72 18
    public function visitArray($data, TypeMetadataInterface $type, ContextInterface $context)
73
    {
74 18
        return parent::visitArray($data === '[]' ? [] : $data, $type, $context);
75
    }
76
77
    /**
78
     * {@inheritdoc}
79
     */
80 99
    protected function decode($data)
81
    {
82 99
        $result = $first = [];
83 99
        $headers = null;
84
85 99
        $resource = fopen('php://temp', 'r+');
86 99
        fwrite($resource, $data);
87 99
        rewind($resource);
88
89 99
        while (($fields = fgetcsv($resource, 0, $this->delimiter, $this->enclosure, $this->escapeChar)) !== false) {
90 99
            if ($fields === [null]) {
91 3
                continue;
92
            }
93
94 96
            $fieldsCount = count($fields);
95
96 96
            if ($headers === null) {
97 96
                $first = $fields;
98 96
                $headersCount = $fieldsCount;
99
100 96
                $headers = array_map(function ($value) {
101 96
                    return explode($this->keySeparator, $value);
102 96
                }, $fields);
103
104 96
                continue;
105
            }
106
107 75
            if ($fieldsCount !== $headersCount) {
0 ignored issues
show
Bug introduced by
The variable $headersCount does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
108
                throw new \InvalidArgumentException(sprintf(
109
                    'The input dimension is not equals for all entries (Expected: %d, got %d).',
110
                    $headersCount,
111
                    $fieldsCount
112
                ));
113
            }
114
115 75
            foreach ($fields as $key => $value) {
116 75
                $this->expand($headers[$key], $value, $result);
117 50
            }
118 50
        }
119
120 99
        fclose($resource);
121
122 99
        if (!empty($result)) {
123 75
            return $result;
124
        }
125
126 24
        if (empty($first)) {
127 3
            return;
128
        }
129
130 21
        if (count($first) === 1) {
131 21
            return reset($first);
132
        }
133
134
        return $first;
135
    }
136
137
    /**
138
     * @param string[] $paths
139
     * @param mixed    $data
140
     * @param mixed[]  $result
141
     */
142 75
    private function expand(array $paths, $data, array &$result)
143
    {
144 75
        $path = array_shift($paths);
145
146 75
        if (empty($paths)) {
147 75
            $result[$path] = $data !== '' ? $data : null;
148 50
        } else {
149 12
            if (!isset($result[$path])) {
150 12
                $result[$path] = [];
151 8
            }
152
153 12
            $this->expand($paths, $data, $result[$path]);
154
        }
155 75
    }
156
}
157