PO5::libxml_display_error()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 14
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 17
rs 9.7998
1
<?php
2
3
namespace PlatiOnline;
4
5
use sylouuu\Curl\Method as Curl;
6
use phpseclib\Crypt\AES as AES;
7
use phpseclib\Crypt\RSA as RSA;
8
9
class PO5
10
{
11
    // private
12
    private $f_request 	  							= null;
13
    private $f_secure 	  							= null;
14
    private $aes_key 	  							= null;
15
    private $iv 		  							= null;
16
    private $iv_itsn 	  							= null;
17
    private $rsa_key_enc  							= null;
18
    private $rsa_key_dec  							= null;
19
    private $url		  							= 'https://secure.plationline.ro/';
20
    private $url_sv_request_xml 					= 'https://secure.plationline.ro/xml_validation/po.request.v5.xsd'; 		// any call
21
    private $url_sv_auth_xml 						= 'https://secure.plationline.ro/xml_validation/f_message.auth.v5.xsd'; 	// auth
22
    private $url_sv_auth_url_xml 					= 'https://secure.plationline.ro/xml_validation/auth.url.response.v5.xsd'; // auth url
23
    private $url_sv_auth_response_xml 				= 'https://secure.plationline.ro/xml_validation/auth.response.v5.xsd'; 	// auth response
24
    private $url_sv_auth_response_soap_xml 			= 'https://secure.plationline.ro/xml_validation/auth.soap.response.v5.xsd';// auth response soap
0 ignored issues
show
introduced by
The private property $url_sv_auth_response_soap_xml is not used, and could be removed.
Loading history...
25
    private $url_sv_itsn_xml 						= 'https://secure.plationline.ro/xml_validation/itsn.v5.xsd'; 				// itsn
26
    private $url_sv_query_xml 						= 'https://secure.plationline.ro/xml_validation/f_message.query.v5.xsd'; 	// query
27
    private $url_sv_itsn_response_xml 				= 'https://secure.plationline.ro/xml_validation/query.response.v5.xsd'; 	// query response
28
    private $url_sv_query_by_date_xml 				= 'https://secure.plationline.ro/xml_validation/f_message.query-by-date.v5.xsd'; 	// query by date
29
    private $url_sv_query_by_date_response_xml 		= 'https://secure.plationline.ro/xml_validation/query-by-date.response.v5.xsd'; 	// query response
30
    private $url_sv_settle_xml 						= 'https://secure.plationline.ro/xml_validation/f_message.settle.v5.xsd'; 	// settle
31
    private $url_sv_settle_response_xml 			= 'https://secure.plationline.ro/xml_validation/settle.response.v5.xsd'; 	// settle response
32
    private $url_sv_void_xml 						= 'https://secure.plationline.ro/xml_validation/f_message.void.v5.xsd'; 	// void
33
    private $url_sv_void_response_xml 				= 'https://secure.plationline.ro/xml_validation/void.response.v5.xsd'; 	// void response
34
    private $url_sv_refund_xml 						= 'https://secure.plationline.ro/xml_validation/f_message.refund.v5.xsd'; 	// refund
35
    private $url_sv_refund_response_xml 			= 'https://secure.plationline.ro/xml_validation/refund.response.v5.xsd'; 	// refund response
36
    private $url_sv_paylink_xml 					= 'https://secure.plationline.ro/xml_validation/v5/f_message.paylink.xsd'; 	// paylink
37
    private $url_sv_paylink_response_xml 			= 'https://secure.plationline.ro/xml_validation/v5/pay.link.by.trxid.url.response.xsd'; 	// paylink response
38
39
    // public
40
    public $f_login 	= null;
41
    public $version		= null;
42
    public $test_mode   = 0;
43
44
    public function __construct()
45
    {
46
        $this->version = "PO 5.1.0 XML";
47
        $errors = array();
48
        $php_min_version = '5.5';
49
        $curl_min_version = '7.29.0';
50
        $openssl_min_version = 0x1000100f; //1.0.1
51
        if (version_compare(phpversion(), $php_min_version, '<')) {
52
            $errors[] = 'PHP version ' . $php_min_version . ' is needed to use PlatiOnline kit. Current PHP version: ' . phpversion();
53
        }
54
        if (!extension_loaded('soap')) {
55
            $errors[] = 'SOAP extension active is needed to use PlatiOnline kit. The SOAP extension is currently DISABLED!';
56
        }
57
        if (!extension_loaded('curl')) {
58
            $errors[] = 'cURL extension active is needed to use PlatiOnline kit. The cURL extension is currently DISABLED!';
59
        }
60
        if (version_compare(curl_version()['version'], $curl_min_version, '<')) {
61
            $errors[] = 'cURL version ' . $curl_min_version . ' is needed to use PlatiOnline kit. The cURL version is currently ' . curl_version()['version'] . '!';
62
        }
63
        if (OPENSSL_VERSION_NUMBER < $openssl_min_version) {
64
            $errors[] = 'OpenSSL version 1.0.1 is needed to use PlatiOnline kit.';
65
        }
66
        if (!empty($errors)) { //daca avem erori
67
            $errors_string = '';
68
            foreach ($errors as $error) {
69
                $errors_string .= $error . "; ";
70
            }
71
            throw new \Exception($errors_string . 'Please fix the above mentioned errors to use this PlatiOnline kit');
72
        }
73
        self::registerAutoload('phpseclib');
74
        self::registerAutoload('Curl');
75
    }
76
77
    //////////////////////////////////////////////////////////////
78
    // 						PUBLIC METHODS						//
79
    //////////////////////////////////////////////////////////////
80
81
    // setez cheia RSA pentru criptare
82
    public function setRSAKeyEncrypt($rsa_key_enc)
83
    {
84
        $this->rsa_key_enc = $rsa_key_enc;
85
    }
86
87
    // setez cheia RSA pentru decriptare
88
    public function setRSAKeyDecrypt($rsa_key_dec)
89
    {
90
        $this->rsa_key_dec = $rsa_key_dec;
91
    }
92
93
    // setez initial vector
94
    public function setIV($iv)
95
    {
96
        $this->iv = $iv;
97
    }
98
99
    // setez initial vector ITSN
100
    public function setIVITSN($iv)
101
    {
102
        $this->iv_itsn = $iv;
103
    }
104
105
    public function paylink($f_request, $f_action = 21)
106
    {
107
        // ne asiguram ca stergem tot ce e in campul f_request
108
        $this->f_request = null;
109
        $f_request['f_action'] = $f_action;
110
        $request = $this->setFRequest($f_request, 'po_payment_link_by_trxid', $this->url_sv_paylink_xml);
111
112
        $opts = array(
113
            'http' => array(
114
                'user_agent' => 'PlatiOnline-SOAP'
115
            )
116
        );
117
        $context = stream_context_create($opts);
118
        $client  = new \SoapClient(null, array(
119
            'location' 		 => $this->url,
120
            'uri'      		 => 'pay-link-by-trxid',
121
            'stream_context' => $context
122
        ));
123
124
        $response = $client->__doRequest($request, $this->url, 'pay-link-by-trxid', 1);
125
126
        if (empty($response)) {
127
            throw new \Exception('ERROR: Nu am putut comunica cu serverul PO pentru operatiunea de autorizare!');
128
        }
129
        $this->validate_xml($response, $this->url_sv_paylink_response_xml);
130
        $paylink_response = $this->xml_to_object($response);
131
132
        if ($this->get_xml_tag_content($paylink_response, 'PO_ERROR_CODE') == 1) {
133
            throw new \Exception($this->get_xml_tag_content($paylink_response, 'PO_ERROR_REASON'));
0 ignored issues
show
Bug introduced by
It seems like $this->get_xml_tag_conte...nse, 'PO_ERROR_REASON') can also be of type false; however, parameter $message of Exception::__construct() 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

133
            throw new \Exception(/** @scrutinizer ignore-type */ $this->get_xml_tag_content($paylink_response, 'PO_ERROR_REASON'));
Loading history...
134
        } else {
135
            $redirect_url 	= $this->get_xml_tag_content($paylink_response, 'PO_REDIRECT_URL');
136
            $X_TRANS_ID 	= $this->get_xml_tag_content($paylink_response, 'X_TRANS_ID');
0 ignored issues
show
Unused Code introduced by
The assignment to $X_TRANS_ID is dead and can be removed.
Loading history...
137
            if (!empty($redirect_url)) {
138
                header('Location: ' . $redirect_url);
139
            } else {
140
                throw new \Exception('ERROR: Serverul nu a intors URL-ul pentru a finaliza tranzactia!');
141
            }
142
        }
143
    }
