Passed
Push — master ( 67ed62...5da465 )
by Gaetano
02:56
created

Encoder   C

Complexity

Total Complexity 55

Size/Duplication

Total Lines 290
Duplicated Lines 0 %

Test Coverage

Coverage 68.31%

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 152
c 4
b 0
f 0
dl 0
loc 290
rs 6
ccs 97
cts 142
cp 0.6831
wmc 55

3 Methods

Rating   Name   Duplication   Size   Complexity  
D decode() 0 78 20
D encode() 0 86 22
C decodeXml() 0 69 13

How to fix   Complexity   

Complex Class

Complex classes like Encoder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Encoder, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace PhpXmlRpc;
4
5
use PhpXmlRpc\Helper\Logger;
6
use PhpXmlRpc\Helper\XMLParser;
7
8
/**
9
 * A helper class to easily convert between Value objects and php native values
10
 * @todo implement an interface
11
 */
12
class Encoder
13
{
14
    /**
15
     * Takes an xmlrpc value in object format and translates it into native PHP types.
16
     *
17
     * Works with xmlrpc requests objects as input, too.
18
     *
19
     * Given proper options parameter, can rebuild generic php object instances (provided those have been encoded to
20
     * xmlrpc format using a corresponding option in php_xmlrpc_encode())
21
     * PLEASE NOTE that rebuilding php objects involves calling their constructor function.
22
     * This means that the remote communication end can decide which php code will get executed on your server, leaving
23
     * the door possibly open to 'php-injection' style of attacks (provided you have some classes defined on your server
24
     * that might wreak havoc if instances are built outside an appropriate context).
25
     * Make sure you trust the remote server/client before eanbling this!
26
     *
27
     * @author Dan Libby ([email protected])
28
     *
29
     * @param Value|Request $xmlrpcVal
30
     * @param array $options if 'decode_php_objs' is set in the options array, xmlrpc structs can be decoded into php
31
     *                       objects; if 'dates_as_objects' is set xmlrpc datetimes are decoded as php DateTime objects
32
     *
33
     * @return mixed
34
     */
35 234
    public function decode($xmlrpcVal, $options = array())
36
    {
37 234
        switch ($xmlrpcVal->kindOf()) {
38 234
            case 'scalar':
39 234
                if (in_array('extension_api', $options)) {
40
                    $val = reset($xmlrpcVal->me);
0 ignored issues
show
Bug introduced by
The property me does not seem to exist on PhpXmlRpc\Request.
Loading history...
41
                    $typ = key($xmlrpcVal->me);
42
                    switch ($typ) {
43
                        case 'dateTime.iso8601':
44
                            $xmlrpcVal->scalar = $val;
0 ignored issues
show
Bug introduced by
The property scalar does not seem to exist on PhpXmlRpc\Request.
Loading history...
Bug introduced by
The property scalar does not seem to exist on PhpXmlRpc\Value.
Loading history...
45
                            $xmlrpcVal->type = 'datetime';
0 ignored issues
show
Bug introduced by
The property type does not seem to exist on PhpXmlRpc\Value.
Loading history...
Bug introduced by
The property type does not seem to exist on PhpXmlRpc\Request.
Loading history...
46
                            $xmlrpcVal->timestamp = \PhpXmlRpc\Helper\Date::iso8601Decode($val);
0 ignored issues
show
Bug introduced by
The property timestamp does not seem to exist on PhpXmlRpc\Value.
Loading history...
Bug introduced by
The property timestamp does not seem to exist on PhpXmlRpc\Request.
Loading history...
47
48
                            return $xmlrpcVal;
49
                        case 'base64':
50
                            $xmlrpcVal->scalar = $val;
51
                            $xmlrpcVal->type = $typ;
52
53
                            return $xmlrpcVal;
54
                        default:
55
                            return $xmlrpcVal->scalarval();
0 ignored issues
show
Bug introduced by
The method scalarval() does not exist on PhpXmlRpc\Request. ( Ignorable by Annotation )

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

55
                            return $xmlrpcVal->/** @scrutinizer ignore-call */ scalarval();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
56
                    }
57
                }
58 234
                if (in_array('dates_as_objects', $options) && $xmlrpcVal->scalartyp() == 'dateTime.iso8601') {
0 ignored issues
show
Bug introduced by
The method scalartyp() does not exist on PhpXmlRpc\Request. ( Ignorable by Annotation )

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

58
                if (in_array('dates_as_objects', $options) && $xmlrpcVal->/** @scrutinizer ignore-call */ scalartyp() == 'dateTime.iso8601') {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
59
                    // we return a Datetime object instead of a string since now the constructor of xmlrpc value accepts
60
                    // safely strings, ints and datetimes, we cater to all 3 cases here
61
                    $out = $xmlrpcVal->scalarval();
62
                    if (is_string($out)) {
63
                        $out = strtotime($out);
64
                    }
65
                    if (is_int($out)) {
66
                        $result = new \DateTime();
67
                        $result->setTimestamp($out);
68
69
                        return $result;
70
                    } elseif (is_a($out, 'DateTimeInterface')) {
71
                        return $out;
72
                    }
73
                }
74
75 234
                return $xmlrpcVal->scalarval();
76 232
            case 'array':
77 96
                $arr = array();
78 96
                foreach($xmlrpcVal as $value) {
79 96
                    $arr[] = $this->decode($value, $options);
80
                }
81
82 96
                return $arr;
83 175
            case 'struct':
84
                // If user said so, try to rebuild php objects for specific struct vals.
85
                /// @todo should we raise a warning for class not found?
86
                // shall we check for proper subclass of xmlrpc value instead of presence of _php_class to detect
87
                // what we can do?
88 59
                if (in_array('decode_php_objs', $options) && $xmlrpcVal->_php_class != ''
0 ignored issues
show
Bug introduced by
The property _php_class does not seem to exist on PhpXmlRpc\Request.
Loading history...
89 39
                    && class_exists($xmlrpcVal->_php_class)
90
                ) {
91 20
                    $obj = @new $xmlrpcVal->_php_class();
92 20
                    foreach ($xmlrpcVal as $key => $value) {
93 20
                        $obj->$key = $this->decode($value, $options);
94
                    }
95
96 20
                    return $obj;
97
                } else {
98 40
                    $arr = array();
99 40
                    foreach ($xmlrpcVal as $key => $value) {
100 40
                        $arr[$key] = $this->decode($value, $options);
101
                    }
102
103 40
                    return $arr;
104
                }
105 117
            case 'msg':
106 117
                $paramCount = $xmlrpcVal->getNumParams();
0 ignored issues
show
Bug introduced by
The method getNumParams() does not exist on PhpXmlRpc\Value. ( Ignorable by Annotation )

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

106
                /** @scrutinizer ignore-call */ 
107
                $paramCount = $xmlrpcVal->getNumParams();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
107 117
                $arr = array();
108 117
                for ($i = 0; $i < $paramCount; $i++) {
109 117
                    $arr[] = $this->decode($xmlrpcVal->getParam($i), $options);
0 ignored issues
show
Bug introduced by
The method getParam() does not exist on PhpXmlRpc\Value. ( Ignorable by Annotation )

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

109
                    $arr[] = $this->decode($xmlrpcVal->/** @scrutinizer ignore-call */ getParam($i), $options);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
110
                }
111
112 117
                return $arr;
113
        }
114
    }
115
116
    /**
117
     * Takes native php types and encodes them into xmlrpc PHP object format.
118
     * It will not re-encode xmlrpc value objects.
119
     *
120
     * Feature creep -- could support more types via optional type argument
121
     * (string => datetime support has been added, ??? => base64 not yet)
122
     *
123
     * If given a proper options parameter, php object instances will be encoded into 'special' xmlrpc values, that can
124
     * later be decoded into php objects by calling php_xmlrpc_decode() with a corresponding option
125
     *
126
     * @author Dan Libby ([email protected])
127
     *
128
     * @param mixed $phpVal the value to be converted into an xmlrpc value object
129
     * @param array $options can include 'encode_php_objs', 'auto_dates', 'null_extension' or 'extension_api'
130
     *
131
     * @return \PhpXmlrpc\Value
132
     */
133 219
    public function encode($phpVal, $options = array())
134
    {
135 219
        $type = gettype($phpVal);
136 219
        switch ($type) {
137 219
            case 'string':
138 214
                if (in_array('auto_dates', $options) && preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $phpVal)) {
139 1
                    $xmlrpcVal = new Value($phpVal, Value::$xmlrpcDateTime);
140
                } else {
141 214
                    $xmlrpcVal = new Value($phpVal, Value::$xmlrpcString);
142
                }
143 214
                break;
144 105
            case 'integer':
145 100
                $xmlrpcVal = new Value($phpVal, Value::$xmlrpcInt);
146 100
                break;
147 27
            case 'double':
148 1
                $xmlrpcVal = new Value($phpVal, Value::$xmlrpcDouble);
149 1
                break;
150
            // Add support for encoding/decoding of booleans, since they are supported in PHP
151 27
            case 'boolean':
152 1
                $xmlrpcVal = new Value($phpVal, Value::$xmlrpcBoolean);
153 1
                break;
154 27
            case 'array':
155
                // PHP arrays can be encoded to either xmlrpc structs or arrays, depending on whether they are hashes
156
                // or plain 0..n integer indexed
157
                // A shorter one-liner would be
158
                // $tmp = array_diff(array_keys($phpVal), range(0, count($phpVal)-1));
159
                // but execution time skyrockets!
160 24
                $j = 0;
161 24
                $arr = array();
162 24
                $ko = false;
163 24
                foreach ($phpVal as $key => $val) {
164 24
                    $arr[$key] = $this->encode($val, $options);
165 24
                    if (!$ko && $key !== $j) {
166 22
                        $ko = true;
167
                    }
168 24
                    $j++;
169
                }
170 24
                if ($ko) {
171 22
                    $xmlrpcVal = new Value($arr, Value::$xmlrpcStruct);
172
                } else {
173 3
                    $xmlrpcVal = new Value($arr, Value::$xmlrpcArray);
174
                }
175 24
                break;
176 4
            case 'object':
177 2
                if (is_a($phpVal, 'PhpXmlRpc\Value')) {
178 1
                    $xmlrpcVal = $phpVal;
179 1
                } elseif (is_a($phpVal, 'DateTimeInterface')) {
180
                    $xmlrpcVal = new Value($phpVal->format('Ymd\TH:i:s'), Value::$xmlrpcStruct);
181
                } else {
182 1
                    $arr = array();
183 1
                    foreach($phpVal as $k => $v) {
184 1
                        $arr[$k] = $this->encode($v, $options);
185
                    }
186 1
                    $xmlrpcVal = new Value($arr, Value::$xmlrpcStruct);
187 1
                    if (in_array('encode_php_objs', $options)) {
188
                        // let's save original class name into xmlrpc value:
189
                        // might be useful later on...
190 1
                        $xmlrpcVal->_php_class = get_class($phpVal);
191
                    }
192
                }
193 2
                break;
194 2
            case 'NULL':
195 2
                if (in_array('extension_api', $options)) {
196
                    $xmlrpcVal = new Value('', Value::$xmlrpcString);
197 2
                } elseif (in_array('null_extension', $options)) {
198
                    $xmlrpcVal = new Value('', Value::$xmlrpcNull);
199
                } else {
200 2
                    $xmlrpcVal = new Value();
201
                }
202 2
                break;
203
            case 'resource':
204
                if (in_array('extension_api', $options)) {
205
                    $xmlrpcVal = new Value((int)$phpVal, Value::$xmlrpcInt);
206
                } else {
207
                    $xmlrpcVal = new Value();
208
                }
209
                break;
210
            // catch "user function", "unknown type"
211
            default:
212
                // giancarlo pinerolo <[email protected]>
213
                // it has to return an empty object in case, not a boolean.
214
                $xmlrpcVal = new Value();
215
                break;
216
        }
217
218 219
        return $xmlrpcVal;
219
    }
220
221
    /**
222
     * Convert the xml representation of a method response, method request or single
223
     * xmlrpc value into the appropriate object (a.k.a. deserialize).
224
     *
225
     * Q: is this a good name for this method? It does something quite different from 'decode' after all
226
     * (returning objects vs returns plain php values)...
227
     *
228
     * @param string $xmlVal
229
     * @param array $options
230
     *
231
     * @return mixed false on error, or an instance of either Value, Request or Response
232
     */
233 3
    public function decodeXml($xmlVal, $options = array())
0 ignored issues
show
Unused Code introduced by
The parameter $options is not used and could be removed. ( Ignorable by Annotation )

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

233
    public function decodeXml($xmlVal, /** @scrutinizer ignore-unused */ $options = array())

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
234
    {
235
        // 'guestimate' encoding
236 3
        $valEncoding = XMLParser::guessEncoding('', $xmlVal);
237 3
        if ($valEncoding != '') {
238
239
            // Since parsing will fail if
240
            // - charset is not specified in the xml prologue,
241
            // - the encoding is not UTF8 and
242
            // - there are non-ascii chars in the text,
243
            // we try to work round that...
244
            // The following code might be better for mb_string enabled installs, but makes the lib about 200% slower...
245
            //if (!is_valid_charset($valEncoding, array('UTF-8'))
246 3
            if (!in_array($valEncoding, array('UTF-8', 'US-ASCII')) && !XMLParser::hasEncoding($xmlVal)) {
247
                if ($valEncoding == 'ISO-8859-1') {
248
                    $xmlVal = utf8_encode($xmlVal);
249
                } else {
250
                    if (extension_loaded('mbstring')) {
251
                        $xmlVal = mb_convert_encoding($xmlVal, 'UTF-8', $valEncoding);
252
                    } else {
253
                        Logger::instance()->errorLog('XML-RPC: ' . __METHOD__ . ': invalid charset encoding of xml text: ' . $valEncoding);
254
                    }
255
                }
256
            }
257
        }
258
259
        // What if internal encoding is not in one of the 3 allowed? We use the broadest one, ie. utf8!
260 3
        if (!in_array(PhpXmlRpc::$xmlrpc_internalencoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII'))) {
261
            $options = array(XML_OPTION_TARGET_ENCODING => 'UTF-8');
262
        } else {
263 3
            $options = array(XML_OPTION_TARGET_ENCODING => PhpXmlRpc::$xmlrpc_internalencoding);
264
        }
265
266 3
        $xmlRpcParser = new XMLParser($options);
267 3
        $xmlRpcParser->parse($xmlVal, XMLParser::RETURN_XMLRPCVALS, XMLParser::ACCEPT_REQUEST | XMLParser::ACCEPT_RESPONSE | XMLParser::ACCEPT_VALUE);
268
269 3
        if ($xmlRpcParser->_xh['isf'] > 1) {
270
            // test that $xmlrpc->_xh['value'] is an obj, too???
271
272
            Logger::instance()->errorLog($xmlRpcParser->_xh['isf_reason']);
273
274
            return false;
275
        }
276
277 3
        switch ($xmlRpcParser->_xh['rt']) {
278 3
            case 'methodresponse':
279 3
                $v = $xmlRpcParser->_xh['value'];
280 3
                if ($xmlRpcParser->_xh['isf'] == 1) {
281
                    /** @var Value $vc */
282
                    $vc = $v['faultCode'];
283
                    /** @var Value $vs */
284
                    $vs = $v['faultString'];
285
                    $r = new Response(0, $vc->scalarval(), $vs->scalarval());
286
                } else {
287 3
                    $r = new Response($v);
288
                }
289
290 3
                return $r;
291 1
            case 'methodcall':
292 1
                $req = new Request($xmlRpcParser->_xh['method']);
0 ignored issues
show
Bug introduced by
$xmlRpcParser->_xh['method'] of type false is incompatible with the type string expected by parameter $methodName of PhpXmlRpc\Request::__construct(). ( Ignorable by Annotation )

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

292
                $req = new Request(/** @scrutinizer ignore-type */ $xmlRpcParser->_xh['method']);
Loading history...
293 1
                for ($i = 0; $i < count($xmlRpcParser->_xh['params']); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
294 1
                    $req->addParam($xmlRpcParser->_xh['params'][$i]);
295
                }
296
297 1
                return $req;
298 1
            case 'value':
299 1
                return $xmlRpcParser->_xh['value'];
300
            default:
301
                return false;
302
        }
303
    }
304
305
}
306