Completed
Push — master ( 5239a1...572556 )
by Alexei
9s
created

Client::setVerifyResponse()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
// Copyright 1999-2016. Parallels IP Holdings GmbH.
3
4
namespace PleskX\Api;
5
use SimpleXMLElement;
6
7
/**
8
 * Client for Plesk XML-RPC API
9
 */
10
class Client
11
{
12
    const RESPONSE_SHORT = 1;
13
    const RESPONSE_FULL = 2;
14
15
    protected $_host;
16
    protected $_port;
17
    protected $_protocol;
18
    protected $_login;
19
    protected $_password;
20
    protected $_secretKey;
21
    protected $_version = '';
22
23
    protected static $_isExecutionsLogEnabled = false;
24
    protected static $_executionLog = [];
25
26
    protected $_operatorsCache = [];
27
28
    /**
29
     * @var callable
30
     */
31
    protected $_verifyResponseCallback;
32
33
    /**
34
     * Create client
35
     *
36
     * @param string $host
37
     * @param int $port
38
     * @param string $protocol
39
     */
40
    public function __construct($host, $port = 8443, $protocol = 'https')
41
    {
42
        $this->_host = $host;
43
        $this->_port = $port;
44
        $this->_protocol = $protocol;
45
    }
46
47
    /**
48
     * Setup credentials for authentication
49
     *
50
     * @param string $login
51
     * @param string $password
52
     */
53
    public function setCredentials($login, $password)
54
    {
55
        $this->_login = $login;
56
        $this->_password = $password;
57
    }
58
59
    /**
60
     * Define secret key for alternative authentication
61
     *
62
     * @param string $secretKey
63
     */
64
    public function setSecretKey($secretKey)
65
    {
66
        $this->_secretKey = $secretKey;
67
    }
68
69
    /**
70
     * Set default version for requests
71
     *
72
     * @param string $version
73
     */
74
    public function setVersion($version)
75
    {
76
        $this->_version = $version;
77
    }
78
79
    /**
80
     * Set custom function to verify response of API call according your own needs. Default verifying will be used if it is not specified
81
     *
82
     * @param callable|null $function
83
     */
84
    public function setVerifyResponse(callable $function = null)
85
    {
86
        $this->_verifyResponseCallback = $function;
87
    }
88
89
    /**
90
     * Retrieve host used for communication
91
     *
92
     * @return string
93
     */
94
    public function getHost()
95
    {
96
        return $this->_host;
97
    }
98
99
    /**
100
     * Retrieve port used for communication
101
     *
102
     * @return int
103
     */
104
    public function getPort()
105
    {
106
        return $this->_port;
107
    }
108
109
    /**
110
     * Retrieve name of the protocol (http or https) used for communication
111
     *
112
     * @return string
113
     */
114
    public function getProtocol()
115
    {
116
        return $this->_protocol;
117
    }
118
119
    /**
120
     * Retrieve XML template for packet
121
     *
122
     * @param string|null $version
123
     * @return SimpleXMLElement
124
     */
125
    public function getPacket($version = null)
126
    {
127
        $protocolVersion = !is_null($version) ? $version : $this->_version;
128
        $content = "<?xml version='1.0' encoding='UTF-8' ?>";
129
        $content .= "<packet" . ("" === $protocolVersion ? "" : " version='$protocolVersion'") . "/>";
130
        return new SimpleXMLElement($content);
131
    }
132
133
    /**
134
     * Perform API request
135
     *
136
     * @param string|array|SimpleXMLElement $request
137
     * @param int $mode
138
     * @return XmlResponse
139
     */
140
    public function request($request, $mode = self::RESPONSE_SHORT)
141
    {
142
        if ($request instanceof SimpleXMLElement) {
143
            $request = $request->asXml();
144
        } else {
145
            $xml = $this->getPacket();
146
147
            if (is_array($request)) {
148
                $request = $this->_arrayToXml($request, $xml)->asXML();
149
            } else if (preg_match('/^[a-z]/', $request)) {
150
                $request = $this->_expandRequestShortSyntax($request, $xml);
151
            }
152
        }
153
154
        if ('sdk' == $this->_protocol) {
155
            $version = ('' == $this->_version) ? null : $this->_version;
156
            $requestXml = new SimpleXMLElement((string)$request);
157
            $xml = \pm_ApiRpc::getService($version)->call($requestXml->children()[0]->asXml(), $this->_login);
158
        } else {
159
            $xml = $this->_performHttpRequest($request);
0 ignored issues
show
Security Bug introduced by
It seems like $request can also be of type false; however, PleskX\Api\Client::_performHttpRequest() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
160
        }
161
162
        $this->_verifyResponseCallback
163
            ? call_user_func($this->_verifyResponseCallback, $xml)
164
            : $this->_verifyResponse($xml);
165
166
        return (self::RESPONSE_FULL == $mode) ? $xml : $xml->xpath('//result')[0];
167
    }
168
169
    /**
170
     * Perform HTTP request to end-point
171
     *
172
     * @param string $request
173
     * @return XmlResponse
174
     * @throws Client\Exception
175
     */
176
    private function _performHttpRequest($request)
177
    {
178
        $curl = curl_init();
179
180
        curl_setopt($curl, CURLOPT_URL, "$this->_protocol://$this->_host:$this->_port/enterprise/control/agent.php");
181
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
182
        curl_setopt($curl, CURLOPT_POST, true);
183
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
184
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
185
        curl_setopt($curl, CURLOPT_HTTPHEADER, $this->_getHeaders());
186
        curl_setopt($curl, CURLOPT_POSTFIELDS, $request);
187
188
        $result = curl_exec($curl);
189
190
        if (false === $result) {
191
            throw new Client\Exception(curl_error($curl), curl_errno($curl));
192
        }
193
194
        if (self::$_isExecutionsLogEnabled) {
195
            self::$_executionLog[] = [
196
                'trace' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS),
197
                'request' => $request,
198
                'response' => $result,
199
            ];
200
        }
201
202
        curl_close($curl);
203
204
        $xml = new XmlResponse($result);
205
        return $xml;
206
    }
