Completed
Push — master ( ec9861...35e0a4 )
by
unknown
16s queued 10s
created

Client::request()   C

Complexity

Conditions 7
Paths 24

Size

Total Lines 25
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 17
nc 24
nop 2
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
     * Create client
30
     *
31
     * @param string $host
32
     * @param int $port
33
     * @param string $protocol
34
     */
35
    public function __construct($host, $port = 8443, $protocol = 'https')
36
    {
37
        $this->_host = $host;
38
        $this->_port = $port;
39
        $this->_protocol = $protocol;
40
    }
41
42
    /**
43
     * Setup credentials for authentication
44
     *
45
     * @param string $login
46
     * @param string $password
47
     */
48
    public function setCredentials($login, $password)
49
    {
50
        $this->_login = $login;
51
        $this->_password = $password;
52
    }
53
54
    /**
55
     * Define secret key for alternative authentication
56
     *
57
     * @param string $secretKey
58
     */
59
    public function setSecretKey($secretKey)
60
    {
61
        $this->_secretKey = $secretKey;
62
    }
63
64
    /**
65
     * Set default version for requests
66
     *
67
     * @param string $version
68
     */
69
    public function setVersion($version)
70
    {
71
        $this->_version = $version;
72
    }
73
74
    /**
75
     * Retrieve host used for communication
76
     *
77
     * @return string
78
     */
79
    public function getHost()
80
    {
81
        return $this->_host;
82
    }
83
84
    /**
85
     * Retrieve port used for communication
86
     *
87
     * @return int
88
     */
89
    public function getPort()
90
    {
91
        return $this->_port;
92
    }
93
94
    /**
95
     * Retrieve name of the protocol (http or https) used for communication
96
     *
97
     * @return string
98
     */
99
    public function getProtocol()
100
    {
101
        return $this->_protocol;
102
    }
103
104
    /**
105
     * Retrieve XML template for packet
106
     *
107
     * @param string|null $version
108
     * @return SimpleXMLElement
109
     */
110
    public function getPacket($version = null)
111
    {
112
        $protocolVersion = !is_null($version) ? $version : $this->_version;
113
        $content = "<?xml version='1.0' encoding='UTF-8' ?>";
114
        $content .= "<packet" . ("" === $protocolVersion ? "" : " version='$protocolVersion'") . "/>";
115
        return new SimpleXMLElement($content);
116
    }
117
118
    /**
119
     * Perform API request
120
     *
121
     * @param string|array|SimpleXMLElement $request
122
     * @param int $mode
123
     * @return XmlResponse
124
     */
125
    public function request($request, $mode = self::RESPONSE_SHORT)
126
    {
127
        if ($request instanceof SimpleXMLElement) {
128
            $request = $request->asXml();
129
        } else {
130
            $xml = $this->getPacket();
131
132
            if (is_array($request)) {
133
                $request = $this->_arrayToXml($request, $xml)->asXML();
134
            } else if (preg_match('/^[a-z]/', $request)) {
135
                $request = $this->_expandRequestShortSyntax($request, $xml);
136
            }
137
        }
138
139
        if ('sdk' == $this->_protocol) {
140
            $version = ('' == $this->_version) ? null : $this->_version;
141
            $requestXml = new SimpleXMLElement((string)$request);
142
            $xml = \pm_ApiRpc::getService($version)->call($requestXml->children()[0]->asXml(), $this->_login);
143
        } else {
144
            $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...
145
        }
146
        $this->_verifyResponse($xml);
147
148
        return (self::RESPONSE_FULL == $mode) ? $xml : $xml->xpath('//result')[0];
149
    }
150
151
    /**
152
     * Perform HTTP request to end-point
153
     *
154
     * @param string $request
155
     * @return XmlResponse
156
     * @throws Client\Exception
157
     */
158
    private function _performHttpRequest($request)
159
    {
160
        $curl = curl_init();
161
162
        curl_setopt($curl, CURLOPT_URL, "$this->_protocol://$this->_host:$this->_port/enterprise/control/agent.php");
163
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
164
        curl_setopt($curl, CURLOPT_POST, true);
165
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
166
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
167
        curl_setopt($curl, CURLOPT_HTTPHEADER, $this->_getHeaders());
168
        curl_setopt($curl, CURLOPT_POSTFIELDS, $request);
169
170
        $result = curl_exec($curl);
171
172
        if (false === $result) {
173
            throw new Client\Exception(curl_error($curl), curl_errno($curl));
174
        }
175
176
        if (self::$_isExecutionsLogEnabled) {
177
            self::$_executionLog[] = [
178
                'trace' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS),
179
                'request' => $request,
180
                'response' => $result,
181
            ];
182
        }
