Full Code of plesk/api-php-lib for AI

master 678d09a3ff43 cached
129 files
194.0 KB
53.0k tokens
529 symbols
1 requests
Download .txt
Showing preview only (223K chars total). Download the full file or copy to clipboard to get everything.
Repository: plesk/api-php-lib
Branch: master
Commit: 678d09a3ff43
Files: 129
Total size: 194.0 KB

Directory structure:
gitextract_ygetwf0b/

├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       └── test.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── composer.json
├── docker-compose.yml
├── phpcs.xml.dist
├── phpunit-watcher.yml
├── phpunit.xml.dist
├── psalm.xml
├── src/
│   └── Api/
│       ├── AbstractStruct.php
│       ├── Client/
│       │   └── Exception.php
│       ├── Client.php
│       ├── Exception.php
│       ├── InternalClient.php
│       ├── Operator/
│       │   ├── Aps.php
│       │   ├── Certificate.php
│       │   ├── Customer.php
│       │   ├── Database.php
│       │   ├── DatabaseServer.php
│       │   ├── Dns.php
│       │   ├── DnsTemplate.php
│       │   ├── EventLog.php
│       │   ├── Ip.php
│       │   ├── Locale.php
│       │   ├── LogRotation.php
│       │   ├── Mail.php
│       │   ├── PhpHandler.php
│       │   ├── ProtectedDirectory.php
│       │   ├── Reseller.php
│       │   ├── ResellerPlan.php
│       │   ├── SecretKey.php
│       │   ├── Server.php
│       │   ├── ServicePlan.php
│       │   ├── ServicePlanAddon.php
│       │   ├── Session.php
│       │   ├── Site.php
│       │   ├── SiteAlias.php
│       │   ├── Subdomain.php
│       │   ├── Ui.php
│       │   ├── VirtualDirectory.php
│       │   └── Webspace.php
│       ├── Operator.php
│       ├── Struct/
│       │   ├── Certificate/
│       │   │   └── Info.php
│       │   ├── Customer/
│       │   │   ├── GeneralInfo.php
│       │   │   └── Info.php
│       │   ├── Database/
│       │   │   ├── Info.php
│       │   │   └── UserInfo.php
│       │   ├── DatabaseServer/
│       │   │   └── Info.php
│       │   ├── Dns/
│       │   │   └── Info.php
│       │   ├── EventLog/
│       │   │   ├── DetailedEvent.php
│       │   │   └── Event.php
│       │   ├── Ip/
│       │   │   └── Info.php
│       │   ├── Locale/
│       │   │   └── Info.php
│       │   ├── Mail/
│       │   │   ├── GeneralInfo.php
│       │   │   └── Info.php
│       │   ├── PhpHandler/
│       │   │   └── Info.php
│       │   ├── ProtectedDirectory/
│       │   │   ├── DataInfo.php
│       │   │   ├── Info.php
│       │   │   └── UserInfo.php
│       │   ├── Reseller/
│       │   │   ├── GeneralInfo.php
│       │   │   └── Info.php
│       │   ├── SecretKey/
│       │   │   └── Info.php
│       │   ├── Server/
│       │   │   ├── Admin.php
│       │   │   ├── GeneralInfo.php
│       │   │   ├── Preferences.php
│       │   │   ├── SessionPreferences.php
│       │   │   ├── Statistics/
│       │   │   │   ├── DiskSpace.php
│       │   │   │   ├── LoadAverage.php
│       │   │   │   ├── Memory.php
│       │   │   │   ├── Objects.php
│       │   │   │   ├── Other.php
│       │   │   │   ├── Swap.php
│       │   │   │   └── Version.php
│       │   │   ├── Statistics.php
│       │   │   └── UpdatesInfo.php
│       │   ├── ServicePlan/
│       │   │   └── Info.php
│       │   ├── ServicePlanAddon/
│       │   │   └── Info.php
│       │   ├── Session/
│       │   │   └── Info.php
│       │   ├── Site/
│       │   │   ├── GeneralInfo.php
│       │   │   ├── HostingInfo.php
│       │   │   └── Info.php
│       │   ├── SiteAlias/
│       │   │   ├── GeneralInfo.php
│       │   │   └── Info.php
│       │   ├── Subdomain/
│       │   │   └── Info.php
│       │   ├── Ui/
│       │   │   └── CustomButton.php
│       │   └── Webspace/
│       │       ├── DiskUsage.php
│       │       ├── GeneralInfo.php
│       │       ├── HostingPropertyInfo.php
│       │       ├── Info.php
│       │       ├── Limit.php
│       │       ├── LimitDescriptor.php
│       │       ├── LimitInfo.php
│       │       ├── Limits.php
│       │       ├── PermissionDescriptor.php
│       │       ├── PermissionInfo.php
│       │       ├── PhpSettings.php
│       │       └── PhysicalHostingDescriptor.php
│       └── XmlResponse.php
├── tests/
│   ├── AbstractTestCase.php
│   ├── ApiClientTest.php
│   ├── CertificateTest.php
│   ├── CustomerTest.php
│   ├── DatabaseServerTest.php
│   ├── DatabaseTest.php
│   ├── DnsTemplateTest.php
│   ├── DnsTest.php
│   ├── EventLogTest.php
│   ├── IpTest.php
│   ├── LocaleTest.php
│   ├── MailTest.php
│   ├── PhpHandlerTest.php
│   ├── ProtectedDirectoryTest.php
│   ├── ResellerTest.php
│   ├── SecretKeyTest.php
│   ├── ServerTest.php
│   ├── ServicePlanAddonTest.php
│   ├── ServicePlanTest.php
│   ├── SessionTest.php
│   ├── SiteAliasTest.php
│   ├── SiteTest.php
│   ├── SubdomainTest.php
│   ├── UiTest.php
│   ├── Utility/
│   │   ├── KeyLimitChecker.php
│   │   └── PasswordProvider.php
│   └── WebspaceTest.php
└── wait-for-plesk.sh

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/dependabot.yml
================================================
version: 2

updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "monthly"
    groups:
      main:
        patterns:
          - "*"
    commit-message:
      prefix: "TECH Dependabot [github-actions] "

  - package-ecosystem: "composer"
    directory: "/"
    schedule:
      interval: "weekly"
    commit-message:
      prefix: "TECH Dependabot [composer] "


================================================
FILE: .github/workflows/test.yml
================================================
name: Tests

on:
  push:
    branches:
      - master
  pull_request:

jobs:
  test:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v6
      - run: docker compose run tests
      - uses: codecov/codecov-action@v6
        with:
          token: ${{ secrets.CODECOV_TOKEN }}


================================================
FILE: .gitignore
================================================
/.env
/vendor/
/phpunit.xml
/.phpunit.result.cache
/coverage.xml


================================================
FILE: Dockerfile
================================================
FROM php:8.2-cli

RUN apt-get update \
    && apt-get install -y unzip \
    && docker-php-ext-install pcntl \
    && pecl install xdebug \
    && echo "zend_extension=xdebug.so" > /usr/local/etc/php/conf.d/xdebug.ini \
    && echo "xdebug.mode=coverage" >> /usr/local/etc/php/conf.d/xdebug.ini \
    && curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer


================================================
FILE: LICENSE
================================================
Copyright 1999-2025. WebPros International GmbH.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.


================================================
FILE: README.md
================================================
## PHP library for Plesk XML-RPC API