207
208
    /**
209
     * Perform multiple API requests using single HTTP request
210
     *
211
     * @param $requests
212
     * @param int $mode
213
     * @return array
214
     * @throws Client\Exception
215
     */
216
    public function multiRequest($requests, $mode = self::RESPONSE_SHORT)
217
    {
218
219
        $requestXml = $this->getPacket();
220
221
        foreach ($requests as $request) {
222
            if ($request instanceof SimpleXMLElement) {
223
                throw new Client\Exception('SimpleXML type of request is not supported for multi requests.');
224
            } else {
225
                if (is_array($request)) {
226
                    $request = $this->_arrayToXml($request, $requestXml)->asXML();
227
                } else if (preg_match('/^[a-z]/', $request)) {
228
                    $this->_expandRequestShortSyntax($request, $requestXml);
229
                }
230
            }
231
            $responses[] = $this->request($request);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$responses was never initialized. Although not strictly required by PHP, it is generally a good practice to add $responses = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
232
        }
233
234
        if ('sdk' == $this->_protocol) {
235
            throw new Client\Exception('Multi requests are not supported via SDK.');
236
        } else {
237
            $responseXml = $this->_performHttpRequest($requestXml->asXML());
0 ignored issues
show
Security Bug introduced by
It seems like $requestXml->asXML() targeting SimpleXMLElement::asXML() can also be of type false; however, PleskX\Api\Client::_performHttpRequest() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
238
        }
239
240
        $responses = [];
241
        foreach ($responseXml->children() as $childNode) {
242
            $xml = $this->getPacket();
243
            $dom = dom_import_simplexml($xml)->ownerDocument;
244
245
            $childDomNode = dom_import_simplexml($childNode);
246
            $childDomNode = $dom->importNode($childDomNode, true);
247
            $dom->documentElement->appendChild($childDomNode);
248
249
            $response = simplexml_load_string($dom->saveXML());
250
            $responses[] = (self::RESPONSE_FULL == $mode) ? $response : $response->xpath('//result')[0];
251
        }
252
253
        return $responses;
254
    }
255
256
    /**
257
     * Retrieve list of headers needed for request
258
     *
259
     * @return array
260
     */
261
    protected function _getHeaders()
262
    {
263
        $headers = array(
264
            "Content-Type: text/xml",
265
            "HTTP_PRETTY_PRINT: TRUE",
266
        );
267
268
        if ($this->_secretKey) {
269
            $headers[] = "KEY: $this->_secretKey";
270
        } else {
271
            $headers[] = "HTTP_AUTH_LOGIN: $this->_login";
272
            $headers[] = "HTTP_AUTH_PASSWD: $this->_password";
273
        }
274
275
        return $headers;
276
    }
277
278
    /**
279
     * Enable or disable execution log
280
     *
281
     * @param bool $enable
282
     */
283
    public static function enableExecutionLog($enable = true)
284
    {
285
        self::$_isExecutionsLogEnabled = $enable;
286
    }
287
288
    /**
289
     * Retrieve execution log
290
     *
291
     * @return array
292
     */
293
    public static function getExecutionLog()
294
    {
295
        return self::$_executionLog;
296
    }
297
298
    /**
299
     * Verify that response does not contain errors
300
     *
301
     * @param XmlResponse $xml
302
     * @throws Exception
303
     */
