ValueLabel::write()   B
last analyzed

Complexity

Conditions 7
Paths 20

Size

Total Lines 36
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 7
eloc 23
c 2
b 1
f 0
nc 20
nop 1
dl 0
loc 36
ccs 0
cts 24
cp 0
crap 56
rs 8.6186
1
<?php
2
3
namespace SPSS\Sav\Record;
4
5
use SPSS\Buffer;
6
use SPSS\Exception;
7
use SPSS\Sav\Record;
8
use SPSS\Utils;
9
10
/**
11
 * The value label records documented in this section are used for numeric and short string variables only.
12
 * Long string variables may have value labels, but their value labels are recorded using a different record type
13
 *
14
 * @see \SPSS\Sav\Record\Info\LongStringValueLabels
15
 */
16
class ValueLabel extends Record
17
{
18
    const TYPE = 3;
19
    const LABEL_MAX_LENGTH = 255;
20
21
    /**
22
     * @var array
23
     */
24
    public $labels = [];
25
26
    /**
27
     * @var array
28
     * A list of dictionary indexes of variables to which to apply the value labels
29
     * String variables wider than 8 bytes may not be specified in this list.
30
     */
31
    public $indexes = [];
32
33
    /**
34
     * @var Variable[]
35
     */
36
    protected $variables = [];
37
38
    /**
39
     * @param array $variables
40
     */
41
    public function setVariables($variables)
42
    {
43
        $this->variables = $variables;
44
    }
45
46
    /**
47
     * @param Buffer $buffer
48
     * @throws Exception
49
     */
50
    public function read(Buffer $buffer)
51
    {
52
        /** @var int $labelCount Number of value labels present in this record. */
53
        $labelCount = $buffer->readInt();
54
55
        for ($i = 0; $i < $labelCount; $i++) {
56
            // A numeric value or a short string value padded as necessary to 8 bytes in length.
57
            $value = $buffer->readDouble();
58
            $labelLength = ord($buffer->read(1));
59
            $label = $buffer->readString(Utils::roundUp($labelLength + 1, 8) - 1);
60
            $this->labels[] = [
61
                'value' => $value,
62
                'label' => rtrim($label),
0 ignored issues
show
Bug introduced by
It seems like $label can also be of type false; however, parameter $str of rtrim() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

62
                'label' => rtrim(/** @scrutinizer ignore-type */ $label),
Loading history...
63
            ];
64
        }
65
66
        // The value label variables record is always immediately followed after a value label record.
67
        $recType = $buffer->readInt();
68
        if ($recType != 4) {
69
            throw new Exception(
70
                sprintf('Error reading Variable Index record: bad record type [%s]. Expecting Record Type 4.', $recType)
71
            );
72
        }
73
74
        // Number of variables that the associated value labels from the value label record are to be applied.
75
        $varCount = $buffer->readInt();
76
        for ($i = 0; $i < $varCount; $i++) {
77
78
            $varIndex = $buffer->readInt() - 1;
79
80
            // Decode values for short variables
81
            if (isset($this->variables[$varIndex])) {
82
                $varWidth = $this->variables[$varIndex]->width;
83
                if ($varWidth > 0) {
84
                    foreach ($this->labels as $labelIdx => $label) {
85
                        $this->labels[$labelIdx]['value'] = rtrim(Utils::doubleToString($label['value']));
86
                    }
87
                }
88
            }
89
90
            $this->indexes[] = $varIndex;
91
        }
92
93
    }
94
95
    /**
96
     * @param Buffer $buffer
97
     */
98
    public function write(Buffer $buffer)
99
    {
100
        $convertToDouble = false;
101
        $varIndex = reset($this->indexes);
102
        if ($varIndex !== false && isset($this->variables[$varIndex - 1])) {
103
            $varWidth = $this->variables[$varIndex - 1]->width;
104
            $convertToDouble = $varWidth > 0;
105
        }
106
107
        // Value label record.
108
        $buffer->writeInt(self::TYPE);
109
        $buffer->writeInt(count($this->labels));
110
        foreach ($this->labels as $item) {
111
            $labelLength = min(mb_strlen($item['label']), self::LABEL_MAX_LENGTH);
112
            $label = mb_substr($item['label'], 0, $labelLength);
113
            $labelLengthBytes = mb_strlen($label, '8bit');
114
            while ($labelLengthBytes > 255) {
115
                // Strip one char, can be multiple bytes
116
                $label = mb_substr($label, 0, -1);
117
                $labelLengthBytes = mb_strlen($label, '8bit');
118
            }
119
120
            if ($convertToDouble) {
121
                $item['value'] = Utils::stringToDouble($item['value']);
122
            }
123
124
            $buffer->writeDouble($item['value']);
125
            $buffer->write(chr($labelLengthBytes));
126
            $buffer->writeString($label, Utils::roundUp($labelLengthBytes + 1, 8) - 1);
127
        }
128
129
        // Value label variable record.
130
        $buffer->writeInt(4);
131
        $buffer->writeInt(count($this->indexes));
132
        foreach ($this->indexes as $varIndex) {
133
            $buffer->writeInt($varIndex);
134
        }
135
    }
136
}
137