[![Test Status](https://github.com/plesk/api-php-lib/actions/workflows/test.yml/badge.svg)](https://github.com/plesk/api-php-lib/actions/workflows/test.yml)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/plesk/api-php-lib/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/plesk/api-php-lib/?branch=master)
[![codecov](https://codecov.io/gh/plesk/api-php-lib/branch/master/graph/badge.svg?token=5Kwbddpdeb)](https://codecov.io/gh/plesk/api-php-lib)

PHP object-oriented library for Plesk XML-RPC API.

## Install Via Composer

[Composer](https://getcomposer.org/) is a preferable way to install the library:

`composer require plesk/api-php-lib`

## Usage Examples

Here is an example on how to use the library and create a customer with desired properties:
```php
$client = new \PleskX\Api\Client($host);
$client->setCredentials($login, $password);

$client->customer()->create([
    'cname' => 'Plesk',
    'pname' => 'John Smith',
    'login' => 'john',
    'passwd' => 'secret',
    'email' => 'john@smith.com',
]);
```

It is possible to use a secret key instead of password for authentication.

```php
$client = new \PleskX\Api\Client($host);
$client->setSecretKey($secretKey)
```

In case of Plesk extension creation one can use an internal mechanism to access XML-RPC API. It does not require to pass authentication because the extension works in the context of Plesk.

```php
$client = new \PleskX\Api\InternalClient();
$protocols = $client->server()->getProtos();
```

For additional examples see tests/ directory.

## How to Run Unit Tests

One the possible ways to become familiar with the library is to check the unit tests.

To run the unit tests use the following command:

`REMOTE_HOST=your-plesk-host.dom REMOTE_PASSWORD=password composer test`

To use custom port one can provide a URL (e.g. for Docker container):

`REMOTE_URL=https://your-plesk-host.dom:port REMOTE_PASSWORD=password composer test`

One more way to run tests is to use Docker:

`docker-compose run tests`

## Continuous Testing

During active development it could be more convenient to run tests in continuous manner. Here is the way how to achieve it:

`REMOTE_URL=https://your-plesk-host.dom:port REMOTE_PASSWORD=password composer test:watch`


================================================
FILE: composer.json
================================================
{
    "name": "plesk/api-php-lib",
    "type": "library",
    "description": "PHP object-oriented library for Plesk XML-RPC API",
    "license": "Apache-2.0",
    "authors": [
        {
            "name": "Alexei Yuzhakov",
            "email": "sibprogrammer@gmail.com"
        },
        {
            "name": "WebPros International GmbH.",
            "email": "plesk-dev-leads@plesk.com"
        }
    ],
    "require": {
        "php": "^7.4 || ^8.0",
        "ext-curl": "*",
        "ext-xml": "*",
        "ext-simplexml": "*",
        "ext-dom": "*"
    },
    "require-dev": {
        "phpunit/phpunit": "^9",
        "spatie/phpunit-watcher": "^1.22",
        "vimeo/psalm": "^4.10 || ^5.0",
        "squizlabs/php_codesniffer": "^3.6 || ^4.0"
    },
    "config": {
        "process-timeout": 0,
        "platform": {
            "php": "7.4.27"
        }
    },
    "scripts": {
        "test": "phpunit",
        "test:watch": "phpunit-watcher watch",
        "lint": [
            "psalm",
            "phpcs"
        ]
    },
    "autoload": {
        "psr-4": {
            "PleskX\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "PleskXTest\\": "tests/"
        }
    },
    "extra": {
        "branch-alias": {
            "dev-master": "2.0.x-dev"
        }
    }
}


================================================
FILE: docker-compose.yml
================================================
# Copyright 1999-2025. WebPros International GmbH.
version: '3'
services:
  plesk:
    image: plesk/plesk:latest
    logging:
      driver: none
    ports:
      ["8443:8443"]
    tmpfs:
      - /tmp
      - /run
      - /run/lock
    volumes:
      - /sys/fs/cgroup:/sys/fs/cgroup
    cgroup: host
  tests:
    build: .
    environment:
      REMOTE_URL: https://plesk:8443
      REMOTE_PASSWORD: changeme1Q**
    command: >
      bash -c "cd /opt/api-php-lib
      && composer install
      && ./wait-for-plesk.sh
      && composer lint
      && composer test -- --testdox"
    depends_on:
      - plesk
    links:
      - plesk
    volumes:
      - .:/opt/api-php-lib


================================================
FILE: phpcs.xml.dist
================================================
<?xml version="1.0"?>
<!-- Copyright 1999-2025. WebPros International GmbH. -->
<ruleset name="PHP library for Plesk XML-RPC API">
    <file>src</file>
    <file>tests</file>
    <rule ref="Generic">
        <exclude name="Generic.WhiteSpace.DisallowSpaceIndent"/>
        <exclude name="Generic.Files.EndFileNoNewline"/>
        <exclude name="Generic.Files.LowercasedFilename.NotFound"/>
        <exclude name="Generic.PHP.RequireStrictTypes"/>
        <exclude name="Generic.PHP.ClosingPHPTag"/>
        <exclude name="Generic.PHP.UpperCaseConstant"/>
        <exclude name="Generic.Arrays.DisallowShortArraySyntax"/>
        <exclude name="Generic.Classes.OpeningBraceSameLine"/>
        <exclude name="Generic.Functions.OpeningFunctionBraceKernighanRitchie"/>
        <exclude name="Generic.Formatting.MultipleStatementAlignment"/>
        <exclude name="Generic.Formatting.SpaceBeforeCast"/>
        <exclude name="Generic.Formatting.SpaceAfterNot"/>
        <exclude name="Generic.Commenting.DocComment"/>
        <exclude name="Generic.ControlStructures.DisallowYodaConditions"/>
    </rule>
    <rule ref="PSR1"/>
    <rule ref="PSR2"/>
    <rule ref="PSR12">
        <exclude name="PSR12.Files.FileHeader"/>
    </rule>
</ruleset>


================================================
FILE: phpunit-watcher.yml
================================================
# Copyright 1999-2025. WebPros International GmbH.
phpunit:
  arguments: '--stop-on-failure'
  timeout: 0


================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 1999-2025. WebPros International GmbH. -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" bootstrap="vendor/autoload.php" verbose="true" colors="true">
  <coverage processUncoveredFiles="true">
    <include>
      <directory suffix=".php">./src</directory>
    </include>
    <report>
      <clover outputFile="coverage.xml"/>
    </report>
  </coverage>
  <testsuites>
    <testsuite name="E2E">
      <directory>./tests</directory>
    </testsuite>
  </testsuites>
  <php>
    <ini name="error_reporting" value="-1"/>
    <env name="REMOTE_URL" value=""/>
    <env name="REMOTE_PASSWORD" value=""/>
  </php>
  <logging/>
</phpunit>


================================================
FILE: psalm.xml
================================================
<?xml version="1.0"?>
<!-- Copyright 1999-2025. WebPros International GmbH. -->
<psalm
    errorLevel="3"
    resolveFromConfigFile="true"
    findUnusedBaselineEntry="true"
    findUnusedCode="false"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="https://getpsalm.org/schema/config"
    xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
    <projectFiles>
        <directory name="src" />
        <ignoreFiles>
            <directory name="vendor" />
        </ignoreFiles>
    </projectFiles>

    <issueHandlers>
        <PropertyNotSetInConstructor errorLevel="suppress" />
        <UndefinedPropertyFetch errorLevel="suppress" />
    </issueHandlers>
</psalm>


================================================
FILE: src/Api/AbstractStruct.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api;

abstract class AbstractStruct
{
    /**
     * @param string $property
     * @param mixed $value
     *
     * @throws \Exception
     */
    public function __set(string $property, $value)
    {
        throw new \Exception("Try to set an undeclared property '$property' to a value: $value.");
    }

    /**
     * Initialize list of scalar properties by response.
     *
     * @param \SimpleXMLElement $apiResponse
     * @param array $properties
     *
     * @throws \Exception
     */
    protected function initScalarProperties(\SimpleXMLElement $apiResponse, array $properties): void
    {
        foreach ($properties as $property) {
            if (is_array($property)) {
                $classPropertyName = current($property);
                $value = $apiResponse->{key($property)};
            } else {
                /** @psalm-suppress PossiblyInvalidArgument */
                $classPropertyName = $this->underToCamel(str_replace('-', '_', $property));
                $value = $apiResponse->$property;
            }

            $reflectionProperty = new \ReflectionProperty($this, $classPropertyName);
            $propertyType = $reflectionProperty->getType();
            if (is_null($propertyType)) {
                $docBlock = $reflectionProperty->getDocComment();
                $propertyType = preg_replace('/^.+ @var ([a-z]+) .+$/', '\1', $docBlock);
            } else {
                /** @psalm-suppress UndefinedMethod */
                $propertyType = $propertyType->getName();
            }

            if ('string' == $propertyType) {
                $value = (string) $value;
            } elseif ('int' == $propertyType) {
                $value = (int) $value;
            } elseif ('bool' == $propertyType) {
                $value = in_array((string) $value, ['true', 'on', 'enabled']);
            } else {
                throw new \Exception("Unknown property type '$propertyType'.");
            }

            $this->$classPropertyName = $value;
        }
    }

    /**
     * Convert underscore separated words into camel case.
     *
     * @param string $under
     *
     * @return string
     */
    private function underToCamel(string $under): string
    {
        $under = '_' . str_replace('_', ' ', strtolower($under));

        return ltrim(str_replace(' ', '', ucwords($under)), '_');
    }
}


================================================
FILE: src/Api/Client/Exception.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Client;

/**
 * Transport layer exception.
 */
class Exception extends \Exception
{
}


================================================
FILE: src/Api/Client.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api;

use DOMDocument;
use SimpleXMLElement;

/**
 * Client for Plesk XML-RPC API.
 */
class Client
{
    public const RESPONSE_SHORT = 1;
    public const RESPONSE_FULL = 2;

    private string $host;
    private int $port;
    private string $protocol;
    protected string $login = '';
    private string $password = '';
    private string $proxy = '';
    private string $secretKey = '';
    private string $version = '';

    protected array $operatorsCache = [];

    /**
     * @var callable|null
     */
    protected $verifyResponseCallback;

    /**
     * Create client.
     *
     * @param string $host
     * @param int $port
     * @param string $protocol
     */
    public function __construct(string $host, int $port = 8443, string $protocol = 'https')
    {
        $this->host = $host;
        $this->port = $port;
        $this->protocol = $protocol;
    }

    /**
     * Setup credentials for authentication.
     *
     * @param string $login
     * @param string $password
     */
    public function setCredentials(string $login, string $password): void
    {
        $this->login = $login;
        $this->password = $password;
    }

    /**
     * Define secret key for alternative authentication.
     *
     * @param string $secretKey
     */
    public function setSecretKey(string $secretKey): void
    {
        $this->secretKey = $secretKey;
    }

    /**
     * Set proxy server for requests.
     *
     * @param string $proxy
     */
    public function setProxy(string $proxy): void
    {
        $this->proxy = $proxy;
    }

    /**
     * Set default version for requests.
     *
     * @param string $version
     */
    public function setVersion(string $version): void
    {
        $this->version = $version;
    }

    /**
     * Set custom function to verify response of API call according your own needs.
     * Default verifying will be used if it is not specified.
     *
     * @param callable|null $function
     */
    public function setVerifyResponse(?callable $function = null): void
    {
        $this->verifyResponseCallback = $function;
    }

    /**
     * Retrieve host used for communication.
     *
     * @return string
     */
    public function getHost(): string
    {
        return $this->host;
    }

    /**
     * Retrieve port used for communication.
     *
     * @return int
     */
    public function getPort(): int
    {
        return $this->port;
    }

    /**
     * Retrieve name of the protocol (http or https) used for communication.
     *
     * @return string
     */
    public function getProtocol(): string
    {
        return $this->protocol;
    }

    /**
     * Retrieve XML template for packet.
     *
     * @param string|null $version
     *
     * @return SimpleXMLElement
     */
    public function getPacket($version = null): SimpleXMLElement
    {
        $protocolVersion = !is_null($version) ? $version : $this->version;
        $content = "<?xml version='1.0' encoding='UTF-8' ?>";
        $content .= '<packet' . ('' === $protocolVersion ? '' : " version='$protocolVersion'") . '/>';

        return new SimpleXMLElement($content);
    }

    /**
     * Perform API request.
     *
     * @param string|array|SimpleXMLElement $request
     * @param int $mode
     *
     * @return XmlResponse
     * @throws \Exception
     */
    public function request($request, int $mode = self::RESPONSE_SHORT): XmlResponse
    {
        if ($request instanceof SimpleXMLElement) {
            $request = $request->asXml();
        } else {
            $xml = $this->getPacket();

            if (is_array($request)) {
                $request = $this->arrayToXml($request, $xml)->asXML();
            } elseif (preg_match('/^[a-z]/', $request)) {
                $request = $this->expandRequestShortSyntax($request, $xml);
            }
        }

        if ('sdk' == $this->protocol) {
            $xml = $this->performSdkCall((string) $request);
        } else {
            $xml = $this->performHttpRequest((string) $request);
        }

        $this->verifyResponseCallback
            ? call_user_func($this->verifyResponseCallback, $xml)
            : $this->verifyResponse($xml);

        $result = (self::RESPONSE_FULL === $mode)
            ? $xml
            : ($xml->xpath('//result') ?: [null])[0];

        return new XmlResponse($result ? (string) $result->asXML() : '');
    }

    private function performSdkCall(string $request): XmlResponse
    {
        $version = ('' == $this->version) ? null : $this->version;

        $requestXml = new SimpleXMLElement($request);
        $innerNodes = $requestXml->children();
        $innerXml = $innerNodes && count($innerNodes) > 0 && $innerNodes[0] ? $innerNodes[0]->asXml() : '';

        /** @psalm-suppress UndefinedClass */
        $result = \pm_ApiRpc::getService($version)->call($innerXml, $this->login);

        return new XmlResponse($result ? (string) $result->asXML() : '');
    }

    /**
     * Perform HTTP request to end-point.
     *
     * @param string $request
     *
     * @throws Client\Exception
     *
     * @return XmlResponse
     */
    private function performHttpRequest($request)
    {
        $curl = curl_init();

        curl_setopt($curl, CURLOPT_URL, "$this->protocol://$this->host:$this->port/enterprise/control/agent.php");
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_POST, true);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $this->getHeaders());
        curl_setopt($curl, CURLOPT_POSTFIELDS, $request);

        if ('' !== $this->proxy) {
            curl_setopt($curl, CURLOPT_PROXY, $this->proxy);
        }

        $result = curl_exec($curl);

        if (false === $result) {
            throw new Client\Exception(curl_error($curl), curl_errno($curl));
        }

        curl_close($curl);

        return new XmlResponse((string) $result);
    }

    /**
     * Perform multiple API requests using single HTTP request.
     *
     * @param array $requests
     * @param int $mode
     *
     * @throws Client\Exception
     *
     * @return array
     */
    public function multiRequest(array $requests, int $mode = self::RESPONSE_SHORT): array
    {
        $requestXml = $this->getPacket();

        foreach ($requests as $request) {
            if ($request instanceof SimpleXMLElement) {
                throw new Client\Exception('SimpleXML type of request is not supported for multi requests.');
            } else {
                if (is_array($request)) {
                    $request = $this->arrayToXml($request, $requestXml)->asXML();
                    if (!$request) {
                        throw new Client\Exception('Failed to create an XML string for request');
                    }
                } elseif (preg_match('/^[a-z]/', $request)) {
                    $this->expandRequestShortSyntax($request, $requestXml);
                }
            }
        }

        if ('sdk' == $this->protocol) {
            throw new Client\Exception('Multi requests are not supported via SDK.');
        } else {
            $xmlString = $requestXml->asXML();
            if (!$xmlString) {
                throw new Client\Exception('Failed to create an XML string for request');
            }
            $responseXml = $this->performHttpRequest($xmlString);
        }

        return $this->splitResponseToArray($responseXml, $mode);
    }

    private function splitResponseToArray(XmlResponse $responseXml, $mode = self::RESPONSE_SHORT): array
    {
        $responses = [];

        $nodes = $responseXml->children();
        if (!$nodes) {
            return [];
        }

        foreach ($nodes as $childNode) {
            $dom = $this->getDomDocument($this->getPacket());
            if (!$dom) {
                continue;
            }

            $childDomNode = dom_import_simplexml($childNode);
            if (!is_null($childDomNode)) {
                $childDomNode = $dom->importNode($childDomNode, true);
                $dom->documentElement->appendChild($childDomNode);
            }

            $response = simplexml_load_string($dom->saveXML());
            if (!$response) {
                return [];
            }

            $responses[] = (self::RESPONSE_FULL == $mode)
                ? $response
                : ($response->xpath('//result') ?: [null])[0];
        }

        return $responses;
    }

    private function getDomDocument(SimpleXMLElement $xml): ?DOMDocument
    {
        $dom = dom_import_simplexml($xml);
        if (is_null($dom)) {
            return null;
        }

        return $dom->ownerDocument;
    }

    /**
     * Retrieve list of headers needed for request.
     *
     * @return array
     */
    private function getHeaders()
    {
        $headers = [
            'Content-Type: text/xml',
            'HTTP_PRETTY_PRINT: TRUE',
        ];

        if ($this->secretKey) {
            $headers[] = "KEY: $this->secretKey";
        } else {
            $headers[] = "HTTP_AUTH_LOGIN: $this->login";
            $headers[] = "HTTP_AUTH_PASSWD: $this->password";
        }

        return $headers;
    }

    /**
     * Verify that response does not contain errors.
     *
     * @param XmlResponse $xml
     *
     * @throws Exception
     */
    private function verifyResponse($xml): void
    {
        if ($xml->system && $xml->system->status && 'error' == (string) $xml->system->status) {
            throw new Exception((string) $xml->system->errtext, (int) $xml->system->errcode);
        }

        if ($xml->xpath('//status[text()="error"]') && $xml->xpath('//errcode') && $xml->xpath('//errtext')) {
            $errorCode = (int) ($xml->xpath('//errcode') ?: [null])[0];
            $errorMessage = (string) ($xml->xpath('//errtext') ?: [null])[0];

            throw new Exception($errorMessage, $errorCode);
        }
    }

    /**
     * Expand short syntax (some.method.call) into full XML representation.
     *
     * @param string $request
     * @param SimpleXMLElement $xml
     *
     * @return false|string
     */
    private function expandRequestShortSyntax($request, SimpleXMLElement $xml)
    {
        $parts = explode('.', $request);
        $node = $xml;
        $lastParts = end($parts);

        foreach ($parts as $part) {
            // phpcs:ignore
            @list($name, $value) = explode('=', $part);
            if ($part !== $lastParts) {
                $node = $node->addChild($name);
            } else {
                $node->{$name} = (string) $value;
            }
        }

        return $xml->asXML();
    }

    /**
     * Convert array to XML representation.
     *
     * @param array $array
     * @param SimpleXMLElement $xml
     * @param string $parentEl
     *
     * @return SimpleXMLElement
     */
    private function arrayToXml(array $array, SimpleXMLElement $xml, $parentEl = null)
    {
        foreach ($array as $key => $value) {
            $el = is_int($key) && $parentEl ? $parentEl : $key;
            if (is_array($value)) {
                $this->arrayToXml($value, $this->isAssocArray($value) ? $xml->addChild($el) : $xml, $el);
            } elseif (!isset($xml->{$el})) {
                $xml->{$el} = (string) $value;
            } else {
                $xml->{$el}[] = (string) $value;
            }
        }

        return $xml;
    }

    /**
     * @param array $array
     *
     * @return bool
     */
    private function isAssocArray(array $array)
    {
        return $array && array_keys($array) !== range(0, count($array) - 1);
    }

    /**
     * @param string $name
     *
     * @return mixed
     */
    private function getOperator(string $name)
    {
        if (!isset($this->operatorsCache[$name])) {
            $className = '\\PleskX\\Api\\Operator\\' . $name;
            /** @psalm-suppress InvalidStringClass */
            $this->operatorsCache[$name] = new $className($this);
        }

        return $this->operatorsCache[$name];
    }

    public function server(): Operator\Server
    {
        return $this->getOperator('Server');
    }

    public function customer(): Operator\Customer
    {
        return $this->getOperator('Customer');
    }

    public function webspace(): Operator\Webspace
    {
        return $this->getOperator('Webspace');
    }

    public function subdomain(): Operator\Subdomain
    {
        return $this->getOperator('Subdomain');
    }

    public function dns(): Operator\Dns
    {
        return $this->getOperator('Dns');
    }

    public function dnsTemplate(): Operator\DnsTemplate
    {
        return $this->getOperator('DnsTemplate');
    }

    public function databaseServer(): Operator\DatabaseServer
    {
        return $this->getOperator('DatabaseServer');
    }

    public function mail(): Operator\Mail
    {
        return $this->getOperator('Mail');
    }

    public function certificate(): Operator\Certificate
    {
        return $this->getOperator('Certificate');
    }

    public function siteAlias(): Operator\SiteAlias
    {
        return $this->getOperator('SiteAlias');
    }

    public function ip(): Operator\Ip
    {
        return $this->getOperator('Ip');
    }

    public function eventLog(): Operator\EventLog
    {
        return $this->getOperator('EventLog');
    }

    public function secretKey(): Operator\SecretKey
    {
        return $this->getOperator('SecretKey');
    }

    public function ui(): Operator\Ui
    {
        return $this->getOperator('Ui');
    }

    public function servicePlan(): Operator\ServicePlan
    {
        return $this->getOperator('ServicePlan');
    }

    public function virtualDirectory(): Operator\VirtualDirectory
    {
        return $this->getOperator('VirtualDirectory');
    }

    public function database(): Operator\Database
    {
        return $this->getOperator('Database');
    }

    public function session(): Operator\Session
    {
        return $this->getOperator('Session');
    }

    public function locale(): Operator\Locale
    {
        return $this->getOperator('Locale');
    }

    public function logRotation(): Operator\LogRotation
    {
        return $this->getOperator('LogRotation');
    }

    public function protectedDirectory(): Operator\ProtectedDirectory
    {
        return $this->getOperator('ProtectedDirectory');
    }

    public function reseller(): Operator\Reseller
    {
        return $this->getOperator('Reseller');
    }

    public function resellerPlan(): Operator\ResellerPlan
    {
        return $this->getOperator('ResellerPlan');
    }

    public function aps(): Operator\Aps
    {
        return $this->getOperator('Aps');
    }

    public function servicePlanAddon(): Operator\ServicePlanAddon
    {
        return $this->getOperator('ServicePlanAddon');
    }

    public function site(): Operator\Site
    {
        return $this->getOperator('Site');
    }

    public function phpHandler(): Operator\PhpHandler
    {
        return $this->getOperator('PhpHandler');
    }
}


================================================
FILE: src/Api/Exception.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api;

/**
 * Exceptions for XML-RPC API client.
 */
class Exception extends \Exception
{
}


================================================
FILE: src/Api/InternalClient.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api;

/**
 * Internal client for Plesk XML-RPC API (via SDK).
 */
class InternalClient extends Client
{
    public function __construct()
    {
        parent::__construct('localhost', 0, 'sdk');
    }

    /**
     * Setup login to execute requests under certain user.
     *
     * @param string $login
     */
    public function setLogin(string $login): void
    {
        $this->login = $login;
    }
}


================================================
FILE: src/Api/Operator/Aps.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

class Aps extends \PleskX\Api\Operator
{
}


================================================
FILE: src/Api/Operator/Certificate.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

use PleskX\Api\Struct\Certificate as Struct;

class Certificate extends \PleskX\Api\Operator
{
    public function generate(array $properties): Struct\Info
    {
        $packet = $this->client->getPacket();
        $info = $packet->addChild($this->wrapperTag)->addChild('generate')->addChild('info');

        foreach ($properties as $name => $value) {
            $info->{$name} = $value;
        }

        $response = $this->client->request($packet);

        return new Struct\Info($response);
    }

    /**
     * @param array $properties
     * @param string|Struct\Info $certificate
     * @param string|null $privateKey
     */
    public function install(array $properties, $certificate, ?string $privateKey = null): bool
    {
        return $this->callApi('install', $properties, $certificate, $privateKey);
    }

    /**
     * @param array $properties
     * @param Struct\Info $certificate
     */
    public function update(array $properties, Struct\Info $certificate): bool
    {
        return $this->callApi('update', $properties, $certificate);
    }

    /**
     * @param string $method
     * @param array $properties
     * @param string|Struct\Info $certificate
     * @param string|null $privateKey
     */
    private function callApi(string $method, array $properties, $certificate, ?string $privateKey = null): bool
    {
        $packet = $this->client->getPacket();

        $installTag = $packet->addChild($this->wrapperTag)->addChild($method);
        foreach ($properties as $name => $value) {
            $installTag->{$name} = $value;
        }

        $contentTag = $installTag->addChild('content');
        if (is_string($certificate)) {
            $contentTag->addChild('csr', $certificate);
            $contentTag->addChild('pvt', $privateKey);
        } elseif ($certificate instanceof \PleskX\Api\Struct\Certificate\Info) {
            foreach ($certificate->getMapping() as $name => $value) {
                $contentTag->{$name} = $value;
            }
        }
        $result = $this->client->request($packet);

        return 'ok' == (string) $result->status;
    }

    public function delete(string $name, array $properties): bool
    {
        $packet = $this->client->getPacket();

        $removeTag = $packet->addChild($this->wrapperTag)->addChild('remove');
        $removeTag->addChild('filter')->addChild('name', $name);

        foreach ($properties as $name => $value) {
            $removeTag->{$name} = $value;
        }

        $result = $this->client->request($packet);

        return 'ok' == (string) $result->status;
    }
}


================================================
FILE: src/Api/Operator/Customer.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

use PleskX\Api\Struct\Customer as Struct;

class Customer extends \PleskX\Api\Operator
{
    public function create(array $properties): Struct\Info
    {
        $packet = $this->client->getPacket();
        $info = $packet->addChild($this->wrapperTag)->addChild('add')->addChild('gen_info');

        foreach ($properties as $name => $value) {
            $info->{$name} = $value;
        }

        $response = $this->client->request($packet);

        return new Struct\Info($response);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return bool
     */
    public function delete(string $field, $value): bool
    {
        return $this->deleteBy($field, $value);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\GeneralInfo
     */
    public function get(string $field, $value): Struct\GeneralInfo
    {
        $items = $this->getItems(Struct\GeneralInfo::class, 'gen_info', $field, $value);

        return reset($items);
    }

    /**
     * @return Struct\GeneralInfo[]
     */
    public function getAll(): array
    {
        return $this->getItems(Struct\GeneralInfo::class, 'gen_info');
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return bool
     */
    public function enable(string $field, $value): bool
    {
        return $this->setProperties($field, $value, ['status' => 0]);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return bool
     */
    public function disable(string $field, $value): bool
    {
        return $this->setProperties($field, $value, ['status' => 16]);
    }

    /**
     * @param string $field
     * @param int|string $value
     * @param array $properties
     *
     * @return bool
     */
    public function setProperties(string $field, $value, array $properties): bool
    {
        $packet = $this->client->getPacket();
        $setTag = $packet->addChild($this->wrapperTag)->addChild('set');
        $setTag->addChild('filter')->addChild($field, (string) $value);
        $genInfoTag = $setTag->addChild('values')->addChild('gen_info');
        foreach ($properties as $property => $propertyValue) {
            $genInfoTag->addChild($property, (string) $propertyValue);
        }

        $response = $this->client->request($packet);

        return 'ok' === (string) $response->status;
    }
}


================================================
FILE: src/Api/Operator/Database.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

use PleskX\Api\Struct\Database as Struct;
use PleskX\Api\XmlResponse;

class Database extends \PleskX\Api\Operator
{
    public function create(array $properties): Struct\Info
    {
        return new Struct\Info($this->process('add-db', $properties));
    }

    public function createUser(array $properties): Struct\UserInfo
    {
        return new Struct\UserInfo($this->process('add-db-user', $properties));
    }

    private function process(string $command, array $properties): XmlResponse
    {
        $packet = $this->client->getPacket();
        $info = $packet->addChild($this->wrapperTag)->addChild($command);

        foreach ($properties as $name => $value) {
            if (false !== strpos($value, '&')) {
                $info->$name = $value;
                continue;
            }
            $info->{$name} = $value;
        }

        return $this->client->request($packet);
    }

    public function updateUser(array $properties): bool
    {
        $response = $this->process('set-db-user', $properties);

        return 'ok' === (string) $response->status;
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\Info
     */
    public function get(string $field, $value): Struct\Info
    {
        $items = $this->getAll($field, $value);

        return reset($items);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\UserInfo
     */
    public function getUser(string $field, $value): Struct\UserInfo
    {
        $items = $this->getAllUsers($field, $value);

        return reset($items);
    }

    /**
     * @param string|null $field
     * @param int|string $value
     *
     * @return Struct\Info[]
     */
    public function getAll(?string $field, $value): array
    {
        $response = $this->getBy('get-db', $field, $value);
        $items = [];
        foreach ((array) $response->xpath('//result') as $xmlResult) {
            if ($xmlResult && isset($xmlResult->id) && (int) $xmlResult->id > 0) {
                $items[] = new Struct\Info($xmlResult);
            }
        }

        return $items;
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\UserInfo[]
     */
    public function getAllUsers(string $field, $value): array
    {
        $response = $this->getBy('get-db-users', $field, $value);
        $items = [];
        foreach ((array) $response->xpath('//result') as $xmlResult) {
            if ($xmlResult) {
                $items[] = new Struct\UserInfo($xmlResult);
            }
        }

        return $items;
    }

    /**
     * @param string $command
     * @param string|null $field
     * @param int|string $value
     *
     * @return XmlResponse
     */
    private function getBy(string $command, ?string $field, $value): XmlResponse
    {
        $packet = $this->client->getPacket();
        $getTag = $packet->addChild($this->wrapperTag)->addChild($command);

        $filterTag = $getTag->addChild('filter');
        if (!is_null($field)) {
            $filterTag->{$field} = (string) $value;
        }

        return $this->client->request($packet, \PleskX\Api\Client::RESPONSE_FULL);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return bool
     */
    public function delete(string $field, $value): bool
    {
        return $this->deleteBy($field, $value, 'del-db');
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return bool
     */
    public function deleteUser(string $field, $value): bool
    {
        return $this->deleteBy($field, $value, 'del-db-user');
    }
}


================================================
FILE: src/Api/Operator/DatabaseServer.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

use PleskX\Api\Struct\DatabaseServer as Struct;

class DatabaseServer extends \PleskX\Api\Operator
{
    protected string $wrapperTag = 'db_server';

    public function getSupportedTypes(): array
    {
        $response = $this->request('get-supported-types');

        return (array) $response->type;
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\Info
     */
    public function get(string $field, $value): Struct\Info
    {
        $items = $this->getBy($field, $value);

        return reset($items);
    }

    /**
     * @return Struct\Info[]
     */
    public function getAll(): array
    {
        return $this->getBy();
    }

    public function getDefault(string $type): Struct\Info
    {
        $packet = $this->client->getPacket();
        $getTag = $packet->addChild($this->wrapperTag)->addChild('get-default');
        $filterTag = $getTag->addChild('filter');
        /** @psalm-suppress UndefinedPropertyAssignment */
        $filterTag->type = $type;

        $response = $this->client->request($packet);

        return new Struct\Info($response);
    }

    /**
     * @param string|null $field
     * @param int|string|null $value
     *
     * @return Struct\Info[]
     */
    private function getBy($field = null, $value = null): array
    {
        $packet = $this->client->getPacket();
        $getTag = $packet->addChild($this->wrapperTag)->addChild('get');

        $filterTag = $getTag->addChild('filter');
        if (!is_null($field)) {
            $filterTag->{$field} = (string) $value;
        }

        $response = $this->client->request($packet, \PleskX\Api\Client::RESPONSE_FULL);

        $items = [];
        foreach ((array) $response->xpath('//result') as $xmlResult) {
            if (!$xmlResult) {
                continue;
            }
            if (!is_null($xmlResult->data)) {
                $item = new Struct\Info($xmlResult->data);
                $item->id = (int) $xmlResult->id;
                $items[] = $item;
            }
        }

        return $items;
    }
}


================================================
FILE: src/Api/Operator/Dns.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

use PleskX\Api\Struct\Dns as Struct;

class Dns extends \PleskX\Api\Operator
{
    public function create(array $properties): Struct\Info
    {
        $packet = $this->client->getPacket();
        $info = $packet->addChild($this->wrapperTag)->addChild('add_rec');

        foreach ($properties as $name => $value) {
            $info->{$name} = $value;
        }

        return new Struct\Info($this->client->request($packet));
    }

    /**
     * Send multiply records by one request.
     *
     * @param array $records
     *
     * @return \SimpleXMLElement[]
     */
    public function bulkCreate(array $records): array
    {
        $packet = $this->client->getPacket();

        foreach ($records as $properties) {
            $info = $packet->addChild($this->wrapperTag)->addChild('add_rec');

            foreach ($properties as $name => $value) {
                $info->{$name} = $value;
            }
        }

        $response = $this->client->request($packet, \PleskX\Api\Client::RESPONSE_FULL);
        $items = [];
        foreach ((array) $response->xpath('//result') as $xmlResult) {
            if ($xmlResult) {
                $items[] = $xmlResult;
            }
        }

        return $items;
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\Info
     */
    public function get(string $field, $value): Struct\Info
    {
        $items = $this->getAll($field, $value);

        return reset($items);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\Info[]
     */
    public function getAll(string $field, $value): array
    {
        $packet = $this->client->getPacket();
        $getTag = $packet->addChild($this->wrapperTag)->addChild('get_rec');

        $filterTag = $getTag->addChild('filter');
        $filterTag->addChild($field, (string) $value);

        $response = $this->client->request($packet, \PleskX\Api\Client::RESPONSE_FULL);
        $items = [];
        foreach ((array) $response->xpath('//result') as $xmlResult) {
            if (!$xmlResult) {
                continue;
            }
            if (!is_null($xmlResult->data)) {
                $item = new Struct\Info($xmlResult->data);
                $item->id = (int) $xmlResult->id;
                $items[] = $item;
            }
        }

        return $items;
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return bool
     */
    public function delete(string $field, $value): bool
    {
        return $this->deleteBy($field, $value, 'del_rec');
    }

    /**
     * Delete multiply records by one request.
     *
     * @param array $recordIds
     *
     * @return \SimpleXMLElement[]
     */
    public function bulkDelete(array $recordIds): array
    {
        $packet = $this->client->getPacket();

        foreach ($recordIds as $recordId) {
            $packet->addChild($this->wrapperTag)->addChild('del_rec')
                ->addChild('filter')->addChild('id', $recordId);
        }

        $response = $this->client->request($packet, \PleskX\Api\Client::RESPONSE_FULL);
        $items = [];
        foreach ((array) $response->xpath('//result') as $xmlResult) {
            if ($xmlResult) {
                $items[] = $xmlResult;
            }
        }

        return $items;
    }
}


================================================
FILE: src/Api/Operator/DnsTemplate.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

use PleskX\Api\Struct\Dns as Struct;

class DnsTemplate extends \PleskX\Api\Operator
{
    protected string $wrapperTag = 'dns';

    /**
     * @param array $properties
     *
     * @return Struct\Info
     */
    public function create(array $properties): Struct\Info
    {
        $packet = $this->client->getPacket();
        $info = $packet->addChild($this->wrapperTag)->addChild('add_rec');

        unset($properties['site-id'], $properties['site-alias-id']);
        foreach ($properties as $name => $value) {
            $info->{$name} = $value;
        }

        return new Struct\Info($this->client->request($packet));
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\Info
     */
    public function get(string $field, $value): Struct\Info
    {
        $items = $this->getAll($field, $value);

        return reset($items);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\Info[]
     */
    public function getAll($field = null, $value = null): array
    {
        $packet = $this->client->getPacket();
        $getTag = $packet->addChild($this->wrapperTag)->addChild('get_rec');

        $filterTag = $getTag->addChild('filter');
        if (!is_null($field)) {
            $filterTag->{$field} = (string) $value;
        }
        $getTag->addChild('template');

        $response = $this->client->request($packet, \PleskX\Api\Client::RESPONSE_FULL);
        $items = [];
        foreach ((array) $response->xpath('//result') as $xmlResult) {
            if (!$xmlResult) {
                continue;
            }
            if (!is_null($xmlResult->data)) {
                $item = new Struct\Info($xmlResult->data);
                $item->id = (int) $xmlResult->id;
                $items[] = $item;
            }
        }

        return $items;
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return bool
     */
    public function delete(string $field, $value): bool
    {
        $packet = $this->client->getPacket();
        $delTag = $packet->addChild($this->wrapperTag)->addChild('del_rec');
        $delTag->addChild('filter')->addChild($field, (string) $value);
        $delTag->addChild('template');

        $response = $this->client->request($packet);

        return 'ok' === (string) $response->status;
    }
}


================================================
FILE: src/Api/Operator/EventLog.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

use PleskX\Api\Struct\EventLog as Struct;

class EventLog extends \PleskX\Api\Operator
{
    protected string $wrapperTag = 'event_log';

    /**
     * @return Struct\Event[]
     */
    public function get(): array
    {
        $records = [];
        $response = $this->request('get');

        foreach ($response->event ?? [] as $eventInfo) {
            $records[] = new Struct\Event($eventInfo);
        }

        return $records;
    }

    /**
     * @return Struct\DetailedEvent[]
     */
    public function getDetailedLog(): array
    {
        $records = [];
        $response = $this->request('get_events');

        foreach ($response->event ?? [] as $eventInfo) {
            $records[] = new Struct\DetailedEvent($eventInfo);
        }

        return $records;
    }

    public function getLastId(): int
    {
        return (int) $this->request('get-last-id')->getValue('id');
    }
}


================================================
FILE: src/Api/Operator/Ip.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

use PleskX\Api\Struct\Ip as Struct;

class Ip extends \PleskX\Api\Operator
{
    /**
     * @return Struct\Info[]
     */
    public function get(): array
    {
        $ips = [];
        $packet = $this->client->getPacket();
        $packet->addChild($this->wrapperTag)->addChild('get');
        $response = $this->client->request($packet);

        foreach ($response->addresses->ip_info ?? [] as $ipInfo) {
            $ips[] = new Struct\Info($ipInfo);
        }

        return $ips;
    }
}


================================================
FILE: src/Api/Operator/Locale.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

use PleskX\Api\Struct\Locale as Struct;

class Locale extends \PleskX\Api\Operator
{
    /**
     * @param string|null $id
     *
     * @return Struct\Info|Struct\Info[]
     */
    public function get($id = null)
    {
        $locales = [];
        $packet = $this->client->getPacket();
        $filter = $packet->addChild($this->wrapperTag)->addChild('get')->addChild('filter');

        if (!is_null($id)) {
            $filter->addChild('id', $id);
        }

        $response = $this->client->request($packet, \PleskX\Api\Client::RESPONSE_FULL);

        foreach ($response->locale->get->result ?? [] as $localeInfo) {
            if (!is_null($localeInfo->info)) {
                $locales[(string) $localeInfo->info->id] = new Struct\Info($localeInfo->info);
            }
        }

        return !is_null($id) ? reset($locales) : $locales;
    }
}


================================================
FILE: src/Api/Operator/LogRotation.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

class LogRotation extends \PleskX\Api\Operator
{
}


================================================
FILE: src/Api/Operator/Mail.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

use PleskX\Api\Client;
use PleskX\Api\Operator;
use PleskX\Api\Struct\Mail as Struct;

class Mail extends Operator
{
    public function create(string $name, int $siteId, bool $mailbox = false, string $password = ''): Struct\Info
    {
        $packet = $this->client->getPacket();
        $info = $packet->addChild($this->wrapperTag)->addChild('create');

        $filter = $info->addChild('filter');
        $filter->addChild('site-id', (string) $siteId);
        $mailname = $filter->addChild('mailname');
        $mailname->addChild('name', $name);
        if ($mailbox) {
            $mailname->addChild('mailbox')->addChild('enabled', 'true');
        }
        if (!empty($password)) {
            /** @psalm-suppress UndefinedPropertyAssignment */
            $mailname->addChild('password')->value = $password;
        }

        $response = $this->client->request($packet);

        /** @psalm-suppress PossiblyNullArgument */
        return new Struct\Info($response->mailname);
    }

    /**
     * @param string $field
     * @param int|string $value
     * @param int $siteId
     *
     * @return bool
     */
    public function delete(string $field, $value, int $siteId): bool
    {
        $packet = $this->client->getPacket();
        $filter = $packet->addChild($this->wrapperTag)->addChild('remove')->addChild('filter');

        $filter->addChild('site-id', (string) $siteId);
        $filter->{$field} = (string) $value;

        $response = $this->client->request($packet);

        return 'ok' === (string) $response->status;
    }

    public function get(string $name, int $siteId): Struct\GeneralInfo
    {
        $items = $this->getAll($siteId, $name);

        return reset($items);
    }

    /**
     * @param int $siteId
     * @param string|null $name
     *
     * @return Struct\GeneralInfo[]
     */
    public function getAll(int $siteId, $name = null): array
    {
        $packet = $this->client->getPacket();
        $getTag = $packet->addChild($this->wrapperTag)->addChild('get_info');

        $filterTag = $getTag->addChild('filter');
        $filterTag->addChild('site-id', (string) $siteId);
        if (!is_null($name)) {
            $filterTag->addChild('name', $name);
        }

        $response = $this->client->request($packet, Client::RESPONSE_FULL);
        $items = [];
        foreach ((array) $response->xpath('//result') as $xmlResult) {
            if (!$xmlResult || !isset($xmlResult->mailname)) {
                continue;
            }
            $item = new Struct\GeneralInfo($xmlResult->mailname);
            $items[] = $item;
        }

        return $items;
    }
}


================================================
FILE: src/Api/Operator/PhpHandler.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

use PleskX\Api\Client;
use PleskX\Api\Operator;
use PleskX\Api\Struct\PhpHandler\Info;

class PhpHandler extends Operator
{
    /**
     * @param string|null $field
     * @param int|string|null $value
     *
     * @return Info
     */
    public function get($field = null, $value = null): ?Info
    {
        $packet = $this->client->getPacket();
        $getTag = $packet->addChild($this->wrapperTag)->addChild('get');
        $filterTag = $getTag->addChild('filter');

        if (!is_null($field)) {
            $filterTag->addChild($field, (string) $value);
        }

        $response = $this->client->request($packet, Client::RESPONSE_FULL);
        $xmlResult = ($response->xpath('//result') ?: [null])[0];

        return $xmlResult ? new Info($xmlResult) : null;
    }

    /**
     * @param string|null $field
     * @param int|string $value
     *
     * @return Info[]
     */
    public function getAll($field = null, $value = null): array
    {
        $packet = $this->client->getPacket();
        $getTag = $packet->addChild($this->wrapperTag)->addChild('get');

        $filterTag = $getTag->addChild('filter');
        if (!is_null($field)) {
            $filterTag->addChild($field, (string) $value);
        }

        $response = $this->client->request($packet, Client::RESPONSE_FULL);
        $items = [];
        foreach ((array) $response->xpath('//result') as $xmlResult) {
            if (!$xmlResult) {
                continue;
            }
            $item = new Info($xmlResult);
            $items[] = $item;
        }

        return $items;
    }
}


================================================
FILE: src/Api/Operator/ProtectedDirectory.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

use PleskX\Api\Client;
use PleskX\Api\Operator;
use PleskX\Api\Struct\ProtectedDirectory as Struct;

class ProtectedDirectory extends Operator
{
    protected string $wrapperTag = 'protected-dir';

    public function add(string $name, int $siteId, string $header = ''): Struct\Info
    {
        $packet = $this->client->getPacket();
        $info = $packet->addChild($this->wrapperTag)->addChild('add');

        $info->addChild('site-id', (string) $siteId);
        $info->addChild('name', $name);
        $info->addChild('header', $header);

        return new Struct\Info($this->client->request($packet));
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return bool
     */
    public function delete(string $field, $value): bool
    {
        return $this->deleteBy($field, $value, 'delete');
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\DataInfo
     */
    public function get(string $field, $value): Struct\DataInfo
    {
        $items = $this->getAll($field, $value);

        return reset($items);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\DataInfo[]
     */
    public function getAll(string $field, $value): array
    {
        $response = $this->getBy('get', $field, $value);
        $items = [];
        foreach ((array) $response->xpath('//result/data') as $xmlResult) {
            if (!$xmlResult) {
                continue;
            }
            $items[] = new Struct\DataInfo($xmlResult);
        }

        return $items;
    }

    /**
     * @param Struct\Info $protectedDirectory
     * @param string $login
     * @param string $password
     *
     * @return Struct\UserInfo
     * @psalm-suppress UndefinedPropertyAssignment
     */
    public function addUser($protectedDirectory, $login, $password)
    {
        $packet = $this->client->getPacket();
        $info = $packet->addChild($this->wrapperTag)->addChild('add-user');

        $info->{'pd-id'} = (string) $protectedDirectory->id;
        $info->login = $login;
        $info->password = $password;

        return new Struct\UserInfo($this->client->request($packet));
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return bool
     */
    public function deleteUser($field, $value)
    {
        return $this->deleteBy($field, $value, 'delete-user');
    }

    /**
     * @param string $command
     * @param string $field
     * @param int|string $value
     *
     * @return \PleskX\Api\XmlResponse
     */
    private function getBy(string $command, string $field, $value)
    {
        $packet = $this->client->getPacket();
        $getTag = $packet->addChild($this->wrapperTag)->addChild($command);

        $filterTag = $getTag->addChild('filter');
        $filterTag->{$field} = (string) $value;

        return $this->client->request($packet, Client::RESPONSE_FULL);
    }
}


================================================
FILE: src/Api/Operator/Reseller.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

use PleskX\Api\Struct\Reseller as Struct;

class Reseller extends \PleskX\Api\Operator
{
    public function create(array $properties): Struct\Info
    {
        $packet = $this->client->getPacket();
        $info = $packet->addChild($this->wrapperTag)->addChild('add')->addChild('gen-info');

        foreach ($properties as $name => $value) {
            $info->{$name} = $value;
        }

        $response = $this->client->request($packet);

        return new Struct\Info($response);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return bool
     */
    public function delete(string $field, $value): bool
    {
        return $this->deleteBy($field, $value);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\GeneralInfo
     */
    public function get(string $field, $value): Struct\GeneralInfo
    {
        $items = $this->getAll($field, $value);

        return reset($items);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\GeneralInfo[]
     */
    public function getAll($field = null, $value = null): array
    {
        $packet = $this->client->getPacket();
        $getTag = $packet->addChild($this->wrapperTag)->addChild('get');

        $filterTag = $getTag->addChild('filter');
        if (!is_null($field)) {
            $filterTag->addChild($field, (string) $value);
        }

        $datasetTag = $getTag->addChild('dataset');
        $datasetTag->addChild('gen-info');
        $datasetTag->addChild('permissions');

        $response = $this->client->request($packet, \PleskX\Api\Client::RESPONSE_FULL);

        $items = [];
        foreach ((array) $response->xpath('//result') as $xmlResult) {
            if (!$xmlResult || !$xmlResult->data) {
                continue;
            }

            $item = new Struct\GeneralInfo($xmlResult->data);
            $item->id = (int) $xmlResult->id;
            $items[] = $item;
        }

        return $items;
    }
}


================================================
FILE: src/Api/Operator/ResellerPlan.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

class ResellerPlan extends \PleskX\Api\Operator
{
}


================================================
FILE: src/Api/Operator/SecretKey.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

use PleskX\Api\Struct\SecretKey as Struct;

class SecretKey extends \PleskX\Api\Operator
{
    protected string $wrapperTag = 'secret_key';

    public function create(string $ipAddress = '', string $description = ''): string
    {
        $packet = $this->client->getPacket();
        $createTag = $packet->addChild($this->wrapperTag)->addChild('create');

        if ('' !== $ipAddress) {
            $createTag->addChild('ip_address', $ipAddress);
        }

        if ('' !== $description) {
            $createTag->addChild('description', $description);
        }

        $response = $this->client->request($packet);

        return (string) $response->key;
    }

    public function delete(string $keyId): bool
    {
        return $this->deleteBy('key', $keyId, 'delete');
    }

    public function get(string $keyId): Struct\Info
    {
        $items = $this->getBy($keyId);

        return reset($items);
    }

    /**
     * @return Struct\Info[]
     */
    public function getAll(): array
    {
        return $this->getBy();
    }

    /**
     * @param string|null $keyId
     *
     * @return Struct\Info[]
     */
    public function getBy($keyId = null): array
    {
        $packet = $this->client->getPacket();
        $getTag = $packet->addChild($this->wrapperTag)->addChild('get_info');

        $filterTag = $getTag->addChild('filter');
        if (!is_null($keyId)) {
            $filterTag->addChild('key', $keyId);
        }

        $response = $this->client->request($packet, \PleskX\Api\Client::RESPONSE_FULL);

        $items = [];
        foreach ((array) $response->xpath('//result/key_info') as $keyInfo) {
            if (!$keyInfo) {
                continue;
            }
            $items[] = new Struct\Info($keyInfo);
        }

        return $items;
    }
}


================================================
FILE: src/Api/Operator/Server.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

use PleskX\Api\Struct\Server as Struct;
use PleskX\Api\XmlResponse;

class Server extends \PleskX\Api\Operator
{
    public function getProtos(): array
    {
        $packet = $this->client->getPacket();
        $packet->addChild($this->wrapperTag)->addChild('get_protos');
        $response = $this->client->request($packet);

        /** @psalm-suppress PossiblyNullPropertyFetch */
        return (array) $response->protos->proto;
    }

    public function getGeneralInfo(): Struct\GeneralInfo
    {
        return new Struct\GeneralInfo($this->getInfo('gen_info'));
    }

    public function getPreferences(): Struct\Preferences
    {
        return new Struct\Preferences($this->getInfo('prefs'));
    }

    public function getAdmin(): Struct\Admin
    {
        return new Struct\Admin($this->getInfo('admin'));
    }

    public function getKeyInfo(): array
    {
        $keyInfo = [];
        $keyInfoXml = $this->getInfo('key');

        foreach ($keyInfoXml->property ?? [] as $property) {
            $keyInfo[(string) $property->name] = (string) $property->value;
        }

        return $keyInfo;
    }

    public function getComponents(): array
    {
        $components = [];
        $componentsXml = $this->getInfo('components');

        foreach ($componentsXml->component ?? [] as $component) {
            $components[(string) $component->name] = (string) $component->version;
        }

        return $components;
    }

    public function getServiceStates(): array
    {
        $states = [];
        $statesXml = $this->getInfo('services_state');

        foreach ($statesXml->srv ?? [] as $service) {
            $states[(string) $service->id] = [
                'id' => (string) $service->id,
                'title' => (string) $service->title,
                'state' => (string) $service->state,
            ];
        }

        return $states;
    }

    public function getSessionPreferences(): Struct\SessionPreferences
    {
        return new Struct\SessionPreferences($this->getInfo('session_setup'));
    }

    public function getShells(): array
    {
        $shells = [];
        $shellsXml = $this->getInfo('shells');

        foreach ($shellsXml->shell ?? [] as $shell) {
            $shells[(string) $shell->name] = (string) $shell->path;
        }

        return $shells;
    }

    public function getNetworkInterfaces(): array
    {
        $interfacesXml = $this->getInfo('interfaces');

        return (array) $interfacesXml->interface;
    }

    public function getStatistics(): Struct\Statistics
    {
        return new Struct\Statistics($this->getInfo('stat'));
    }

    public function getSiteIsolationConfig(): array
    {
        $config = [];
        $configXml = $this->getInfo('site-isolation-config');

        foreach ($configXml->property ?? [] as $property) {
            $config[(string) $property->name] = (string) $property->value;
        }

        return $config;
    }

    public function getUpdatesInfo(): Struct\UpdatesInfo
    {
        return new Struct\UpdatesInfo($this->getInfo('updates'));
    }

    public function createSession(string $login, string $clientIp): string
    {
        $packet = $this->client->getPacket();
        $sessionNode = $packet->addChild($this->wrapperTag)->addChild('create_session');
        $sessionNode->addChild('login', $login);
        $dataNode = $sessionNode->addChild('data');
        $dataNode->addChild('user_ip', base64_encode($clientIp));
        $dataNode->addChild('source_server');
        $response = $this->client->request($packet);

        return (string) $response->id;
    }

    private function getInfo(string $operation): XmlResponse
    {
        $packet = $this->client->getPacket();
        $packet->addChild($this->wrapperTag)->addChild('get')->addChild($operation);
        $response = $this->client->request($packet);

        return $response->$operation;
    }
}


================================================
FILE: src/Api/Operator/ServicePlan.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

use PleskX\Api\Struct\ServicePlan as Struct;

class ServicePlan extends \PleskX\Api\Operator
{
    public function create(array $properties): Struct\Info
    {
        $response = $this->request(['add' => $properties]);

        return new Struct\Info($response);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return bool
     */
    public function delete(string $field, $value): bool
    {
        return $this->deleteBy($field, $value);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\Info
     */
    public function get(string $field, $value): Struct\Info
    {
        $items = $this->getBy($field, $value);

        return reset($items);
    }

    /**
     * @return Struct\Info[]
     */
    public function getAll(): array
    {
        return $this->getBy();
    }

    /**
     * @param string|null $field
     * @param int|string|null $value
     *
     * @return Struct\Info[]
     */
    private function getBy($field = null, $value = null): array
    {
        $packet = $this->client->getPacket();
        $getTag = $packet->addChild($this->wrapperTag)->addChild('get');

        $filterTag = $getTag->addChild('filter');
        if (!is_null($field)) {
            $filterTag->addChild($field, (string) $value);
        }

        $response = $this->client->request($packet, \PleskX\Api\Client::RESPONSE_FULL);

        $items = [];
        foreach ((array) $response->xpath('//result') as $xmlResult) {
            if (!$xmlResult) {
                continue;
            }
            $items[] = new Struct\Info($xmlResult);
        }

        return $items;
    }
}


================================================
FILE: src/Api/Operator/ServicePlanAddon.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

use PleskX\Api\Struct\ServicePlanAddon as Struct;

class ServicePlanAddon extends \PleskX\Api\Operator
{
    public function create(array $properties): Struct\Info
    {
        $response = $this->request(['add' => $properties]);

        return new Struct\Info($response);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return bool
     */
    public function delete(string $field, $value): bool
    {
        return $this->deleteBy($field, $value);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\Info
     */
    public function get(string $field, $value): Struct\Info
    {
        $items = $this->getBy($field, $value);

        return reset($items);
    }

    /**
     * @return Struct\Info[]
     */
    public function getAll(): array
    {
        return $this->getBy();
    }

    /**
     * @param string|null $field
     * @param int|string|null $value
     *
     * @return Struct\Info[]
     */
    private function getBy($field = null, $value = null): array
    {
        $packet = $this->client->getPacket();
        $getTag = $packet->addChild($this->wrapperTag)->addChild('get');

        $filterTag = $getTag->addChild('filter');
        if (!is_null($field)) {
            $filterTag->addChild($field, (string) $value);
        }

        $response = $this->client->request($packet, \PleskX\Api\Client::RESPONSE_FULL);

        $items = [];
        foreach ((array) $response->xpath('//result') as $xmlResult) {
            if (!$xmlResult) {
                continue;
            }
            $items[] = new Struct\Info($xmlResult);
        }

        return $items;
    }
}


================================================
FILE: src/Api/Operator/Session.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

use PleskX\Api\Struct\Session as Struct;

class Session extends \PleskX\Api\Operator
{
    public function create(string $username, string $userIp): string
    {
        $packet = $this->client->getPacket();
        $creator = $packet->addChild('server')->addChild('create_session');

        $creator->addChild('login', $username);
        $loginData = $creator->addChild('data');

        $loginData->addChild('user_ip', base64_encode($userIp));
        $loginData->addChild('source_server', '');

        $response = $this->client->request($packet);

        return (string) $response->id;
    }

    /**
     * @return Struct\Info[]
     */
    public function get(): array
    {
        $sessions = [];
        $response = $this->request('get');

        foreach ($response->session ?? [] as $sessionInfo) {
            $sessions[(string) $sessionInfo->id] = new Struct\Info($sessionInfo);
        }

        return $sessions;
    }

    public function terminate(string $sessionId): bool
    {
        $response = $this->request("terminate.session-id=$sessionId");

        return 'ok' === (string) $response->status;
    }
}


================================================
FILE: src/Api/Operator/Site.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

use PleskX\Api\Struct\Site as Struct;

class Site extends \PleskX\Api\Operator
{
    public const PROPERTIES_HOSTING = 'hosting';

    public function create(array $properties): Struct\Info
    {
        $packet = $this->client->getPacket();
        $info = $packet->addChild($this->wrapperTag)->addChild('add');

        $infoGeneral = $info->addChild('gen_setup');
        foreach ($properties as $name => $value) {
            if (!is_scalar($value)) {
                continue;
            }
            $infoGeneral->{$name} = (string) $value;
        }

        // set hosting properties
        if (isset($properties[static::PROPERTIES_HOSTING]) && is_array($properties[static::PROPERTIES_HOSTING])) {
            $hostingNode = $info->addChild('hosting')->addChild('vrt_hst');
            foreach ($properties[static::PROPERTIES_HOSTING] as $name => $value) {
                $propertyNode = $hostingNode->addChild('property');
                /** @psalm-suppress UndefinedPropertyAssignment */
                $propertyNode->name = $name;
                /** @psalm-suppress UndefinedPropertyAssignment */
                $propertyNode->value = $value;
            }
        }

        $response = $this->client->request($packet);

        return new Struct\Info($response);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return bool
     */
    public function delete(string $field, $value): bool
    {
        return $this->deleteBy($field, $value);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return ?Struct\GeneralInfo
     */
    public function get(string $field, $value): ?Struct\GeneralInfo
    {
        $items = $this->getItems(Struct\GeneralInfo::class, 'gen_info', $field, $value);

        return reset($items) ?: null;
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\HostingInfo|null
     */
    public function getHosting(string $field, $value): ?Struct\HostingInfo
    {
        $items = $this->getItems(
            Struct\HostingInfo::class,
            'hosting',
            $field,
            $value,
            function (\SimpleXMLElement $node) {
                return isset($node->vrt_hst);
            }
        );

        return empty($items) ? null : reset($items);
    }

    /**
     * @return Struct\GeneralInfo[]
     */
    public function getAll(): array
    {
        return $this->getItems(Struct\GeneralInfo::class, 'gen_info');
    }
}


================================================
FILE: src/Api/Operator/SiteAlias.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

use PleskX\Api\Struct\SiteAlias as Struct;

class SiteAlias extends \PleskX\Api\Operator
{
    public function create(array $properties, array $preferences = []): Struct\Info
    {
        $packet = $this->client->getPacket();
        $info = $packet->addChild($this->wrapperTag)->addChild('create');

        if (count($preferences) > 0) {
            $prefs = $info->addChild('pref');

            foreach ($preferences as $key => $value) {
                $prefs->addChild($key, is_bool($value) ? ($value ? '1' : '0') : $value);
            }
        }

        $info->addChild('site-id', $properties['site-id']);
        $info->addChild('name', $properties['name']);

        $response = $this->client->request($packet);

        return new Struct\Info($response);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return bool
     */
    public function delete(string $field, $value): bool
    {
        return $this->deleteBy($field, $value, 'delete');
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\GeneralInfo
     */
    public function get(string $field, $value): Struct\GeneralInfo
    {
        $items = $this->getAll($field, $value);

        return reset($items);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\GeneralInfo[]
     */
    public function getAll($field = null, $value = null): array
    {
        $packet = $this->client->getPacket();
        $getTag = $packet->addChild($this->wrapperTag)->addChild('get');

        $filterTag = $getTag->addChild('filter');
        if (!is_null($field)) {
            $filterTag->{$field} = (string) $value;
        }

        $response = $this->client->request($packet, \PleskX\Api\Client::RESPONSE_FULL);
        $items = [];
        foreach ((array) $response->xpath('//result') as $xmlResult) {
            if (!$xmlResult) {
                continue;
            }
            if (!is_null($xmlResult->info)) {
                $item = new Struct\GeneralInfo($xmlResult->info);
                $items[] = $item;
            }
        }

        return $items;
    }
}


================================================
FILE: src/Api/Operator/Subdomain.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

use PleskX\Api\Struct\Subdomain as Struct;

class Subdomain extends \PleskX\Api\Operator
{
    public function create(array $properties): Struct\Info
    {
        $packet = $this->client->getPacket();
        $info = $packet->addChild($this->wrapperTag)->addChild('add');

        foreach ($properties as $name => $value) {
            if (is_array($value)) {
                foreach ($value as $propertyName => $propertyValue) {
                    $property = $info->addChild($name);
                    /** @psalm-suppress UndefinedPropertyAssignment */
                    $property->name = $propertyName;
                    /** @psalm-suppress UndefinedPropertyAssignment */
                    $property->value = $propertyValue;
                }
                continue;
            }
            $info->{$name} = $value;
        }

        $response = $this->client->request($packet);

        return new Struct\Info($response);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return bool
     */
    public function delete(string $field, $value): bool
    {
        return $this->deleteBy($field, $value);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\Info
     */
    public function get(string $field, $value): Struct\Info
    {
        $items = $this->getAll($field, $value);

        return reset($items);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\Info[]
     */
    public function getAll($field = null, $value = null): array
    {
        $packet = $this->client->getPacket();
        $getTag = $packet->addChild($this->wrapperTag)->addChild('get');

        $filterTag = $getTag->addChild('filter');
        if (!is_null($field)) {
            $filterTag->addChild($field, (string) $value);
        }

        $response = $this->client->request($packet, \PleskX\Api\Client::RESPONSE_FULL);

        $items = [];
        foreach ((array) $response->xpath('//result') as $xmlResult) {
            if (!$xmlResult || empty($xmlResult->data)) {
                continue;
            }
            $item = new Struct\Info($xmlResult->data);
            $item->id = (int) $xmlResult->id;
            $items[] = $item;
        }

        return $items;
    }
}


================================================
FILE: src/Api/Operator/Ui.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

use PleskX\Api\Struct\Ui as Struct;

class Ui extends \PleskX\Api\Operator
{
    public function getNavigation(): array
    {
        $response = $this->request('get-navigation');

        /** @psalm-suppress ImplicitToStringCast, PossiblyNullArgument */
        return unserialize(base64_decode($response->navigation));
    }

    public function createCustomButton(string $owner, array $properties): int
    {
        $packet = $this->client->getPacket();
        $buttonNode = $packet->addChild($this->wrapperTag)->addChild('create-custombutton');
        $buttonNode->addChild('owner')->addChild($owner);
        $propertiesNode = $buttonNode->addChild('properties');

        foreach ($properties as $name => $value) {
            $propertiesNode->{$name} = $value;
        }

        $response = $this->client->request($packet);

        return (int) $response->id;
    }

    public function getCustomButton(int $id): Struct\CustomButton
    {
        $response = $this->request("get-custombutton.filter.custombutton-id=$id");

        return new Struct\CustomButton($response);
    }

    public function deleteCustomButton(int $id): bool
    {
        return $this->deleteBy('custombutton-id', $id, 'delete-custombutton');
    }
}


================================================
FILE: src/Api/Operator/VirtualDirectory.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

class VirtualDirectory extends \PleskX\Api\Operator
{
    protected string $wrapperTag = 'virtdir';
}


================================================
FILE: src/Api/Operator/Webspace.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Operator;

use PleskX\Api\Operator;
use PleskX\Api\Struct\Webspace as Struct;

class Webspace extends Operator
{
    public function getPermissionDescriptor(): Struct\PermissionDescriptor
    {
        $response = $this->request('get-permission-descriptor.filter');

        return new Struct\PermissionDescriptor($response);
    }

    public function getLimitDescriptor(): Struct\LimitDescriptor
    {
        $response = $this->request('get-limit-descriptor.filter');

        return new Struct\LimitDescriptor($response);
    }

    public function getPhysicalHostingDescriptor(): Struct\PhysicalHostingDescriptor
    {
        $response = $this->request('get-physical-hosting-descriptor.filter');

        return new Struct\PhysicalHostingDescriptor($response);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\PhpSettings
     */
    public function getPhpSettings(string $field, $value): Struct\PhpSettings
    {
        $packet = $this->client->getPacket();
        $getTag = $packet->addChild($this->wrapperTag)->addChild('get');

        $getTag->addChild('filter')->addChild($field, (string) $value);
        $getTag->addChild('dataset')->addChild('php-settings');

        $response = $this->client->request($packet, \PleskX\Api\Client::RESPONSE_FULL);

        return new Struct\PhpSettings($response);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\Limits
     */
    public function getLimits(string $field, $value): Struct\Limits
    {
        $items = $this->getItems(Struct\Limits::class, 'limits', $field, $value);

        return reset($items);
    }

    /**
     * @param array $properties
     * @param array|null $hostingProperties
     * @param string $planName
     *
     * @return Struct\Info
     */
    public function create(array $properties, ?array $hostingProperties = null, string $planName = ''): Struct\Info
    {
        $packet = $this->client->getPacket();
        $info = $packet->addChild($this->wrapperTag)->addChild('add');

        $infoGeneral = $info->addChild('gen_setup');
        foreach ($properties as $name => $value) {
            if (is_array($value)) {
                continue;
            } else {
                $infoGeneral->addChild($name, (string) $value);
            }
        }

        if ($hostingProperties) {
            $infoHosting = $info->addChild('hosting')->addChild('vrt_hst');
            foreach ($hostingProperties as $name => $value) {
                $property = $infoHosting->addChild('property');
                /** @psalm-suppress UndefinedPropertyAssignment */
                $property->name = $name;
                /** @psalm-suppress UndefinedPropertyAssignment */
                $property->value = $value;
            }

            if (isset($properties['ip_address'])) {
                foreach ((array) $properties['ip_address'] as $ipAddress) {
                    $infoHosting->addChild('ip_address', $ipAddress);
                }
            }
        }

        if ('' !== $planName) {
            $info->addChild('plan-name', $planName);
        }

        $response = $this->client->request($packet);

        return new Struct\Info($response, $properties['name'] ?? '');
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return bool
     */
    public function delete(string $field, $value): bool
    {
        return $this->deleteBy($field, $value);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\GeneralInfo
     */
    public function get(string $field, $value): Struct\GeneralInfo
    {
        $items = $this->getItems(Struct\GeneralInfo::class, 'gen_info', $field, $value);

        return reset($items);
    }

    /**
     * @return Struct\GeneralInfo[]
     */
    public function getAll(): array
    {
        return $this->getItems(Struct\GeneralInfo::class, 'gen_info');
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return Struct\DiskUsage
     */
    public function getDiskUsage(string $field, $value): Struct\DiskUsage
    {
        $items = $this->getItems(Struct\DiskUsage::class, 'disk_usage', $field, $value);

        return reset($items);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return bool
     */
    public function enable(string $field, $value): bool
    {
        return $this->setProperties($field, $value, ['status' => 0]);
    }

    /**
     * @param string $field
     * @param int|string $value
     *
     * @return bool
     */
    public function disable(string $field, $value): bool
    {
        return $this->setProperties($field, $value, ['status' => 16]);
    }

    /**
     * @param string $field
     * @param int|string $value
     * @param array $properties
     *
     * @return bool
     */
    public function setProperties(string $field, $value, array $properties): bool
    {
        $packet = $this->client->getPacket();
        $setTag = $packet->addChild($this->wrapperTag)->addChild('set');
        $setTag->addChild('filter')->addChild($field, (string) $value);
        $genInfoTag = $setTag->addChild('values')->addChild('gen_setup');
        foreach ($properties as $property => $propertyValue) {
            $genInfoTag->addChild($property, (string) $propertyValue);
        }

        $response = $this->client->request($packet);

        return 'ok' === (string) $response->status;
    }
}


================================================
FILE: src/Api/Operator.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api;

class Operator
{
    protected string $wrapperTag = '';
    protected Client $client;

    public function __construct(Client $client)
    {
        $this->client = $client;

        if ('' === $this->wrapperTag) {
            $classNameParts = explode('\\', get_class($this));
            $this->wrapperTag = end($classNameParts);
            $this->wrapperTag = strtolower(preg_replace('/([a-z])([A-Z])/', '\1-\2', $this->wrapperTag));
        }
    }

    /**
     * Perform plain API request.
     *
     * @param string|array $request
     * @param int $mode
     *
     * @return XmlResponse
     */
    public function request($request, $mode = Client::RESPONSE_SHORT): XmlResponse
    {
        $wrapperTag = $this->wrapperTag;

        if (is_array($request)) {
            $request = [$wrapperTag => $request];
        } elseif (preg_match('/^[a-z]/', $request)) {
            $request = "$wrapperTag.$request";
        } else {
            $request = "<$wrapperTag>$request</$wrapperTag>";
        }

        return $this->client->request($request, $mode);
    }

    /**
     * @param string $field
     * @param int|string $value
     * @param string $deleteMethodName
     *
     * @return bool
     */
    protected function deleteBy(string $field, $value, string $deleteMethodName = 'del'): bool
    {
        $response = $this->request([
            $deleteMethodName => [
                'filter' => [
                    $field => $value,
                ],
            ],
        ]);

        return 'ok' === (string) $response->status;
    }

    /**
     * @param string $structClass
     * @param string $infoTag
     * @param string|null $field
     * @param int|string|null $value
     * @param callable|null $filter
     *
     * @return array
     */
    protected function getItems($structClass, $infoTag, $field = null, $value = null, ?callable $filter = null): array
    {
        $packet = $this->client->getPacket();
        $getTag = $packet->addChild($this->wrapperTag)->addChild('get');

        $filterTag = $getTag->addChild('filter');
        if (!is_null($field)) {
            $filterTag->{$field} = (string) $value;
        }

        $getTag->addChild('dataset')->addChild($infoTag);

        $response = $this->client->request($packet, \PleskX\Api\Client::RESPONSE_FULL);

        $items = [];
        foreach ((array) $response->xpath('//result') as $xmlResult) {
            if (!$xmlResult || !isset($xmlResult->data) || !isset($xmlResult->data->$infoTag)) {
                continue;
            }
            if (!is_null($filter) && !$filter($xmlResult->data->$infoTag)) {
                continue;
            }
            /** @psalm-suppress InvalidStringClass */
            $item = new $structClass($xmlResult->data->$infoTag);
            if (isset($xmlResult->id) && property_exists($item, 'id')) {
                $item->id = (int) $xmlResult->id;
            }
            $items[] = $item;
        }

        return $items;
    }
}


================================================
FILE: src/Api/Struct/Certificate/Info.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Certificate;

use PleskX\Api\AbstractStruct;

class Info extends AbstractStruct
{
    public ?string $request = null;
    public ?string $privateKey = null;
    public ?string $publicKey = null;
    public ?string $publicKeyCA = null;

    public function __construct($input)
    {
        if ($input instanceof \SimpleXMLElement) {
            $this->initScalarProperties($input, [
                ['csr' => 'request'],
                ['pvt' => 'privateKey'],
            ]);
        } else {
            foreach ($input as $name => $value) {
                $this->$name = $value;
            }
        }
    }

    public function getMapping(): array
    {
        return array_filter([
            'csr' => $this->request,
            'pvt' => $this->privateKey,
            'cert' => $this->publicKey,
            'ca' => $this->publicKeyCA,
        ]);
    }
}


================================================
FILE: src/Api/Struct/Customer/GeneralInfo.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Customer;

use PleskX\Api\AbstractStruct;

class GeneralInfo extends AbstractStruct
{
    public int $id;
    public string $company;
    public string $personalName;
    public string $login;
    public string $guid;
    public string $email;
    public string $phone;
    public string $fax;
    public string $address;
    public string $postalCode;
    public string $city;
    public string $state;
    public string $country;
    public string $description;
    public string $externalId;
    public bool $enabled;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            ['cname' => 'company'],
            ['pname' => 'personalName'],
            'login',
            'guid',
            'email',
            'phone',
            'fax',
            'address',
            ['pcode' => 'postalCode'],
            'city',
            'state',
            'country',
            'external-id',
            'description',
        ]);

        $this->enabled = '0' === (string) $apiResponse->status;
    }
}


================================================
FILE: src/Api/Struct/Customer/Info.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Customer;

use PleskX\Api\AbstractStruct;

class Info extends AbstractStruct
{
    public int $id;
    public string $guid;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'id',
            'guid',
        ]);
    }
}


================================================
FILE: src/Api/Struct/Database/Info.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Database;

use PleskX\Api\AbstractStruct;

class Info extends AbstractStruct
{
    public int $id;
    public string $name;
    public string $type;
    public int $webspaceId;
    public int $dbServerId;
    public int $defaultUserId;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'id',
            'name',
            'type',
            'webspace-id',
            'db-server-id',
            'default-user-id',
        ]);
    }
}


================================================
FILE: src/Api/Struct/Database/UserInfo.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Database;

use PleskX\Api\AbstractStruct;

class UserInfo extends AbstractStruct
{
    public int $id;
    public string $login;
    public int $dbId;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'id',
            'login',
            'db-id',
        ]);
    }
}


================================================
FILE: src/Api/Struct/DatabaseServer/Info.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\DatabaseServer;

use PleskX\Api\AbstractStruct;

class Info extends AbstractStruct
{
    public int $id;
    public string $host;
    public int $port;
    public string $type;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'id',
            'host',
            'port',
            'type',
        ]);
    }
}


================================================
FILE: src/Api/Struct/Dns/Info.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Dns;

use PleskX\Api\AbstractStruct;

class Info extends AbstractStruct
{
    public int $id;
    public int $siteId;
    public int $siteAliasId;
    public string $type;
    public string $host;
    public string $value;
    public string $opt;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'id',
            'site-id',
            'site-alias-id',
            'type',
            'host',
            'value',
            'opt',
        ]);
    }
}


================================================
FILE: src/Api/Struct/EventLog/DetailedEvent.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\EventLog;

use PleskX\Api\AbstractStruct;

class DetailedEvent extends AbstractStruct
{
    public int $id;
    public string $type;
    public int $time;
    public string $class;
    public string $objectId;
    public string $user;
    public string $host;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'id',
            'type',
            'time',
            'class',
            ['obj_id' => 'objectId'],
            'user',
            'host',
        ]);
    }
}


================================================
FILE: src/Api/Struct/EventLog/Event.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\EventLog;

use PleskX\Api\AbstractStruct;

class Event extends AbstractStruct
{
    public string $type;
    public int $time;
    public string $class;
    public string $id;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'type',
            'time',
            'class',
            'id',
        ]);
    }
}


================================================
FILE: src/Api/Struct/Ip/Info.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Ip;

use PleskX\Api\AbstractStruct;

class Info extends AbstractStruct
{
    public string $ipAddress;
    public string $netmask;
    public string $type;
    public string $interface;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'ip_address',
            'netmask',
            'type',
            'interface',
        ]);
    }
}


================================================
FILE: src/Api/Struct/Locale/Info.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Locale;

use PleskX\Api\AbstractStruct;

class Info extends AbstractStruct
{
    public string $id;
    public string $language;
    public string $country;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'id',
            ['lang' => 'language'],
            'country',
        ]);
    }
}


================================================
FILE: src/Api/Struct/Mail/GeneralInfo.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Mail;

use PleskX\Api\AbstractStruct;

class GeneralInfo extends AbstractStruct
{
    public int $id;
    public string $name;
    public string $description;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'id',
            'name',
            'description',
        ]);
    }
}


================================================
FILE: src/Api/Struct/Mail/Info.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Mail;

use PleskX\Api\AbstractStruct;

class Info extends AbstractStruct
{
    public int $id;
    public string $name;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'id',
            'name',
        ]);
    }
}


================================================
FILE: src/Api/Struct/PhpHandler/Info.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\PhpHandler;

use PleskX\Api\AbstractStruct;

class Info extends AbstractStruct
{
    public string $id;
    public string $displayName;
    public string $fullVersion;
    public string $version;
    public string $type;
    public string $path;
    public string $clipath;
    public string $phpini;
    public string $custom;
    public string $handlerStatus;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'id',
            'display-name',
            'full-version',
            'version',
            'type',
            'path',
            'clipath',
            'phpini',
            'custom',
            'handler-status',
        ]);
    }
}


================================================
FILE: src/Api/Struct/ProtectedDirectory/DataInfo.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\ProtectedDirectory;

use PleskX\Api\AbstractStruct;

class DataInfo extends AbstractStruct
{
    public string $name;
    public string $header;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'name',
            'header',
        ]);
    }
}


================================================
FILE: src/Api/Struct/ProtectedDirectory/Info.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\ProtectedDirectory;

use PleskX\Api\AbstractStruct;

class Info extends AbstractStruct
{
    public int $id;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'id',
        ]);
    }
}