144
145
    public function auth($f_request, $f_action = 2)
146
    {
147
        // ne asiguram ca stergem tot ce e in campul f_request
148
        $this->f_request = null;
149
        $f_request['f_action'] = $f_action;
150
        $request = $this->setFRequest($f_request, 'po_auth_request', $this->url_sv_auth_xml);
151
152
        $opts = array(
153
            'http' => array(
154
                'user_agent' => 'PlatiOnline-SOAP'
155
            )
156
        );
157
        $context = stream_context_create($opts);
158
        $client  = new \SoapClient(null, array(
159
            'location' 		 => $this->url,
160
            'uri'      		 => 'auth-only',
161
            'stream_context' => $context
162
        ));
163
164
        $response = $client->__doRequest($request, $this->url, 'auth-only', 1);
165
        if (empty($response)) {
166
            throw new \Exception('ERROR: Nu am putut comunica cu serverul PO pentru operatiunea de autorizare!');
167
        }
168
169
        $this->validate_xml($response, $this->url_sv_auth_url_xml);
170
        $auth_response = $this->xml_to_object($response);
171
        if ($this->get_xml_tag_content($auth_response, 'PO_ERROR_CODE') == 1) {
172
            throw new \Exception($this->get_xml_tag_content($auth_response, 'PO_ERROR_REASON'));
0 ignored issues
show
Bug introduced by
It seems like $this->get_xml_tag_conte...nse, 'PO_ERROR_REASON') can also be of type false; however, parameter $message of Exception::__construct() 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

172
            throw new \Exception(/** @scrutinizer ignore-type */ $this->get_xml_tag_content($auth_response, 'PO_ERROR_REASON'));
Loading history...
173
        } else {
174
            $redirect_url = $this->get_xml_tag_content($auth_response, 'PO_REDIRECT_URL');
175
            if (!empty($redirect_url)) {
176
                header('Location: ' . $redirect_url);
177
            } else {
178
                throw new \Exception('ERROR: Serverul nu a intors URL-ul pentru a finaliza tranzactia!');
179
            }
180
        }
