Completed
Push — master ( 457275...01783f )
by Márk
02:30
created

Renderer::render()   C

Complexity

Conditions 7
Paths 10

Size

Total Lines 41
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 7

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 41
ccs 24
cts 24
cp 1
rs 6.7273
cc 7
eloc 20
nc 10
nop 1
crap 7
1
<?php
2
3
namespace Indigo\Ini;
4
5
use Indigo\Ini\Exception\RendererException;
6
7
/**
8
 * Renders an INI array.
9
 *
10
 * @author Márk Sági-Kazár <[email protected]>
11
 */
12
class Renderer
13
{
14
    /**
15
     * Constants determining how the library should handle array values
16
     */
17
    const ARRAY_MODE_ARRAY = 1;
18
    const ARRAY_MODE_CONCAT = 2;
19
20
    /**
21
     * Constants determining how the library should handle boolean values
22
     */
23
    const BOOLEAN_MODE_INTEGER = 1;
24
    const BOOLEAN_MODE_BOOL_STRING = 2;
25
    const BOOLEAN_MODE_STRING = 3;
26
27
    /**
28
     * @var int
29
     */
30
    protected $arrayMode = self::ARRAY_MODE_ARRAY;
31
32
    /**
33
     * @var int
34
     */
35
    protected $booleanMode = self::BOOLEAN_MODE_INTEGER;
36
37
    /**
38
     * @param int $arrayMode
39
     * @param int $booleanMode
40
     */
41 14
    public function __construct($arrayMode = self::ARRAY_MODE_ARRAY, $booleanMode = self::BOOLEAN_MODE_INTEGER)
42
    {
43 14
        $this->arrayMode = $arrayMode;
44 14
        $this->booleanMode = $booleanMode;
45 14
    }
46
47
    /**
48
     * Renders an INI configuration.
49
     *
50
     * @param array $ini
51
     *
52
     * @return string
53
     */
54 13
    public function render(array $ini)
55
    {
56 13
        $output = [];
57
58 13
        foreach ($ini as $sectionName => $section) {
59 13
            $sectionOutput = [];
60
61 13
            if (!is_array($section)) {
62 1
                throw new RendererException('The section must contain an array of key-value pairs');
63
            }
64
65
            // Values without keys are stored in this temporary array
66 12
            $sectionIni = [];
67
68 12
            foreach ($section as $key => $value) {
69 12
                if (is_numeric($key)) {
70 3
                    if (!is_array($value)) {
71 3
                        $value = [$value];
72 3
                    }
73
74 3
                    $sectionIni = array_merge($sectionIni, $value);
75 3
                    continue;
76
                }
77
78 12
                $sectionOutput = array_merge($sectionOutput, $this->renderKeyValuePair($key, $value));
79 11
            }
80
81 11
            if (count($sectionIni) > 0) {
82 3
                $sectionOutput = array_merge($this->renderKeyValuePair($sectionName, $sectionIni), $sectionOutput);
83 3
            }
84
85 11
            array_unshift($sectionOutput, sprintf('[%s]', $sectionName));
86
87
            // Write a linefeed after sections
88 11
            $sectionOutput[] = "\n";
89
90 11
            $output = array_merge($output, $sectionOutput);
91 11
        }
92
93 11
        return implode("\n", $output);
94
    }
95
96
    /**
97
     * Renders a key-value pair.
98
     *
99
     * @param string $key
100
     * @param mixed  $value
101
     *
102
     * @return array
103
     */
104 12
    protected function renderKeyValuePair($key, $value)
105
    {
106 12
        $output = [];
107 12
        $value = $this->normalizeValue($value);
108
109 11
        if (is_array($value)) {
110 3
            foreach ($value as $v) {
111 3
                $output[] = sprintf('%s[] = %s', $key, $v);
112 3
            }
113 3
        } else {
114 10
            $output[] = sprintf('%s = %s', $key, $value);
115
        }
116
117 11
        return $output;
118
    }
119
120
    /**
121
     * Normalize value to valid INI format.
122
     *
123
     * @param mixed $value
124
     *
125
     * @return string
126
     */
127 12
    protected function normalizeValue($value)
128
    {
129 12
        if (is_array($value)) {
130 7
            $value = $this->normalizeArray($value);
131 11
        } elseif (is_bool($value)) {
132 4
            $value = $this->normalizeBoolean($value);
133 11
        } elseif (is_null($value)) {
134 1
            $value = 'null';
135 1
        }
136
137 11
        if (is_string($value)) {
138 10
            $value = sprintf('"%s"', $value);
139 10
        }
140
141 11
        return $value;
142
    }
143
144
    /**
145
     * Normalizes arrays.
146
     *
147
     * @param array $value
148
     *
149
     * @return array|string
150
     */
151 7
    protected function normalizeArray($value)
152
    {
153 7
        switch ($this->arrayMode) {
154 7
            case self::ARRAY_MODE_CONCAT:
155 3
                foreach ($value as &$v) {
156 3
                    $v = trim($this->normalizeValue($v), '"'); // We don't want string normalization here
157 3
                }
158
159 3
                return implode(",", $value);
160
161
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
162 4
            case self::ARRAY_MODE_ARRAY:
163 4
            default:
164 4
                foreach ($value as &$v) {
165 4
                    if (is_array($v)) {
166 1
                        throw new RendererException('Multi-dimensional arrays are not supported by this array mode');
167
                    }
168
169 3
                    $v = $this->normalizeValue($v);
170 3
                }
171
172 3
                return $value;
173
174
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
175
        }
176
    }
177
178
    /**
179
     * Normalizes a boolean value;
180
     *
181
     * @param bool $value
182
     *
183
     * @return int|string
184
     */
185 4
    protected function normalizeBoolean($value)
186
    {
187 4
        switch ($this->booleanMode) {
188 4
            case self::BOOLEAN_MODE_BOOL_STRING:
189 1
                return $value === true ? 'true' : 'false';
190
191
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
192
193 3
            case self::BOOLEAN_MODE_STRING:
194 1
                return $value === true ? 'On' : 'Off';
195
196
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
197
198 2
            case self::BOOLEAN_MODE_INTEGER:
199 2
            default:
200 2
                return (int) $value;
201
202
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
203
        }
204
    }
205
}
206