Scalar::__construct()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 3
Bugs 0 Features 1
Metric Value
c 3
b 0
f 1
dl 0
loc 9
ccs 7
cts 7
cp 1
rs 9.6666
cc 2
eloc 6
nc 2
nop 2
crap 2
1
<?php
2
3
namespace Zerg\Field;
4
5
use PhpBio\Endian;
6
use Zerg\Stream\AbstractStream;
7
8
/**
9
 * Scalar field represents any simple and atomic part of data.
10
 *
11
 * This is an abstract class, so it should be extended by implementation classes.
12
 * Scalar field should have size {@see $size} which can be overridden by size callback {@see $sizeCallback}
13
 * and may have count {@see AbstractField::$size}. Also scalar return value can be overridden
14
 * by formatter {@see $formatter}
15
 *
16
 * @since 0.1
17
 * @package Zerg\Field
18
 */
19
abstract class Scalar extends AbstractField
20
{
21
    const ENDIAN_BIG = 1;
22
    const ENDIAN_LITTLE = 2;
23
24
    /**
25
     * @var int|string|callable Size of field in bits/bytes, path to value in DataSet or callback.
26
     */
27
    protected $size;
28
29
    /**
30
     * @var callable Callback that changes value of the field.
31
     */
32
    protected $formatter;
33
34
    /**
35
     * @var int Field endian.
36
     * @since 1.0
37
     */
38
    protected $endian;
39
40
    /**
41
     * @var array Human names of some common used sizes.
42
     */
43
    private $sizes = [
44
        'BIT'         => 1,
45
        'SEMI_NIBBLE' => 2,
46
        'NIBBLE'      => 4,
47
        'BYTE'        => 8,
48
        'SHORT'       => 16,
49
        'WORD'        => 32,
50
        'DWORD'       => 64,
51
    ];
52
53
    /**
54
     * Read part of data from source and return value in necessary format.
55
     *
56
     * This is abstract method, so each implementation should return it's own
57
     * type of value.
58
     *
59
     * @param AbstractStream $stream Stream from which read.
60
     * @return int|string|null Value type depend by implementation.
61
     */
62
    abstract public function read(AbstractStream $stream);
63
64
65 50
    public function __construct($size, $options = [])
66
    {
67 50
        if (is_array($size)) {
68 3
            $this->configure($size);
69 3
        } else {
70 50
            $this->setSize($size);
71 50
            $this->configure($options);
72
        }
73 49
    }
74
75
    /**
76
     * Return final value of size.
77
     *
78
     * If size was set as DataSet path or callback, it will be processed here.
79
     *
80
     * @return int Final value of size.
81
     * @throws ConfigurationException If the value was less than zero.
82
     */
83 30
    public function getSize()
84
    {
85 30
        $size = (int) $this->resolveProperty('size');
86
87 29
        if ($size < 0) {
88 1
            throw new ConfigurationException('Field size should not be less 0');
89
        }
90
91 28
        return $size;
92
    }
93
94
    /**
95
     * Process and sets size.
96
     *
97
     * Size can be represented as a string containing on of size key words {@see $sizes}.
98
     * Also you can set path to already parsed value in DataSet.
99
     *
100
     * @param int|string $size Size in bits/bytes or DataSet path.
101
     * @return static For chaining.
102
     */
103 50
    public function setSize($size)
104
    {
105 50
        if (is_string($size) && $parsed = $this->parseSizeWord($size)) {
106 14
            $this->size = $parsed;
107 14
        } else {
108 38
            $this->size = $size;
109
        }
110 50
        return $this;
111
    }
112
113
    /**
114
     * Getter for the value callback.
115
     *
116
     * @return callable
117
     */
118 1
    public function getFormatter()
119
    {
120 1
        return $this->formatter;
121
    }
122
123
    /**
124
     * Setter for the value callback.
125
     *
126
     * @param callable $formatter
127
     * @return $this
128
     */
129 3
    public function setFormatter($formatter)
130
    {
131 3
        $this->formatter = $formatter;
132 3
        return $this;
133
    }
134
135
    /**
136
     * @return int
137
     * @since 1.0
138
     */
139 25
    public function getEndian()
140
    {
141 25
        return $this->endian;
142
    }
143
144
    /**
145
     * @param int $endian
146
     * @since 1.0
147
     * @return $this
148
     */
149 4
    public function setEndian($endian)
150
    {
151 4
        if (!in_array($endian, [Endian::ENDIAN_BIG, Endian::ENDIAN_LITTLE])) {
152 1
            throw new ConfigurationException(
153 1
                sprintf('Endian must be %d for Big endian of %d for Little', Endian::ENDIAN_BIG, Endian::ENDIAN_LITTLE)
154 1
            );
155
        }
156 3
        $this->endian = $endian;
157 3
        return $this;
158
    }
159
160
    /**
161
     * Reads and process value from Stream.
162
     *
163
     * @api
164
     * @param AbstractStream $stream Stream from which read.
165
     * @return mixed The final value.
166
     */
167 16
    public function parse(AbstractStream $stream)
168
    {
169 16
        $value = $this->format($this->read($stream));
170 16
        $this->validate($value);
171 12
        return $value;
172
    }
173
174
    /**
175
     * Applies value formatter to read value.
176
     *
177
     * @param int|string|null $value Read value.
178
     * @return mixed Processed value.
179
     */
180 16
    private function format($value)
181
    {
182 16
        if (is_callable($this->formatter)) {
183 1
            $value = call_user_func($this->formatter, $value, $this->dataSet);
184 1
        }
185 16
        return $value;
186
    }
187
188
    /**
189
     * Process given string and return appropriate size value.
190
     *
191
     * @param $word
192
     * @return int
193
     */
194 15
    private function parseSizeWord($word)
195
    {
196 15
        $sizeWord = strtoupper(preg_replace('/([a-z])([A-Z])/', '$1_$2', $word));
197 15
        if (array_key_exists($sizeWord, $this->sizes)) {
198 14
            $size = $this->sizes[$sizeWord];
199 14
        } else {
200 2
            $size = 0;
201
        }
202 15
        return $size;
203
    }
204
}