304
    protected function _verifyResponse($xml)
305
    {
306
        if ($xml->system && $xml->system->status && 'error' == (string)$xml->system->status) {
307
            throw new Exception((string)$xml->system->errtext, (int)$xml->system->errcode);
308
        }
309
310
        if ($xml->xpath('//status[text()="error"]') && $xml->xpath('//errcode') && $xml->xpath('//errtext')) {
311
            $errorCode = (int)$xml->xpath('//errcode')[0];
312
            $errorMessage = (string)$xml->xpath('//errtext')[0];
313
            throw new Exception($errorMessage, $errorCode);
314
        }
315
    }
316
317
    /**
318
     * Expand short syntax (some.method.call) into full XML representation
319
     *
320
     * @param string $request
321
     * @param SimpleXMLElement $xml
322
     * @return string
323
     */
324
    protected function _expandRequestShortSyntax($request, SimpleXMLElement $xml)
325
    {
326
        $parts = explode('.', $request);
327
        $node = $xml;
328
329
        foreach ($parts as $part) {
330
            @list($name, $value) = explode('=', $part);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
331
            $node = $node->addChild($name, $value);
332
        }
333
334
        return $xml->asXML();
335
    }
336
337
    /**
338
     * Convert array to XML representation
339
     *
340
     * @param array $array
341
     * @param SimpleXMLElement $xml
342
     * @return SimpleXMLElement
343
     */
344
    protected function _arrayToXml(array $array, SimpleXMLElement $xml)
345
    {
346
        foreach ($array as $key => $value) {
347
            if (is_array($value)) {
348
                $this->_arrayToXml($value, $xml->addChild($key));
349
            } else {
350
                $xml->addChild($key, $value);
351
            }
352
        }
353
354
        return $xml;
355
    }
356
357
    /**
358
     * @param string $name
359
     * @return \PleskX\Api\Operator
360
     */
361
    protected function _getOperator($name)
362
    {
363
        if (!isset($this->_operatorsCache[$name])) {
364
            $className = '\\PleskX\\Api\\Operator\\' . $name;
365
            $this->_operatorsCache[$name] = new $className($this);
366
        }
367
368
        return $this->_operatorsCache[$name];
369
    }
370
371
    /**
372
     * @return Operator\Server
373
     */
374
    public function server()
375
    {
376
        return $this->_getOperator('Server');
377
    }
378
379
    /**
380
     * @return Operator\Customer
381
     */
382
    public function customer()
383
    {
384
        return $this->_getOperator('Customer');
385
    }
386
387
    /**
388
     * @return Operator\Webspace
389
     */
390
    public function webspace()
391
    {
392
        return $this->_getOperator('Webspace');
393
    }
394
395
    /**
396
     * @return Operator\Subdomain
397
     */
398
    public function subdomain()
399
    {
400
        return $this->_getOperator('Subdomain');
401
    }
402
403
    /**
404
     * @return Operator\Dns
405
     */
406
    public function dns()
407
    {
408
        return $this->_getOperator('Dns');
409
    }
410
411
    /**
412
     * @return Operator\DatabaseServer
413
     */
414
    public function databaseServer()
415
    {
416
        return $this->_getOperator('DatabaseServer');
417
    }
418
419
    /**
420
     * @return Operator\Mail
421
     */
422
    public function mail()
423
    {
424
        return $this->_getOperator('Mail');
425
    }
426
427
    /**
428
     * @return Operator\Migration
429
     */
430
    public function migration()
431
    {
432
        return $this->_getOperator('Migration');
433
    }
434
435
    /**
436
     * @return Operator\Certificate
437
     */
438
    public function certificate()
439
    {
440
        return $this->_getOperator('Certificate');
441
    }
442
443
    /**
444
     * @return Operator\SiteAlias
445
     */
446
    public function siteAlias()
447
    {
448
        return $this->_getOperator('SiteAlias');
449
    }
450
451
    /**
452
     * @return Operator\Ip
453
     */
454
    public function ip()
455
    {
456
        return $this->_getOperator('Ip');
457
    }
458
459
    /**
460
     * @return Operator\EventLog
461
     */
462
    public function eventLog()
463
    {
464
        return $this->_getOperator('EventLog');
465
    }
466
467
    /**
468
     * @return Operator\SpamFilter
469
     */
470
    public function spamFilter()
471
    {
472
        return $this->_getOperator('SpamFilter');
473
    }
474
475
    /**
476
     * @return Operator\SecretKey
477
     */
478
    public function secretKey()
479
    {
480
        return $this->_getOperator('SecretKey');
481
    }
482
483
    /**
484
     * @return Operator\Ui
485
     */
486
    public function ui()
487
    {
488
        return $this->_getOperator('Ui');
489
    }