181
    }
182
183
    // obtin raspunsul pentru cererea de autorizare
184
    public function auth_response($f_relay_message, $f_crypt_message)
185
    {
186
        return $this->decrypt_response($f_relay_message, $f_crypt_message, $this->url_sv_auth_response_xml);
187
    }
188
189
    // obtin datele din notificarea ITSN
190
    public function itsn($f_relay_message, $f_crypt_message)
191
    {
192
        return $this->decrypt_response($f_relay_message, $f_crypt_message, $this->url_sv_itsn_xml);
193
    }
194
195
    // interogare
196
    public function query($f_request, $f_action = 0)
197
    {
198
        // ne asiguram ca stergem tot ce e in campul f_request
199
        $this->f_request = null;
200
        $f_request['f_action'] = $f_action;
201
        $request = $this->setFRequest($f_request, 'po_query', $this->url_sv_query_xml);
202
203
        $opts = array(
204
            'http' => array(
205
                'user_agent' => 'PlatiOnline-SOAP'
206
            )
207
        );
208
        $context = stream_context_create($opts);
209
        $client  = new \SoapClient(null, array(
210
            'location' 		 => $this->url,
211
            'uri'      		 => 'query',
212
            'stream_context' => $context
213
        ));
214
        $response = $client->__doRequest($request, $this->url, 'query', 1);
215
216
        if (empty($response)) {
217
            throw new \Exception('ERROR: Nu am putut comunica cu serverul PO pentru operatiunea de interogare!');
218
        }
219
        // validez xml-ul primit ca raspuns de la PO
220
        $this->validate_xml($response, $this->url_sv_itsn_response_xml);
221
        return $this->xml_to_object($response);
222
    }