183
184
        curl_close($curl);
185
186
        $xml = new XmlResponse($result);
187
        return $xml;
188
    }
189
190
    /**
191
     * Perform multiple API requests using single HTTP request
192
     *
193
     * @param $requests
194
     * @param int $mode
195
     * @return array
196
     * @throws Client\Exception
197
     */
198
    public function multiRequest($requests, $mode = self::RESPONSE_SHORT)
199
    {
200
201
        $requestXml = $this->getPacket();
202
203
        foreach ($requests as $request) {
204
            if ($request instanceof SimpleXMLElement) {
205
                throw new Client\Exception('SimpleXML type of request is not supported for multi requests.');
206
            } else {
207
                if (is_array($request)) {
208
                    $request = $this->_arrayToXml($request, $requestXml)->asXML();
209
                } else if (preg_match('/^[a-z]/', $request)) {
210
                    $this->_expandRequestShortSyntax($request, $requestXml);
211
                }
212
            }
213
            $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...
214
        }
215
216
        if ('sdk' == $this->_protocol) {
217
            throw new Client\Exception('Multi requests are not supported via SDK.');
218
        } else {
219
            $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...
220
        }
221
222
        $responses = [];
223
        foreach ($responseXml->children() as $childNode) {
224
            $xml = $this->getPacket();
225
            $dom = dom_import_simplexml($xml)->ownerDocument;
226
227
            $childDomNode = dom_import_simplexml($childNode);
228
            $childDomNode = $dom->importNode($childDomNode, true);
229
            $dom->documentElement->appendChild($childDomNode);
230
231
            $response = simplexml_load_string($dom->saveXML());
232
            $responses[] = (self::RESPONSE_FULL == $mode) ? $response : $response->xpath('//result')[0];
233
        }
234
235
        return $responses;
236
    }
237
238
    /**
239
     * Retrieve list of headers needed for request
240
     *
241
     * @return array
242
     */
243
    protected function _getHeaders()
244
    {
245
        $headers = array(
246
            "Content-Type: text/xml",
247
            "HTTP_PRETTY_PRINT: TRUE",
248
        );
249
250
        if ($this->_secretKey) {
251
            $headers[] = "KEY: $this->_secretKey";
252
        } else {
253
            $headers[] = "HTTP_AUTH_LOGIN: $this->_login";
254
            $headers[] = "HTTP_AUTH_PASSWD: $this->_password";
255
        }
256
257
        return $headers;
258
    }
259
260
    /**
261
     * Enable or disable execution log
262
     *
263
     * @param bool $enable
264
     */
265
    public static function enableExecutionLog($enable = true)
266
    {
267
        self::$_isExecutionsLogEnabled = $enable;
268
    }
269
270
    /**
271
     * Retrieve execution log
272
     *
273
     * @return array
274
     */
275
    public static function getExecutionLog()
276
    {
277
        return self::$_executionLog;
278
    }
279
280
    /**
281
     * Verify that response does not contain errors
282
     *
283
     * @param XmlResponse $xml
284
     * @throws Exception
285
     */
286
    protected function _verifyResponse($xml)
287
    {
288
        if ($xml->system && $xml->system->status && 'error' == (string)$xml->system->status) {
289
            throw new Exception((string)$xml->system->errtext, (int)$xml->system->errcode);
290
        }
291
292
        if ($xml->xpath('//status[text()="error"]') && $xml->xpath('//errcode') && $xml->xpath('//errtext')) {
293
            $errorCode = (int)$xml->xpath('//errcode')[0];
294
            $errorMessage = (string)$xml->xpath('//errtext')[0];
295
            throw new Exception($errorMessage, $errorCode);
296
        }
297
    }
298
299
    /**
300
     * Expand short syntax (some.method.call) into full XML representation
301
     *
302
     * @param string $request
303
     * @param SimpleXMLElement $xml
304
     * @return string
305
     */
