Passed
Branch breakout (2dc8ab)
by Paul
02:07
created

SIP2Message::getVariable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 6
ccs 5
cts 5
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace lordelph\SIP2;
4
5
use lordelph\SIP2\Exception\LogicException;
6
7
/**
8
 * SIP2Message Class
9
 *
10
 * This class provides support for getting/setting request and response variables with some magic methods
11
 * as well as SIP2 CRC calculation
12
 *
13
 * @licence    https://opensource.org/licenses/MIT
14
 * @copyright  Paul Dixon <[email protected]>
15
 */
16
abstract class SIP2Message
17
{
18
    /**
19
     * @var array provides a list of the variables this message can use. Array key is the variable name in
20
     * StudlyCaps, value is an array which can contain type, default values
21
     */
22
    protected $var=[];
23
24
    /** @var integer|null current timestamp, useful for testing */
25
    protected $timestamp = null;
26
27
28
    /**
29
     * Calculate SIP2 CRC value
30
     * @param string $buf
31
     * @return string
32
     */
33 38
    protected static function crc(string $buf) : string
34
    {
35 38
        $sum = 0;
36
37 38
        $len = strlen($buf);
38 38
        for ($n = 0; $n < $len; $n++) {
39 38
            $sum = $sum + ord(substr($buf, $n, 1));
40
        }
41
42 38
        $crc = ($sum & 0xFFFF) * -1;
43
44
        /* 2008.03.15 - Fixed a bug that allowed the checksum to be larger then 4 digits */
45 38
        return substr(sprintf("%4X", $crc), -4, 4);
46
    }
47
48
    /**
49
     * Check if class supports given variable
50
     * @param string $name
51
     * @return bool
52
     */
53 20
    public function hasVariable(string $name) : bool
54
    {
55 20
        return isset($this->var[$name]);
56
    }
57
58
    /**
59
     * Set default value for a variable - this can be overridden by setVariable
60
     *
61
     * This method will allow you to attempt to set a default for a variable which the derived class
62
     * does not support, in which case it is silently ignored.
63
     *
64
     * @param string $name
65
     * @param string|array $value
66
     */
67 4
    public function setDefault(string $name, $value)
68
    {
69 4
        if ($this->hasVariable($name)) {
70 1
            $this->var[$name]['default'] = $value;
71
        }
72 4
    }
73
74
    /**
75
     * Get value of specific variable.
76
     *
77
     * There is also a magic method which instead of getVariable('PatronStatus') would instead allow
78
     * getPatronStatus() to be called
79
     *
80
     * @param string $varName
81
     * @return string|array
82
     */
83 38
    public function getVariable(string $varName)
84
    {
85 38
        $this->ensureVariableExists($varName);
86 37
        return $this->var[$varName]['value'] ??
87 19
            $this->var[$varName]['default'] ??
88 37
            $this->handleMissing($varName);
89
    }
90
91
    /**
92
     * Get name/values of all variables
93
     *
94
     * This can be useful for building JSON-based results of SIP2 responses
95
     * @return array
96
     */
97 1
    public function getAll()
98
    {
99 1
        $result=[];
100 1
        foreach ($this->var as $name => $data) {
101 1
            $result[$name] = $this->getVariable($name);
102
        }
103 1
        return $result;
104
    }
105
106
    /**
107
     * Set variable
108
     *
109
     * Variables which are defined as timestamps are converted to SIP2 date format automatically
110
     *
111
     * @param $varName
112
     * @param string|array $value
113
     */
114 35
    public function setVariable($varName, $value)
115
    {
116 35
        $this->ensureVariableExists($varName);
117
118
        //check type...
119 35
        $type = $this->var[$varName]['type'] ?? 'string';
120
        switch ($type) {
121 35
            case 'timestamp':
122 1
                $value = $this->datestamp($value);
123 1
                break;
124 35
            case 'array':
125 15
                $value = is_array($value) ? $value : [$value];
126 15
                break;
127
        }
128
129 35
        $this->var[$varName]['value'] = $value;
130 35
    }
131
132
    /**
133
     * If $varName is defined as an array, this will append given value. Otherwise value is set as normal
134
     * @param string $varName
135
     * @param string $value
136
     */
137 16
    public function addVariable(string $varName, $value)
138
    {
139 16
        $this->ensureVariableExists($varName);
140 16
        $type = $this->var[$varName]['type'] ?? 'string';
141 16
        if (($type === 'array') && isset($this->var[$varName]['value'])) {
142 4
            $this->var[$varName]['value'][] = $value;
143
        } else {
144 16
            $this->setVariable($varName, $value);
145
        }
146 16
    }
147
148
149
    /**
150
     * Get current timestamp, which can be override with setTimestamp for testing
151
     * @return int
152
     */
153 12
    public function getTimestamp()
154
    {
155 12
        return $this->timestamp ?? time();
156
    }
157
158
    /**
159
     * Sets current timestamp, which is useful for creating predictable tests
160
     * @param $timestamp
161
     */
162 14
    public function setTimestamp($timestamp)
163
    {
164 14
        $this->timestamp = $timestamp;
165 14
    }
166
167 12
    protected function datestamp($timestamp = '')
168
    {
169
        /* generate a SIP2 compatible datestamp */
170
        /* From the spec:
171
        * YYYYMMDDZZZZHHMMSS.
172
        * All dates and times are expressed according to the ANSI standard X3.30 for date and X3.43 for time.
173
        * The ZZZZ field should contain blanks (code $20) to represent local time. To represent universal time,
174
        *  a Z character(code $5A) should be put in the last (right hand) position of the ZZZZ field.
175
        * To represent other time zones the appropriate character should be used; a Q character (code $51)
176
        * should be put in the last (right hand) position of the ZZZZ field to represent Atlantic Standard Time.
177
        * When possible local time is the preferred format.
178
        */
179 12
        if ($timestamp != '') {
180
            /* Generate a proper date time from the date provided */
181 1
            return date('Ymd    His', $timestamp);
182
        } else {
183
            /* Current Date/Time */
184 12
            return date('Ymd    His', $this->getTimestamp());
185
        }
186
    }
187
188 38
    protected function ensureVariableExists($name)
189
    {
190 38
        if (!isset($this->var[$name])) {
191 1
            throw new LogicException(get_class($this) . ' has no ' . $name . ' member');
192
        }
193 38
    }
194
195 1
    protected function handleMissing($varName)
196
    {
197 1
        throw new LogicException(get_class($this) . '::set' . $varName . ' must be called');
198
    }
199
200 34
    public function __call($name, $arguments)
201
    {
202 34
        if (!preg_match('/^(get|set)(.+)$/', $name, $match)) {
203 1
            throw new LogicException(get_class($this) . ' has no ' . $name . ' method');
204
        }
205 33
        $varName = $match[2];
206
207
        //get?
208 33
        if ($match[1] === 'get') {
209 17
            return $this->getVariable($varName);
210
        }
211
        //set
212 18
        $this->setVariable($varName, $arguments[0]);
213 18
        return $this;
214
    }
215
}
216