Completed
Pull Request — master (#30)
by
unknown
01:42
created

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