306
    protected function _expandRequestShortSyntax($request, SimpleXMLElement $xml)
307
    {
308
        $parts = explode('.', $request);
309
        $node = $xml;
310
311
        foreach ($parts as $part) {
312
            @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...
313
            $node = $node->addChild($name, $value);
314
        }
315
316
        return $xml->asXML();
317
    }
318
319
    /**
320
     * Convert array to XML representation
321
     *
322
     * @param array $array
323
     * @param SimpleXMLElement $xml
324
     * @return SimpleXMLElement
325
     */
326
    protected function _arrayToXml(array $array, SimpleXMLElement $xml)
327
    {
328
        foreach ($array as $key => $value) {
329
            if (is_array($value)) {
330
                $this->_arrayToXml($value, $xml->addChild($key));
331
            } else {
332
                $xml->addChild($key, $value);
333
            }
334
        }
335
336
        return $xml;
337
    }
338
339
    /**
340
     * @param string $name
341
     * @return \PleskX\Api\Operator
342
     */
343
    protected function _getOperator($name)
344
    {
345
        if (!isset($this->_operatorsCache[$name])) {
346
            $className = '\\PleskX\\Api\\Operator\\' . $name;
347
            $this->_operatorsCache[$name] = new $className($this);
348
        }
349
350
        return $this->_operatorsCache[$name];
351
    }
352
353
    /**
354
     * @return Operator\Server
355
     */
356
    public function server()
357
    {
358
        return $this->_getOperator('Server');
359
    }
360
361
    /**
362
     * @return Operator\Customer
363
     */
364
    public function customer()
365
    {
366
        return $this->_getOperator('Customer');
367
    }
368
369
    /**
370
     * @return Operator\Webspace
371
     */
372
    public function webspace()
373
    {
374
        return $this->_getOperator('Webspace');
375
    }
376
377
    /**
378
     * @return Operator\Subdomain
379
     */
380
    public function subdomain()
381
    {
382
        return $this->_getOperator('Subdomain');
383
    }
384
385
    /**
386
     * @return Operator\Dns
387
     */
388
    public function dns()
389
    {
390
        return $this->_getOperator('Dns');
391
    }
392
393
    /**
394
     * @return Operator\DatabaseServer
395
     */
396
    public function databaseServer()
397
    {
398
        return $this->_getOperator('DatabaseServer');
399
    }
400
401
    /**
402
     * @return Operator\Mail
403
     */
404
    public function mail()
405
    {
406
        return $this->_getOperator('Mail');
407
    }
408
409
    /**
410
     * @return Operator\Migration
411
     */
412
    public function migration()
413
    {
414
        return $this->_getOperator('Migration');
415
    }
416
417
    /**
418
     * @return Operator\Certificate
419
     */
420
    public function certificate()
421
    {
422
        return $this->_getOperator('Certificate');
423
    }
424
425
    /**
426
     * @return Operator\SiteAlias
427
     */
428
    public function siteAlias()
429
    {
430
        return $this->_getOperator('SiteAlias');
431
    }
432
433
    /**
434
     * @return Operator\Ip
435
     */
436
    public function ip()
437
    {
438
        return $this->_getOperator('Ip');
439
    }
440
441
    /**
442
     * @return Operator\EventLog
443
     */
444
    public function eventLog()
445
    {
446
        return $this->_getOperator('EventLog');
447
    }
448
449
    /**
450
     * @return Operator\SpamFilter
451
     */
452
    public function spamFilter()
453
    {
454
        return $this->_getOperator('SpamFilter');
455
    }
456
457
    /**
458
     * @return Operator\SecretKey
459
     */
460
    public function secretKey()
461
    {
462
        return $this->_getOperator('SecretKey');
463
    }
464
465
    /**
466
     * @return Operator\Ui
467
     */
468
    public function ui()
469
    {
470
        return $this->_getOperator('Ui');
471
    }
472
473
    /**
474
     * @return Operator\ServicePlan
475
     */
476
    public function servicePlan()
477
    {
478
        return $this->_getOperator('ServicePlan');
479
    }
480
481
    /**
482
     * @return Operator\WebUser
483
     */
484
    public function webUser()
485
    {
486
        return $this->_getOperator('WebUser');
487
    }
488
489
    /**
490
     * @return Operator\MailList
491
     */
492
    public function mailList()