223
224
    public function query_by_date($f_request, $f_action = 0)
225
    {
226
        // ne asiguram ca stergem tot ce e in campul f_request
227
        $this->f_request = null;
228
        $f_request['f_action'] = $f_action;
229
        $request = $this->setFRequest($f_request, 'po_query', $this->url_sv_query_by_date_xml);
230
231
        $opts = array(
232
            'http' => array(
233
                'user_agent' => 'PlatiOnline-SOAP'
234
            )
235
        );
236
        $context = stream_context_create($opts);
237
        $client  = new \SoapClient(null, array(
238
            'location' 		 => $this->url,
239
            'uri'      		 => 'query-by-date',
240
            'stream_context' => $context
241
        ));
242
        $response = $client->__doRequest($request, $this->url, 'query-by-date', 1);
243
        if (empty($response)) {
244
            throw new \Exception('ERROR: Nu am putut comunica cu serverul PO pentru operatiunea de interogare!');
245
        }
246
        // validez xml-ul primit ca raspuns de la PO
247
        $this->validate_xml($response, $this->url_sv_query_by_date_response_xml);
248
        return $this->xml_to_object($response);
249
    }
250
251
    public function settle($f_request, $f_action = 3)
252
    {
253
        // ne asiguram ca stergem tot ce e in campul f_request
254
        $this->f_request = null;
255
        $f_request['f_action'] = $f_action;
256
        $request = $this->setFRequest($f_request, 'po_settle', $this->url_sv_settle_xml);
257
258
        $opts = array(
259
            'http' => array(
260
                'user_agent' => 'PlatiOnline-SOAP'
261
            )
262
        );
263
        $context = stream_context_create($opts);
264
        $client  = new \SoapClient(null, array(
265
            'location' 		 => $this->url,
266
            'uri'      		 => 'settle',
267
            'stream_context' => $context
268
        ));
269
        $response = $client->__doRequest($request, $this->url, 'settle', 1);
270
271
        if (empty($response)) {
272
            throw new \Exception('ERROR: Nu am putut comunica cu serverul PO pentru operatiunea de incasare!');
273
        }
274
275
        // validez xml-ul primit ca raspuns de la PO
276
        $this->validate_xml($response, $this->url_sv_settle_response_xml);
277
278
        return $this->xml_to_object($response);
279
    }
280
281
    public function void($f_request, $f_action = 7)
282
    {
283
        // ne asiguram ca stergem tot ce e in campul f_request
284
        $this->f_request = null;
285
        $f_request['f_action'] = $f_action;
286
        $request = $this->setFRequest($f_request, 'po_void', $this->url_sv_void_xml);
287
288
        $opts = array(
289
            'http' => array(
290
                'user_agent' => 'PlatiOnline-SOAP'
291
            )
292
        );
293
        $context = stream_context_create($opts);
294
        $client  = new \SoapClient(null, array(
295
            'location' 		 => $this->url,
296
            'uri'      		 => 'void',
297
            'stream_context' => $context
298
        ));
299
300
        $response = $client->__doRequest($request, $this->url, 'void', 1);
301
302
        if (empty($response)) {
303
            throw new \Exception('ERROR: Nu am putut comunica cu serverul PO pentru operatiunea de anulare!');
304
        }
305
306
        // validez xml-ul primit ca raspuns de la PO
307
        $this->validate_xml($response, $this->url_sv_void_response_xml);
308
309
        return $this->xml_to_object($response);
310
    }