================================================
FILE: src/Api/Struct/ProtectedDirectory/UserInfo.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\ProtectedDirectory;

use PleskX\Api\AbstractStruct;

class UserInfo extends AbstractStruct
{
    public int $id;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'id',
        ]);
    }
}


================================================
FILE: src/Api/Struct/Reseller/GeneralInfo.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Reseller;

use PleskX\Api\AbstractStruct;

class GeneralInfo extends AbstractStruct
{
    public int $id;
    public string $personalName;
    public string $login;
    public array $permissions;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        if (!is_null($apiResponse->{'gen-info'})) {
            $this->initScalarProperties($apiResponse->{'gen-info'}, [
                ['pname' => 'personalName'],
                'login',
            ]);
        }

        $this->permissions = [];
        foreach ($apiResponse->permissions->permission ?? [] as $permissionInfo) {
            $this->permissions[(string) $permissionInfo->name] = (string) $permissionInfo->value;
        }
    }
}


================================================
FILE: src/Api/Struct/Reseller/Info.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Reseller;

use PleskX\Api\AbstractStruct;

class Info extends AbstractStruct
{
    public int $id;
    public string $guid;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'id',
            'guid',
        ]);
    }
}


================================================
FILE: src/Api/Struct/SecretKey/Info.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\SecretKey;

