ArrayNode::getDefaultValue()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 9
rs 9.6666
cc 2
eloc 5
nc 2
nop 0
1
<?php
2
/*
3
 * This file is part of the Borobudur-Config package.
4
 *
5
 * (c) Hexacodelabs <http://hexacodelabs.com>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Borobudur\Config\Definition;
12
13
use Borobudur\Config\Exception\InvalidArgumentException;
14
use Borobudur\Config\Exception\InvalidConfigurationException;
15
use Borobudur\Config\Exception\InvalidTypeException;
16
17
/**
18
 * @author      Iqbal Maulana <[email protected]>
19
 * @created     8/10/15
20
 */
21
class ArrayNode extends AbstractNode implements PrototypeNodeInterface
22
{
23
    /**
24
     * @var NodeInterface[]
25
     */
26
    protected $children = array();
27
28
    /**
29
     * @var bool|null
30
     */
31
    protected $defaultValueSet = null;
32
33
    /**
34
     * {@inheritdoc}
35
     */
36
    public function setName($name)
37
    {
38
        $this->name = $name;
39
    }
40
41
    /**
42
     * Append node.
43
     *
44
     * @param NodeInterface $child
45
     */
46
    public function append(NodeInterface $child)
47
    {
48
        $name = $child->getName();
49
50
        if (empty($name)) {
51
            throw new InvalidArgumentException('Child node name cannot be empty.');
52
        }
53
54
        if (isset($this->children[$name])) {
55
            throw new InvalidArgumentException(sprintf('Child node named "%s" already exists.', $name));
56
        }
57
58
        $this->children[$name] = $child;
59
    }
60
61
    /**
62
     * {@inheritdoc}
63
     */
64
    public function setDefaultValue($value)
65
    {
66
        // can't set default value for array node
67
    }
68
69
    /**
70
     * Get children node default values.
71
     *
72
     * @return array
73
     */
74
    public function getDefaultValue()
75
    {
76
        $defaults = array();
77
        foreach($this->children as $node) {
78
            $defaults[$node->getName()] = $node->getDefaultValue();
79
        }
80
81
        return $defaults;
82
    }
83
84
    /**
85
     * {@inheritdoc}
86
     */
87
    public function hasDefaultValue()
88
    {
89
        if (null === $this->defaultValueSet) {
90
            $this->defaultValueSet = 0 !== count($this->getDefaultValue());
91
        }
92
93
        return $this->defaultValueSet;
94
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99
    protected function validateType($value)
100
    {
101
        if (!is_array($value)) {
102
            throw InvalidTypeException::invalidExpectedType($this->getPath(), 'array', $value);
103
        }
104
    }
105
106
    /**
107
     * {@inheritdoc}
108
     */
109
    protected function normalizeValue($value)
110
    {
111
        $normalized = array();
112
        foreach ($value as $name => $val) {
113
            if (isset($this->children[$name])) {
114
                $normalized[$name] = $this->children[$name]->normalize($val);
115
                unset($value[$name]);
116
            }
117
        }
118
119
        $this->assertEmptyUnrecognizedConfig($value);
120
121
        return $normalized;
122
    }
123
124
    /**
125
     * {@inheritdoc}
126
     */
127
    protected function mergeValues($left, $right)
128
    {
129
        foreach ($right as $key => $value) {
130
            if (!array_key_exists($key, $left)) {
131
                $left[$key] = $value;
132
                continue;
133
            }
134
135
            if (!isset($this->children[$key])) {
136
                throw new InvalidArgumentException(sprintf('Children "%s" is not defined.', $key));
137
            }
138
139
            $left[$key] = $this->children[$key]->merge($left[$key], $value);
140
        }
141
142
        return $left;
143
    }
144
145
    /**
146
     * {@inheritdoc}
147
     */
148
    protected function finalizeValue($value)
149
    {
150
        foreach ($this->children as $name => $child) {
151
            if (!array_key_exists($name, $value)) {
152
                $this->assertRequired($child->isRequired(), $name);
153
154
                if ($child->hasDefaultValue()) {
155
                    $value[$name] = $child->getDefaultValue();
156
                }
157
158
                continue;
159
            }
160
161
            $value[$name] = $child->finalize($value[$name]);
162
        }
163
164
        return $value;
165
    }
166
167
    /**
168
     * Assert if child is required.
169
     *
170
     * @param bool $required
171
     * @param string $name
172
     *
173
     * @throws InvalidConfigurationException
174
     */
175
    private function assertRequired($required, $name)
176
    {
177
        if (true === $required) {
178
            throw new InvalidConfigurationException(sprintf(
179
                'Child node "%s" at path "%s" should be configured.',
180
                $name,
181
                $this->getPath()
182
            ));
183
        }
184
    }
185
186
    /**
187
     * Assert that unrecognized config should be empty.
188
     *
189
     * @param array $value
190
     *
191
     * @throws InvalidConfigurationException
192
     */
193
    private function assertEmptyUnrecognizedConfig($value)
194
    {
195
        if (count($value)) {
196
            throw new InvalidConfigurationException(sprintf(
197
                'Unrecognized option%s "%s" under "%s"',
198
                1 === count($value) ? '' : 's',
199
                implode(', ', array_keys($value)),
200
                $this->getPath()
201
            ));
202
        }
203
    }
204
}
205