Completed
Push — master ( 30a664...27c677 )
by Alexei
02:14
created

Client::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 3
1
<?php
2
// Copyright 1999-2016. Parallels IP Holdings GmbH.
3
4
namespace PleskX\Api;
5
use SimpleXMLElement;
6
7
/**
8
 * Client for Plesk API-RPC
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 int
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 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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
        $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...
140
141
        return (self::RESPONSE_FULL == $mode) ? $xml : $xml->xpath('//result')[0];
142
    }
143
144
    /**
145
     * Perform HTTP request to end-point
146
     *
147
     * @param string $request
148
     * @return XmlResponse
149
     * @throws Exception
150
     */
151
    protected function _performHttpRequest($request)
152
    {
153
        $curl = curl_init();
154
155
        curl_setopt($curl, CURLOPT_URL, "$this->_protocol://$this->_host:$this->_port/enterprise/control/agent.php");
156
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
157
        curl_setopt($curl, CURLOPT_POST, true);
158
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
159
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
160
        curl_setopt($curl, CURLOPT_HTTPHEADER, $this->_getHeaders());
161
        curl_setopt($curl, CURLOPT_POSTFIELDS, $request);
162
163
        $result = curl_exec($curl);
164
165
        if (false === $result) {
166
            throw new Client\Exception(curl_error($curl), curl_errno($curl));
167
        }
168
169
        if (self::$_isExecutionsLogEnabled) {
170
            self::$_executionLog[] = [
171
                'trace' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS),
172
                'request' => $request,
173
                'response' => $result,
174
            ];
175
        }
176
177
        curl_close($curl);
178
179
        $xml = new XmlResponse($result);
180
        $this->_verifyResponse($xml);
181
182
        return $xml;
183
    }
184
185
    /**
186
     * Perform multiple API requests using single HTTP request
187
     *
188
     * @param $requests
189
     * @param int $mode
190
     * @return array
191
     */
192
    public function multiRequest($requests, $mode = self::RESPONSE_SHORT)
193
    {
194
195
        $requestXml = $this->getPacket();
196
197
        foreach ($requests as $request) {
198 View Code Duplication
            if ($request instanceof SimpleXMLElement) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
199
                // TODO: implement
200
            } else {
201
                if (is_array($request)) {
202
                    $request = $this->_arrayToXml($request, $requestXml)->asXML();
203
                } else if (preg_match('/^[a-z]/', $request)) {
204
                    $this->_expandRequestShortSyntax($request, $requestXml);
205
                }
206
            }
207
            $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...
208
        }
209
210
        $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...
211
212
        $responses = [];
213
        foreach ($responseXml->children() as $childNode) {
214
            $xml = $this->getPacket();
215
            $dom = dom_import_simplexml($xml)->ownerDocument;
216
217
            $childDomNode = dom_import_simplexml($childNode);
218
            $childDomNode = $dom->importNode($childDomNode, true);
219
            $dom->documentElement->appendChild($childDomNode);
220
221
            $response = simplexml_load_string($dom->saveXML());
222
            $responses[] = (self::RESPONSE_FULL == $mode) ? $response : $response->xpath('//result')[0];
223
        }
224
225
        return $responses;
226
    }
227
228
    /**
229
     * Retrieve list of headers needed for request
230
     *
231
     * @return array
232
     */
233
    protected function _getHeaders()
234
    {
235
        $headers = array(
236
            "Content-Type: text/xml",
237
            "HTTP_PRETTY_PRINT: TRUE",
238
        );
239
240
        if ($this->_secretKey) {
241
            $headers[] = "KEY: $this->_secretKey";
242
        } else {
243
            $headers[] = "HTTP_AUTH_LOGIN: $this->_login";
244
            $headers[] = "HTTP_AUTH_PASSWD: $this->_password";
245
        }
246
247
        return $headers;
248
    }
249
250
    /**
251
     * Enable or disable execution log
252
     *
253
     * @param bool $enable
254
     */
255
    public static function enableExecutionLog($enable = true)
256
    {
257
        self::$_isExecutionsLogEnabled = $enable;
258
    }
259
260
    /**
261
     * Retrieve execution log
262
     *
263
     * @return array
264
     */
265
    public static function getExecutionLog()
266
    {
267
        return self::$_executionLog;
268
    }
269
270
    /**
271
     * Verify that response does not contain errors
272
     *
273
     * @param XmlResponse $xml
274
     * @throws \Exception
275
     */
276
    protected function _verifyResponse($xml)
