Encoder   A
last analyzed

Complexity

Total Complexity 31

Size/Duplication

Total Lines 228
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
wmc 31
lcom 1
cbo 1
dl 0
loc 228
rs 9.92
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A encode() 0 6 1
A process() 0 16 6
A getCurrentType() 0 19 6
A encodeString() 0 4 1
A encodeInteger() 0 4 1
A encodeArray() 0 12 3
A encodeList() 0 10 2
A encodeDictionary() 0 17 3
A isList() 0 10 3
A isDictionary() 0 10 3
A encodeObject() 0 10 2
1
<?php namespace Cornford\Bencoded;
2
3
use Cornford\Bencoded\Contracts\EncodableInterface;
4
use Cornford\Bencoded\Exceptions\InvalidEncodeInputException;
5
6
class Encoder implements EncodableInterface
7
{
8
    const TYPE_ARRAY = 0;
9
    const TYPE_OBJECT = 1;
10
    const TYPE_INTEGER = 2;
11
    const TYPE_DOUBLE = 3;
12
    const TYPE_STRING = 4;
13
14
    const DELIMITER_END  = 'e';
15
    const DELIMITER_DICTIONARY = 'd';
16
    const DELIMITER_INTEGER  = 'i';
17
    const DELIMITER_LIST = 'l';
18
19
    const HEX_NULL = "\x00";
20
21
    const SEPARATOR = ':';
22
23
    /**
24
     * Content.
25
     *
26
     * @var mixed
27
     */
28
    private $content;
29
30
    /**
31
     * Encode content.
32
     *
33
     * @param mixed $content
34
     *
35
     * @throws InvalidEncodeInputException
36
     *
37
     * @return string
38
     */
39
    public function encode($content)
40
    {
41
        $this->content = $content;
42
43
        return $this->process();
44
    }
45
46
    /**
47
     * Process from the current position.
48
     *
49
     * @throws InvalidEncodeInputException
50
     *
51
     * @return string
52
     */
53
    private function process()
54
    {
55
        switch ($this->getCurrentType()) {
56
            case self::TYPE_STRING:
57
                return $this->encodeString();
58
            case self::TYPE_INTEGER:
59
            case self::TYPE_DOUBLE:
60
                return $this->encodeInteger();
61
            case self::TYPE_ARRAY:
62
                return $this->encodeArray();
63
            case self::TYPE_OBJECT:
64
                return $this->encodeObject();
65
            default:
66
                return self::HEX_NULL;
67
        }
68
    }
69
70
    /**
71
     * Get type of the value at the current pointer.
72
     *
73
     * @throws InvalidEncodeInputException
74
     *
75
     * @return integer
76
     */
77
    private function getCurrentType()
78
    {
79
        switch (gettype($this->content)) {
80
            case 'string':
81
                return self::TYPE_STRING;
82
            case 'integer':
83
                return self::TYPE_INTEGER;
84
            case 'double':
85
                return self::TYPE_DOUBLE;
86
            case 'array':
87
                return self::TYPE_ARRAY;
88
            case 'object':
89
                return self::TYPE_OBJECT;
90
            default:
91
                break;
92
        }
93
94
        throw new InvalidEncodeInputException('Invalid type input encountered: "' . $this->content . '"');
95
    }
96
97
    /**
98
     * Encode string.
99
     *
100
     * @return string
101
     */
102
    private function encodeString()
103
    {
104
        return (strlen($this->content) . self::SEPARATOR . $this->content);
105
    }
106
107
    /**
108
     * Encode integer.
109
     *
110
     * @return string
111
     */
112
    private function encodeInteger()
113
    {
114
        return (self::DELIMITER_INTEGER . $this->content . self::DELIMITER_END);
115
    }
116
117
    /**
118
     * Encode array.
119
     *
120
     * @throws InvalidEncodeInputException
121
     *
122
     * @return string
123
     */
124
    private function encodeArray()
125
    {
126
        $return = '';
127
128
        if ($this->isList($this->content)) {
129
            $return = $this->encodeList();
130
        } elseif ($this->isDictionary($this->content)) {
131
            $return = $this->encodeDictionary();
132
        }
133
134
        return $return . self::DELIMITER_END;
135
    }
136
137
    /**
138
     * Encode list.
139
     *
140
     * @throws InvalidEncodeInputException
141
     *
142
     * @return string
143
     */
144
    private function encodeList()
145
    {
146
        $return = self::DELIMITER_LIST;
147
148
        foreach ((array) $this->content as $value) {
149
            $return .= $this->encode($value);
150
        }
151
152
        return $return;
153
    }
154
155
    /**
156
     * Encode dictionary.
157
     *
158
     * @throws InvalidEncodeInputException
159
     *
160
     * @return string
161
     */
162
    private function encodeDictionary()
163
    {
164
        $content = (array) $this->content;
165
166
        ksort($content, SORT_STRING);
167
        $return = self::DELIMITER_DICTIONARY;
168
169
        foreach ($content as $key => $value) {
170
            if (strval($key)[0] == self::HEX_NULL) {
171
                continue;
172
            }
173
174
            $return .= $this->encode(strval($key)) . $this->encode($value);
175
        }
176
177
        return $return;
178
    }
179
180
    /**
181
     * Is list?
182
     *
183
     * @param array $array
184
     *
185
     * @return bool
186
     */
187
    private function isList(array $array)
188
    {
189
        foreach (array_keys($array) as $key) {
190
            if (!is_int($key)) {
191
                return false;
192
            }
193
        }
194
195
        return true;
196
    }
197
198
    /**
199
     * Is dictionary?
200
     *
201
     * @param array $array
202
     *
203
     * @return bool
204
     */
205
    private function isDictionary(array $array)
206
    {
207
        foreach (array_keys($array) as $key) {
208
            if (!is_string($key)) {
209
                return false;
210
            }
211
        }
212
213
        return true;
214
    }
215
216
    /**
217
     * Encode object.
218
     *
219
     * @throws InvalidEncodeInputException
220
     *
221
     * @return string
222
     */
223
    private function encodeObject()
224
    {
225
        if (method_exists($this->content, 'toArray')) {
226
            $this->content = $this->content->toArray();
227
        } else {
228
            $this->content = (array) $this->content;
229
        }
230
231
        return $this->encodeArray();
232
    }
233
}
234