use PleskX\Api\AbstractStruct;

class Info extends AbstractStruct
{
    public string $key;
    public string $ipAddress;
    public string $description;
    public string $login;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'key',
            'ip_address',
            'description',
            'login',
        ]);
    }
}


================================================
FILE: src/Api/Struct/Server/Admin.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Server;

use PleskX\Api\AbstractStruct;

class Admin extends AbstractStruct
{
    public string $companyName;
    public string $name;
    public string $email;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            ['admin_cname' => 'companyName'],
            ['admin_pname' => 'name'],
            ['admin_email' => 'email'],
        ]);
    }
}


================================================
FILE: src/Api/Struct/Server/GeneralInfo.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Server;

use PleskX\Api\AbstractStruct;

class GeneralInfo extends AbstractStruct
{
    public string $serverName;
    public string $serverGuid;
    public string $mode;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'server_name',
            'server_guid',
            'mode',
        ]);
    }
}


================================================
FILE: src/Api/Struct/Server/Preferences.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Server;

use PleskX\Api\AbstractStruct;

class Preferences extends AbstractStruct
{
    public int $statTtl;
    public int $trafficAccounting;
    public int $restartApacheInterval;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'stat_ttl',
            'traffic_accounting',
            'restart_apache_interval',
        ]);
    }
}


================================================
FILE: src/Api/Struct/Server/SessionPreferences.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Server;

use PleskX\Api\AbstractStruct;

class SessionPreferences extends AbstractStruct
{
    public int $loginTimeout;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'login_timeout',
        ]);
    }
}


================================================
FILE: src/Api/Struct/Server/Statistics/DiskSpace.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Server\Statistics;

use PleskX\Api\AbstractStruct;

class DiskSpace extends AbstractStruct
{
    public int $total;
    public int $used;
    public int $free;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'total',
            'used',
            'free',
        ]);
    }
}


================================================
FILE: src/Api/Struct/Server/Statistics/LoadAverage.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Server\Statistics;

use PleskX\Api\AbstractStruct;

class LoadAverage extends AbstractStruct
{
    public float $load1min;
    public float $load5min;
    public float $load15min;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->load1min = (float) $apiResponse->l1 / 100.0;
        $this->load5min = (float) $apiResponse->l5 / 100.0;
        $this->load15min = (float) $apiResponse->l15 / 100.0;
    }
}


================================================
FILE: src/Api/Struct/Server/Statistics/Memory.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Server\Statistics;

use PleskX\Api\AbstractStruct;

class Memory extends AbstractStruct
{
    public int $total;
    public int $used;
    public int $free;
    public int $shared;
    public int $buffer;
    public int $cached;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'total',
            'used',
            'free',
            'shared',
            'buffer',
            'cached',
        ]);
    }
}


================================================
FILE: src/Api/Struct/Server/Statistics/Objects.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Server\Statistics;

use PleskX\Api\AbstractStruct;

class Objects extends AbstractStruct
{
    public int $clients;
    public int $domains;
    public int $databases;
    public int $activeDomains;
    public int $mailBoxes;
    public int $mailRedirects;
    public int $mailGroups;
    public int $mailResponders;
    public int $databaseUsers;
    public int $problemClients;
    public int $problemDomains;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'clients',
            'domains',
            'databases',
            ['active_domains' => 'activeDomains'],
            ['mail_boxes' => 'mailBoxes'],
            ['mail_redirects' => 'mailRedirects'],
            ['mail_groups' => 'mailGroups'],
            ['mail_responders' => 'mailResponders'],
            ['database_users' => 'databaseUsers'],
            ['problem_clients' => 'problemClients'],
            ['problem_domains' => 'problemDomains'],
        ]);
    }
}


================================================
FILE: src/Api/Struct/Server/Statistics/Other.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Server\Statistics;

use PleskX\Api\AbstractStruct;

class Other extends AbstractStruct
{
    public string $cpu;
    public int $uptime;
    public bool $insideVz;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'cpu',
            'uptime',
            ['inside_vz' => 'insideVz'],
        ]);
    }
}


================================================
FILE: src/Api/Struct/Server/Statistics/Swap.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Server\Statistics;

use PleskX\Api\AbstractStruct;

class Swap extends AbstractStruct
{
    public int $total;
    public int $used;
    public int $free;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'total',
            'used',
            'free',
        ]);
    }
}


================================================
FILE: src/Api/Struct/Server/Statistics/Version.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Server\Statistics;

use PleskX\Api\AbstractStruct;

class Version extends AbstractStruct
{
    public string $internalName;
    public string $version;
    public string $build;
    public string $osName;
    public string $osVersion;
    public string $osRelease;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            ['plesk_name' => 'internalName'],
            ['plesk_version' => 'version'],
            ['plesk_build' => 'build'],
            ['plesk_os' => 'osName'],
            ['plesk_os_version' => 'osVersion'],
            ['os_release' => 'osRelease'],
        ]);
    }
}


================================================
FILE: src/Api/Struct/Server/Statistics.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Server;

use PleskX\Api\AbstractStruct;

class Statistics extends AbstractStruct
{
    /** @var Statistics\Objects */
    public $objects;

    /** @var Statistics\Version */
    public $version;

    /** @var Statistics\Other */
    public $other;

    /** @var Statistics\LoadAverage */
    public $loadAverage;

    /** @var Statistics\Memory */
    public $memory;

    /** @var Statistics\Swap */
    public $swap;

    /** @var Statistics\DiskSpace[] */
    public $diskSpace;

    /**
     * @param \SimpleXMLElement $apiResponse
     * @psalm-suppress PossiblyNullArgument
     */
    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->objects = new Statistics\Objects($apiResponse->objects);
        $this->version = new Statistics\Version($apiResponse->version);
        $this->other = new Statistics\Other($apiResponse->other);
        $this->loadAverage = new Statistics\LoadAverage($apiResponse->load_avg);
        $this->memory = new Statistics\Memory($apiResponse->mem);
        $this->swap = new Statistics\Swap($apiResponse->swap);

        $this->diskSpace = [];
        foreach ($apiResponse->diskspace ?? [] as $disk) {
            $this->diskSpace[(string) $disk->device->name] = new Statistics\DiskSpace($disk->device);
        }
    }
}


================================================
FILE: src/Api/Struct/Server/UpdatesInfo.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Server;

use PleskX\Api\AbstractStruct;

class UpdatesInfo extends AbstractStruct
{
    public string $lastInstalledUpdate;
    public bool $installUpdatesAutomatically;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'last_installed_update',
            'install_updates_automatically',
        ]);
    }
}


================================================
FILE: src/Api/Struct/ServicePlan/Info.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\ServicePlan;

use PleskX\Api\AbstractStruct;

class Info extends AbstractStruct
{
    public int $id;
    public string $name;
    public string $guid;
    public string $externalId;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'id',
            'name',
            'guid',
            'external-id',
        ]);
    }
}


================================================
FILE: src/Api/Struct/ServicePlanAddon/Info.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\ServicePlanAddon;

use PleskX\Api\AbstractStruct;

class Info extends AbstractStruct
{
    public int $id;
    public string $name;
    public string $guid;
    public string $externalId;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'id',
            'name',
            'guid',
            'external-id',
        ]);
    }
}


================================================
FILE: src/Api/Struct/Session/Info.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Session;

use PleskX\Api\AbstractStruct;

class Info extends AbstractStruct
{
    public string $id;
    public string $type;
    public string $ipAddress;
    public string $login;
    public string $loginTime;
    public string $idle;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'id',
            'type',
            'ip-address',
            'login',
            'login-time',
            'idle',
        ]);
    }
}


================================================
FILE: src/Api/Struct/Site/GeneralInfo.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Site;

use PleskX\Api\AbstractStruct;

class GeneralInfo extends AbstractStruct
{
    public int $id;
    public string $creationDate;
    public string $name;
    public string $asciiName;
    public string $guid;
    public string $status;
    public int $realSize;
    public array $ipAddresses = [];
    public string $description;
    public string $webspaceGuid;
    public int $webspaceId;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            ['cr_date' => 'creationDate'],
            'name',
            'ascii-name',
            'status',
            'real_size',
            'guid',
            'description',
            'webspace-guid',
            'webspace-id',
        ]);

        foreach ($apiResponse->dns_ip_address ?? [] as $ip) {
            $this->ipAddresses[] = (string) $ip;
        }
    }
}


================================================
FILE: src/Api/Struct/Site/HostingInfo.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Site;

use PleskX\Api\AbstractStruct;

class HostingInfo extends AbstractStruct
{
    public array $properties = [];
    public string $ipAddress;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        foreach ($apiResponse->vrt_hst->property ?? [] as $property) {
            $this->properties[(string) $property->name] = (string) $property->value;
        }

        if (!is_null($apiResponse->vrt_hst)) {
            $this->initScalarProperties($apiResponse->vrt_hst, [
                'ip_address',
            ]);
        }
    }
}


================================================
FILE: src/Api/Struct/Site/Info.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Site;

use PleskX\Api\AbstractStruct;

class Info extends AbstractStruct
{
    public int $id;
    public string $guid;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'id',
            'guid',
        ]);
    }
}


================================================
FILE: src/Api/Struct/SiteAlias/GeneralInfo.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\SiteAlias;

use PleskX\Api\AbstractStruct;

class GeneralInfo extends AbstractStruct
{
    public string $name;
    public string $asciiName;
    public string $status;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'name',
            'ascii-name',
            'status',
        ]);
    }
}


================================================
FILE: src/Api/Struct/SiteAlias/Info.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\SiteAlias;

use PleskX\Api\AbstractStruct;

class Info extends AbstractStruct
{
    public string $status;
    public int $id;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'id',
            'status',
        ]);
    }
}


================================================
FILE: src/Api/Struct/Subdomain/Info.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Subdomain;

use PleskX\Api\AbstractStruct;

class Info extends AbstractStruct
{
    public int $id;
    public string $parent;
    public string $name;
    public array $properties;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->properties = [];
        $this->initScalarProperties($apiResponse, [
            'id',
            'parent',
            'name',
        ]);
        foreach ($apiResponse->property ?? [] as $propertyInfo) {
            $this->properties[(string) $propertyInfo->name] = (string) $propertyInfo->value;
        }
    }
}


================================================
FILE: src/Api/Struct/Ui/CustomButton.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Ui;

use PleskX\Api\AbstractStruct;

class CustomButton extends AbstractStruct
{
    public int $id;
    public int $sortKey;
    public bool $public;
    public bool $internal;
    public bool $noFrame;
    public string $place;
    public string $url;
    public string $text;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, ['id']);

        if (!is_null($apiResponse->properties)) {
            $this->initScalarProperties($apiResponse->properties, [
                'sort_key',
                'public',
                'internal',
                ['noframe' => 'noFrame'],
                'place',
                'url',
                'text',
            ]);
        }
    }
}


================================================
FILE: src/Api/Struct/Webspace/DiskUsage.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Webspace;

use PleskX\Api\AbstractStruct;

class DiskUsage extends AbstractStruct
{
    public int $httpdocs;
    public int $httpsdocs;
    public int $subdomains;
    public int $anonftp;
    public int $logs;
    public int $dbases;
    public int $mailboxes;
    public int $maillists;
    public int $domaindumps;
    public int $configs;
    public int $chroot;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'httpdocs',
            'httpsdocs',
            'subdomains',
            'anonftp',
            'logs',
            'dbases',
            'mailboxes',
            'maillists',
            'domaindumps',
            'configs',
            'chroot',
        ]);
    }
}


================================================
FILE: src/Api/Struct/Webspace/GeneralInfo.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Webspace;

use PleskX\Api\AbstractStruct;

class GeneralInfo extends AbstractStruct
{
    public int $id;
    public string $creationDate;
    public string $name;
    public string $asciiName;
    public string $status;
    public int $realSize;
    public int $ownerId;
    public array $ipAddresses = [];
    public string $guid;
    public string $vendorGuid;
    public string $description;
    public string $adminDescription;
    public bool $enabled;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            ['cr_date' => 'creationDate'],
            'name',
            'ascii-name',
            'status',
            'real_size',
            'owner-id',
            'guid',
            'vendor-guid',
            'description',
            'admin-description',
        ]);

        foreach ($apiResponse->dns_ip_address ?? [] as $ip) {
            $this->ipAddresses[] = (string) $ip;
        }

        $this->enabled = '0' === (string) $apiResponse->status;
    }
}


================================================
FILE: src/Api/Struct/Webspace/HostingPropertyInfo.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Webspace;

use PleskX\Api\AbstractStruct;

class HostingPropertyInfo extends AbstractStruct
{
    public string $name;
    public string $type;
    public string $label;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'name',
            'type',
            'label',
        ]);
    }
}


================================================
FILE: src/Api/Struct/Webspace/Info.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Webspace;

use PleskX\Api\AbstractStruct;

class Info extends AbstractStruct
{
    public int $id;
    public string $guid;
    public string $name;

    public function __construct(\SimpleXMLElement $apiResponse, string $name = '')
    {
        $this->initScalarProperties($apiResponse, [
            'id',
            'guid',
        ]);
        $this->name = $name;
    }
}


================================================
FILE: src/Api/Struct/Webspace/Limit.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Webspace;

use PleskX\Api\AbstractStruct;

class Limit extends AbstractStruct
{
    public string $name;
    public string $value;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'name',
            'value',
        ]);
    }
}


================================================
FILE: src/Api/Struct/Webspace/LimitDescriptor.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Webspace;

use PleskX\Api\AbstractStruct;

class LimitDescriptor extends AbstractStruct
{
    public array $limits;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->limits = [];

        foreach ($apiResponse->descriptor->property ?? [] as $propertyInfo) {
            $this->limits[(string) $propertyInfo->name] = new LimitInfo($propertyInfo);
        }
    }
}


================================================
FILE: src/Api/Struct/Webspace/LimitInfo.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Webspace;

use PleskX\Api\AbstractStruct;

class LimitInfo extends AbstractStruct
{
    public string $name;
    public string $type;
    public string $label;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'name',
            'type',
            'label',
        ]);
    }
}


================================================
FILE: src/Api/Struct/Webspace/Limits.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Webspace;

use PleskX\Api\AbstractStruct;

class Limits extends AbstractStruct
{
    public string $overuse;
    public array $limits;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, ['overuse']);
        $this->limits = [];

        foreach ($apiResponse->limit ?? [] as $limit) {
            $this->limits[(string) $limit->name] = new Limit($limit);
        }
    }
}


================================================
FILE: src/Api/Struct/Webspace/PermissionDescriptor.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Webspace;

use PleskX\Api\AbstractStruct;

class PermissionDescriptor extends AbstractStruct
{
    public array $permissions;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->permissions = [];

        foreach ($apiResponse->descriptor->property ?? [] as $propertyInfo) {
            $this->permissions[(string) $propertyInfo->name] = new PermissionInfo($propertyInfo);
        }
    }
}


================================================
FILE: src/Api/Struct/Webspace/PermissionInfo.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Webspace;

use PleskX\Api\AbstractStruct;

class PermissionInfo extends AbstractStruct
{
    public string $name;
    public string $type;
    public string $label;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->initScalarProperties($apiResponse, [
            'name',
            'type',
            'label',
        ]);
    }
}


================================================
FILE: src/Api/Struct/Webspace/PhpSettings.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Webspace;

use PleskX\Api\AbstractStruct;

class PhpSettings extends AbstractStruct
{
    public array $properties;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->properties = [];

        foreach ($apiResponse->webspace->get->result->data->{'php-settings'}->setting ?? [] as $setting) {
            $this->properties[(string) $setting->name] = (string) $setting->value;
        }
    }
}


================================================
FILE: src/Api/Struct/Webspace/PhysicalHostingDescriptor.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api\Struct\Webspace;

use PleskX\Api\AbstractStruct;

class PhysicalHostingDescriptor extends AbstractStruct
{
    public array $properties;

    public function __construct(\SimpleXMLElement $apiResponse)
    {
        $this->properties = [];

        foreach ($apiResponse->descriptor->property ?? [] as $propertyInfo) {
            $this->properties[(string) $propertyInfo->name] = new HostingPropertyInfo($propertyInfo);
        }
    }
}


================================================
FILE: src/Api/XmlResponse.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskX\Api;

/**
 * XML wrapper for responses.
 */
class XmlResponse extends \SimpleXMLElement
{
    /**
     * Retrieve value by node name.
     *
     * @param string $node
     *
     * @return string
     */
    public function getValue(string $node): string
    {
        $result = $this->xpath('//' . $node);
        if (is_array($result) && isset($result[0])) {
            return (string) $result[0];
        }

        return '';
    }
}


================================================
FILE: tests/AbstractTestCase.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskXTest;

use PHPUnit\Framework\TestCase;
use PleskX\Api\Client;
use PleskXTest\Utility\PasswordProvider;

abstract class AbstractTestCase extends TestCase
{
    protected static Client $client;

    private static array $webspaces = [];
    private static array $servicePlans = [];
    private static array $servicePlanAddons = [];

    public static function setUpBeforeClass(): void
    {
        $login = getenv('REMOTE_LOGIN') ?: 'admin';
        $password = getenv('REMOTE_PASSWORD') ?: 'changeme1Q**';
        $host = getenv('REMOTE_HOST') ?: 'localhost';
        $port = 8443;
        $scheme = 'https';

        $url = getenv('REMOTE_URL');
        if ($url) {
            $parsedUrl = parse_url($url);
            list($host, $port, $scheme) = [$parsedUrl['host'], $parsedUrl['port'], $parsedUrl['scheme']];
        }

        static::$client = new Client($host, $port, $scheme);
        static::$client->setCredentials($login, $password);

        $proxy = getenv('REMOTE_PROXY');
        if ($proxy) {
            static::$client->setProxy($proxy);
        }
    }

    public static function tearDownAfterClass(): void
    {
        foreach (self::$webspaces as $webspace) {
            try {
                static::$client->webspace()->delete('id', $webspace->id);
                // phpcs:ignore
            } catch (\Exception $e) {
            }
        }

        foreach (self::$servicePlans as $servicePlan) {
            try {
                static::$client->servicePlan()->delete('id', $servicePlan->id);
                // phpcs:ignore
            } catch (\Exception $e) {
            }
        }

        foreach (self::$servicePlanAddons as $servicePlanAddon) {
            try {
                static::$client->servicePlanAddon()->delete('id', $servicePlanAddon->id);
                // phpcs:ignore
            } catch (\Exception $e) {
            }
        }
    }

    protected static function getIpAddress(): string
    {
        $ips = static::$client->ip()->get();
        $ipInfo = reset($ips);

        return $ipInfo->ipAddress;
    }

    protected static function createWebspace(): \PleskX\Api\Struct\Webspace\Info
    {
        $id = uniqid();
        $webspace = static::$client->webspace()->create(
            [
                'name' => "test{$id}.test",
                'ip_address' => static::getIpAddress(),
            ],
            [
                'ftp_login' => "u{$id}",
                'ftp_password' => PasswordProvider::STRONG_PASSWORD,
            ]
        );
        self::$webspaces[] = $webspace;

        return $webspace;
    }

    protected static function createServicePlan(): \PleskX\Api\Struct\ServicePlan\Info
    {
        $id = uniqid();
        $servicePlan = static::$client->servicePlan()->create(['name' => "test{$id}plan"]);

        self::$servicePlans[] = $servicePlan;

        return $servicePlan;
    }

    protected static function createServicePlanAddon(): \PleskX\Api\Struct\ServicePlanAddon\Info
    {
        $id = uniqid();
        $servicePlanAddon = static::$client->servicePlanAddon()->create(['name' => "test{$id}planaddon"]);

        self::$servicePlanAddons[] = $servicePlanAddon;

        return $servicePlanAddon;
    }
}


================================================
FILE: tests/ApiClientTest.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskXTest;

use PleskX\Api\Client\Exception;

class ApiClientTest extends AbstractTestCase
{
    public function testWrongProtocol()
    {
        $this->expectException(\PleskX\Api\Exception::class);
        $this->expectExceptionCode(1005);

        $packet = static::$client->getPacket('100.0.0');
        $packet->addChild('server')->addChild('get_protos');
        static::$client->request($packet);
    }

    public function testUnknownOperator()
    {
        $this->expectException(\PleskX\Api\Exception::class);
        $this->expectExceptionCode(1014);

        $packet = static::$client->getPacket();
        $packet->addChild('unknown');
        static::$client->request($packet);
    }

    public function testInvalidXmlRequest()
    {
        $this->expectException(\PleskX\Api\Exception::class);
        $this->expectExceptionCode(1014);

        static::$client->request('<packet><wrongly formatted xml</packet>');
    }

    public function testInvalidCredentials()
    {
        $this->expectException(\PleskX\Api\Exception::class);
        $this->expectExceptionCode(1001);

        $host = static::$client->getHost();
        $port = static::$client->getPort();
        $protocol = static::$client->getProtocol();
        $client = new \PleskX\Api\Client($host, $port, $protocol);
        $client->setCredentials('bad-login', 'bad-password');
        $packet = static::$client->getPacket();
        $packet->addChild('server')->addChild('get_protos');
        $client->request($packet);
    }

    public function testInvalidSecretKey()
    {
        $this->expectException(\PleskX\Api\Exception::class);
        $this->expectExceptionCode(11003);

        $host = static::$client->getHost();
        $port = static::$client->getPort();
        $protocol = static::$client->getProtocol();
        $client = new \PleskX\Api\Client($host, $port, $protocol);
        $client->setSecretKey('bad-key');
        $packet = static::$client->getPacket();
        $packet->addChild('server')->addChild('get_protos');
        $client->request($packet);
    }

    public function testLatestMajorProtocol()
    {
        $packet = static::$client->getPacket('1.6');
        $packet->addChild('server')->addChild('get_protos');
        $result = static::$client->request($packet);
        $this->assertEquals('ok', $result->status);
    }

    public function testLatestMinorProtocol()
    {
        $packet = static::$client->getPacket('1.6.5');
        $packet->addChild('server')->addChild('get_protos');
        $result = static::$client->request($packet);
        $this->assertEquals('ok', $result->status);
    }

    public function testRequestShortSyntax()
    {
        $response = static::$client->request('server.get.gen_info');
        $this->assertGreaterThan(0, strlen($response->gen_info->server_name));
    }

    public function testOperatorPlainRequest()
    {
        $response = static::$client->server()->request('get.gen_info');
        $this->assertGreaterThan(0, strlen($response->gen_info->server_name));
        $this->assertEquals(36, strlen($response->getValue('server_guid')));
    }

    public function testRequestArraySyntax()
    {
        $response = static::$client->request([
            'server' => [
                'get' => [
                    'gen_info' => '',
                ],
            ],
        ]);
        $this->assertGreaterThan(0, strlen($response->gen_info->server_name));
    }

    public function testOperatorArraySyntax()
    {
        $response = static::$client->server()->request(['get' => ['gen_info' => '']]);
        $this->assertGreaterThan(0, strlen($response->gen_info->server_name));
    }

    public function testMultiRequest()
    {
        $responses = static::$client->multiRequest([
            'server.get_protos',
            'server.get.gen_info',
        ]);

        $this->assertCount(2, $responses);

        $protos = (array) $responses[0]->protos->proto;
        $generalInfo = $responses[1];

        $this->assertContains('1.6.6.0', $protos);
        $this->assertGreaterThan(0, strlen($generalInfo->gen_info->server_name));
    }

    public function testConnectionError()
    {
        $this->expectException(\PleskX\Api\Client\Exception::class);

        $client = new \PleskX\Api\Client('invalid-host.dom');
        $client->server()->getProtos();
    }

    public function testGetHost()
    {
        $client = new \PleskX\Api\Client('example.dom');
        $this->assertEquals('example.dom', $client->getHost());
    }

    public function testGetPort()
    {
        $client = new \PleskX\Api\Client('example.dom', 12345);
        $this->assertEquals(12345, $client->getPort());
    }

    public function testGetProtocol()
    {
        $client = new \PleskX\Api\Client('example.dom', 8880, 'http');
        $this->assertEquals('http', $client->getProtocol());
    }

    public function testSetVerifyResponse()
    {
        static::$client->setVerifyResponse(function ($xml) {
            if ($xml->xpath('//proto')) {
                throw new Exception('proto');
            }
        });

        try {
            static::$client->server()->getProtos();
        } catch (Exception $e) {
            $this->assertEquals('proto', $e->getMessage());
        } finally {
            static::$client->setVerifyResponse();
        }
    }
}


================================================
FILE: tests/CertificateTest.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskXTest;

use PleskX\Api\Struct\Certificate as Struct;

class CertificateTest extends AbstractTestCase
{
    private array $certificateProperties = [
        'bits' => 2048,
        'country' => 'CH',
        'state' => 'Schaffhausen',
        'location' => 'Schaffhausen',
        'company' => 'Plesk',
        'email' => 'info@plesk.com',
        'name' => 'plesk.com',
    ];
    private array $distinguishedNames = [
        "countryName" => "CH",
        "stateOrProvinceName" => "Schaffhausen",
        "localityName" => "Schaffhausen",
        "organizationName" => "Plesk",
        "emailAddress" => "info@plesk.com"
    ];

    public function testGenerate()
    {
        $certificate = static::$client->certificate()->generate($this->certificateProperties);
        $this->assertGreaterThan(0, strlen($certificate->request));
        $this->assertStringStartsWith('-----BEGIN CERTIFICATE REQUEST-----', $certificate->request);
        $this->assertGreaterThan(0, strlen($certificate->privateKey));
        $this->assertStringStartsWith('-----BEGIN PRIVATE KEY-----', $certificate->privateKey);
    }

    public function testInstall()
    {
        $certificate = static::$client->certificate()->generate($this->certificateProperties);

        $result = static::$client->certificate()->install([
            'name' => 'test',
            'admin' => true,
        ], $certificate->request, $certificate->privateKey);
        $this->assertTrue($result);

        static::$client->certificate()->delete('test', ['admin' => true]);
    }

    public function testUpdate()
    {
        $payLoad = [
            'name' => 'test',
            'admin' => true,
        ];
        $certificate = static::$client->certificate()->generate($this->certificateProperties);
        static::$client->certificate()->install($payLoad, $certificate);

        $certificate = $this->generateCertificateOpenSsl($this->distinguishedNames);
        $result = static::$client->certificate()->update($payLoad, $certificate);
        $this->assertTrue($result);

        static::$client->certificate()->delete('test', ['admin' => true]);
    }

    public function testDelete()
    {
        $certificate = static::$client->certificate()->generate($this->certificateProperties);

        static::$client->certificate()->install([
            'name' => 'test',
            'admin' => true,
        ], $certificate);

        $result = static::$client->certificate()->delete('test', ['admin' => true]);
        $this->assertTrue($result);
    }

    private function generateCertificateOpenSsl(array $dn): Struct\Info
    {
        $privkey = openssl_pkey_new([
            "private_key_bits" => 2048,
            "private_key_type" => OPENSSL_KEYTYPE_RSA,
        ]);
        $csr = openssl_csr_new($dn, $privkey, ['digest_alg' => 'sha256']);
        $x509 = openssl_csr_sign($csr, null, $privkey, $days = 365, ['digest_alg' => 'sha256']);

        openssl_csr_export($csr, $csrout);
        openssl_x509_export($x509, $certout);
        openssl_pkey_export($privkey, $pkeyout);

        return new Struct\Info([
            'publicKey' => $certout,
            'request' => $csrout,
            'privateKey' => $pkeyout
        ]);
    }
}


================================================
FILE: tests/CustomerTest.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskXTest;

use PleskXTest\Utility\KeyLimitChecker;
use PleskXTest\Utility\PasswordProvider;

class CustomerTest extends AbstractTestCase
{
    private array $customerProperties;

    public function setUp(): void
    {
        $this->customerProperties = [
            'cname' => 'Plesk',
            'pname' => 'John Smith',
            'login' => 'john-unit-test',
            'passwd' => PasswordProvider::STRONG_PASSWORD,
            'email' => 'john@smith.com',
            'external-id' => 'link:12345',
            'description' => 'Good guy',
        ];
    }