277
    {
278
        if ($xml->system && $xml->system->status && 'error' == (string)$xml->system->status) {
279
            throw new Exception((string)$xml->system->errtext, (int)$xml->system->errcode);
280
        }
281
282
        if ($xml->xpath('//status[text()="error"]') && $xml->xpath('//errcode') && $xml->xpath('//errtext')) {
283
            $errorCode = (int)$xml->xpath('//errcode')[0];
284
            $errorMessage = (string)$xml->xpath('//errtext')[0];
285
            throw new Exception($errorMessage, $errorCode);
286
        }
287
    }
288
289
    /**
290
     * Expand short syntax (some.method.call) into full XML representation
291
     *
292
     * @param string $request
293
     * @param SimpleXMLElement $xml
294
     * @return string
295
     */
296
    protected function _expandRequestShortSyntax($request, SimpleXMLElement $xml)
297
    {
298
        $parts = explode('.', $request);
299
        $node = $xml;
300
301
        foreach ($parts as $part) {
302
            @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...
303
            $node = $node->addChild($name, $value);
304
        }
305
306
        return $xml->asXML();
307
    }
308
309
    /**
310
     * Convert array to XML representation
311
     *
312
     * @param array $array
313
     * @param SimpleXMLElement $xml
314
     * @return SimpleXMLElement
315
     */
316
    protected function _arrayToXml(array $array, SimpleXMLElement $xml)
317
    {
318
        foreach ($array as $key => $value) {
319
            if (is_array($value)) {
320
                $this->_arrayToXml($value, $xml->addChild($key));
321
            } else {
322
                $xml->addChild($key, $value);
323
            }
324
        }
325
326
        return $xml;
327
    }
328
329
    /**
330
     * @param string $name
331
     * @return \PleskX\Api\Operator
332
     */
333
    protected function _getOperator($name)
334
    {
335
        if (!isset($this->_operatorsCache[$name])) {
336
            $className = '\\PleskX\\Api\\Operator\\' . $name;
337
            $this->_operatorsCache[$name] = new $className($this);
338
        }
339
340
        return $this->_operatorsCache[$name];
341
    }
342
343
    /**
344
     * @return Operator\Server
345
     */
346
    public function server()
347
    {
348
        return $this->_getOperator('Server');
349
    }
350
351
    /**
352
     * @return Operator\Customer
353
     */
354
    public function customer()
355
    {
356
        return $this->_getOperator('Customer');
357
    }
358
359
    /**
360
     * @return Operator\Webspace
361
     */
362
    public function webspace()
363
    {
364
        return $this->_getOperator('Webspace');
365
    }
366
367
    /**
368
     * @return Operator\Subdomain
369
     */
370
    public function subdomain()
371
    {
372
        return $this->_getOperator('Subdomain');
373
    }
374
375
    /**
376
     * @return Operator\Dns
377
     */
378
    public function dns()
379
    {
380
        return $this->_getOperator('Dns');
381
    }
382
383
    /**
384
     * @return Operator\DatabaseServer
385
     */
386
    public function databaseServer()
387
    {
388
        return $this->_getOperator('DatabaseServer');
389
    }
390
391
    /**
392
     * @return Operator\Mail
393
     */
394
    public function mail()
395
    {
396
        return $this->_getOperator('Mail');
397
    }
398
399
    /**
400
     * @return Operator\Migration
401
     */
402
    public function migration()
403
    {
404
        return $this->_getOperator('Migration');
405
    }
406
407
    /**
408
     * @return Operator\Certificate
409
     */
410
    public function certificate()
411
    {
412
        return $this->_getOperator('Certificate');
413
    }
414
415
    /**
416
     * @return Operator\SiteAlias
417
     */
418
    public function siteAlias()
419
    {
420
        return $this->_getOperator('SiteAlias');
421
    }
422
423
    /**
424
     * @return Operator\Ip
425
     */
426
    public function ip()
427
    {
428
        return $this->_getOperator('Ip');
429
    }
430
431
    /**
432
     * @return Operator\EventLog
433
     */
434
    public function eventLog()
435
    {
436
        return $this->_getOperator('EventLog');
437
    }
438
439
    /**
440
     * @return Operator\SpamFilter
441
     */
442
    public function spamFilter()
443
    {
444
        return $this->_getOperator('SpamFilter');
445
    }
446
447
    /**
448
     * @return Operator\SecretKey
449
     */
450
    public function secretKey()
451
    {
452
        return $this->_getOperator('SecretKey');
453
    }
454
455
    /**
456
     * @return Operator\Ui
457
     */
458
    public function ui()
459
    {
460
        return $this->_getOperator('Ui');
461
    }
462
463
    /**
464
     * @return Operator\ServicePlan
465
     */
466
    public function servicePlan()