490
491
    /**
492
     * @return Operator\ServicePlan
493
     */
494
    public function servicePlan()
495
    {
496
        return $this->_getOperator('ServicePlan');
497
    }
498
499
    /**
500
     * @return Operator\WebUser
501
     */
502
    public function webUser()
503
    {
504
        return $this->_getOperator('WebUser');
505
    }
506
507
    /**
508
     * @return Operator\MailList
509
     */
510
    public function mailList()
511
    {
512
        return $this->_getOperator('MailList');
513
    }
514
515
    /**
516
     * @return Operator\VirtualDirectory
517
     */
518
    public function virtualDirectory()
519
    {
520
        return $this->_getOperator('VirtualDirectory');
521
    }
522
523
    /**
524
     * @return Operator\Database
525
     */
526
    public function database()
527
    {
528
        return $this->_getOperator('Database');
529
    }
530
531
    /**
532
     * @return Operator\FtpUser
533
     */
534
    public function ftpUser()
535
    {
536
        return $this->_getOperator('FtpUser');
537
    }
538
539
    /**
540
     * @return Operator\Session
541
     */
542
    public function session()
543
    {
544
        return $this->_getOperator('Session');
545
    }
546
547
    /**
548
     * @return Operator\Updater
549
     */
550
    public function updater()
551
    {
552
        return $this->_getOperator('Updater');
553
    }
554
555
    /**
556
     * @return Operator\Locale
557
     */
558
    public function locale()
559
    {
560
        return $this->_getOperator('Locale');
561
    }
562
563
    /**
564
     * @return Operator\LogRotation
565
     */
566
    public function logRotation()
567
    {
568
        return $this->_getOperator('LogRotation');
569
    }
570
571
    /**
572
     * @return Operator\BackupManager
573
     */
574
    public function backupManager()
575
    {
576
        return $this->_getOperator('BackupManager');
577
    }
578
579
    /**
580
     * @return Operator\Sso
581
     */
582
    public function sso()
583
    {
584
        return $this->_getOperator('Sso');
585
    }
586
587
    /**
588
     * @return Operator\ProtectedDirectory
589
     */
590
    public function protectedDirectory()
591
    {
592
        return $this->_getOperator('ProtectedDirectory');
593
    }
594
595
    /**
596
     * @return Operator\Reseller
597
     */
598
    public function reseller()
599
    {
600
        return $this->_getOperator('Reseller');
601
    }
602
603
    /**
604
     * @return Operator\ResellerPlan
605
     */
606
    public function resellerPlan()
607
    {
608
        return $this->_getOperator('ResellerPlan');
609
    }
610
611
    /**
612
     * @return Operator\Aps
613
     */
614
    public function aps()
615
    {
616
        return $this->_getOperator('Aps');
617
    }
618
619
    /**
620
     * @return Operator\ServicePlanAddon
621
     */
622
    public function servicePlanAddon()
623
    {
624
        return $this->_getOperator('ServicePlanAddon');
625
    }
626
627
    /**
628
     * @return Operator\Site
629
     */
630
    public function site()
631
    {
632
        return $this->_getOperator('Site');
633
    }
634
635
    /**
636
     * @return Operator\User
637
     */
638
    public function user()
639
    {
640
        return $this->_getOperator('User');
641
    }
642
643
    /**
644
     * @return Operator\Role
645
     */
646
    public function role()
647
    {
648
        return $this->_getOperator('Role');
649
    }
650
651
    /**
652
     * @return Operator\BusinessLogicUpgrade
653
     */
654
    public function businessLogicUpgrade()
655
    {
656
        return $this->_getOperator('BusinessLogicUpgrade');
657
    }
658
659
    /**
660
     * @return Operator\Webmail
661
     */
662
    public function webmail()
663
    {
664
        return $this->_getOperator('Webmail');
665
    }
666
667
    /**
668
     * @return Operator\PlanItem
669
     */
670
    public function planItem()
671
    {
672
        return $this->_getOperator('PlanItem');
673
    }
674
675
    /**
676
     * @return Operator\Sitebuilder
677
     */
678
    public function sitebuilder()
679
    {
680
        return $this->_getOperator('Sitebuilder');
681
    }
682
683
    /**
684
     * @return Operator\ServiceNode
685
     */
686
    public function serviceNode()
687
    {
688
        return $this->_getOperator('ServiceNode');
689
    }
690
691
    /**
692
     * @return Operator\IpBan
693
     */
694
    public function ipBan()
695
    {
696
        return $this->_getOperator('IpBan');
697
    }
698
699
    /**
700
     * @return Operator\WpInstance
701
     */
702
    public function wpInstance()
703
    {
704
        return $this->_getOperator('WpInstance');
705
    }
706
707
}
708