311
312
    public function refund($f_request, $f_action = 1)
313
    {
314
        // ne asiguram ca stergem tot ce e in campul f_request
315
        $this->f_request = null;
316
        $f_request['f_action'] = $f_action;
317
        $request = $this->setFRequest($f_request, 'po_refund', $this->url_sv_refund_xml);
318
319
        $opts = array(
320
            'http' => array(
321
                'user_agent' => 'PlatiOnline-SOAP'
322
            )
323
        );
324
        $context = stream_context_create($opts);
325
        $client  = new \SoapClient(null, array(
326
            'location' 		 => $this->url,
327
            'uri'      		 => 'refund',
328
            'stream_context' => $context
329
        ));
330
        $response = $client->__doRequest($request, $this->url, 'refund', 1);
331
332
        if (empty($response)) {
333
            throw new \Exception('ERROR: Nu am putut comunica cu serverul PO pentru operatiunea de creditare!');
334
        }
335
336
        // validez xml-ul primit ca raspuns de la PO
337
        $this->validate_xml($response, $this->url_sv_refund_response_xml);
338
339
        return $this->xml_to_object($response);
340
    }
341
342
    public function get_xml_tag($object, $tag)
343
    {
344
        $children = $object->children;
345
        foreach ($children as $child) {
346
            if (trim(strtoupper($child->name)) == trim(strtoupper($tag))) {
347
                return $child;
348
            }
349
        }
350
        return false;
351
    }
352
353
    public function get_xml_tag_content($object, $tag)
354
    {
355
        $children = $object->children;
356
        foreach ($children as $child) {
357
            if (trim(strtoupper($child->name)) == trim(strtoupper($tag))) {
358
                return $child->content;
359
            }
360
        }
361
        return false;
362
    }
363
364
    public function parse_soap_response($soap)
365
    {
366
        return $this->xml_to_object($soap);
367
    }
368
369
    //////////////////////////////////////////////////////////////
370
    // 					END PUBLIC METHODS						//
371
    //////////////////////////////////////////////////////////////
372
373
    //////////////////////////////////////////////////////////////
374
    // 					  PRIVATE METHODS						//
375
    //////////////////////////////////////////////////////////////
376
377
    private static function registerAutoload($classname)
0 ignored issues
show
Unused Code introduced by
The parameter $classname 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

377
    private static function registerAutoload(/** @scrutinizer ignore-unused */ $classname)

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...
378
    {
379
        spl_autoload_extensions('.php'); // Only Autoload PHP Files
380
        spl_autoload_register(function ($classname) {
381
            if (strpos($classname, '\\') !== false) {
382
                // Namespaced Classes
383
                $classfile = str_replace('\\', '/', $classname);
384
                if ($classname[0] !== '/') {
385
                    $classfile = dirname(__FILE__) . '/libraries/' . $classfile . '.php';
386
                }
387
                require($classfile);
388
            }
389
        });
390
    }
391
392
    // criptez f_request cu AES
393
    private function AESEnc()
394
    {
395
        $this->aes_key 		= substr(hash('sha256', uniqid(), 0), 0, 32);
396
        $aes 				= new AES();
397
        $aes->setIV($this->iv);
398
        $aes->setKey($this->aes_key);
399
        $this->f_request 	= bin2hex(base64_encode($aes->encrypt($this->f_request)));
400
    }
401
402
    // criptez cheia AES cu RSA
403
    private function RSAEnc()