467
    {
468
        return $this->_getOperator('ServicePlan');
469
    }
470
471
    /**
472
     * @return Operator\WebUser
473
     */
474
    public function webUser()
475
    {
476
        return $this->_getOperator('WebUser');
477
    }
478
479
    /**
480
     * @return Operator\MailList
481
     */
482
    public function mailList()
483
    {
484
        return $this->_getOperator('MailList');
485
    }
486
487
    /**
488
     * @return Operator\VirtualDirectory
489
     */
490
    public function virtualDirectory()
491
    {
492
        return $this->_getOperator('VirtualDirectory');
493
    }
494
495
    /**
496
     * @return Operator\Database
497
     */
498
    public function database()
499
    {
500
        return $this->_getOperator('Database');
501
    }
502
503
    /**
504
     * @return Operator\FtpUser
505
     */
506
    public function ftpUser()
507
    {
508
        return $this->_getOperator('FtpUser');
509
    }
510
511
    /**
512
     * @return Operator\Session
513
     */
514
    public function session()
515
    {
516
        return $this->_getOperator('Session');
517
    }
518
519
    /**
520
     * @return Operator\Updater
521
     */
522
    public function updater()
523
    {
524
        return $this->_getOperator('Updater');
525
    }
526
527
    /**
528
     * @return Operator\Locale
529
     */
530
    public function locale()
531
    {
532
        return $this->_getOperator('Locale');
533
    }
534
535
    /**
536
     * @return Operator\LogRotation
537
     */
538
    public function logRotation()
539
    {
540
        return $this->_getOperator('LogRotation');
541
    }
542
543
    /**
544
     * @return Operator\BackupManager
545
     */
546
    public function backupManager()
547
    {
548
        return $this->_getOperator('BackupManager');
549
    }
550
551
    /**
552
     * @return Operator\Sso
553
     */
554
    public function sso()
555
    {
556
        return $this->_getOperator('Sso');
557
    }
558
559
    /**
560
     * @return Operator\ProtectedDirectory
561
     */
562
    public function protectedDirectory()
563
    {
564
        return $this->_getOperator('ProtectedDirectory');
565
    }
566
567
    /**
568
     * @return Operator\Reseller
569
     */
570
    public function reseller()
571
    {
572
        return $this->_getOperator('Reseller');
573
    }
574
575
    /**
576
     * @return Operator\ResellerPlan
577
     */
578
    public function resellerPlan()
579
    {
580
        return $this->_getOperator('ResellerPlan');
581
    }
582
583
    /**
584
     * @return Operator\Aps
585
     */
586
    public function aps()
587
    {
588
        return $this->_getOperator('Aps');
589
    }
590
591
    /**
592
     * @return Operator\ServicePlanAddon
593
     */
594
    public function servicePlanAddon()
595
    {
596
        return $this->_getOperator('ServicePlanAddon');
597
    }
598
599
    /**
600
     * @return Operator\Site
601
     */
602
    public function site()
603
    {
604
        return $this->_getOperator('Site');
605
    }
606
607
    /**
608
     * @return Operator\User
609
     */
610
    public function user()
611
    {
612
        return $this->_getOperator('User');
613
    }
614
615
    /**
616
     * @return Operator\Role
617
     */
618
    public function role()
619
    {
620
        return $this->_getOperator('Role');
621
    }
622
623
    /**
624
     * @return Operator\BusinessLogicUpgrade
625
     */
626
    public function businessLogicUpgrade()
627
    {
628
        return $this->_getOperator('BusinessLogicUpgrade');
629
    }
630
631
    /**
632
     * @return Operator\Webmail
633
     */
634
    public function webmail()
635
    {
636
        return $this->_getOperator('Webmail');
637
    }
638
639
    /**
640
     * @return Operator\PlanItem
641
     */
642
    public function planItem()
643
    {
644
        return $this->_getOperator('PlanItem');
645
    }
646
647
    /**
648
     * @return Operator\Sitebuilder
649
     */
650
    public function sitebuilder()
651
    {
652
        return $this->_getOperator('Sitebuilder');
653
    }
654
655
    /**
656
     * @return Operator\ServiceNode
657
     */
658
    public function serviceNode()
659
    {
660
        return $this->_getOperator('ServiceNode');
661
    }
662
663
    /**
664
     * @return Operator\IpBan
665
     */
666
    public function ipBan()
667
    {
668
        return $this->_getOperator('IpBan');
669
    }
670
671
    /**
672
     * @return Operator\WpInstance
673
     */
674
    public function wpInstance()
675
    {
676
        return $this->_getOperator('WpInstance');
677
    }
678
679
}
680