493
    {
494
        return $this->_getOperator('MailList');
495
    }
496
497
    /**
498
     * @return Operator\VirtualDirectory
499
     */
500
    public function virtualDirectory()
501
    {
502
        return $this->_getOperator('VirtualDirectory');
503
    }
504
505
    /**
506
     * @return Operator\Database
507
     */
508
    public function database()
509
    {
510
        return $this->_getOperator('Database');
511
    }
512
513
    /**
514
     * @return Operator\FtpUser
515
     */
516
    public function ftpUser()
517
    {
518
        return $this->_getOperator('FtpUser');
519
    }
520
521
    /**
522
     * @return Operator\Session
523
     */
524
    public function session()
525
    {
526
        return $this->_getOperator('Session');
527
    }
528
529
    /**
530
     * @return Operator\Updater
531
     */
532
    public function updater()
533
    {
534
        return $this->_getOperator('Updater');
535
    }
536
537
    /**
538
     * @return Operator\Locale
539
     */
540
    public function locale()
541
    {
542
        return $this->_getOperator('Locale');
543
    }
544
545
    /**
546
     * @return Operator\LogRotation
547
     */
548
    public function logRotation()
549
    {
550
        return $this->_getOperator('LogRotation');
551
    }
552
553
    /**
554
     * @return Operator\BackupManager
555
     */
556
    public function backupManager()
557
    {
558
        return $this->_getOperator('BackupManager');
559
    }
560
561
    /**
562
     * @return Operator\Sso
563
     */
564
    public function sso()
565
    {
566
        return $this->_getOperator('Sso');
567
    }
568
569
    /**
570
     * @return Operator\ProtectedDirectory
571
     */
572
    public function protectedDirectory()
573
    {
574
        return $this->_getOperator('ProtectedDirectory');
575
    }
576
577
    /**
578
     * @return Operator\Reseller
579
     */
580
    public function reseller()
581
    {
582
        return $this->_getOperator('Reseller');
583
    }
584
585
    /**
586
     * @return Operator\ResellerPlan
587
     */
588
    public function resellerPlan()
589
    {
590
        return $this->_getOperator('ResellerPlan');
591
    }
592
593
    /**
594
     * @return Operator\Aps
595
     */
596
    public function aps()
597
    {
598
        return $this->_getOperator('Aps');
599
    }
600
601
    /**
602
     * @return Operator\ServicePlanAddon
603
     */
604
    public function servicePlanAddon()
605
    {
606
        return $this->_getOperator('ServicePlanAddon');
607
    }
608
609
    /**
610
     * @return Operator\Site
611
     */
612
    public function site()
613
    {
614
        return $this->_getOperator('Site');
615
    }
616
617
    /**
618
     * @return Operator\User
619
     */
620
    public function user()
621
    {
622
        return $this->_getOperator('User');
623
    }
624
625
    /**
626
     * @return Operator\Role
627
     */
628
    public function role()
629
    {
630
        return $this->_getOperator('Role');
631
    }
632
633
    /**
634
     * @return Operator\BusinessLogicUpgrade
635
     */
636
    public function businessLogicUpgrade()
637
    {
638
        return $this->_getOperator('BusinessLogicUpgrade');
639
    }
640
641
    /**
642
     * @return Operator\Webmail
643
     */
644
    public function webmail()
645
    {
646
        return $this->_getOperator('Webmail');
647
    }
648
649
    /**
650
     * @return Operator\PlanItem
651
     */
652
    public function planItem()
653
    {
654
        return $this->_getOperator('PlanItem');
655
    }
656
657
    /**
658
     * @return Operator\Sitebuilder
659
     */
660
    public function sitebuilder()
661
    {
662
        return $this->_getOperator('Sitebuilder');
663
    }
664
665
    /**
666
     * @return Operator\ServiceNode
667
     */
668
    public function serviceNode()
669
    {
670
        return $this->_getOperator('ServiceNode');
671
    }
672
673
    /**
674
     * @return Operator\IpBan
675
     */
676
    public function ipBan()
677
    {
678
        return $this->_getOperator('IpBan');
679
    }
680
681
    /**
682
     * @return Operator\WpInstance
683
     */
684
    public function wpInstance()
685
    {
686
        return $this->_getOperator('WpInstance');
687
    }
688
689
}
690