    public function testCreate()
    {
        $customer = static::$client->customer()->create($this->customerProperties);
        $this->assertIsInt($customer->id);
        $this->assertGreaterThan(0, $customer->id);

        static::$client->customer()->delete('id', $customer->id);
    }

    public function testDelete()
    {
        $customer = static::$client->customer()->create($this->customerProperties);
        $result = static::$client->customer()->delete('id', $customer->id);
        $this->assertTrue($result);
    }

    public function testGet()
    {
        $customer = static::$client->customer()->create($this->customerProperties);
        $customerInfo = static::$client->customer()->get('id', $customer->id);
        $this->assertEquals('Plesk', $customerInfo->company);
        $this->assertEquals('John Smith', $customerInfo->personalName);
        $this->assertEquals('john-unit-test', $customerInfo->login);
        $this->assertEquals('john@smith.com', $customerInfo->email);
        $this->assertEquals('Good guy', $customerInfo->description);
        $this->assertEquals('link:12345', $customerInfo->externalId);
        $this->assertEquals($customer->id, $customerInfo->id);

        static::$client->customer()->delete('id', $customer->id);
    }

    public function testGetAll()
    {
        $keyInfo = static::$client->server()->getKeyInfo();

        if (!KeyLimitChecker::checkByType($keyInfo, KeyLimitChecker::LIMIT_CLIENTS, 2)) {
            $this->markTestSkipped('License does not allow to create more than 1 customer.');
        }

        static::$client->customer()->create([
            'pname' => 'John Smith',
            'login' => 'customer-a',
            'passwd' => PasswordProvider::STRONG_PASSWORD,
        ]);
        static::$client->customer()->create([
            'pname' => 'Mike Black',
            'login' => 'customer-b',
            'passwd' => PasswordProvider::STRONG_PASSWORD,
        ]);

        $customersInfo = static::$client->customer()->getAll();
        $this->assertIsArray($customersInfo);

        $customersCheck = array_filter($customersInfo, function ($value) {
            return $value->personalName === 'John Smith' || $value->personalName === 'Mike Black';
        });
        $this->assertCount(2, $customersCheck);

        static::$client->customer()->delete('login', 'customer-a');
        static::$client->customer()->delete('login', 'customer-b');
    }

    public function testGetAllEmpty()
    {
        $customersInfo = static::$client->customer()->getAll();
        $this->assertCount(0, $customersInfo);
    }

    public function testEnable()
    {
        $customer = static::$client->customer()->create($this->customerProperties);
        static::$client->customer()->disable('id', $customer->id);
        static::$client->customer()->enable('id', $customer->id);
        $customerInfo = static::$client->customer()->get('id', $customer->id);
        $this->assertTrue($customerInfo->enabled);

        static::$client->customer()->delete('id', $customer->id);
    }

    public function testDisable()
    {
        $customer = static::$client->customer()->create($this->customerProperties);
        static::$client->customer()->disable('id', $customer->id);
        $customerInfo = static::$client->customer()->get('id', $customer->id);
        $this->assertFalse($customerInfo->enabled);

        static::$client->customer()->delete('id', $customer->id);
    }

    public function testSetProperties()
    {
        $customer = static::$client->customer()->create($this->customerProperties);
        static::$client->customer()->setProperties('id', $customer->id, [
            'pname' => 'Mike Black',
            'email' => 'mike@black.com',
        ]);
        $customerInfo = static::$client->customer()->get('id', $customer->id);
        $this->assertEquals('Mike Black', $customerInfo->personalName);
        $this->assertEquals('mike@black.com', $customerInfo->email);

        static::$client->customer()->delete('id', $customer->id);
    }
}


================================================
FILE: tests/DatabaseServerTest.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskXTest;

class DatabaseServerTest extends AbstractTestCase
{
    public function testGetSupportedTypes()
    {
        $types = static::$client->databaseServer()->getSupportedTypes();
        $this->assertGreaterThan(0, count($types));
        $this->assertContains('mysql', $types);
    }

    public function testGet()
    {
        $dbServer = static::$client->databaseServer()->get('id', 1);
        $this->assertEquals('localhost', $dbServer->host);
        $this->assertGreaterThan(0, $dbServer->port);
    }

    public function testGetAll()
    {
        $dbServers = static::$client->databaseServer()->getAll();
        $this->assertIsArray($dbServers);
        $this->assertGreaterThan(0, count($dbServers));
        $this->assertEquals('localhost', $dbServers[0]->host);
    }

    public function testGetDefault()
    {
        $dbServer = static::$client->databaseServer()->getDefault('mysql');
        $this->assertEquals('mysql', $dbServer->type);
        $this->assertGreaterThan(0, $dbServer->id);
    }
}


================================================
FILE: tests/DatabaseTest.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskXTest;

use PleskXTest\Utility\PasswordProvider;

class DatabaseTest extends AbstractTestCase
{
    private static \PleskX\Api\Struct\Webspace\Info $webspace;
    private static \PleskX\Api\Struct\DatabaseServer\Info $databaseServer;

    public static function setUpBeforeClass(): void
    {
        parent::setUpBeforeClass();
        static::$webspace = static::createWebspace();
        static::$databaseServer = static::$client->databaseServer()->getDefault('mysql');
    }

    public function testCreate()
    {
        $database = $this->createDatabase([
            'webspace-id' => static::$webspace->id,
            'name' => 'test1',
            'type' => static::$databaseServer->type,
            'db-server-id' => static::$databaseServer->id,
        ]);
        static::$client->database()->delete('id', $database->id);
    }

    public function testCreateUser()
    {
        $database = $this->createDatabase([
            'webspace-id' => static::$webspace->id,
            'name' => 'test1',
            'type' => static::$databaseServer->type,
            'db-server-id' => static::$databaseServer->id,
        ]);
        $user = $this->createUser([
            'db-id' => $database->id,
            'login' => 'test_user1',
            'password' => PasswordProvider::STRONG_PASSWORD,
        ]);
        static::$client->database()->deleteUser('id', $user->id);
        static::$client->database()->delete('id', $database->id);
    }

    public function testUpdateUser()
    {
        $database = $this->createDatabase([
            'webspace-id' => static::$webspace->id,
            'name' => 'test1',
            'type' => static::$databaseServer->type,
            'db-server-id' => static::$databaseServer->id,
        ]);
        $user = $this->createUser([
            'db-id' => $database->id,
            'login' => 'test_user1',
            'password' => PasswordProvider::STRONG_PASSWORD,
        ]);
        $updatedUser = static::$client->database()->updateUser([
            'id' => $user->id,
            'login' => 'test_user2',
            'password' => PasswordProvider::STRONG_PASSWORD,
        ]);
        $this->assertEquals(true, $updatedUser);
        static::$client->database()->deleteUser('id', $user->id);
        static::$client->database()->delete('id', $database->id);
    }

    public function testGetById()
    {
        $database = $this->createDatabase([
            'webspace-id' => static::$webspace->id,
            'name' => 'test1',
            'type' => static::$databaseServer->type,
            'db-server-id' => static::$databaseServer->id,
        ]);

        $db = static::$client->database()->get('id', $database->id);
        $this->assertEquals('test1', $db->name);
        $this->assertEquals(static::$databaseServer->type, $db->type);
        $this->assertEquals(static::$webspace->id, $db->webspaceId);
        $this->assertEquals(static::$databaseServer->id, $db->dbServerId);

        static::$client->database()->delete('id', $database->id);
    }

    public function testGetAllByWebspaceId()
    {
        $db1 = $this->createDatabase([
            'webspace-id' => static::$webspace->id,
            'name' => 'test1',
            'type' => static::$databaseServer->type,
            'db-server-id' => static::$databaseServer->id,
        ]);
        $db2 = $this->createDatabase([
            'webspace-id' => static::$webspace->id,
            'name' => 'test2',
            'type' => static::$databaseServer->type,
            'db-server-id' => static::$databaseServer->id,
        ]);
        $databases = static::$client->database()->getAll('webspace-id', static::$webspace->id);
        $this->assertEquals('test1', $databases[0]->name);
        $this->assertEquals('test2', $databases[1]->name);
        $this->assertEquals(static::$webspace->id, $databases[0]->webspaceId);
        $this->assertEquals(static::$databaseServer->id, $databases[1]->dbServerId);

        static::$client->database()->delete('id', $db1->id);
        static::$client->database()->delete('id', $db2->id);
    }

    public function testGetUserById()
    {
        $database = $this->createDatabase([
            'webspace-id' => static::$webspace->id,
            'name' => 'test1',
            'type' => static::$databaseServer->type,
            'db-server-id' => static::$databaseServer->id,
        ]);

        $user = $this->createUser([
            'db-id' => $database->id,
            'login' => 'test_user1',
            'password' => PasswordProvider::STRONG_PASSWORD,
        ]);

        $dbUser = static::$client->database()->getUser('id', $user->id);
        $this->assertEquals('test_user1', $dbUser->login);
        $this->assertEquals($database->id, $dbUser->dbId);

        static::$client->database()->deleteUser('id', $user->id);
        static::$client->database()->delete('id', $database->id);
    }

    public function testGetAllUsersByDbId()
    {
        $db1 = $this->createDatabase([
            'webspace-id' => static::$webspace->id,
            'name' => 'test1',
            'type' => static::$databaseServer->type,
            'db-server-id' => static::$databaseServer->id,
        ]);
        $db2 = $this->createDatabase([
            'webspace-id' => static::$webspace->id,
            'name' => 'test2',
            'type' => static::$databaseServer->type,
            'db-server-id' => static::$databaseServer->id,
        ]);
        $user1 = $this->createUser([
            'db-id' => $db1->id,
            'login' => 'test_user1',
            'password' => PasswordProvider::STRONG_PASSWORD,
        ]);

        $user2 = $this->createUser([
            'db-id' => $db1->id,
            'login' => 'test_user2',
            'password' => PasswordProvider::STRONG_PASSWORD,
        ]);

        $user3 = $this->createUser([
            'db-id' => $db2->id,
            'login' => 'test_user3',
            'password' => PasswordProvider::STRONG_PASSWORD,
        ]);

        $dbUsers = static::$client->database()->getAllUsers('db-id', $db1->id);
        $this->assertEquals(2, count($dbUsers));
        $this->assertEquals('test_user1', $dbUsers[0]->login);
        $this->assertEquals('test_user2', $dbUsers[1]->login);

        static::$client->database()->deleteUser('id', $user1->id);
        static::$client->database()->deleteUser('id', $user2->id);
        static::$client->database()->deleteUser('id', $user3->id);
        static::$client->database()->delete('id', $db1->id);
        static::$client->database()->delete('id', $db2->id);
    }

    public function testDelete()
    {
        $database = $this->createDatabase([
            'webspace-id' => static::$webspace->id,
            'name' => 'test1',
            'type' => static::$databaseServer->type,
            'db-server-id' => static::$databaseServer->id,
        ]);
        $result = static::$client->database()->delete('id', $database->id);
        $this->assertTrue($result);
    }

    public function testDeleteUser()
    {
        $database = $this->createDatabase([
            'webspace-id' => static::$webspace->id,
            'name' => 'test1',
            'type' => static::$databaseServer->type,
            'db-server-id' => static::$databaseServer->id,
        ]);
        $user = $this->createUser([
            'db-id' => $database->id,
            'login' => 'test_user1',
            'password' => PasswordProvider::STRONG_PASSWORD,
        ]);

        $result = static::$client->database()->deleteUser('id', $user->id);
        $this->assertTrue($result);
        static::$client->database()->delete('id', $database->id);
    }

    private function createDatabase(array $params): \PleskX\Api\Struct\Database\Info
    {
        $database = static::$client->database()->create($params);
        $this->assertIsInt($database->id);
        $this->assertGreaterThan(0, $database->id);

        return $database;
    }

    private function createUser(array $params): \PleskX\Api\Struct\Database\UserInfo
    {
        $user = static::$client->database()->createUser($params);
        $this->assertIsInt($user->id);
        $this->assertGreaterThan(0, $user->id);

        return $user;
    }

    public function testGetAllForWebspaceWithNoDatabases()
    {
        $databases = static::$client->database()->getAll('webspace-id', static::$webspace->id);

        $this->assertIsArray($databases);
        $this->assertEmpty($databases);
    }
}


================================================
FILE: tests/DnsTemplateTest.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskXTest;

class DnsTemplateTest extends AbstractTestCase
{
    private static bool $isDnsSupported;

    public static function setUpBeforeClass(): void
    {
        parent::setUpBeforeClass();

        $serviceStates = static::$client->server()->getServiceStates();
        static::$isDnsSupported = $serviceStates['dns'] && ('running' == $serviceStates['dns']['state']);
    }

    protected function setUp(): void
    {
        parent::setUp();

        if (!static::$isDnsSupported) {
            $this->markTestSkipped('DNS system is not supported.');
        }
    }

    public function testCreate()
    {
        $dns = static::$client->dnsTemplate()->create([
            'type' => 'TXT',
            'host' => 'test.create',
            'value' => 'value',
        ]);
        $this->assertIsInt($dns->id);
        $this->assertGreaterThan(0, $dns->id);
        $this->assertEquals(0, $dns->siteId);
        $this->assertEquals(0, $dns->siteAliasId);
        static::$client->dnsTemplate()->delete('id', $dns->id);
    }

    public function testGetById()
    {
        $dns = static::$client->dnsTemplate()->create([
            'type' => 'TXT',
            'host' => 'test.get.by.id',
            'value' => 'value',
        ]);

        $dnsInfo = static::$client->dnsTemplate()->get('id', $dns->id);
        $this->assertEquals('TXT', $dnsInfo->type);
        $this->assertEquals('value', $dnsInfo->value);

        static::$client->dnsTemplate()->delete('id', $dns->id);
    }

    public function testGetAll()
    {
        $dns = static::$client->dnsTemplate()->create([
            'type' => 'TXT',
            'host' => 'test.get.all',
            'value' => 'value',
        ]);
        $dns2 = static::$client->dnsTemplate()->create([
            'type' => 'TXT',
            'host' => 'test.get.all',
            'value' => 'value2',
        ]);
        $dnsInfo = static::$client->dnsTemplate()->getAll();
        $dsRecords = [];
        foreach ($dnsInfo as $dnsRec) {
            if ('TXT' === $dnsRec->type && 0 === strpos($dnsRec->host, 'test.get.all')) {
                $dsRecords[] = $dnsRec;
            }
        }
        $this->assertCount(2, $dsRecords);

        static::$client->dnsTemplate()->delete('id', $dns->id);
        static::$client->dnsTemplate()->delete('id', $dns2->id);
    }

    public function testDelete()
    {
        $dns = static::$client->dnsTemplate()->create([
            'type' => 'TXT',
            'host' => 'test.delete',
            'value' => 'value',
        ]);
        $result = static::$client->dnsTemplate()->delete('id', $dns->id);
        $this->assertTrue($result);
    }
}


================================================
FILE: tests/DnsTest.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskXTest;

class DnsTest extends AbstractTestCase
{
    private static \PleskX\Api\Struct\Webspace\Info $webspace;
    private static bool $isDnsSupported;

    public static function setUpBeforeClass(): void
    {
        parent::setUpBeforeClass();

        $serviceStates = static::$client->server()->getServiceStates();
        static::$isDnsSupported = isset($serviceStates['dns']) && ('running' == $serviceStates['dns']['state']);

        if (static::$isDnsSupported) {
            static::$webspace = static::createWebspace();
        }
    }

    protected function setUp(): void
    {
        parent::setUp();

        if (!static::$isDnsSupported) {
            $this->markTestSkipped('DNS system is not supported.');
        }
    }

    public function testCreate()
    {
        $dns = static::$client->dns()->create([
            'site-id' => static::$webspace->id,
            'type' => 'TXT',
            'host' => 'host',
            'value' => 'value',
        ]);
        $this->assertIsInt($dns->id);
        $this->assertGreaterThan(0, $dns->id);
        static::$client->dns()->delete('id', $dns->id);
    }

    /**
     * @return \SimpleXMLElement[]
     */
    public function testBulkCreate(): array
    {
        $response = static::$client->dns()->bulkCreate([
            [
                'site-id' => static::$webspace->id,
                'type' => 'TXT',
                'host' => 'host',
                'value' => 'value',
            ],
            [
                'site-id' => static::$webspace->id,
                'type' => 'A',
                'host' => 'host',
                'value' => '1.1.1.1',
            ],
            [
                'site-id' => static::$webspace->id,
                'type' => 'MX',
                'host' => 'custom-mail',
                'value' => '1.1.1.1',
                'opt' => '10',
            ],
        ]);

        $this->assertCount(3, $response);

        foreach ($response as $xml) {
            $this->assertEquals('ok', (string) $xml->status);
            $this->assertGreaterThan(0, (int) $xml->id);
        }

        return $response;
    }

    /**
     * @depends testBulkCreate
     *
     * @param \SimpleXMLElement[] $createdRecords
     */
    public function testBulkDelete(array $createdRecords)
    {
        $createdRecordIds = array_map(function ($record) {
            return (int) $record->id;
        }, $createdRecords);

        $response = static::$client->dns()->bulkDelete($createdRecordIds);

        $this->assertCount(3, $response);

        foreach ($response as $xml) {
            $this->assertEquals('ok', (string) $xml->status);
            $this->assertGreaterThan(0, (int) $xml->id);
        }
    }

    public function testGetById()
    {
        $dns = static::$client->dns()->create([
            'site-id' => static::$webspace->id,
            'type' => 'TXT',
            'host' => '',
            'value' => 'value',
        ]);

        $dnsInfo = static::$client->dns()->get('id', $dns->id);
        $this->assertEquals('TXT', $dnsInfo->type);
        $this->assertEquals(static::$webspace->id, $dnsInfo->siteId);
        $this->assertEquals('value', $dnsInfo->value);

        static::$client->dns()->delete('id', $dns->id);
    }

    public function testGetAllByWebspaceId()
    {
        $dns = static::$client->dns()->create([
            'site-id' => static::$webspace->id,
            'type' => 'DS',
            'host' => 'host',
            'value' => '60485 5 1 2BB183AF5F22588179A53B0A98631FAD1A292118',
        ]);
        $dns2 = static::$client->dns()->create([
            'site-id' => static::$webspace->id,
            'type' => 'DS',
            'host' => 'host',
            'value' => '60485 5 1 2BB183AF5F22588179A53B0A98631FAD1A292119',
        ]);
        $dnsInfo = static::$client->dns()->getAll('site-id', static::$webspace->id);
        $dsRecords = [];
        foreach ($dnsInfo as $dnsRec) {
            if ('DS' == $dnsRec->type) {
                $dsRecords[] = $dnsRec;
            }
        }
        $this->assertEquals(2, count($dsRecords));
        foreach ($dsRecords as $dsRecord) {
            $this->assertEquals(static::$webspace->id, $dsRecord->siteId);
        }

        static::$client->dns()->delete('id', $dns->id);
        static::$client->dns()->delete('id', $dns2->id);
    }

    public function testDelete()
    {
        $dns = static::$client->dns()->create([
            'site-id' => static::$webspace->id,
            'type' => 'TXT',
            'host' => 'host',
            'value' => 'value',
        ]);
        $result = static::$client->dns()->delete('id', $dns->id);
        $this->assertTrue($result);
    }
}


================================================
FILE: tests/EventLogTest.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskXTest;

class EventLogTest extends AbstractTestCase
{
    public function testGet()
    {
        $events = static::$client->eventLog()->get();
        $this->assertGreaterThan(0, $events);

        $event = reset($events);
        $this->assertGreaterThan(0, $event->time);
    }

    public function testGetDetailedLog()
    {
        $events = static::$client->eventLog()->getDetailedLog();
        $this->assertGreaterThan(0, $events);

        $event = reset($events);
        $this->assertGreaterThan(0, $event->time);
    }

    public function testGetLastId()
    {
        $lastId = static::$client->eventLog()->getLastId();
        $this->assertGreaterThan(0, $lastId);
    }
}


================================================
FILE: tests/IpTest.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskXTest;

class IpTest extends AbstractTestCase
{
    public function testGet()
    {
        $ips = static::$client->ip()->get();
        $this->assertGreaterThan(0, count($ips));

        $ip = reset($ips);
        $this->assertMatchesRegularExpression('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $ip->ipAddress);
    }
}


================================================
FILE: tests/LocaleTest.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskXTest;

class LocaleTest extends AbstractTestCase
{
    public function testGet()
    {
        $locales = static::$client->locale()->get();
        $this->assertGreaterThan(0, count($locales));

        $locale = $locales['en-US'];
        $this->assertEquals('en-US', $locale->id);
    }

    public function testGetById()
    {
        $locale = static::$client->locale()->get('en-US');
        $this->assertEquals('en-US', $locale->id);
    }
}


================================================
FILE: tests/MailTest.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskXTest;

use PleskXTest\Utility\PasswordProvider;

class MailTest extends AbstractTestCase
{
    private static \PleskX\Api\Struct\Webspace\Info $webspace;
    private static bool $isMailSupported;

    public static function setUpBeforeClass(): void
    {
        parent::setUpBeforeClass();

        $serviceStates = static::$client->server()->getServiceStates();
        static::$isMailSupported = isset($serviceStates['smtp']) && ('running' == $serviceStates['smtp']['state']);

        if (static::$isMailSupported) {
            static::$webspace = static::createWebspace();
        }
    }

    protected function setUp(): void
    {
        parent::setUp();

        if (!static::$isMailSupported) {
            $this->markTestSkipped('Mail system is not supported.');
        }
    }

    public function testCreate()
    {
        $mailname = static::$client->mail()->create(
            'test',
            static::$webspace->id,
            true,
            PasswordProvider::STRONG_PASSWORD
        );

        $this->assertIsInt($mailname->id);
        $this->assertGreaterThan(0, $mailname->id);
        $this->assertEquals('test', $mailname->name);

        static::$client->mail()->delete('name', $mailname->name, static::$webspace->id);
    }

    public function testCreateMultiForwarding()
    {
        $mailname = static::$client->request([
            'mail' => [
                'create' => [
                    'filter' => [
                        'site-id' => static::$webspace->id,
                        'mailname' => [
                            'name' => 'test',
                            'mailbox' => [
                                'enabled' => true,
                            ],
                            'forwarding' => [
                                'enabled' => true,
                                'address' => [
                                    'user1@example.com',
                                    'user2@example.com',
                                ],
                            ],
                            'alias' => [
                                'test1',
                                'test2',
                            ],
                            'password' => [
                                'value' => PasswordProvider::STRONG_PASSWORD,
                            ],
                        ],
                    ],
                ],
            ],
        ]);

        $mailnameInfo = static::$client->request([
            'mail' => [
                'get_info' => [
                    'filter' => [
                        'site-id' => static::$webspace->id,
                        'name' => 'test',
                    ],
                    'forwarding' => null,
                    'aliases' => null,
                ],
            ],
        ]);

        $this->assertSame(2, count($mailnameInfo->mailname->forwarding->address));
        $this->assertSame(2, count($mailnameInfo->mailname->alias));

        static::$client->mail()->delete('name', 'test', static::$webspace->id);
    }

    public function testDelete()
    {
        $mailname = static::$client->mail()->create('test', static::$webspace->id);

        $result = static::$client->mail()->delete('name', $mailname->name, static::$webspace->id);
        $this->assertTrue($result);
    }

    public function testGet()
    {
        $mailname = static::$client->mail()->create('test', static::$webspace->id);

        $mailnameInfo = static::$client->mail()->get('test', static::$webspace->id);
        $this->assertEquals('test', $mailnameInfo->name);
        $this->assertEquals($mailname->id, $mailnameInfo->id);

        static::$client->mail()->delete('name', $mailname->name, static::$webspace->id);
    }

    public function testGetAll()
    {
        $mailname = static::$client->mail()->create('test', static::$webspace->id);

        $mailnamesInfo = static::$client->mail()->getAll(static::$webspace->id);
        $this->assertCount(1, $mailnamesInfo);
        $this->assertEquals('test', $mailnamesInfo[0]->name);

        static::$client->mail()->delete('name', $mailname->name, static::$webspace->id);
    }

    public function testGetAllWithoutMailnames()
    {
        $mailnamesInfo = static::$client->mail()->getAll(static::$webspace->id);
        $this->assertCount(0, $mailnamesInfo);
    }
}


================================================
FILE: tests/PhpHandlerTest.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskXTest;

class PhpHandlerTest extends AbstractTestCase
{
    public function testGet()
    {
        $handler = static::$client->phpHandler()->get();

        $this->assertTrue(property_exists($handler, 'type'));
    }

    public function testGetAll()
    {
        $handlers = static::$client->phpHandler()->getAll();

        $this->assertIsArray($handlers);
        $this->assertNotEmpty($handlers);

        $handler = current($handlers);

        $this->assertIsObject($handler);
        $this->assertTrue(property_exists($handler, 'type'));
    }

    public function testGetUnknownHandlerThrowsException()
    {
        $this->expectException(\PleskX\Api\Exception::class);
        $this->expectExceptionMessage('Php handler does not exists');

        static::$client->phpHandler()->get('id', 'this-handler-does-not-exist');
    }
}


================================================
FILE: tests/ProtectedDirectoryTest.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskXTest;

use PleskXTest\Utility\PasswordProvider;

class ProtectedDirectoryTest extends AbstractTestCase
{
    private static \PleskX\Api\Struct\Webspace\Info $webspace;

    public static function setUpBeforeClass(): void
    {
        parent::setUpBeforeClass();
        static::$webspace = static::createWebspace();
    }

    public function testAdd()
    {
        $protectedDirectory = static::$client->protectedDirectory()->add('/', static::$webspace->id);

        $this->assertIsObject($protectedDirectory);
        $this->assertGreaterThan(0, $protectedDirectory->id);

        static::$client->protectedDirectory()->delete('id', $protectedDirectory->id);
    }

    public function testAddInvalidDirectory()
    {
        $this->expectException(\PleskX\Api\Exception::class);
        $this->expectExceptionCode(1019);

        static::$client->protectedDirectory()->add('', static::$webspace->id);
    }

    public function testDelete()
    {
        $protectedDirectory = static::$client->protectedDirectory()->add('/', static::$webspace->id);

        $result = static::$client->protectedDirectory()->delete('id', $protectedDirectory->id);
        $this->assertTrue($result);
    }

    public function testGetById()
    {
        $protectedDirectory = static::$client->protectedDirectory()->add('test', static::$webspace->id);

        $foundDirectory = static::$client->protectedDirectory()->get('id', $protectedDirectory->id);
        $this->assertEquals('test', $foundDirectory->name);

        static::$client->protectedDirectory()->delete('id', $protectedDirectory->id);
    }

    public function testGetUnknownDirectory()
    {
        $this->expectException(\PleskX\Api\Exception::class);
        $this->expectExceptionCode(1013);

        $nonExistentDirectoryId = 99999999;
        static::$client->protectedDirectory()->get('id', $nonExistentDirectoryId);
    }

    public function testAddUser()
    {
        $protectedDirectory = static::$client->protectedDirectory()->add('/', static::$webspace->id);

        $user = static::$client->protectedDirectory()->addUser(
            $protectedDirectory,
            'john',
            PasswordProvider::STRONG_PASSWORD
        );
        $this->assertGreaterThan(0, $user->id);

        static::$client->protectedDirectory()->delete('id', $protectedDirectory->id);
    }

    public function testDeleteUser()
    {
        $protectedDirectory = static::$client->protectedDirectory()->add('/', static::$webspace->id);

        $user = static::$client->protectedDirectory()->addUser(
            $protectedDirectory,
            'john',
            PasswordProvider::STRONG_PASSWORD
        );
        $result = static::$client->protectedDirectory()->deleteUser('id', $user->id);
        $this->assertTrue($result);

        static::$client->protectedDirectory()->delete('id', $protectedDirectory->id);
    }
}


================================================
FILE: tests/ResellerTest.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskXTest;

use PleskXTest\Utility\KeyLimitChecker;
use PleskXTest\Utility\PasswordProvider;

class ResellerTest extends AbstractTestCase
{
    private array $resellerProperties;

    public function setUp(): void
    {
        $this->resellerProperties = [
            'pname' => 'John Reseller',
            'login' => 'reseller-unit-test',
            'passwd' => PasswordProvider::STRONG_PASSWORD,
        ];
    }