404
    {
405
        $rsa = new RSA();
406
        $rsa->loadKey($this->rsa_key_enc);
407
        $rsa->setEncryptionMode(RSA::ENCRYPTION_PKCS1);
408
        $this->f_secure = base64_encode($rsa->encrypt($this->aes_key));
409
    }
410
411
    // setez f_request, criptez f_request cu AES si cheia AES cu RSA
412
    private function setFRequest($f_request, $type, $validation_url)
413
    {
414
        // aici construiesc XML din array
415
        $xml = new \SimpleXMLElement('<' . $type . '/>');
416
417
        // test mode
418
        if ($type == 'po_auth_request') {
419
            if ($this->test_mode == 0) {
420
                $f_request['f_test_request'] = 0;
421
            } else {
422
                $f_request['f_test_request'] = 1;
423
            }
424
425
            $f_request['f_sequence']  = rand(1, 1000);
426
            $f_request['f_customer_ip'] = $_SERVER['REMOTE_ADDR'];
427
        }
428
429
        $f_request['f_timestamp'] = date('Y-m-d\TH:i:sP');
430
        // set f_login
431
        $f_request['f_login'] = $this->f_login;
432
433
        // sortez parametrii alfabetic
434
        ksort($f_request);
435
436
        $this->array2xml($f_request, $xml);
437
        $this->f_request = $xml->asXML();
438
439
        // validez XML conform schemei (parametrul 2)
440
        $this->validate_xml($this->f_request, $validation_url);
441
442
        $this->AESEnc();
443
        $this->RSAEnc();
444
445
        $request 						= array();
446
447
        $request['f_login'] 			= $this->f_login;
448
        $request['f_message']			= $this->f_request;
449
        $request['f_crypt_message ']	= $this->f_secure;
450
451
        $xml_auth_soap = new \SimpleXMLElement('<po_request/>');
452
        $this->array2xml($request, $xml_auth_soap);
453
        $xml_auth_soap = $xml_auth_soap->asXML();
454
        $this->validate_xml($xml_auth_soap, $this->url_sv_request_xml);
455
        return $xml_auth_soap;
456
    }
457
458
    // function definition to convert array to xml
459
    private function array2xml($arr, &$xml_arr)
460
    {
461
        foreach ($arr as $key => $value) {
462
            if (is_array($value)) {
463
                if (!is_numeric($key)) {
464
                    if (strpos($key, 'coupon') !== false) {
465
                        $subnode = $xml_arr->addChild("coupon");
466
                    } else {
467
                        $subnode = $xml_arr->addChild("$key");
468
                    }
469
                    $this->array2xml($value, $subnode);
470
                } else {
471
                    $subnode = $xml_arr->addChild("item");
472
                    $this->array2xml($value, $subnode);
473
                }
474
            } else {
475
                $xml_arr->addChild("$key", htmlspecialchars("$value"));
476
            }
477
        }
478
    }
479
480
    private function validate_xml($poxml, $url)
481
    {
482
        libxml_use_internal_errors(true);
483
        $xml = new \DOMDocument();
484
        $xml->loadXML($poxml);
485
        $request = new Curl\Get($url);
486
        $request->send();
487
        if ($request->getStatus() != 200) {
488
            throw new \Exception('Nu am putut obtine schema de validare de la PlatiOnline');
489
        }
490
        $schemaPO5 = $request->getResponse();
491
        if (!$xml->schemaValidateSource($schemaPO5)) {
492
            $errors = libxml_get_errors();
493
            $finalmsg   =   array();
0 ignored issues
show
Unused Code introduced by
The assignment to $finalmsg is dead and can be removed.
Loading history...
494
            foreach ($errors as $error) {
495
                throw new \Exception($this->libxml_display_error($error));
496
            }
497
            libxml_clear_errors();
498
            throw new \Exception('INVALID XML');
499
        }
500
    }
501
502
    private function decrypt_response($f_relay_message, $f_crypt_message, $validation_url)