    public function testCreate()
    {
        $reseller = static::$client->reseller()->create($this->resellerProperties);
        $this->assertIsInt($reseller->id);
        $this->assertGreaterThan(0, $reseller->id);

        static::$client->reseller()->delete('id', $reseller->id);
    }

    public function testDelete()
    {
        $reseller = static::$client->reseller()->create($this->resellerProperties);
        $result = static::$client->reseller()->delete('id', $reseller->id);
        $this->assertTrue($result);
    }

    public function testGet()
    {
        $reseller = static::$client->reseller()->create($this->resellerProperties);
        $resellerInfo = static::$client->reseller()->get('id', $reseller->id);
        $this->assertEquals('John Reseller', $resellerInfo->personalName);
        $this->assertEquals('reseller-unit-test', $resellerInfo->login);
        $this->assertGreaterThan(0, count($resellerInfo->permissions));
        $this->assertEquals($reseller->id, $resellerInfo->id);

        static::$client->reseller()->delete('id', $reseller->id);
    }

    public function testGetAll()
    {
        $keyInfo = static::$client->server()->getKeyInfo();

        if (!KeyLimitChecker::checkByType($keyInfo, KeyLimitChecker::LIMIT_RESELLERS, 2)) {
            $this->markTestSkipped('License does not allow to create more than 1 reseller.');
        }

        static::$client->reseller()->create([
            'pname' => 'John Reseller',
            'login' => 'reseller-a',
            'passwd' => PasswordProvider::STRONG_PASSWORD,
        ]);
        static::$client->reseller()->create([
            'pname' => 'Mike Reseller',
            'login' => 'reseller-b',
            'passwd' => PasswordProvider::STRONG_PASSWORD,
        ]);

        $resellersInfo = static::$client->reseller()->getAll();
        $this->assertCount(2, $resellersInfo);
        $this->assertEquals('John Reseller', $resellersInfo[0]->personalName);
        $this->assertEquals('reseller-a', $resellersInfo[0]->login);

        static::$client->reseller()->delete('login', 'reseller-a');
        static::$client->reseller()->delete('login', 'reseller-b');
    }

    public function testGetAllEmpty()
    {
        $resellersInfo = static::$client->reseller()->getAll();
        $this->assertCount(0, $resellersInfo);
    }
}


================================================
FILE: tests/SecretKeyTest.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskXTest;

use PleskX\Api\Exception;

class SecretKeyTest extends AbstractTestCase
{
    public function testCreate()
    {
        $keyId = static::$client->secretKey()->create('192.168.0.1');
        $this->assertMatchesRegularExpression(
            '/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/',
            $keyId
        );
        static::$client->secretKey()->delete($keyId);
    }

    public function testCreateAutoIp()
    {
        $keyId = static::$client->secretKey()->create();
        $this->assertNotEmpty($keyId);
        static::$client->secretKey()->delete($keyId);
    }

    public function testCreateMultiIps()
    {
        $keyId = static::$client->secretKey()->create(join(',', ['192.168.0.1', '192.168.0.2']));
        $this->assertMatchesRegularExpression(
            '/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/',
            $keyId
        );
        static::$client->secretKey()->delete($keyId);
    }

    public function testCreateWithDescription()
    {
        $keyId = static::$client->secretKey()->create('192.168.0.1', 'test key');
        $keyInfo = static::$client->secretKey()->get($keyId);

        $this->assertEquals('test key', $keyInfo->description);

        static::$client->secretKey()->delete($keyId);
    }

    public function testGet()
    {
        $keyId = static::$client->secretKey()->create('192.168.0.1');
        $keyInfo = static::$client->secretKey()->get($keyId);

        $this->assertNotEmpty($keyInfo->key);
        $this->assertEquals('192.168.0.1', $keyInfo->ipAddress);
        $this->assertEquals('admin', $keyInfo->login);

        static::$client->secretKey()->delete($keyId);
    }

    public function testGetAll()
    {
        $keyIds = [];
        $keyIds[] = static::$client->secretKey()->create('192.168.0.1');
        $keyIds[] = static::$client->secretKey()->create('192.168.0.2');

        $keys = static::$client->secretKey()->getAll();
        $this->assertGreaterThanOrEqual(2, count($keys));

        $keyIpAddresses = array_map(function ($key) {
            return $key->ipAddress;
        }, $keys);
        $this->assertContains('192.168.0.1', $keyIpAddresses);
        $this->assertContains('192.168.0.2', $keyIpAddresses);

        foreach ($keyIds as $keyId) {
            static::$client->secretKey()->delete($keyId);
        }
    }

    public function testDelete()
    {
        $keyId = static::$client->secretKey()->create('192.168.0.1');
        static::$client->secretKey()->delete($keyId);

        try {
            static::$client->secretKey()->get($keyId);
            $this->fail("Secret key $keyId was not deleted.");
        } catch (Exception $exception) {
            $this->assertEquals(1013, $exception->getCode());
        }
    }

    public function testListEmpty()
    {
        $keys = static::$client->secretKey()->getAll();
        foreach ($keys as $key) {
            static::$client->secretKey()->delete($key->key);
        }

        $keys = static::$client->secretKey()->getAll();
        $this->assertEquals(0, count($keys));
    }
}


================================================
FILE: tests/ServerTest.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskXTest;

class ServerTest extends AbstractTestCase
{
    public function testGetProtos()
    {
        $protos = static::$client->server()->getProtos();
        $this->assertIsArray($protos);
        $this->assertContains('1.6.3.0', $protos);
    }

    public function testGetGenInfo()
    {
        $generalInfo = static::$client->server()->getGeneralInfo();
        $this->assertGreaterThan(0, strlen($generalInfo->serverName));
        $this->assertMatchesRegularExpression(
            '/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/',
            strtolower($generalInfo->serverGuid)
        );
        $this->assertEquals('standard', $generalInfo->mode);
    }

    public function testGetPreferences()
    {
        $preferences = static::$client->server()->getPreferences();
        $this->assertIsNumeric($preferences->statTtl);
        $this->assertGreaterThan(0, $preferences->statTtl);
        $this->assertEquals(0, $preferences->restartApacheInterval);
    }

    public function testGetAdmin()
    {
        $admin = static::$client->server()->getAdmin();
        $this->assertGreaterThan(0, strlen($admin->name));
        $this->assertStringContainsString('@', $admin->email);
    }

    public function testGetKeyInfo()
    {
        $keyInfo = static::$client->server()->getKeyInfo();
        $this->assertIsArray($keyInfo);
        $this->assertGreaterThan(0, count($keyInfo));
        $this->assertArrayHasKey('plesk_key_id', $keyInfo);
        $this->assertArrayHasKey('lim_date', $keyInfo);
    }

    public function testGetComponents()
    {
        $components = static::$client->server()->getComponents();
        $this->assertIsArray($components);
        $this->assertGreaterThan(0, count($components));
        $this->assertArrayHasKey('psa', $components);
    }

    public function testGetServiceStates()
    {
        $serviceStates = static::$client->server()->getServiceStates();

        $this->assertIsArray($serviceStates);
        $this->assertGreaterThan(0, count($serviceStates));

        $service = current($serviceStates);
        $this->assertIsArray($service);
        $this->assertArrayHasKey('id', $service);
        $this->assertArrayHasKey('title', $service);
        $this->assertArrayHasKey('state', $service);
    }

    public function testGetSessionPreferences()
    {
        $preferences = static::$client->server()->getSessionPreferences();
        $this->assertIsNumeric($preferences->loginTimeout);
        $this->assertGreaterThan(0, $preferences->loginTimeout);
    }

    public function testGetShells()
    {
        $shells = static::$client->server()->getShells();

        $this->assertIsArray($shells);
        $this->assertGreaterThan(0, count($shells));
    }

    public function testGetNetworkInterfaces()
    {
        $netInterfaces = static::$client->server()->getNetworkInterfaces();
        $this->assertIsArray($netInterfaces);
        $this->assertGreaterThan(0, count($netInterfaces));
    }

    public function testGetStatistics()
    {
        $stats = static::$client->server()->getStatistics();
        $this->assertIsNumeric($stats->objects->clients);
        $this->assertIsNumeric($stats->objects->domains);
        $this->assertIsNumeric($stats->objects->databases);
        $this->assertEquals('psa', $stats->version->internalName);
        $this->assertNotEmpty($stats->version->osName);
        $this->assertNotEmpty($stats->version->osVersion);
        $this->assertGreaterThan(0, $stats->other->uptime);
        $this->assertGreaterThan(0, strlen($stats->other->cpu));
        $this->assertIsFloat($stats->loadAverage->load1min);
        $this->assertGreaterThan(0, $stats->memory->total);
        $this->assertGreaterThan($stats->memory->free, $stats->memory->total);
        $this->assertIsNumeric($stats->swap->total);
        $this->assertIsArray($stats->diskSpace);
    }

    public function testGetSiteIsolationConfig()
    {
        $config = static::$client->server()->getSiteIsolationConfig();
        $this->assertIsArray($config);
        $this->assertGreaterThan(0, count($config));
        $this->assertArrayHasKey('php', $config);
    }

    public function testGetUpdatesInfo()
    {
        $updatesInfo = static::$client->server()->getUpdatesInfo();
        $this->assertIsBool($updatesInfo->installUpdatesAutomatically);
    }
}


================================================
FILE: tests/ServicePlanAddonTest.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskXTest;

class ServicePlanAddonTest extends AbstractTestCase
{
    public function testGet()
    {
        $servicePlanAddon = static::createServicePlanAddon();
        $servicePlanAddonInfo = static::$client->servicePlanAddon()->get('id', $servicePlanAddon->id);

        $this->assertNotEmpty($servicePlanAddonInfo->name);
        $this->assertSame($servicePlanAddon->id, $servicePlanAddonInfo->id);
    }

    public function testGetAll()
    {
        static::createServicePlanAddon();
        static::createServicePlanAddon();
        static::createServicePlanAddon();

        $servicePlanAddons = static::$client->servicePlanAddon()->getAll();
        $this->assertIsArray($servicePlanAddons);
        $this->assertGreaterThan(2, count($servicePlanAddons));
        $this->assertNotEmpty($servicePlanAddons[0]->name);
    }

    public function testCreate()
    {
        $servicePlanAddon = static::createServicePlanAddon();
        $this->assertGreaterThan(0, $servicePlanAddon->id);

        static::$client->servicePlanAddon()->delete('id', $servicePlanAddon->id);
    }

    public function testDelete()
    {
        $servicePlanAddon = static::createServicePlanAddon();
        $result = static::$client->servicePlanAddon()->delete('id', $servicePlanAddon->id);

        $this->assertTrue($result);
    }

    public function testCreateComplex()
    {
        $properties = [
            'name' => 'Complex Service Plan Addon',
            'limits' => [
                'limit' => [
                    'name' => 'disk_space',
                    'value' => 1024 * 1024 * 1024, // 1 GB
                ],
            ],
        ];

        $servicePlanAddon = static::$client->servicePlanAddon()->create($properties);
        $this->assertGreaterThan(0, $servicePlanAddon->id);

        static::$client->servicePlanAddon()->delete('id', $servicePlanAddon->id);
    }
}


================================================
FILE: tests/ServicePlanTest.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskXTest;

class ServicePlanTest extends AbstractTestCase
{
    public function testGet()
    {
        $servicePlan = static::createServicePlan();
        $servicePlanInfo = static::$client->servicePlan()->get('id', $servicePlan->id);
        $this->assertNotEmpty($servicePlanInfo->name);
        $this->assertSame($servicePlan->id, $servicePlanInfo->id);

        static::$client->servicePlan()->delete('id', $servicePlan->id);
    }

    public function testGetAll()
    {
        static::createServicePlan();
        static::createServicePlan();
        static::createServicePlan();

        $servicePlans = static::$client->servicePlan()->getAll();
        $this->assertIsArray($servicePlans);
        $this->assertGreaterThan(2, count($servicePlans));
        $this->assertNotEmpty($servicePlans[0]->name);
    }

    public function testCreateServicePlan()
    {
        $servicePlan = static::createServicePlan();
        $this->assertGreaterThan(0, $servicePlan->id);

        static::$client->servicePlan()->delete('id', $servicePlan->id);
    }

    public function testDelete()
    {
        $servicePlan = static::createServicePlan();
        $result = static::$client->servicePlan()->delete('id', $servicePlan->id);

        $this->assertTrue($result);
    }

    public function testCreateComplexServicePlan()
    {
        $properties = [
            'name' => 'Complex Service Plan',
            'limits' => [
                'overuse' => 'block',
                'limit' => [
                    'name' => 'disk_space',
                    'value' => 1024 * 1024 * 1024, // 1 GB
                ],
            ],
            'preferences' => [
                'stat' => 6,
                'maillists' => 'true',
            ],
            'hosting' => [
                'property' => [
                    [
                        'name' => 'ftp_quota',
                        'value' => '-1',
                    ],
                    [
                        'name' => 'ssl',
                        'value' => 'true',
                    ],
                ],
            ],
        ];

        $servicePlan = static::$client->servicePlan()->create($properties);
        $this->assertGreaterThan(0, $servicePlan->id);

        static::$client->servicePlan()->delete('id', $servicePlan->id);
    }
}


================================================
FILE: tests/SessionTest.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskXTest;

class SessionTest extends AbstractTestCase
{
    public function testCreate()
    {
        $sessionToken = static::$client->session()->create('admin', '127.0.0.1');

        $this->assertIsString($sessionToken);
        $this->assertGreaterThan(10, strlen($sessionToken));
    }

    public function testGet()
    {
        $sessionId = static::$client->server()->createSession('admin', '127.0.0.1');
        $sessions = static::$client->session()->get();
        $this->assertArrayHasKey($sessionId, $sessions);

        $sessionInfo = $sessions[$sessionId];
        $this->assertEquals('admin', $sessionInfo->login);
        $this->assertEquals('127.0.0.1', $sessionInfo->ipAddress);
        $this->assertEquals($sessionId, $sessionInfo->id);
    }

    public function testTerminate()
    {
        $sessionId = static::$client->server()->createSession('admin', '127.0.0.1');
        static::$client->session()->terminate($sessionId);
        $sessions = static::$client->session()->get();
        $this->assertArrayNotHasKey($sessionId, $sessions);
    }
}


================================================
FILE: tests/SiteAliasTest.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskXTest;

class SiteAliasTest extends AbstractTestCase
{
    private static \PleskX\Api\Struct\Webspace\Info $webspace;

    public static function setUpBeforeClass(): void
    {
        parent::setUpBeforeClass();
        static::$webspace = static::createWebspace();
    }

    private function createSiteAlias($name, array $properties = []): \PleskX\Api\Struct\SiteAlias\Info
    {
        $properties = array_merge([
            'name' => $name,
            'site-id' => static::$webspace->id,
        ], $properties);

        return static::$client->siteAlias()->create($properties);
    }

    public function testCreate()
    {
        $siteAlias = $this->createSiteAlias('alias.dom');

        $this->assertIsNumeric($siteAlias->id);
        $this->assertGreaterThan(0, $siteAlias->id);

        static::$client->siteAlias()->delete('id', $siteAlias->id);
    }

    public function testDelete()
    {
        $siteAlias = $this->createSiteAlias('alias.dom');

        $result = static::$client->siteAlias()->delete('id', $siteAlias->id);
        $this->assertTrue($result);
    }

    public function testGet()
    {
        $siteAlias = $this->createSiteAlias('alias.dom');

        $siteAliasInfo = static::$client->siteAlias()->get('id', $siteAlias->id);
        $this->assertEquals('alias.dom', $siteAliasInfo->name);

        static::$client->siteAlias()->delete('id', $siteAlias->id);
    }

    public function testGetAll()
    {
        $siteAlias = $this->createSiteAlias('alias.dom');
        $siteAlias2 = $this->createSiteAlias('alias2.dom');

        $siteAliasInfo = static::$client->siteAlias()->get('id', $siteAlias->id);
        $this->assertEquals('alias.dom', $siteAliasInfo->name);

        $siteAliasesInfo = static::$client->siteAlias()->getAll('site-id', self::$webspace->id);
        $this->assertCount(2, $siteAliasesInfo);
        $this->assertEquals('alias.dom', $siteAliasesInfo[0]->name);
        $this->assertEquals('alias.dom', $siteAliasesInfo[0]->asciiName);

        static::$client->siteAlias()->delete('id', $siteAlias->id);
        static::$client->siteAlias()->delete('id', $siteAlias2->id);
    }
}


================================================
FILE: tests/SiteTest.php
================================================
<?php
// Copyright 1999-2025. WebPros International GmbH.

namespace PleskXTest;

use PleskXTest\Utility\KeyLimitChecker;

class SiteTest extends AbstractTestCase
{
    private static \PleskX\Api\Struct\Webspace\Info $webspace;

    public static function setUpBeforeClass(): void
    {
        parent::setUpBeforeClass();
        static::$webspace = static::createWebspace();
    }

    protected function setUp(): void
    {
        parent::setUp();

        $keyInfo = static::$client->server()->getKeyInfo();

        if (!KeyLimitChecker::checkByType($keyInfo, KeyLimitChecker::LIMIT_DOMAINS, 2)) {
            $this->markTestSkipped('License does not allow to create more than 1 domain.');
        }
    }

    private function createSite($name, array $properties = []): \PleskX\Api\Struct\Site\Info
    {
        $properties = array_merge([
            'name' => $name,
            'webspace-id' => static::$webspace->id,
        ], $properties);

        return static::$client->site()->create($properties);
    }

    public function testCreate()
    {
        $site = $this->createSite('addon.dom');

        $this->assertIsNumeric($site->id);
        $this->assertGreaterThan(0, $site->id);

        static::$client->site()->delete('id', $site->id);
    }

    public function testDelete()
    {
        $site = $this->createSite('addon.dom');

        $result = static::$client->site()->delete('id', $site->id);
        $this->assertTrue($result);
    }

    public function testGet()
    {
        $site = $this->createSite('addon.dom');

        $siteInfo = static::$client->site()->get('id', $site->id);
        $this->assertEquals('addon.dom', $siteInfo->name);
        $this->assertMatchesRegularExpression("/^\d{4}-\d{2}-\d{2}$/", $siteInfo->creationDate);
        $this->assertEquals(36, strlen($siteInfo->guid));

        $siteGuid = $siteInfo->guid;
        $siteInfo = static::$client->site()->get('guid', $siteGuid);
        $this->assertEquals($site->id, $siteInfo->id);

        static::$client->site()->delete('id', $site->id);

        $siteInfo = static::$client->site()->get('parent-id', static::$webspace->id);
        $this->assertNull($siteInfo);
    }

    public function testGetHostingWoHosting()
    {
        $site = $this->createSite('addon.dom');

        $siteHosting = static::$client->site()->getHosting('id', $site->id);
        $this->assertNull($siteHosting);

        static::$client->site()->delete('id', $site->id);
    }

    public function testGetHostingWithHosting()
    {
        $properties = [
            'hosting' => [
                'www_root' => 'addon.dom',
            ],
        ];
        $site = $this->createSite('addon.dom', $properties);

        $siteHosting = static::$client->site()->getHosting('id', $site->id);
        $this->assertArrayHasKey('www_root', $siteHosting->properties);
        $this->assertStringEndsWith('addon.dom', $siteHosting->properties['www_root']);

        static::$client->site()->delete('id', $site->id);
    }

    public function testGetAll()
    {
        $site = $this->createSite('
Download .txt
gitextract_ygetwf0b/

├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       └── test.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── composer.json
├── docker-compose.yml
├── phpcs.xml.dist
├── phpunit-watcher.yml
├── phpunit.xml.dist
├── psalm.xml
├── src/
│   └── Api/
│       ├── AbstractStruct.php
│       ├── Client/
│       │   └── Exception.php
│       ├── Client.php
│       ├── Exception.php
│       ├── InternalClient.php
│       ├── Operator/
│       │   ├── Aps.php
│       │   ├── Certificate.php
│       │   ├── Customer.php
│       │   ├── Database.php
│       │   ├── DatabaseServer.php
│       │   ├── Dns.php
│       │   ├── DnsTemplate.php
│       │   ├── EventLog.php
│       │   ├── Ip.php
│       │   ├── Locale.php
│       │   ├── LogRotation.php
│       │   ├── Mail.php
│       │   ├── PhpHandler.php
│       │   ├── ProtectedDirectory.php
│       │   ├── Reseller.php
│       │   ├── ResellerPlan.php
│       │   ├── SecretKey.php
│       │   ├── Server.php
│       │   ├── ServicePlan.php
│       │   ├── ServicePlanAddon.php
│       │   ├── Session.php
│       │   ├── Site.php
│       │   ├── SiteAlias.php
│       │   ├── Subdomain.php
│       │   ├── Ui.php
│       │   ├── VirtualDirectory.php
│       │   └── Webspace.php
│       ├── Operator.php
│       ├── Struct/
│       │   ├── Certificate/
│       │   │   └── Info.php
│       │   ├── Customer/
│       │   │   ├── GeneralInfo.php
│       │   │   └── Info.php
│       │   ├── Database/
│       │   │   ├── Info.php
│       │   │   └── UserInfo.php
│       │   ├── DatabaseServer/
│       │   │   └── Info.php
│       │   ├── Dns/
│       │   │   └── Info.php
│       │   ├── EventLog/
│       │   │   ├── DetailedEvent.php
│       │   │   └── Event.php
│       │   ├── Ip/
│       │   │   └── Info.php
│       │   ├── Locale/
│       │   │   └── Info.php
│       │   ├── Mail/
│       │   │   ├── GeneralInfo.php
│       │   │   └── Info.php
│       │   ├── PhpHandler/
│       │   │   └── Info.php
│       │   ├── ProtectedDirectory/
│       │   │   ├── DataInfo.php
│       │   │   ├── Info.php
│       │   │   └── UserInfo.php
│       │   ├── Reseller/
│       │   │   ├── GeneralInfo.php
│       │   │   └── Info.php
│       │   ├── SecretKey/
│       │   │   └── Info.php
│       │   ├── Server/
│       │   │   ├── Admin.php
│       │   │   ├── GeneralInfo.php
│       │   │   ├── Preferences.php
│       │   │   ├── SessionPreferences.php
│       │   │   ├── Statistics/
│       │   │   │   ├── DiskSpace.php
│       │   │   │   ├── LoadAverage.php
│       │   │   │   ├── Memory.php
│       │   │   │   ├── Objects.php
│       │   │   │   ├── Other.php
│       │   │   │   ├── Swap.php
│       │   │   │   └── Version.php
│       │   │   ├── Statistics.php
│       │   │   └── UpdatesInfo.php
│       │   ├── ServicePlan/
│       │   │   └── Info.php
│       │   ├── ServicePlanAddon/
│       │   │   └── Info.php
│       │   ├── Session/
│       │   │   └── Info.php
│       │   ├── Site/
│       │   │   ├── GeneralInfo.php
│       │   │   ├── HostingInfo.php
│       │   │   └── Info.php
│       │   ├── SiteAlias/
│       │   │   ├── GeneralInfo.php
│       │   │   └── Info.php
│       │   ├── Subdomain/
│       │   │   └── Info.php
│       │   ├── Ui/
│       │   │   └── CustomButton.php
│       │   └── Webspace/
│       │       ├── DiskUsage.php
│       │       ├── GeneralInfo.php
│       │       ├── HostingPropertyInfo.php
│       │       ├── Info.php
│       │       ├── Limit.php
│       │       ├── LimitDescriptor.php
│       │       ├── LimitInfo.php
│       │       ├── Limits.php
│       │       ├── PermissionDescriptor.php
│       │       ├── PermissionInfo.php
│       │       ├── PhpSettings.php
│       │       └── PhysicalHostingDescriptor.php
│       └── XmlResponse.php
├── tests/
│   ├── AbstractTestCase.php
│   ├── ApiClientTest.php
│   ├── CertificateTest.php
│   ├── CustomerTest.php
│   ├── DatabaseServerTest.php
│   ├── DatabaseTest.php
│   ├── DnsTemplateTest.php
│   ├── DnsTest.php
│   ├── EventLogTest.php
│   ├── IpTest.php
│   ├── LocaleTest.php
│   ├── MailTest.php
│   ├── PhpHandlerTest.php
│   ├── ProtectedDirectoryTest.php
│   ├── ResellerTest.php
│   ├── SecretKeyTest.php
│   ├── ServerTest.php
│   ├── ServicePlanAddonTest.php
│   ├── ServicePlanTest.php
│   ├── SessionTest.php
│   ├── SiteAliasTest.php
│   ├── SiteTest.php
│   ├── SubdomainTest.php
│   ├── UiTest.php
│   ├── Utility/
│   │   ├── KeyLimitChecker.php
│   │   └── PasswordProvider.php
│   └── WebspaceTest.php
└── wait-for-plesk.sh
Download .txt
SYMBOL INDEX (529 symbols across 116 files)

FILE: src/Api/AbstractStruct.php
  class AbstractStruct (line 6) | abstract class AbstractStruct
    method __set (line 14) | public function __set(string $property, $value)
    method initScalarProperties (line 27) | protected function initScalarProperties(\SimpleXMLElement $apiResponse...
    method underToCamel (line 70) | private function underToCamel(string $under): string

FILE: src/Api/Client.php
  class Client (line 12) | class Client
    method __construct (line 40) | public function __construct(string $host, int $port = 8443, string $pr...
    method setCredentials (line 53) | public function setCredentials(string $login, string $password): void
    method setSecretKey (line 64) | public function setSecretKey(string $secretKey): void
    method setProxy (line 74) | public function setProxy(string $proxy): void
    method setVersion (line 84) | public function setVersion(string $version): void
    method setVerifyResponse (line 95) | public function setVerifyResponse(?callable $function = null): void
    method getHost (line 105) | public function getHost(): string
    method getPort (line 115) | public function getPort(): int
    method getProtocol (line 125) | public function getProtocol(): string
    method getPacket (line 137) | public function getPacket($version = null): SimpleXMLElement
    method request (line 155) | public function request($request, int $mode = self::RESPONSE_SHORT): X...
    method performSdkCall (line 186) | private function performSdkCall(string $request): XmlResponse
    method performHttpRequest (line 209) | private function performHttpRequest($request)
    method multiRequest (line 246) | public function multiRequest(array $requests, int $mode = self::RESPON...
    method splitResponseToArray (line 278) | private function splitResponseToArray(XmlResponse $responseXml, $mode ...
    method getDomDocument (line 312) | private function getDomDocument(SimpleXMLElement $xml): ?DOMDocument
    method getHeaders (line 327) | private function getHeaders()
    method verifyResponse (line 351) | private function verifyResponse($xml): void
    method expandRequestShortSyntax (line 373) | private function expandRequestShortSyntax($request, SimpleXMLElement $...
    method arrayToXml (line 401) | private function arrayToXml(array $array, SimpleXMLElement $xml, $pare...
    method isAssocArray (line 422) | private function isAssocArray(array $array)
    method getOperator (line 432) | private function getOperator(string $name)
    method server (line 443) | public function server(): Operator\Server
    method customer (line 448) | public function customer(): Operator\Customer
    method webspace (line 453) | public function webspace(): Operator\Webspace
    method subdomain (line 458) | public function subdomain(): Operator\Subdomain
    method dns (line 463) | public function dns(): Operator\Dns
    method dnsTemplate (line 468) | public function dnsTemplate(): Operator\DnsTemplate
    method databaseServer (line 473) | public function databaseServer(): Operator\DatabaseServer
    method mail (line 478) | public function mail(): Operator\Mail
    method certificate (line 483) | public function certificate(): Operator\Certificate
    method siteAlias (line 488) | public function siteAlias(): Operator\SiteAlias
    method ip (line 493) | public function ip(): Operator\Ip
    method eventLog (line 498) | public function eventLog(): Operator\EventLog
    method secretKey (line 503) | public function secretKey(): Operator\SecretKey
    method ui (line 508) | public function ui(): Operator\Ui
    method servicePlan (line 513) | public function servicePlan(): Operator\ServicePlan
    method virtualDirectory (line 518) | public function virtualDirectory(): Operator\VirtualDirectory
    method database (line 523) | public function database(): Operator\Database
    method session (line 528) | public function session(): Operator\Session
    method locale (line 533) | public function locale(): Operator\Locale
    method logRotation (line 538) | public function logRotation(): Operator\LogRotation
    method protectedDirectory (line 543) | public function protectedDirectory(): Operator\ProtectedDirectory
    method reseller (line 548) | public function reseller(): Operator\Reseller
    method resellerPlan (line 553) | public function resellerPlan(): Operator\ResellerPlan
    method aps (line 558) | public function aps(): Operator\Aps
    method servicePlanAddon (line 563) | public function servicePlanAddon(): Operator\ServicePlanAddon
    method site (line 568) | public function site(): Operator\Site
    method phpHandler (line 573) | public function phpHandler(): Operator\PhpHandler

FILE: src/Api/Client/Exception.php
  class Exception (line 9) | class Exception extends \Exception

FILE: src/Api/Exception.php
  class Exception (line 9) | class Exception extends \Exception

FILE: src/Api/InternalClient.php
  class InternalClient (line 9) | class InternalClient extends Client
    method __construct (line 11) | public function __construct()
    method setLogin (line 21) | public function setLogin(string $login): void

FILE: src/Api/Operator.php
  class Operator (line 6) | class Operator
    method __construct (line 11) | public function __construct(Client $client)
    method request (line 30) | public function request($request, $mode = Client::RESPONSE_SHORT): Xml...
    method deleteBy (line 52) | protected function deleteBy(string $field, $value, string $deleteMetho...
    method getItems (line 74) | protected function getItems($structClass, $infoTag, $field = null, $va...

FILE: src/Api/Operator/Aps.php
  class Aps (line 6) | class Aps extends \PleskX\Api\Operator

FILE: src/Api/Operator/Certificate.php
  class Certificate (line 8) | class Certificate extends \PleskX\Api\Operator
    method generate (line 10) | public function generate(array $properties): Struct\Info
    method install (line 29) | public function install(array $properties, $certificate, ?string $priv...
    method update (line 38) | public function update(array $properties, Struct\Info $certificate): bool
    method callApi (line 49) | private function callApi(string $method, array $properties, $certifica...
    method delete (line 72) | public function delete(string $name, array $properties): bool

FILE: src/Api/Operator/Customer.php
  class Customer (line 8) | class Customer extends \PleskX\Api\Operator
    method create (line 10) | public function create(array $properties): Struct\Info
    method delete (line 30) | public function delete(string $field, $value): bool
    method get (line 41) | public function get(string $field, $value): Struct\GeneralInfo
    method getAll (line 51) | public function getAll(): array
    method enable (line 62) | public function enable(string $field, $value): bool
    method disable (line 73) | public function disable(string $field, $value): bool
    method setProperties (line 85) | public function setProperties(string $field, $value, array $properties...

FILE: src/Api/Operator/Database.php
  class Database (line 9) | class Database extends \PleskX\Api\Operator
    method create (line 11) | public function create(array $properties): Struct\Info
    method createUser (line 16) | public function createUser(array $properties): Struct\UserInfo
    method process (line 21) | private function process(string $command, array $properties): XmlResponse
    method updateUser (line 37) | public function updateUser(array $properties): bool
    method get (line 50) | public function get(string $field, $value): Struct\Info
    method getUser (line 63) | public function getUser(string $field, $value): Struct\UserInfo
    method getAll (line 76) | public function getAll(?string $field, $value): array
    method getAllUsers (line 95) | public function getAllUsers(string $field, $value): array
    method getBy (line 115) | private function getBy(string $command, ?string $field, $value): XmlRe...
    method delete (line 134) | public function delete(string $field, $value): bool
    method deleteUser (line 145) | public function deleteUser(string $field, $value): bool

FILE: src/Api/Operator/DatabaseServer.php
  class DatabaseServer (line 8) | class DatabaseServer extends \PleskX\Api\Operator
    method getSupportedTypes (line 12) | public function getSupportedTypes(): array
    method get (line 25) | public function get(string $field, $value): Struct\Info
    method getAll (line 35) | public function getAll(): array
    method getDefault (line 40) | public function getDefault(string $type): Struct\Info
    method getBy (line 59) | private function getBy($field = null, $value = null): array

FILE: src/Api/Operator/Dns.php
  class Dns (line 8) | class Dns extends \PleskX\Api\Operator
    method create (line 10) | public function create(array $properties): Struct\Info
    method bulkCreate (line 29) | public function bulkCreate(array $records): array
    method get (line 58) | public function get(string $field, $value): Struct\Info
    method getAll (line 71) | public function getAll(string $field, $value): array
    method delete (line 101) | public function delete(string $field, $value): bool
    method bulkDelete (line 113) | public function bulkDelete(array $recordIds): array

FILE: src/Api/Operator/DnsTemplate.php
  class DnsTemplate (line 8) | class DnsTemplate extends \PleskX\Api\Operator
    method create (line 17) | public function create(array $properties): Struct\Info
    method get (line 36) | public function get(string $field, $value): Struct\Info
    method getAll (line 49) | public function getAll($field = null, $value = null): array
    method delete (line 82) | public function delete(string $field, $value): bool

FILE: src/Api/Operator/EventLog.php
  class EventLog (line 8) | class EventLog extends \PleskX\Api\Operator
    method get (line 15) | public function get(): array
    method getDetailedLog (line 30) | public function getDetailedLog(): array
    method getLastId (line 42) | public function getLastId(): int

FILE: src/Api/Operator/Ip.php
  class Ip (line 8) | class Ip extends \PleskX\Api\Operator
    method get (line 13) | public function get(): array

FILE: src/Api/Operator/Locale.php
  class Locale (line 8) | class Locale extends \PleskX\Api\Operator
    method get (line 15) | public function get($id = null)

FILE: src/Api/Operator/LogRotation.php
  class LogRotation (line 6) | class LogRotation extends \PleskX\Api\Operator

FILE: src/Api/Operator/Mail.php
  class Mail (line 10) | class Mail extends Operator
    method create (line 12) | public function create(string $name, int $siteId, bool $mailbox = fals...
    method delete (line 42) | public function delete(string $field, $value, int $siteId): bool
    method get (line 55) | public function get(string $name, int $siteId): Struct\GeneralInfo
    method getAll (line 68) | public function getAll(int $siteId, $name = null): array

FILE: src/Api/Operator/PhpHandler.php
  class PhpHandler (line 10) | class PhpHandler extends Operator
    method get (line 18) | public function get($field = null, $value = null): ?Info
    method getAll (line 40) | public function getAll($field = null, $value = null): array

FILE: src/Api/Operator/ProtectedDirectory.php
  class ProtectedDirectory (line 10) | class ProtectedDirectory extends Operator
    method add (line 14) | public function add(string $name, int $siteId, string $header = ''): S...
    method delete (line 32) | public function delete(string $field, $value): bool
    method get (line 43) | public function get(string $field, $value): Struct\DataInfo
    method getAll (line 56) | public function getAll(string $field, $value): array
    method addUser (line 78) | public function addUser($protectedDirectory, $login, $password)
    method deleteUser (line 96) | public function deleteUser($field, $value)
    method getBy (line 108) | private function getBy(string $command, string $field, $value)

FILE: src/Api/Operator/Reseller.php
  class Reseller (line 8) | class Reseller extends \PleskX\Api\Operator
    method create (line 10) | public function create(array $properties): Struct\Info
    method delete (line 30) | public function delete(string $field, $value): bool
    method get (line 41) | public function get(string $field, $value): Struct\GeneralInfo
    method getAll (line 54) | public function getAll($field = null, $value = null): array

FILE: src/Api/Operator/ResellerPlan.php
  class ResellerPlan (line 6) | class ResellerPlan extends \PleskX\Api\Operator

FILE: src/Api/Operator/SecretKey.php
  class SecretKey (line 8) | class SecretKey extends \PleskX\Api\Operator
    method create (line 12) | public function create(string $ipAddress = '', string $description = '...
    method delete (line 30) | public function delete(string $keyId): bool
    method get (line 35) | public function get(string $keyId): Struct\Info
    method getAll (line 45) | public function getAll(): array
    method getBy (line 55) | public function getBy($keyId = null): array

FILE: src/Api/Operator/Server.php
  class Server (line 9) | class Server extends \PleskX\Api\Operator
    method getProtos (line 11) | public function getProtos(): array
    method getGeneralInfo (line 21) | public function getGeneralInfo(): Struct\GeneralInfo
    method getPreferences (line 26) | public function getPreferences(): Struct\Preferences
    method getAdmin (line 31) | public function getAdmin(): Struct\Admin
    method getKeyInfo (line 36) | public function getKeyInfo(): array
    method getComponents (line 48) | public function getComponents(): array
    method getServiceStates (line 60) | public function getServiceStates(): array
    method getSessionPreferences (line 76) | public function getSessionPreferences(): Struct\SessionPreferences
    method getShells (line 81) | public function getShells(): array
    method getNetworkInterfaces (line 93) | public function getNetworkInterfaces(): array
    method getStatistics (line 100) | public function getStatistics(): Struct\Statistics
    method getSiteIsolationConfig (line 105) | public function getSiteIsolationConfig(): array
    method getUpdatesInfo (line 117) | public function getUpdatesInfo(): Struct\UpdatesInfo
    method createSession (line 122) | public function createSession(string $login, string $clientIp): string
    method getInfo (line 135) | private function getInfo(string $operation): XmlResponse

FILE: src/Api/Operator/ServicePlan.php
  class ServicePlan (line 8) | class ServicePlan extends \PleskX\Api\Operator
    method create (line 10) | public function create(array $properties): Struct\Info
    method delete (line 23) | public function delete(string $field, $value): bool
    method get (line 34) | public function get(string $field, $value): Struct\Info
    method getAll (line 44) | public function getAll(): array
    method getBy (line 55) | private function getBy($field = null, $value = null): array

FILE: src/Api/Operator/ServicePlanAddon.php
  class ServicePlanAddon (line 8) | class ServicePlanAddon extends \PleskX\Api\Operator
    method create (line 10) | public function create(array $properties): Struct\Info
    method delete (line 23) | public function delete(string $field, $value): bool
    method get (line 34) | public function get(string $field, $value): Struct\Info
    method getAll (line 44) | public function getAll(): array
    method getBy (line 55) | private function getBy($field = null, $value = null): array

FILE: src/Api/Operator/Session.php
  class Session (line 8) | class Session extends \PleskX\Api\Operator
    method create (line 10) | public function create(string $username, string $userIp): string
    method get (line 29) | public function get(): array
    method terminate (line 41) | public function terminate(string $sessionId): bool

FILE: src/Api/Operator/Site.php
  class Site (line 8) | class Site extends \PleskX\Api\Operator
    method create (line 12) | public function create(array $properties): Struct\Info
    method delete (line 48) | public function delete(string $field, $value): bool
    method get (line 59) | public function get(string $field, $value): ?Struct\GeneralInfo
    method getHosting (line 72) | public function getHosting(string $field, $value): ?Struct\HostingInfo
    method getAll (line 90) | public function getAll(): array

FILE: src/Api/Operator/SiteAlias.php
  class SiteAlias (line 8) | class SiteAlias extends \PleskX\Api\Operator
    method create (line 10) | public function create(array $properties, array $preferences = []): St...
    method delete (line 37) | public function delete(string $field, $value): bool
    method get (line 48) | public function get(string $field, $value): Struct\GeneralInfo
    method getAll (line 61) | public function getAll($field = null, $value = null): array

FILE: src/Api/Operator/Subdomain.php
  class Subdomain (line 8) | class Subdomain extends \PleskX\Api\Operator
    method create (line 10) | public function create(array $properties): Struct\Info
    method delete (line 40) | public function delete(string $field, $value): bool
    method get (line 51) | public function get(string $field, $value): Struct\Info
    method getAll (line 64) | public function getAll($field = null, $value = null): array

FILE: src/Api/Operator/Ui.php
  class Ui (line 8) | class Ui extends \PleskX\Api\Operator
    method getNavigation (line 10) | public function getNavigation(): array
    method createCustomButton (line 18) | public function createCustomButton(string $owner, array $properties): int
    method getCustomButton (line 34) | public function getCustomButton(int $id): Struct\CustomButton
    method deleteCustomButton (line 41) | public function deleteCustomButton(int $id): bool

FILE: src/Api/Operator/VirtualDirectory.php
  class VirtualDirectory (line 6) | class VirtualDirectory extends \PleskX\Api\Operator

FILE: src/Api/Operator/Webspace.php
  class Webspace (line 9) | class Webspace extends Operator
    method getPermissionDescriptor (line 11) | public function getPermissionDescriptor(): Struct\PermissionDescriptor
    method getLimitDescriptor (line 18) | public function getLimitDescriptor(): Struct\LimitDescriptor
    method getPhysicalHostingDescriptor (line 25) | public function getPhysicalHostingDescriptor(): Struct\PhysicalHosting...
    method getPhpSettings (line 38) | public function getPhpSettings(string $field, $value): Struct\PhpSettings
    method getLimits (line 57) | public function getLimits(string $field, $value): Struct\Limits
    method create (line 71) | public function create(array $properties, ?array $hostingProperties = ...
    method delete (line 117) | public function delete(string $field, $value): bool
    method get (line 128) | public function get(string $field, $value): Struct\GeneralInfo
    method getAll (line 138) | public function getAll(): array
    method getDiskUsage (line 149) | public function getDiskUsage(string $field, $value): Struct\DiskUsage
    method enable (line 162) | public function enable(string $field, $value): bool
    method disable (line 173) | public function disable(string $field, $value): bool
    method setProperties (line 185) | public function setProperties(string $field, $value, array $properties...

FILE: src/Api/Struct/Certificate/Info.php
  class Info (line 8) | class Info extends AbstractStruct
    method __construct (line 15) | public function __construct($input)
    method getMapping (line 29) | public function getMapping(): array

FILE: src/Api/Struct/Customer/GeneralInfo.php
  class GeneralInfo (line 8) | class GeneralInfo extends AbstractStruct
    method __construct (line 27) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Customer/Info.php
  class Info (line 8) | class Info extends AbstractStruct
    method __construct (line 13) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Database/Info.php
  class Info (line 8) | class Info extends AbstractStruct
    method __construct (line 17) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Database/UserInfo.php
  class UserInfo (line 8) | class UserInfo extends AbstractStruct
    method __construct (line 14) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/DatabaseServer/Info.php
  class Info (line 8) | class Info extends AbstractStruct
    method __construct (line 15) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Dns/Info.php
  class Info (line 8) | class Info extends AbstractStruct
    method __construct (line 18) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/EventLog/DetailedEvent.php
  class DetailedEvent (line 8) | class DetailedEvent extends AbstractStruct
    method __construct (line 18) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/EventLog/Event.php
  class Event (line 8) | class Event extends AbstractStruct
    method __construct (line 15) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Ip/Info.php
  class Info (line 8) | class Info extends AbstractStruct
    method __construct (line 15) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Locale/Info.php
  class Info (line 8) | class Info extends AbstractStruct
    method __construct (line 14) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Mail/GeneralInfo.php
  class GeneralInfo (line 8) | class GeneralInfo extends AbstractStruct
    method __construct (line 14) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Mail/Info.php
  class Info (line 8) | class Info extends AbstractStruct
    method __construct (line 13) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/PhpHandler/Info.php
  class Info (line 8) | class Info extends AbstractStruct
    method __construct (line 21) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/ProtectedDirectory/DataInfo.php
  class DataInfo (line 8) | class DataInfo extends AbstractStruct
    method __construct (line 13) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/ProtectedDirectory/Info.php
  class Info (line 8) | class Info extends AbstractStruct
    method __construct (line 12) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/ProtectedDirectory/UserInfo.php
  class UserInfo (line 8) | class UserInfo extends AbstractStruct
    method __construct (line 12) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Reseller/GeneralInfo.php
  class GeneralInfo (line 8) | class GeneralInfo extends AbstractStruct
    method __construct (line 15) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Reseller/Info.php
  class Info (line 8) | class Info extends AbstractStruct
    method __construct (line 13) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/SecretKey/Info.php
  class Info (line 8) | class Info extends AbstractStruct
    method __construct (line 15) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Server/Admin.php
  class Admin (line 8) | class Admin extends AbstractStruct
    method __construct (line 14) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Server/GeneralInfo.php
  class GeneralInfo (line 8) | class GeneralInfo extends AbstractStruct
    method __construct (line 14) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Server/Preferences.php
  class Preferences (line 8) | class Preferences extends AbstractStruct
    method __construct (line 14) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Server/SessionPreferences.php
  class SessionPreferences (line 8) | class SessionPreferences extends AbstractStruct
    method __construct (line 12) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Server/Statistics.php
  class Statistics (line 8) | class Statistics extends AbstractStruct
    method __construct (line 35) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Server/Statistics/DiskSpace.php
  class DiskSpace (line 8) | class DiskSpace extends AbstractStruct
    method __construct (line 14) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Server/Statistics/LoadAverage.php
  class LoadAverage (line 8) | class LoadAverage extends AbstractStruct
    method __construct (line 14) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Server/Statistics/Memory.php
  class Memory (line 8) | class Memory extends AbstractStruct
    method __construct (line 17) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Server/Statistics/Objects.php
  class Objects (line 8) | class Objects extends AbstractStruct
    method __construct (line 22) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Server/Statistics/Other.php
  class Other (line 8) | class Other extends AbstractStruct
    method __construct (line 14) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Server/Statistics/Swap.php
  class Swap (line 8) | class Swap extends AbstractStruct
    method __construct (line 14) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Server/Statistics/Version.php
  class Version (line 8) | class Version extends AbstractStruct
    method __construct (line 17) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Server/UpdatesInfo.php
  class UpdatesInfo (line 8) | class UpdatesInfo extends AbstractStruct
    method __construct (line 13) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/ServicePlan/Info.php
  class Info (line 8) | class Info extends AbstractStruct
    method __construct (line 15) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/ServicePlanAddon/Info.php
  class Info (line 8) | class Info extends AbstractStruct
    method __construct (line 15) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Session/Info.php
  class Info (line 8) | class Info extends AbstractStruct
    method __construct (line 17) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Site/GeneralInfo.php
  class GeneralInfo (line 8) | class GeneralInfo extends AbstractStruct
    method __construct (line 22) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Site/HostingInfo.php
  class HostingInfo (line 8) | class HostingInfo extends AbstractStruct
    method __construct (line 13) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Site/Info.php
  class Info (line 8) | class Info extends AbstractStruct
    method __construct (line 13) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/SiteAlias/GeneralInfo.php
  class GeneralInfo (line 8) | class GeneralInfo extends AbstractStruct
    method __construct (line 14) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/SiteAlias/Info.php
  class Info (line 8) | class Info extends AbstractStruct
    method __construct (line 13) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Subdomain/Info.php
  class Info (line 8) | class Info extends AbstractStruct
    method __construct (line 15) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Ui/CustomButton.php
  class CustomButton (line 8) | class CustomButton extends AbstractStruct
    method __construct (line 19) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Webspace/DiskUsage.php
  class DiskUsage (line 8) | class DiskUsage extends AbstractStruct
    method __construct (line 22) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Webspace/GeneralInfo.php
  class GeneralInfo (line 8) | class GeneralInfo extends AbstractStruct
    method __construct (line 24) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Webspace/HostingPropertyInfo.php
  class HostingPropertyInfo (line 8) | class HostingPropertyInfo extends AbstractStruct
    method __construct (line 14) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Webspace/Info.php
  class Info (line 8) | class Info extends AbstractStruct
    method __construct (line 14) | public function __construct(\SimpleXMLElement $apiResponse, string $na...

FILE: src/Api/Struct/Webspace/Limit.php
  class Limit (line 8) | class Limit extends AbstractStruct
    method __construct (line 13) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Webspace/LimitDescriptor.php
  class LimitDescriptor (line 8) | class LimitDescriptor extends AbstractStruct
    method __construct (line 12) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Webspace/LimitInfo.php
  class LimitInfo (line 8) | class LimitInfo extends AbstractStruct
    method __construct (line 14) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Webspace/Limits.php
  class Limits (line 8) | class Limits extends AbstractStruct
    method __construct (line 13) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Webspace/PermissionDescriptor.php
  class PermissionDescriptor (line 8) | class PermissionDescriptor extends AbstractStruct
    method __construct (line 12) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Webspace/PermissionInfo.php
  class PermissionInfo (line 8) | class PermissionInfo extends AbstractStruct
    method __construct (line 14) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Webspace/PhpSettings.php
  class PhpSettings (line 8) | class PhpSettings extends AbstractStruct
    method __construct (line 12) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/Struct/Webspace/PhysicalHostingDescriptor.php
  class PhysicalHostingDescriptor (line 8) | class PhysicalHostingDescriptor extends AbstractStruct
    method __construct (line 12) | public function __construct(\SimpleXMLElement $apiResponse)

FILE: src/Api/XmlResponse.php
  class XmlResponse (line 9) | class XmlResponse extends \SimpleXMLElement
    method getValue (line 18) | public function getValue(string $node): string

FILE: tests/AbstractTestCase.php
  class AbstractTestCase (line 10) | abstract class AbstractTestCase extends TestCase
    method setUpBeforeClass (line 18) | public static function setUpBeforeClass(): void
    method tearDownAfterClass (line 41) | public static function tearDownAfterClass(): void
    method getIpAddress (line 68) | protected static function getIpAddress(): string
    method createWebspace (line 76) | protected static function createWebspace(): \PleskX\Api\Struct\Webspac...
    method createServicePlan (line 94) | protected static function createServicePlan(): \PleskX\Api\Struct\Serv...
    method createServicePlanAddon (line 104) | protected static function createServicePlanAddon(): \PleskX\Api\Struct...

FILE: tests/ApiClientTest.php
  class ApiClientTest (line 8) | class ApiClientTest extends AbstractTestCase
    method testWrongProtocol (line 10) | public function testWrongProtocol()
    method testUnknownOperator (line 20) | public function testUnknownOperator()
    method testInvalidXmlRequest (line 30) | public function testInvalidXmlRequest()
    method testInvalidCredentials (line 38) | public function testInvalidCredentials()
    method testInvalidSecretKey (line 53) | public function testInvalidSecretKey()
    method testLatestMajorProtocol (line 68) | public function testLatestMajorProtocol()
    method testLatestMinorProtocol (line 76) | public function testLatestMinorProtocol()
    method testRequestShortSyntax (line 84) | public function testRequestShortSyntax()
    method testOperatorPlainRequest (line 90) | public function testOperatorPlainRequest()
    method testRequestArraySyntax (line 97) | public function testRequestArraySyntax()
    method testOperatorArraySyntax (line 109) | public function testOperatorArraySyntax()
    method testMultiRequest (line 115) | public function testMultiRequest()
    method testConnectionError (line 131) | public function testConnectionError()
    method testGetHost (line 139) | public function testGetHost()
    method testGetPort (line 145) | public function testGetPort()
    method testGetProtocol (line 151) | public function testGetProtocol()
    method testSetVerifyResponse (line 157) | public function testSetVerifyResponse()

FILE: tests/CertificateTest.php
  class CertificateTest (line 8) | class CertificateTest extends AbstractTestCase
    method testGenerate (line 27) | public function testGenerate()
    method testInstall (line 36) | public function testInstall()
    method testUpdate (line 49) | public function testUpdate()
    method testDelete (line 65) | public function testDelete()
    method generateCertificateOpenSsl (line 78) | private function generateCertificateOpenSsl(array $dn): Struct\Info

FILE: tests/CustomerTest.php
  class CustomerTest (line 9) | class CustomerTest extends AbstractTestCase
    method setUp (line 13) | public function setUp(): void
    method testCreate (line 26) | public function testCreate()
    method testDelete (line 35) | public function testDelete()
    method testGet (line 42) | public function testGet()
    method testGetAll (line 57) | public function testGetAll()
    method testGetAllEmpty (line 88) | public function testGetAllEmpty()
    method testEnable (line 94) | public function testEnable()
    method testDisable (line 105) | public function testDisable()
    method testSetProperties (line 115) | public function testSetProperties()

FILE: tests/DatabaseServerTest.php
  class DatabaseServerTest (line 6) | class DatabaseServerTest extends AbstractTestCase
    method testGetSupportedTypes (line 8) | public function testGetSupportedTypes()
    method testGet (line 15) | public function testGet()
    method testGetAll (line 22) | public function testGetAll()
    method testGetDefault (line 30) | public function testGetDefault()

FILE: tests/DatabaseTest.php
  class DatabaseTest (line 8) | class DatabaseTest extends AbstractTestCase
    method setUpBeforeClass (line 13) | public static function setUpBeforeClass(): void
    method testCreate (line 20) | public function testCreate()
    method testCreateUser (line 31) | public function testCreateUser()
    method testUpdateUser (line 48) | public function testUpdateUser()
    method testGetById (line 71) | public function testGetById()
    method testGetAllByWebspaceId (line 89) | public function testGetAllByWebspaceId()
    method testGetUserById (line 113) | public function testGetUserById()
    method testGetAllUsersByDbId (line 136) | public function testGetAllUsersByDbId()
    method testDelete (line 180) | public function testDelete()
    method testDeleteUser (line 192) | public function testDeleteUser()
    method createDatabase (line 211) | private function createDatabase(array $params): \PleskX\Api\Struct\Dat...
    method createUser (line 220) | private function createUser(array $params): \PleskX\Api\Struct\Databas...
    method testGetAllForWebspaceWithNoDatabases (line 229) | public function testGetAllForWebspaceWithNoDatabases()

FILE: tests/DnsTemplateTest.php
  class DnsTemplateTest (line 6) | class DnsTemplateTest extends AbstractTestCase
    method setUpBeforeClass (line 10) | public static function setUpBeforeClass(): void
    method setUp (line 18) | protected function setUp(): void
    method testCreate (line 27) | public function testCreate()
    method testGetById (line 41) | public function testGetById()
    method testGetAll (line 56) | public function testGetAll()
    method testDelete (line 81) | public function testDelete()

FILE: tests/DnsTest.php
  class DnsTest (line 6) | class DnsTest extends AbstractTestCase
    method setUpBeforeClass (line 11) | public static function setUpBeforeClass(): void
    method setUp (line 23) | protected function setUp(): void
    method testCreate (line 32) | public function testCreate()
    method testBulkCreate (line 48) | public function testBulkCreate(): array
    method testBulkDelete (line 87) | public function testBulkDelete(array $createdRecords)
    method testGetById (line 103) | public function testGetById()
    method testGetAllByWebspaceId (line 120) | public function testGetAllByWebspaceId()
    method testDelete (line 150) | public function testDelete()

FILE: tests/EventLogTest.php
  class EventLogTest (line 6) | class EventLogTest extends AbstractTestCase
    method testGet (line 8) | public function testGet()
    method testGetDetailedLog (line 17) | public function testGetDetailedLog()
    method testGetLastId (line 26) | public function testGetLastId()

FILE: tests/IpTest.php
  class IpTest (line 6) | class IpTest extends AbstractTestCase
    method testGet (line 8) | public function testGet()

FILE: tests/LocaleTest.php
  class LocaleTest (line 6) | class LocaleTest extends AbstractTestCase
    method testGet (line 8) | public function testGet()
    method testGetById (line 17) | public function testGetById()

FILE: tests/MailTest.php
  class MailTest (line 8) | class MailTest extends AbstractTestCase
    method setUpBeforeClass (line 13) | public static function setUpBeforeClass(): void
    method setUp (line 25) | protected function setUp(): void
    method testCreate (line 34) | public function testCreate()
    method testCreateMultiForwarding (line 50) | public function testCreateMultiForwarding()
    method testDelete (line 101) | public function testDelete()
    method testGet (line 109) | public function testGet()
    method testGetAll (line 120) | public function testGetAll()
    method testGetAllWithoutMailnames (line 131) | public function testGetAllWithoutMailnames()

FILE: tests/PhpHandlerTest.php
  class PhpHandlerTest (line 6) | class PhpHandlerTest extends AbstractTestCase
    method testGet (line 8) | public function testGet()
    method testGetAll (line 15) | public function testGetAll()
    method testGetUnknownHandlerThrowsException (line 28) | public function testGetUnknownHandlerThrowsException()

FILE: tests/ProtectedDirectoryTest.php
  class ProtectedDirectoryTest (line 8) | class ProtectedDirectoryTest extends AbstractTestCase
    method setUpBeforeClass (line 12) | public static function setUpBeforeClass(): void
    method testAdd (line 18) | public function testAdd()
    method testAddInvalidDirectory (line 28) | public function testAddInvalidDirectory()
    method testDelete (line 36) | public function testDelete()
    method testGetById (line 44) | public function testGetById()
    method testGetUnknownDirectory (line 54) | public function testGetUnknownDirectory()
    method testAddUser (line 63) | public function testAddUser()
    method testDeleteUser (line 77) | public function testDeleteUser()

FILE: tests/ResellerTest.php
  class ResellerTest (line 9) | class ResellerTest extends AbstractTestCase
    method setUp (line 13) | public function setUp(): void
    method testCreate (line 22) | public function testCreate()
    method testDelete (line 31) | public function testDelete()
    method testGet (line 38) | public function testGet()
    method testGetAll (line 50) | public function testGetAll()
    method testGetAllEmpty (line 78) | public function testGetAllEmpty()

FILE: tests/SecretKeyTest.php
  class SecretKeyTest (line 8) | class SecretKeyTest extends AbstractTestCase
    method testCreate (line 10) | public function testCreate()
    method testCreateAutoIp (line 20) | public function testCreateAutoIp()
    method testCreateMultiIps (line 27) | public function testCreateMultiIps()
    method testCreateWithDescription (line 37) | public function testCreateWithDescription()
    method testGet (line 47) | public function testGet()
    method testGetAll (line 59) | public function testGetAll()
    method testDelete (line 79) | public function testDelete()
    method testListEmpty (line 92) | public function testListEmpty()

FILE: tests/ServerTest.php
  class ServerTest (line 6) | class ServerTest extends AbstractTestCase
    method testGetProtos (line 8) | public function testGetProtos()
    method testGetGenInfo (line 15) | public function testGetGenInfo()
    method testGetPreferences (line 26) | public function testGetPreferences()
    method testGetAdmin (line 34) | public function testGetAdmin()
    method testGetKeyInfo (line 41) | public function testGetKeyInfo()
    method testGetComponents (line 50) | public function testGetComponents()
    method testGetServiceStates (line 58) | public function testGetServiceStates()
    method testGetSessionPreferences (line 72) | public function testGetSessionPreferences()
    method testGetShells (line 79) | public function testGetShells()
    method testGetNetworkInterfaces (line 87) | public function testGetNetworkInterfaces()
    method testGetStatistics (line 94) | public function testGetStatistics()
    method testGetSiteIsolationConfig (line 112) | public function testGetSiteIsolationConfig()
    method testGetUpdatesInfo (line 120) | public function testGetUpdatesInfo()

FILE: tests/ServicePlanAddonTest.php
  class ServicePlanAddonTest (line 6) | class ServicePlanAddonTest extends AbstractTestCase
    method testGet (line 8) | public function testGet()
    method testGetAll (line 17) | public function testGetAll()
    method testCreate (line 29) | public function testCreate()
    method testDelete (line 37) | public function testDelete()
    method testCreateComplex (line 45) | public function testCreateComplex()

FILE: tests/ServicePlanTest.php
  class ServicePlanTest (line 6) | class ServicePlanTest extends AbstractTestCase
    method testGet (line 8) | public function testGet()
    method testGetAll (line 18) | public function testGetAll()
    method testCreateServicePlan (line 30) | public function testCreateServicePlan()
    method testDelete (line 38) | public function testDelete()
    method testCreateComplexServicePlan (line 46) | public function testCreateComplexServicePlan()

FILE: tests/SessionTest.php
  class SessionTest (line 6) | class SessionTest extends AbstractTestCase
    method testCreate (line 8) | public function testCreate()
    method testGet (line 16) | public function testGet()
    method testTerminate (line 28) | public function testTerminate()

FILE: tests/SiteAliasTest.php
  class SiteAliasTest (line 6) | class SiteAliasTest extends AbstractTestCase
    method setUpBeforeClass (line 10) | public static function setUpBeforeClass(): void
    method createSiteAlias (line 16) | private function createSiteAlias($name, array $properties = []): \Ples...
    method testCreate (line 26) | public function testCreate()
    method testDelete (line 36) | public function testDelete()
    method testGet (line 44) | public function testGet()
    method testGetAll (line 54) | public function testGetAll()

FILE: tests/SiteTest.php
  class SiteTest (line 8) | class SiteTest extends AbstractTestCase
    method setUpBeforeClass (line 12) | public static function setUpBeforeClass(): void
    method setUp (line 18) | protected function setUp(): void
    method createSite (line 29) | private function createSite($name, array $properties = []): \PleskX\Ap...
    method testCreate (line 39) | public function testCreate()
    method testDelete (line 49) | public function testDelete()
    method testGet (line 57) | public function testGet()
    method testGetHostingWoHosting (line 76) | public function testGetHostingWoHosting()
    method testGetHostingWithHosting (line 86) | public function testGetHostingWithHosting()
    method testGetAll (line 102) | public function testGetAll()
    method testGetAllWithoutSites (line 117) | public function testGetAllWithoutSites()

FILE: tests/SubdomainTest.php
  class SubdomainTest (line 6) | class SubdomainTest extends AbstractTestCase
    method setUpBeforeClass (line 11) | public static function setUpBeforeClass(): void
    method createSubdomain (line 19) | private function createSubdomain(string $name): \PleskX\Api\Struct\Sub...
    method testCreate (line 30) | public function testCreate()
    method testDelete (line 40) | public function testDelete()
    method testGet (line 48) | public function testGet()
    method testGetAll (line 61) | public function testGetAll()

FILE: tests/UiTest.php
  class UiTest (line 6) | class UiTest extends AbstractTestCase
    method testGetNavigation (line 14) | public function testGetNavigation()
    method testCreateCustomButton (line 28) | public function testCreateCustomButton()
    method testGetCustomButton (line 36) | public function testGetCustomButton()
    method testDeleteCustomButton (line 46) | public function testDeleteCustomButton()

FILE: tests/Utility/KeyLimitChecker.php
  class KeyLimitChecker (line 6) | class KeyLimitChecker
    method checkByType (line 21) | public static function checkByType(array $keyInfo, $type, $minimalRequ...

FILE: tests/Utility/PasswordProvider.php
  class PasswordProvider (line 6) | class PasswordProvider

FILE: tests/WebspaceTest.php
  class WebspaceTest (line 8) | class WebspaceTest extends AbstractTestCase
    method testGetPermissionDescriptor (line 10) | public function testGetPermissionDescriptor()
    method testGetLimitDescriptor (line 17) | public function testGetLimitDescriptor()
    method testGetDiskUsage (line 24) | public function testGetDiskUsage()
    method testGetPhysicalHostingDescriptor (line 34) | public function testGetPhysicalHostingDescriptor()
    method testGetPhpSettings (line 45) | public function testGetPhpSettings()
    method testGetLimits (line 55) | public function testGetLimits()
    method testCreateWebspace (line 66) | public function testCreateWebspace()
    method testDelete (line 75) | public function testDelete()
    method testDeleteByName (line 83) | public function testDeleteByName()
    method testRequestCreateWebspace (line 91) | public function testRequestCreateWebspace()
    method testGet (line 175) | public function testGet()
    method testEnable (line 191) | public function testEnable()
    method testDisable (line 203) | public function testDisable()
    method testSetProperties (line 214) | public function testSetProperties()
    method testIpsAsArray (line 226) | public function testIpsAsArray()
Condensed preview — 129 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (215K chars).
[
  {
    "path": ".github/dependabot.yml",
    "chars": 405,
    "preview": "version: 2\n\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "chars": 294,
    "preview": "name: Tests\n\non:\n  push:\n    branches:\n      - master\n  pull_request:\n\njobs:\n  test:\n    runs-on: ubuntu-24.04\n    steps"
  },
  {
    "path": ".gitignore",
    "chars": 65,
    "preview": "/.env\n/vendor/\n/phpunit.xml\n/.phpunit.result.cache\n/coverage.xml\n"
  },
  {
    "path": "Dockerfile",
    "chars": 405,
    "preview": "FROM php:8.2-cli\n\nRUN apt-get update \\\n    && apt-get install -y unzip \\\n    && docker-php-ext-install pcntl \\\n    && pe"
  },
  {
    "path": "LICENSE",
    "chars": 574,
    "preview": "Copyright 1999-2025. WebPros International GmbH.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou ma"
  },
  {
    "path": "README.md",
    "chars": 2295,
    "preview": "## PHP library for Plesk XML-RPC API\n\n[![Test Status](https://github.com/plesk/api-php-lib/actions/workflows/test.yml/ba"
  },
  {
    "path": "composer.json",
    "chars": 1320,
    "preview": "{\n    \"name\": \"plesk/api-php-lib\",\n    \"type\": \"library\",\n    \"description\": \"PHP object-oriented library for Plesk XML-"
  },
  {
    "path": "docker-compose.yml",
    "chars": 671,
    "preview": "# Copyright 1999-2025. WebPros International GmbH.\nversion: '3'\nservices:\n  plesk:\n    image: plesk/plesk:latest\n    log"
  },
  {
    "path": "phpcs.xml.dist",
    "chars": 1241,
    "preview": "<?xml version=\"1.0\"?>\n<!-- Copyright 1999-2025. WebPros International GmbH. -->\n<ruleset name=\"PHP library for Plesk XML"
  },
  {
    "path": "phpunit-watcher.yml",
    "chars": 106,
    "preview": "# Copyright 1999-2025. WebPros International GmbH.\nphpunit:\n  arguments: '--stop-on-failure'\n  timeout: 0\n"
  },
  {
    "path": "phpunit.xml.dist",
    "chars": 778,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- Copyright 1999-2025. WebPros International GmbH. -->\n<phpunit xmlns:xsi=\"htt"
  },
  {
    "path": "psalm.xml",
    "chars": 725,
    "preview": "<?xml version=\"1.0\"?>\n<!-- Copyright 1999-2025. WebPros International GmbH. -->\n<psalm\n    errorLevel=\"3\"\n    resolveFro"
  },
  {
    "path": "src/Api/AbstractStruct.php",
    "chars": 2440,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api;\n\nabstract class AbstractStruct\n{\n    /*"
  },
  {
    "path": "src/Api/Client/Exception.php",
    "chars": 166,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Client;\n\n/**\n * Transport layer exceptio"
  },
  {
    "path": "src/Api/Client.php",
    "chars": 15218,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api;\n\nuse DOMDocument;\nuse SimpleXMLElement;"
  },
  {
    "path": "src/Api/Exception.php",
    "chars": 167,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api;\n\n/**\n * Exceptions for XML-RPC API clie"
  },
  {
    "path": "src/Api/InternalClient.php",
    "chars": 484,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api;\n\n/**\n * Internal client for Plesk XML-R"
  },
  {
    "path": "src/Api/Operator/Aps.php",
    "chars": 134,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nclass Aps extends \\PleskX\\Api"
  },
  {
    "path": "src/Api/Operator/Certificate.php",
    "chars": 2688,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nuse PleskX\\Api\\Struct\\Certifi"
  },
  {
    "path": "src/Api/Operator/Customer.php",
    "chars": 2529,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nuse PleskX\\Api\\Struct\\Custome"
  },
  {
    "path": "src/Api/Operator/Database.php",
    "chars": 3799,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nuse PleskX\\Api\\Struct\\Databas"
  },
  {
    "path": "src/Api/Operator/DatabaseServer.php",
    "chars": 2180,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nuse PleskX\\Api\\Struct\\Databas"
  },
  {
    "path": "src/Api/Operator/Dns.php",
    "chars": 3463,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nuse PleskX\\Api\\Struct\\Dns as "
  },
  {
    "path": "src/Api/Operator/DnsTemplate.php",
    "chars": 2491,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nuse PleskX\\Api\\Struct\\Dns as "
  },
  {
    "path": "src/Api/Operator/EventLog.php",
    "chars": 996,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nuse PleskX\\Api\\Struct\\EventLo"
  },
  {
    "path": "src/Api/Operator/Ip.php",
    "chars": 588,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nuse PleskX\\Api\\Struct\\Ip as S"
  },
  {
    "path": "src/Api/Operator/Locale.php",
    "chars": 952,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nuse PleskX\\Api\\Struct\\Locale "
  },
  {
    "path": "src/Api/Operator/LogRotation.php",
    "chars": 142,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nclass LogRotation extends \\Pl"
  },
  {
    "path": "src/Api/Operator/Mail.php",
    "chars": 2731,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nuse PleskX\\Api\\Client;\nuse Pl"
  },
  {
    "path": "src/Api/Operator/PhpHandler.php",
    "chars": 1679,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nuse PleskX\\Api\\Client;\nuse Pl"
  },
  {
    "path": "src/Api/Operator/ProtectedDirectory.php",
    "chars": 3075,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nuse PleskX\\Api\\Client;\nuse Pl"
  },
  {
    "path": "src/Api/Operator/Reseller.php",
    "chars": 2141,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nuse PleskX\\Api\\Struct\\Reselle"
  },
  {
    "path": "src/Api/Operator/ResellerPlan.php",
    "chars": 143,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nclass ResellerPlan extends \\P"
  },
  {
    "path": "src/Api/Operator/SecretKey.php",
    "chars": 1896,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nuse PleskX\\Api\\Struct\\SecretK"
  },
  {
    "path": "src/Api/Operator/Server.php",
    "chars": 4005,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nuse PleskX\\Api\\Struct\\Server "
  },
  {
    "path": "src/Api/Operator/ServicePlan.php",
    "chars": 1778,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nuse PleskX\\Api\\Struct\\Service"
  },
  {
    "path": "src/Api/Operator/ServicePlanAddon.php",
    "chars": 1788,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nuse PleskX\\Api\\Struct\\Service"
  },
  {
    "path": "src/Api/Operator/Session.php",
    "chars": 1223,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nuse PleskX\\Api\\Struct\\Session"
  },
  {
    "path": "src/Api/Operator/Site.php",
    "chars": 2622,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nuse PleskX\\Api\\Struct\\Site as"
  },
  {
    "path": "src/Api/Operator/SiteAlias.php",
    "chars": 2280,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nuse PleskX\\Api\\Struct\\SiteAli"
  },
  {
    "path": "src/Api/Operator/Subdomain.php",
    "chars": 2426,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nuse PleskX\\Api\\Struct\\Subdoma"
  },
  {
    "path": "src/Api/Operator/Ui.php",
    "chars": 1331,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nuse PleskX\\Api\\Struct\\Ui as S"
  },
  {
    "path": "src/Api/Operator/VirtualDirectory.php",
    "chars": 193,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nclass VirtualDirectory extend"
  },
  {
    "path": "src/Api/Operator/Webspace.php",
    "chars": 5638,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Operator;\n\nuse PleskX\\Api\\Operator;\nuse "
  },
  {
    "path": "src/Api/Operator.php",
    "chars": 3073,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api;\n\nclass Operator\n{\n    protected string "
  },
  {
    "path": "src/Api/Struct/Certificate/Info.php",
    "chars": 955,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Certificate;\n\nuse PleskX\\Api\\Abst"
  },
  {
    "path": "src/Api/Struct/Customer/GeneralInfo.php",
    "chars": 1175,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Customer;\n\nuse PleskX\\Api\\Abstrac"
  },
  {
    "path": "src/Api/Struct/Customer/Info.php",
    "chars": 392,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Customer;\n\nuse PleskX\\Api\\Abstrac"
  },
  {
    "path": "src/Api/Struct/Database/Info.php",
    "chars": 610,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Database;\n\nuse PleskX\\Api\\Abstrac"
  },
  {
    "path": "src/Api/Struct/Database/UserInfo.php",
    "chars": 441,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Database;\n\nuse PleskX\\Api\\Abstrac"
  },
  {
    "path": "src/Api/Struct/DatabaseServer/Info.php",
    "chars": 485,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\DatabaseServer;\n\nuse PleskX\\Api\\A"
  },
  {
    "path": "src/Api/Struct/Dns/Info.php",
    "chars": 627,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Dns;\n\nuse PleskX\\Api\\AbstractStru"
  },
  {
    "path": "src/Api/Struct/EventLog/DetailedEvent.php",
    "chars": 647,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\EventLog;\n\nuse PleskX\\Api\\Abstrac"
  },
  {
    "path": "src/Api/Struct/EventLog/Event.php",
    "chars": 485,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\EventLog;\n\nuse PleskX\\Api\\Abstrac"
  },
  {
    "path": "src/Api/Struct/Ip/Info.php",
    "chars": 510,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Ip;\n\nuse PleskX\\Api\\AbstractStruc"
  },
  {
    "path": "src/Api/Struct/Locale/Info.php",
    "chars": 464,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Locale;\n\nuse PleskX\\Api\\AbstractS"
  },
  {
    "path": "src/Api/Struct/Mail/GeneralInfo.php",
    "chars": 454,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Mail;\n\nuse PleskX\\Api\\AbstractStr"
  },
  {
    "path": "src/Api/Struct/Mail/Info.php",
    "chars": 388,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Mail;\n\nuse PleskX\\Api\\AbstractStr"
  },
  {
    "path": "src/Api/Struct/PhpHandler/Info.php",
    "chars": 826,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\PhpHandler;\n\nuse PleskX\\Api\\Abstr"
  },
  {
    "path": "src/Api/Struct/ProtectedDirectory/DataInfo.php",
    "chars": 417,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\ProtectedDirectory;\n\nuse PleskX\\A"
  },
  {
    "path": "src/Api/Struct/ProtectedDirectory/Info.php",
    "chars": 357,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\ProtectedDirectory;\n\nuse PleskX\\A"
  },
  {
    "path": "src/Api/Struct/ProtectedDirectory/UserInfo.php",
    "chars": 361,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\ProtectedDirectory;\n\nuse PleskX\\A"
  },
  {
    "path": "src/Api/Struct/Reseller/GeneralInfo.php",
    "chars": 805,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Reseller;\n\nuse PleskX\\Api\\Abstrac"
  },
  {
    "path": "src/Api/Struct/Reseller/Info.php",
    "chars": 392,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Reseller;\n\nuse PleskX\\Api\\Abstrac"
  },
  {
    "path": "src/Api/Struct/SecretKey/Info.php",
    "chars": 515,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\SecretKey;\n\nuse PleskX\\Api\\Abstra"
  },
  {
    "path": "src/Api/Struct/Server/Admin.php",
    "chars": 516,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Server;\n\nuse PleskX\\Api\\AbstractS"
  },
  {
    "path": "src/Api/Struct/Server/GeneralInfo.php",
    "chars": 475,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Server;\n\nuse PleskX\\Api\\AbstractS"
  },
  {
    "path": "src/Api/Struct/Server/Preferences.php",
    "chars": 510,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Server;\n\nuse PleskX\\Api\\AbstractS"
  },
  {
    "path": "src/Api/Struct/Server/SessionPreferences.php",
    "chars": 380,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Server;\n\nuse PleskX\\Api\\AbstractS"
  },
  {
    "path": "src/Api/Struct/Server/Statistics/DiskSpace.php",
    "chars": 451,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Server\\Statistics;\n\nuse PleskX\\Ap"
  },
  {
    "path": "src/Api/Struct/Server/Statistics/LoadAverage.php",
    "chars": 528,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Server\\Statistics;\n\nuse PleskX\\Ap"
  },
  {
    "path": "src/Api/Struct/Server/Statistics/Memory.php",
    "chars": 586,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Server\\Statistics;\n\nuse PleskX\\Ap"
  },
  {
    "path": "src/Api/Struct/Server/Statistics/Objects.php",
    "chars": 1113,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Server\\Statistics;\n\nuse PleskX\\Ap"
  },
  {
    "path": "src/Api/Struct/Server/Statistics/Other.php",
    "chars": 476,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Server\\Statistics;\n\nuse PleskX\\Ap"
  },
  {
    "path": "src/Api/Struct/Server/Statistics/Swap.php",
    "chars": 446,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Server\\Statistics;\n\nuse PleskX\\Ap"
  },
  {
    "path": "src/Api/Struct/Server/Statistics/Version.php",
    "chars": 755,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Server\\Statistics;\n\nuse PleskX\\Ap"
  },
  {
    "path": "src/Api/Struct/Server/Statistics.php",
    "chars": 1377,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Server;\n\nuse PleskX\\Api\\AbstractS"
  },
  {
    "path": "src/Api/Struct/Server/UpdatesInfo.php",
    "chars": 482,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Server;\n\nuse PleskX\\Api\\AbstractS"
  },
  {
    "path": "src/Api/Struct/ServicePlan/Info.php",
    "chars": 498,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\ServicePlan;\n\nuse PleskX\\Api\\Abst"
  },
  {
    "path": "src/Api/Struct/ServicePlanAddon/Info.php",
    "chars": 503,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\ServicePlanAddon;\n\nuse PleskX\\Api"
  },
  {
    "path": "src/Api/Struct/Session/Info.php",
    "chars": 598,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Session;\n\nuse PleskX\\Api\\Abstract"
  },
  {
    "path": "src/Api/Struct/Site/GeneralInfo.php",
    "chars": 988,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Site;\n\nuse PleskX\\Api\\AbstractStr"
  },
  {
    "path": "src/Api/Struct/Site/HostingInfo.php",
    "chars": 648,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Site;\n\nuse PleskX\\Api\\AbstractStr"
  },
  {
    "path": "src/Api/Struct/Site/Info.php",
    "chars": 388,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Site;\n\nuse PleskX\\Api\\AbstractStr"
  },
  {
    "path": "src/Api/Struct/SiteAlias/GeneralInfo.php",
    "chars": 467,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\SiteAlias;\n\nuse PleskX\\Api\\Abstra"
  },
  {
    "path": "src/Api/Struct/SiteAlias/Info.php",
    "chars": 397,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\SiteAlias;\n\nuse PleskX\\Api\\Abstra"
  },
  {
    "path": "src/Api/Struct/Subdomain/Info.php",
    "chars": 673,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Subdomain;\n\nuse PleskX\\Api\\Abstra"
  },
  {
    "path": "src/Api/Struct/Ui/CustomButton.php",
    "chars": 845,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Ui;\n\nuse PleskX\\Api\\AbstractStruc"
  },
  {
    "path": "src/Api/Struct/Webspace/DiskUsage.php",
    "chars": 860,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Webspace;\n\nuse PleskX\\Api\\Abstrac"
  },
  {
    "path": "src/Api/Struct/Webspace/GeneralInfo.php",
    "chars": 1143,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Webspace;\n\nuse PleskX\\Api\\Abstrac"
  },
  {
    "path": "src/Api/Struct/Webspace/HostingPropertyInfo.php",
    "chars": 461,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Webspace;\n\nuse PleskX\\Api\\Abstrac"
  },
  {
    "path": "src/Api/Struct/Webspace/Info.php",
    "chars": 465,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Webspace;\n\nuse PleskX\\Api\\Abstrac"
  },
  {
    "path": "src/Api/Struct/Webspace/Limit.php",
    "chars": 402,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Webspace;\n\nuse PleskX\\Api\\Abstrac"
  },
  {
    "path": "src/Api/Struct/Webspace/LimitDescriptor.php",
    "chars": 487,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Webspace;\n\nuse PleskX\\Api\\Abstrac"
  },
  {
    "path": "src/Api/Struct/Webspace/LimitInfo.php",
    "chars": 451,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Webspace;\n\nuse PleskX\\Api\\Abstrac"
  },
  {
    "path": "src/Api/Struct/Webspace/Limits.php",
    "chars": 530,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Webspace;\n\nuse PleskX\\Api\\Abstrac"
  },
  {
    "path": "src/Api/Struct/Webspace/PermissionDescriptor.php",
    "chars": 512,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Webspace;\n\nuse PleskX\\Api\\Abstrac"
  },
  {
    "path": "src/Api/Struct/Webspace/PermissionInfo.php",
    "chars": 456,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Webspace;\n\nuse PleskX\\Api\\Abstrac"
  },
  {
    "path": "src/Api/Struct/Webspace/PhpSettings.php",
    "chars": 515,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Webspace;\n\nuse PleskX\\Api\\Abstrac"
  },
  {
    "path": "src/Api/Struct/Webspace/PhysicalHostingDescriptor.php",
    "chars": 519,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api\\Struct\\Webspace;\n\nuse PleskX\\Api\\Abstrac"
  },
  {
    "path": "src/Api/XmlResponse.php",
    "chars": 516,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskX\\Api;\n\n/**\n * XML wrapper for responses.\n */\n"
  },
  {
    "path": "tests/AbstractTestCase.php",
    "chars": 3296,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nuse PHPUnit\\Framework\\TestCase;\nuse Pl"
  },
  {
    "path": "tests/ApiClientTest.php",
    "chars": 5415,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nuse PleskX\\Api\\Client\\Exception;\n\nclas"
  },
  {
    "path": "tests/CertificateTest.php",
    "chars": 3299,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nuse PleskX\\Api\\Struct\\Certificate as S"
  },
  {
    "path": "tests/CustomerTest.php",
    "chars": 4695,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nuse PleskXTest\\Utility\\KeyLimitChecker"
  },
  {
    "path": "tests/DatabaseServerTest.php",
    "chars": 1096,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nclass DatabaseServerTest extends Abstr"
  },
  {
    "path": "tests/DatabaseTest.php",
    "chars": 8524,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nuse PleskXTest\\Utility\\PasswordProvide"
  },
  {
    "path": "tests/DnsTemplateTest.php",
    "chars": 2724,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nclass DnsTemplateTest extends Abstract"
  },
  {
    "path": "tests/DnsTest.php",
    "chars": 4780,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nclass DnsTest extends AbstractTestCase"
  },
  {
    "path": "tests/EventLogTest.php",
    "chars": 762,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nclass EventLogTest extends AbstractTes"
  },
  {
    "path": "tests/IpTest.php",
    "chars": 406,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nclass IpTest extends AbstractTestCase\n"
  },
  {
    "path": "tests/LocaleTest.php",
    "chars": 523,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nclass LocaleTest extends AbstractTestC"
  },
  {
    "path": "tests/MailTest.php",
    "chars": 4450,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nuse PleskXTest\\Utility\\PasswordProvide"
  },
  {
    "path": "tests/PhpHandlerTest.php",
    "chars": 915,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nclass PhpHandlerTest extends AbstractT"
  },
  {
    "path": "tests/ProtectedDirectoryTest.php",
    "chars": 2958,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nuse PleskXTest\\Utility\\PasswordProvide"
  },
  {
    "path": "tests/ResellerTest.php",
    "chars": 2832,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nuse PleskXTest\\Utility\\KeyLimitChecker"
  },
  {
    "path": "tests/SecretKeyTest.php",
    "chars": 3172,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nuse PleskX\\Api\\Exception;\n\nclass Secre"
  },
  {
    "path": "tests/ServerTest.php",
    "chars": 4433,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nclass ServerTest extends AbstractTestC"
  },
  {
    "path": "tests/ServicePlanAddonTest.php",
    "chars": 1956,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nclass ServicePlanAddonTest extends Abs"
  },
  {
    "path": "tests/ServicePlanTest.php",
    "chars": 2396,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nclass ServicePlanTest extends Abstract"
  },
  {
    "path": "tests/SessionTest.php",
    "chars": 1144,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nclass SessionTest extends AbstractTest"
  },
  {
    "path": "tests/SiteAliasTest.php",
    "chars": 2220,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nclass SiteAliasTest extends AbstractTe"
  },
  {
    "path": "tests/SiteTest.php",
    "chars": 3717,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nuse PleskXTest\\Utility\\KeyLimitChecker"
  },
  {
    "path": "tests/SubdomainTest.php",
    "chars": 2798,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nclass SubdomainTest extends AbstractTe"
  },
  {
    "path": "tests/UiTest.php",
    "chars": 1835,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nclass UiTest extends AbstractTestCase\n"
  },
  {
    "path": "tests/Utility/KeyLimitChecker.php",
    "chars": 1500,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest\\Utility;\n\nclass KeyLimitChecker\n{\n    pu"
  },
  {
    "path": "tests/Utility/PasswordProvider.php",
    "chars": 174,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest\\Utility;\n\nclass PasswordProvider\n{\n    p"
  },
  {
    "path": "tests/WebspaceTest.php",
    "chars": 8149,
    "preview": "<?php\n// Copyright 1999-2025. WebPros International GmbH.\n\nnamespace PleskXTest;\n\nuse PleskXTest\\Utility\\PasswordProvide"
  },
  {
    "path": "wait-for-plesk.sh",
    "chars": 387,
    "preview": "#!/bin/bash\n### Copyright 1999-2025. WebPros International GmbH.\n\nCOUNTER=1\n\nwhile : ; do\n    curl -ksL https://plesk:84"
  }
]

About this extraction

This page contains the full source code of the plesk/api-php-lib GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 129 files (194.0 KB), approximately 53.0k tokens, and a symbol index with 529 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!