503
    {
504
        if (empty($f_relay_message)) {
505
            throw new \Exception('Decriptare raspuns - nu se primeste [criptul AES]');
506
        }
507
        if (empty($f_crypt_message)) {
508
            throw new \Exception('Decriptare raspuns - nu se primeste [criptul RSA]');
509
        }
510
        $rsa = new RSA();
511
        $rsa->loadKey($this->rsa_key_dec);
512
        $rsa->setEncryptionMode(RSA::ENCRYPTION_PKCS1);
513
        $aes_key = $rsa->decrypt(base64_decode($f_crypt_message));
514
        if (empty($aes_key)) {
515
            throw new \Exception('Nu am putut decripta cheia AES din RSA');
516
        }
517
        $aes = new AES();
518
        $aes->setIV($this->iv_itsn);
519
        $aes->setKey($aes_key);
520
        $response = $aes->decrypt(base64_decode($this->hex2str($f_relay_message)));
521
        if (empty($response)) {
522
            throw new \Exception('Nu am putut decripta mesajul din criptul AES');
523
        }
524
        $this->validate_xml($response, $validation_url);
525
        return $this->xml_to_object($response);
526
    }
527
528
    private function libxml_display_error($error)
529
    {
530
        $return = "\n";
531
        switch ($error->level) {
532
            case LIBXML_ERR_WARNING:
533
                $return .= "Warning $error->code: ";
534
                break;
535
            case LIBXML_ERR_ERROR:
536
                $return .= "Error $error->code: ";
537
                break;
538
            case LIBXML_ERR_FATAL:
539
                $return .= "Fatal Error $error->code: ";
540
                break;
541
        }
542
        $return .= trim($error->message);
543
        $return .= "\n";
544
        return $return;
545
    }
546
547
    private function hex2str($hex)
548
    {
549
        $str = '';
550
        for ($i=0;$i<strlen($hex);$i+=2) {
551
            $str .= chr(hexdec(substr($hex, $i, 2)));
0 ignored issues
show
Bug introduced by
It seems like hexdec(substr($hex, $i, 2)) can also be of type double; however, parameter $ascii of chr() does only seem to accept integer, 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

551
            $str .= chr(/** @scrutinizer ignore-type */ hexdec(substr($hex, $i, 2)));
Loading history...
552
        }
553
        return $str;
554
    }
555
556
    private function xml_to_object($xml)
557
    {
558
        $parser = xml_parser_create();
559
        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
560
        xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
561
        xml_parse_into_struct($parser, $xml, $tags);
562
        xml_parser_free($parser);
563
564
        $elements = array();  // the currently filling [child] XmlElement array
565
        $stack = array();
566
        foreach ($tags as $tag) {
567
            $index = count($elements);
568
            if ($tag['type'] == "complete" || $tag['type'] == "open") {
569
                $elements[$index] = new XmlElement();
570
                $elements[$index]->name = isset($tag['tag']) ? $tag['tag'] : '';
571
                $elements[$index]->attributes = isset($tag['attributes']) ? $tag['attributes'] : '';
572
                $elements[$index]->content = isset($tag['value']) ? $tag['value'] : '';
573
                if ($tag['type'] == "open") {  // push
574
                    $elements[$index]->children = array();
575
                    $stack[count($stack)] = &$elements;
576
                    $elements = &$elements[$index]->children;
577
                }
578
            }
579
            if ($tag['type'] == "close") {  // pop
580
                $elements = &$stack[count($stack) - 1];
581
                unset($stack[count($stack) - 1]);
582
            }
583
        }
584
        return $elements[0];  // the single top-level element
585
    }
586
587
    //////////////////////////////////////////////////////////////
588
    // 					  END PRIVATE METHODS					//
589
    //////////////////////////////////////////////////////////////
590
}
591
592
class XmlElement
593
{
594
    public $name;
595
    public $attributes;
596
    public $content;
597
    public $children;
598
};
599