├── LICENSE ├── composer.json └── src ├── Api ├── AbstractApi.php ├── Account.php ├── Action.php ├── App.php ├── CdnEndpoint.php ├── Certificate.php ├── Database.php ├── Domain.php ├── DomainRecord.php ├── Droplet.php ├── Firewall.php ├── FloatingIp.php ├── Image.php ├── Key.php ├── LoadBalancer.php ├── Monitoring.php ├── ProjectResource.php ├── Region.php ├── ReservedIp.php ├── Size.php ├── Snapshot.php ├── Tag.php ├── Volume.php └── Vpc.php ├── Client.php ├── Entity ├── AbstractEntity.php ├── Account.php ├── Action.php ├── App.php ├── AppDeployment.php ├── AppDeploymentLog.php ├── AppInstanceSize.php ├── AppRegion.php ├── AppTier.php ├── CdnEndpoint.php ├── Certificate.php ├── Database.php ├── DatabaseBackup.php ├── DatabaseCluster.php ├── DatabaseConnection.php ├── DatabaseMaintenanceWindow.php ├── DatabaseMysqlSettings.php ├── DatabasePool.php ├── DatabaseReplica.php ├── DatabaseRule.php ├── DatabaseUser.php ├── Domain.php ├── DomainRecord.php ├── Droplet.php ├── Firewall.php ├── FirewallLocations.php ├── FirewallRule.php ├── FirewallRuleInbound.php ├── FirewallRuleOutbound.php ├── FloatingIp.php ├── ForwardingRule.php ├── HealthCheck.php ├── Image.php ├── Kernel.php ├── Key.php ├── LoadBalancer.php ├── Meta.php ├── MonitoringAlert.php ├── MonitoringMetric.php ├── Network.php ├── NextBackupWindow.php ├── ProjectResource.php ├── RateLimit.php ├── Region.php ├── ReservedIp.php ├── Size.php ├── Snapshot.php ├── StickySession.php ├── Tag.php ├── Team.php ├── Volume.php └── Vpc.php ├── Exception ├── ApiLimitExceededException.php ├── DiscoveryFailedException.php ├── ErrorException.php ├── ExceptionInterface.php ├── InvalidArgumentException.php ├── InvalidRecordException.php ├── ResourceNotFoundException.php ├── RuntimeException.php └── ValidationFailedException.php ├── HttpClient ├── Builder.php ├── Message │ └── ResponseMediator.php ├── Plugin │ ├── Authentication.php │ ├── ExceptionThrower.php │ └── History.php └── Util │ ├── JsonObject.php │ └── QueryStringBuilder.php ├── ResultPager.php └── ResultPagerInterface.php /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2016 Antoine Kirk 4 | Copyright (c) 2014-2025 Graham Campbell 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "toin0u/digitalocean-v2", 3 | "description": "DigitalOcean API v2 client for PHP", 4 | "keywords": ["DigitalOcean", "API", "Cloud Hosting", "SSD", "VPS"], 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Graham Campbell", 9 | "email": "hello@gjcampbell.co.uk", 10 | "homepage": "https://github.com/GrahamCampbell" 11 | }, 12 | { 13 | "name": "Yassir Hannoun", 14 | "email": "yassir.hannoun@gmail.com", 15 | "homepage": "https://github.com/yassirh" 16 | }, 17 | { 18 | "name": "Antoine Kirk", 19 | "email": "contact@sbin.dk", 20 | "homepage": "https://github.com/toin0u" 21 | } 22 | ], 23 | "require": { 24 | "php": "^8.1", 25 | "ext-json": "*", 26 | "php-http/client-common": "^2.7.2", 27 | "php-http/discovery": "^1.20.0", 28 | "php-http/httplug": "^2.4.1", 29 | "psr/http-client-implementation": "^1.0", 30 | "psr/http-factory-implementation": "^1.0", 31 | "psr/http-message": "^1.1 || ^2.0" 32 | }, 33 | "require-dev": { 34 | "bamarni/composer-bin-plugin": "^1.8.2", 35 | "guzzlehttp/guzzle": "^7.9.2" 36 | }, 37 | "autoload": { 38 | "psr-4": { 39 | "DigitalOceanV2\\": "src/" 40 | } 41 | }, 42 | "autoload-dev": { 43 | "psr-4": { 44 | "DigitalOceanV2\\Tests\\": "tests/" 45 | } 46 | }, 47 | "config": { 48 | "preferred-install": "dist", 49 | "allow-plugins": { 50 | "bamarni/composer-bin-plugin": true, 51 | "php-http/discovery": true 52 | } 53 | }, 54 | "extra": { 55 | "bamarni-bin": { 56 | "bin-links": true, 57 | "forward-command": false 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Api/AbstractApi.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Client; 18 | use DigitalOceanV2\Exception\ExceptionInterface; 19 | use DigitalOceanV2\HttpClient\Message\ResponseMediator; 20 | use DigitalOceanV2\HttpClient\Util\JsonObject; 21 | use DigitalOceanV2\HttpClient\Util\QueryStringBuilder; 22 | use stdClass; 23 | 24 | /** 25 | * @author Antoine Kirk 26 | * @author Graham Campbell 27 | */ 28 | abstract class AbstractApi 29 | { 30 | /** 31 | * The URI prefix. 32 | * 33 | * @var string 34 | */ 35 | private const URI_PREFIX = '/v2/'; 36 | 37 | private readonly Client $client; 38 | 39 | private ?int $perPage; 40 | 41 | private ?int $page; 42 | 43 | public function __construct(Client $client) 44 | { 45 | $this->client = $client; 46 | $this->perPage = null; 47 | $this->page = null; 48 | } 49 | 50 | /** 51 | * Send a GET request with query params. 52 | * 53 | * @param array $headers 54 | * 55 | * @throws ExceptionInterface 56 | */ 57 | protected function get(string $uri, array $params = [], array $headers = []): stdClass 58 | { 59 | if (null !== $this->perPage && !isset($params['per_page'])) { 60 | $params = \array_merge(['per_page' => $this->perPage], $params); 61 | } 62 | 63 | if (null !== $this->page && !isset($params['page'])) { 64 | $params = \array_merge(['page' => $this->page], $params); 65 | } 66 | 67 | $response = $this->client->getHttpClient()->get(self::prepareUri($uri, $params), $headers); 68 | 69 | return ResponseMediator::getContent($response); 70 | } 71 | 72 | /** 73 | * Send a POST request with JSON-encoded params. 74 | * 75 | * @param array $headers 76 | * 77 | * @throws ExceptionInterface 78 | */ 79 | protected function post(string $uri, array $params = [], array $headers = []): stdClass 80 | { 81 | $body = self::prepareJsonBody($params); 82 | 83 | if (null !== $body) { 84 | $headers = self::addJsonContentType($headers); 85 | } 86 | 87 | $response = $this->client->getHttpClient()->post(self::prepareUri($uri), $headers, $body ?? ''); 88 | 89 | return ResponseMediator::getContent($response); 90 | } 91 | 92 | /** 93 | * Send a PUT request with JSON-encoded params. 94 | * 95 | * @param array $headers 96 | * 97 | * @throws ExceptionInterface 98 | */ 99 | protected function put(string $uri, array $params = [], array $headers = []): stdClass 100 | { 101 | $body = self::prepareJsonBody($params); 102 | 103 | if (null !== $body) { 104 | $headers = self::addJsonContentType($headers); 105 | } 106 | 107 | $response = $this->client->getHttpClient()->put(self::prepareUri($uri), $headers, $body ?? ''); 108 | 109 | return ResponseMediator::getContent($response); 110 | } 111 | 112 | /** 113 | * Send a DELETE request with JSON-encoded params. 114 | * 115 | * @param array $headers 116 | * @param array $queryParams 117 | * 118 | * @throws ExceptionInterface 119 | */ 120 | protected function delete(string $uri, array $params = [], array $headers = [], array $queryParams = []): void 121 | { 122 | $body = self::prepareJsonBody($params); 123 | 124 | if (null !== $body) { 125 | $headers = self::addJsonContentType($headers); 126 | } 127 | 128 | $this->client->getHttpClient()->delete(self::prepareUri($uri, $queryParams), $headers, $body ?? ''); 129 | } 130 | 131 | /** 132 | * Prepare the request URI. 133 | */ 134 | private static function prepareUri(string $uri, array $query = []): string 135 | { 136 | return \sprintf('%s%s%s', self::URI_PREFIX, $uri, QueryStringBuilder::build($query)); 137 | } 138 | 139 | /** 140 | * Prepare the request JSON body. 141 | */ 142 | private static function prepareJsonBody(array $params): ?string 143 | { 144 | if (0 === \count($params)) { 145 | return null; 146 | } 147 | 148 | return JsonObject::encode($params); 149 | } 150 | 151 | /** 152 | * Add the JSON content type to the headers if one is not already present. 153 | * 154 | * @param array $headers 155 | * 156 | * @return array 157 | */ 158 | private static function addJsonContentType(array $headers): array 159 | { 160 | return \array_merge([ResponseMediator::CONTENT_TYPE_HEADER => ResponseMediator::JSON_CONTENT_TYPE], $headers); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/Api/Account.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Entity\Account as AccountEntity; 18 | use DigitalOceanV2\Exception\ExceptionInterface; 19 | 20 | /** 21 | * @author Antoine Kirk 22 | * @author Graham Campbell 23 | */ 24 | class Account extends AbstractApi 25 | { 26 | /** 27 | * @throws ExceptionInterface 28 | */ 29 | public function getUserInformation(): AccountEntity 30 | { 31 | $account = $this->get('account'); 32 | 33 | return new AccountEntity($account->account); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Api/Action.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Entity\Action as ActionEntity; 18 | use DigitalOceanV2\Exception\ExceptionInterface; 19 | 20 | /** 21 | * @author Antoine Kirk 22 | * @author Graham Campbell 23 | */ 24 | class Action extends AbstractApi 25 | { 26 | /** 27 | * @throws ExceptionInterface 28 | * 29 | * @return ActionEntity[] 30 | */ 31 | public function getAll(): array 32 | { 33 | $actions = $this->get('actions'); 34 | 35 | return \array_map(function ($action) { 36 | return new ActionEntity($action); 37 | }, $actions->actions); 38 | } 39 | 40 | /** 41 | * @throws ExceptionInterface 42 | */ 43 | public function getById(int $id): ActionEntity 44 | { 45 | $action = $this->get(\sprintf('actions/%d', $id)); 46 | 47 | return new ActionEntity($action->action); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Api/App.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Entity\App as AppEntity; 18 | use DigitalOceanV2\Entity\AppDeployment as AppDeploymentEntity; 19 | use DigitalOceanV2\Entity\AppDeploymentLog as AppDeploymentLogEntity; 20 | use DigitalOceanV2\Entity\AppInstanceSize as AppInstanceSizeEntity; 21 | use DigitalOceanV2\Entity\AppRegion as AppRegionEntity; 22 | use DigitalOceanV2\Entity\AppTier as AppTierEntity; 23 | use DigitalOceanV2\Exception\ExceptionInterface; 24 | 25 | /** 26 | * @author Michael Shihjay Chen 27 | * @author Graham Campbell 28 | */ 29 | class App extends AbstractApi 30 | { 31 | /** 32 | * @throws ExceptionInterface 33 | * 34 | * @return AppEntity[] 35 | */ 36 | public function getAll(): array 37 | { 38 | $apps = $this->get('apps'); 39 | 40 | return \array_map(function ($app) { 41 | return new AppEntity($app); 42 | }, $apps->apps ?? []); 43 | } 44 | 45 | /** 46 | * @throws ExceptionInterface 47 | */ 48 | public function getByID(string $appID): AppEntity 49 | { 50 | $app = $this->get(\sprintf('apps/%s', $appID)); 51 | 52 | return new AppEntity($app->app); 53 | } 54 | 55 | /** 56 | * @throws ExceptionInterface 57 | */ 58 | public function create(array $spec): AppEntity 59 | { 60 | $app = $this->post('apps', [ 61 | 'spec' => $spec, 62 | ]); 63 | 64 | return new AppEntity($app->app); 65 | } 66 | 67 | /** 68 | * @throws ExceptionInterface 69 | */ 70 | public function update(string $appID, array $spec): AppEntity 71 | { 72 | $result = $this->put(\sprintf('apps/%s', $appID), [ 73 | 'spec' => $spec, 74 | ]); 75 | 76 | return new AppEntity($result->app); 77 | } 78 | 79 | /** 80 | * @throws ExceptionInterface 81 | */ 82 | public function remove(string $appID): void 83 | { 84 | $this->delete(\sprintf('apps/%s', $appID)); 85 | } 86 | 87 | /** 88 | * @throws ExceptionInterface 89 | * 90 | * @return AppDeploymentEntity[] 91 | */ 92 | public function getAppDeployments(string $appID): array 93 | { 94 | $deployments = $this->get(\sprintf('apps/%s/deployments', $appID)); 95 | 96 | return \array_map(function ($deployment) { 97 | return new AppDeploymentEntity($deployment); 98 | }, $deployments->deployments); 99 | } 100 | 101 | /** 102 | * @throws ExceptionInterface 103 | */ 104 | public function getAppDeployment(string $appID, string $deploymentID): AppDeploymentEntity 105 | { 106 | $deployment = $this->get(\sprintf('apps/%s/deployments/%s', $appID, $deploymentID)); 107 | 108 | return new AppDeploymentEntity($deployment->deployment); 109 | } 110 | 111 | /** 112 | * @throws ExceptionInterface 113 | */ 114 | public function createAppDeployment(string $appID, bool $force_build = true): AppDeploymentEntity 115 | { 116 | $deployment = $this->post(\sprintf('apps/%s/deployments', $appID), [ 117 | 'force_build' => $force_build, 118 | ]); 119 | 120 | return new AppDeploymentEntity($deployment->deployment); 121 | } 122 | 123 | /** 124 | * @throws ExceptionInterface 125 | */ 126 | public function cancelAppDeployment(string $appID, string $deploymentID): AppDeploymentEntity 127 | { 128 | $deployment = $this->post(\sprintf('apps/%s/deployments/%s/cancel', $appID, $deploymentID)); 129 | 130 | return new AppDeploymentEntity($deployment->deployment); 131 | } 132 | 133 | /** 134 | * @throws ExceptionInterface 135 | */ 136 | public function getDeploymentLogs(string $appID, string $deploymentID, string $componentName): AppDeploymentLogEntity 137 | { 138 | $logs = $this->get(\sprintf('apps/%s/deployments/%s/components/%s/logs', $appID, $deploymentID, $componentName)); 139 | 140 | return new AppDeploymentLogEntity($logs); 141 | } 142 | 143 | /** 144 | * @throws ExceptionInterface 145 | */ 146 | public function getAggregateDeploymentLogs(string $appID, string $deploymentID): AppDeploymentLogEntity 147 | { 148 | $logs = $this->get(\sprintf('apps/%s/deployments/%s/logs', $appID, $deploymentID)); 149 | 150 | return new AppDeploymentLogEntity($logs); 151 | } 152 | 153 | /** 154 | * @throws ExceptionInterface 155 | * 156 | * @return AppRegionEntity[] 157 | */ 158 | public function getRegions(): array 159 | { 160 | $regions = $this->get('apps/regions'); 161 | 162 | return \array_map(function ($region) { 163 | return new AppRegionEntity($region); 164 | }, $regions->regions); 165 | } 166 | 167 | /** 168 | * @throws ExceptionInterface 169 | * 170 | * @return AppTierEntity[] 171 | */ 172 | public function getTiers(): array 173 | { 174 | $tiers = $this->get('apps/tiers'); 175 | 176 | return \array_map(function ($tier) { 177 | return new AppTierEntity($tier); 178 | }, $tiers->tiers); 179 | } 180 | 181 | /** 182 | * @throws ExceptionInterface 183 | */ 184 | public function getTierBySlug(string $slug): AppTierEntity 185 | { 186 | $tier = $this->get(\sprintf('apps/tiers/%s', $slug)); 187 | 188 | return new AppTierEntity($tier); 189 | } 190 | 191 | /** 192 | * @throws ExceptionInterface 193 | * 194 | * @return AppInstanceSizeEntity[] 195 | */ 196 | public function getInstanceSizes(): array 197 | { 198 | $instance_sizes = $this->get('apps/tiers/instance_sizes'); 199 | 200 | return \array_map(function ($instance_size) { 201 | return new AppInstanceSizeEntity($instance_size); 202 | }, $instance_sizes->instance_sizes); 203 | } 204 | 205 | /** 206 | * @throws ExceptionInterface 207 | */ 208 | public function getInstanceSizeBySlug(string $slug): AppInstanceSizeEntity 209 | { 210 | $instance_size = $this->get(\sprintf('apps/tiers/instance_sizes/%s', $slug)); 211 | 212 | return new AppInstanceSizeEntity($instance_size); 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /src/Api/CdnEndpoint.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Entity\CdnEndpoint as CdnEndpointEntity; 18 | use DigitalOceanV2\Exception\ExceptionInterface; 19 | use DigitalOceanV2\Exception\InvalidArgumentException; 20 | 21 | /** 22 | * @author Christian Fuentes 23 | */ 24 | class CdnEndpoint extends AbstractApi 25 | { 26 | /** 27 | * @throws ExceptionInterface 28 | */ 29 | public function create(string $origin, ?int $ttl = null, ?string $certificateId = null, ?string $customDomain = null): CdnEndpointEntity 30 | { 31 | $body = ['origin' => $origin]; 32 | 33 | if (null !== $ttl) { 34 | $body['ttl'] = $ttl; 35 | } 36 | if (null !== $certificateId) { 37 | $body['certificate_id'] = $certificateId; 38 | } 39 | if (null !== $customDomain) { 40 | $body['custom_domain'] = $customDomain; 41 | } 42 | 43 | $endpoint = $this->post('cdn/endpoints', $body); 44 | 45 | return new CdnEndpointEntity($endpoint->endpoint); 46 | } 47 | 48 | /** 49 | * @throws ExceptionInterface 50 | */ 51 | public function getById(string $id): CdnEndpointEntity 52 | { 53 | $endpoint = $this->get(\sprintf('cdn/endpoints/%s', $id)); 54 | 55 | return new CdnEndpointEntity($endpoint->endpoint); 56 | } 57 | 58 | /** 59 | * @throws ExceptionInterface 60 | * 61 | * @return CdnEndpointEntity[] 62 | */ 63 | public function getAll(): array 64 | { 65 | $endpoints = $this->get('cdn/endpoints'); 66 | 67 | return \array_map(function ($action) { 68 | return new CdnEndpointEntity($action); 69 | }, $endpoints->endpoints); 70 | } 71 | 72 | /** 73 | * @throws ExceptionInterface 74 | * @throws InvalidArgumentException 75 | */ 76 | public function update(string $id, ?int $ttl = null, ?string $certificateId = null, ?string $customDomain = null): CdnEndpointEntity 77 | { 78 | if (null === $ttl && null === $certificateId && null === $customDomain) { 79 | throw new InvalidArgumentException('Update method requires at least one parameter to be not null'); 80 | } 81 | 82 | $endpoint = $this->put(\sprintf('cdn/endpoints/%s', $id), [ 83 | 'ttl' => $ttl, 84 | 'certificate_id' => $certificateId, 85 | 'custom_domain' => $customDomain, 86 | ]); 87 | 88 | return new CdnEndpointEntity($endpoint->endpoint); 89 | } 90 | 91 | /** 92 | * @throws ExceptionInterface 93 | */ 94 | public function remove(string $id): void 95 | { 96 | $this->delete(\sprintf('cdn/endpoints/%s', $id)); 97 | } 98 | 99 | /** 100 | * @throws ExceptionInterface 101 | */ 102 | public function purgeCache(string $id, ?array $fileList = null): void 103 | { 104 | $files = $fileList ?? ['*']; 105 | 106 | $this->delete(\sprintf('cdn/endpoints/%s/cache', $id), [ 107 | 'files' => $files, 108 | ]); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/Api/Certificate.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Entity\Certificate as CertificateEntity; 18 | use DigitalOceanV2\Exception\ExceptionInterface; 19 | 20 | /** 21 | * @author Jacob Holmes 22 | */ 23 | class Certificate extends AbstractApi 24 | { 25 | /** 26 | * @throws ExceptionInterface 27 | * 28 | * @return CertificateEntity[] 29 | */ 30 | public function getAll(): array 31 | { 32 | $certificates = $this->get('certificates'); 33 | 34 | return \array_map(function ($certificates) { 35 | return new CertificateEntity($certificates); 36 | }, $certificates->certificates); 37 | } 38 | 39 | /** 40 | * @throws ExceptionInterface 41 | */ 42 | public function getById(string $id): CertificateEntity 43 | { 44 | $certificate = $this->get(\sprintf('certificates/%s', $id)); 45 | 46 | return new CertificateEntity($certificate->certificate); 47 | } 48 | 49 | /** 50 | * @throws ExceptionInterface 51 | */ 52 | public function create(string $name, string $privateKey, string $leafCertificate, ?string $certificateChain = null): CertificateEntity 53 | { 54 | $params = [ 55 | 'type' => 'custom', 56 | 'name' => $name, 57 | 'private_key' => $privateKey, 58 | 'leaf_certificate' => $leafCertificate, 59 | ]; 60 | 61 | if (null !== $certificateChain) { 62 | $params['certificate_chain'] = $certificateChain; 63 | } 64 | 65 | $certificate = $this->post('certificates', $params); 66 | 67 | return new CertificateEntity($certificate->certificate); 68 | } 69 | 70 | /** 71 | * @param string[] $dnsNames 72 | * 73 | * @throws ExceptionInterface 74 | */ 75 | public function createLetsEncrypt(string $name, array $dnsNames): CertificateEntity 76 | { 77 | $params = [ 78 | 'type' => 'lets_encrypt', 79 | 'name' => $name, 80 | 'dns_names' => $dnsNames, 81 | ]; 82 | 83 | $certificate = $this->post('certificates', $params); 84 | 85 | return new CertificateEntity($certificate->certificate); 86 | } 87 | 88 | /** 89 | * @throws ExceptionInterface 90 | */ 91 | public function remove(string $id): void 92 | { 93 | $this->delete(\sprintf('certificates/%s', $id)); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/Api/Database.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Entity\Database as DatabaseEntity; 18 | use DigitalOceanV2\Entity\DatabaseBackup as DatabaseBackupEntity; 19 | use DigitalOceanV2\Entity\DatabaseCluster as DatabaseClusterEntity; 20 | use DigitalOceanV2\Entity\DatabasePool as DatabasePoolEntity; 21 | use DigitalOceanV2\Entity\DatabaseReplica as DatabaseReplicaEntity; 22 | use DigitalOceanV2\Entity\DatabaseRule as DatabaseRuleEntity; 23 | use DigitalOceanV2\Entity\DatabaseUser as DatabaseUserEntity; 24 | use DigitalOceanV2\Exception\ExceptionInterface; 25 | 26 | /** 27 | * @author Filippo Fortino 28 | */ 29 | class Database extends AbstractApi 30 | { 31 | /** 32 | * @throws ExceptionInterface 33 | * 34 | * @return DatabaseClusterEntity[] 35 | */ 36 | public function getAllClusters(?string $tag = null): array 37 | { 38 | $clusters = $this->get('databases', null === $tag ? [] : ['tag_name' => $tag]); 39 | 40 | return \array_map(function ($cluster) { 41 | return new DatabaseClusterEntity($cluster); 42 | }, $clusters->databases ?? []); 43 | } 44 | 45 | /** 46 | * @throws ExceptionInterface 47 | */ 48 | public function getClusterById(string $id): DatabaseClusterEntity 49 | { 50 | $cluster = $this->get(\sprintf('databases/%s', $id)); 51 | 52 | return new DatabaseClusterEntity($cluster->database); 53 | } 54 | 55 | /** 56 | * @throws ExceptionInterface 57 | */ 58 | public function createCluster(string $name, string $engine, string $size, string $region, int $numNodes, ?string $version = null, array $tags = [], ?string $privateNetworkUuid = null): DatabaseClusterEntity 59 | { 60 | $cluster = $this->post('databases', [ 61 | 'name' => $name, 62 | 'engine' => $engine, 63 | 'size' => $size, 64 | 'region' => $region, 65 | 'num_nodes' => $numNodes, 66 | 'version' => $version, 67 | 'tags' => $tags, 68 | 'private_network_uuid' => $privateNetworkUuid, 69 | ]); 70 | 71 | return new DatabaseClusterEntity($cluster->database); 72 | } 73 | 74 | /** 75 | * @throws ExceptionInterface 76 | */ 77 | public function resize(string $clusterId, string $size, int $numNodes): void 78 | { 79 | $this->put(\sprintf('databases/%s/resize', $clusterId), [ 80 | 'size' => $size, 81 | 'num_nodes' => $numNodes, 82 | ]); 83 | } 84 | 85 | /** 86 | * @throws ExceptionInterface 87 | */ 88 | public function migrate(string $clusterId, string $region): void 89 | { 90 | $this->put(\sprintf('databases/%s/migrate', $clusterId), [ 91 | 'region' => $region, 92 | ]); 93 | } 94 | 95 | /** 96 | * @throws ExceptionInterface 97 | */ 98 | public function remove(string $clusterId): void 99 | { 100 | $this->delete(\sprintf('databases/%s', $clusterId)); 101 | } 102 | 103 | /** 104 | * @throws ExceptionInterface 105 | * 106 | * @return DatabaseRuleEntity[] 107 | */ 108 | public function getFirewallRules(string $clusterId): array 109 | { 110 | $rules = $this->get(\sprintf('databases/%s/firewall', $clusterId)); 111 | 112 | return \array_map(function ($rule) { 113 | return new DatabaseRuleEntity($rule); 114 | }, $rules->rules); 115 | } 116 | 117 | /** 118 | * @throws ExceptionInterface 119 | */ 120 | public function updateFirewallRules(string $clusterId, array $rules): void 121 | { 122 | $this->put(\sprintf('databases/%s/firewall', $clusterId), [ 123 | 'rules' => $rules, 124 | ]); 125 | } 126 | 127 | /** 128 | * @throws ExceptionInterface 129 | */ 130 | public function updateMaintenanceWindow(string $clusterId, string $day, string $hour): void 131 | { 132 | $this->put(\sprintf('databases/%s/maintenance', $clusterId), [ 133 | 'day' => $day, 134 | 'hour' => $hour, 135 | ]); 136 | } 137 | 138 | /** 139 | * @throws ExceptionInterface 140 | * 141 | * @return DatabaseBackupEntity[] 142 | */ 143 | public function getBackups(string $clusterId): array 144 | { 145 | $backups = $this->get(\sprintf('databases/%s/backups', $clusterId)); 146 | 147 | return \array_map(function ($backup) { 148 | return new DatabaseBackupEntity($backup); 149 | }, $backups->backups); 150 | } 151 | 152 | /** 153 | * @throws ExceptionInterface 154 | */ 155 | public function createClusterFromBackup(string $name, array $backupRestore, string $engine, string $size, string $region, int $numNodes, ?string $version = null, array $tags = [], ?string $privateNetworkUuid = null): DatabaseClusterEntity 156 | { 157 | $database = $this->post('databases', [ 158 | 'name' => $name, 159 | 'backup_restore' => $backupRestore, 160 | 'engine' => $engine, 161 | 'size' => $size, 162 | 'region' => $region, 163 | 'num_nodes' => $numNodes, 164 | 'version' => $version, 165 | 'tags' => $tags, 166 | 'private_network_uuid' => $privateNetworkUuid, 167 | ]); 168 | 169 | return new DatabaseClusterEntity($database->database); 170 | } 171 | 172 | /** 173 | * @throws ExceptionInterface 174 | * 175 | * @return DatabaseReplicaEntity[] 176 | */ 177 | public function getAllReplicas(string $clusterId): array 178 | { 179 | $replicas = $this->get(\sprintf('databases/%s/replicas', $clusterId)); 180 | 181 | return \array_map(function ($replica) { 182 | return new DatabaseReplicaEntity($replica); 183 | }, $replicas->replicas); 184 | } 185 | 186 | /** 187 | * @throws ExceptionInterface 188 | */ 189 | public function getReplicaByName(string $clusterId, string $name): DatabaseReplicaEntity 190 | { 191 | $replica = $this->get(\sprintf('databases/%s/replicas/%s', $clusterId, $name)); 192 | 193 | return new DatabaseReplicaEntity($replica->replica); 194 | } 195 | 196 | /** 197 | * @throws ExceptionInterface 198 | */ 199 | public function createReplica(string $clusterId, string $name, string $size, ?string $region = null, array $tags = [], ?string $privateNetworkUuid = null): DatabaseReplicaEntity 200 | { 201 | $replica = $this->post(\sprintf('databases/%s/replicas', $clusterId), [ 202 | 'name' => $name, 203 | 'size' => $size, 204 | 'region' => $region, 205 | 'tags' => $tags, 206 | 'private_network_uuid' => $privateNetworkUuid, 207 | ]); 208 | 209 | return new DatabaseReplicaEntity($replica->replica); 210 | } 211 | 212 | /** 213 | * @throws ExceptionInterface 214 | */ 215 | public function removeReplica(string $clusterId, string $name): void 216 | { 217 | $this->delete(\sprintf('databases/%s/replicas/%s', $clusterId, $name)); 218 | } 219 | 220 | /** 221 | * @throws ExceptionInterface 222 | * 223 | * @return DatabaseUserEntity[] 224 | */ 225 | public function getAllUsers(string $clusterId): array 226 | { 227 | $users = $this->get(\sprintf('databases/%s/users', $clusterId)); 228 | 229 | return \array_map(function ($user) { 230 | return new DatabaseUserEntity($user); 231 | }, $users->users); 232 | } 233 | 234 | /** 235 | * @throws ExceptionInterface 236 | */ 237 | public function getUserByName(string $clusterId, string $name): DatabaseUserEntity 238 | { 239 | $user = $this->get(\sprintf('databases/%s/users/%s', $clusterId, $name)); 240 | 241 | return new DatabaseUserEntity($user->user); 242 | } 243 | 244 | /** 245 | * @throws ExceptionInterface 246 | */ 247 | public function createUser(string $clusterId, string $name, ?string $authPlugin = null): DatabaseUserEntity 248 | { 249 | $user = $this->post(\sprintf('databases/%s/users', $clusterId), [ 250 | 'name' => $name, 251 | 'mysql_settings' => [ 252 | 'auth_plugin' => $authPlugin, 253 | ], 254 | ]); 255 | 256 | return new DatabaseUserEntity($user->user); 257 | } 258 | 259 | /** 260 | * @throws ExceptionInterface 261 | */ 262 | public function updateUserMysqlAuthMethod(string $clusterId, string $username, string $authPlugin): DatabaseUserEntity 263 | { 264 | $user = $this->post(\sprintf('databases/%s/users/%s/reset_auth', $clusterId, $username), [ 265 | 'mysql_settings' => [ 266 | 'auth_plugin' => $authPlugin, 267 | ], 268 | ]); 269 | 270 | return new DatabaseUserEntity($user->user); 271 | } 272 | 273 | /** 274 | * @throws ExceptionInterface 275 | */ 276 | public function removeUser(string $clusterId, string $name): void 277 | { 278 | $this->delete(\sprintf('databases/%s/users/%s', $clusterId, $name)); 279 | } 280 | 281 | /** 282 | * @throws ExceptionInterface 283 | * 284 | * @return DatabaseEntity[] 285 | */ 286 | public function getAllDatabases(string $clusterId): array 287 | { 288 | $databases = $this->get(\sprintf('databases/%s/dbs', $clusterId)); 289 | 290 | return \array_map(function ($database) { 291 | return new DatabaseEntity($database); 292 | }, $databases->dbs); 293 | } 294 | 295 | /** 296 | * @throws ExceptionInterface 297 | */ 298 | public function getDatabaseByName(string $clusterId, string $name): DatabaseEntity 299 | { 300 | $database = $this->get(\sprintf('databases/%s/dbs/%s', $clusterId, $name)); 301 | 302 | return new DatabaseEntity($database->db); 303 | } 304 | 305 | /** 306 | * @throws ExceptionInterface 307 | */ 308 | public function createDatabase(string $clusterId, string $name): DatabaseEntity 309 | { 310 | $database = $this->post(\sprintf('databases/%s/dbs', $clusterId), [ 311 | 'name' => $name, 312 | ]); 313 | 314 | return new DatabaseEntity($database->db); 315 | } 316 | 317 | /** 318 | * @throws ExceptionInterface 319 | */ 320 | public function removeDatabase(string $clusterId, string $name): void 321 | { 322 | $this->delete(\sprintf('databases/%s/dbs/%s', $clusterId, $name)); 323 | } 324 | 325 | /** 326 | * @throws ExceptionInterface 327 | * 328 | * @return DatabasePoolEntity[] 329 | */ 330 | public function getAllConnectionPools(string $clusterId): array 331 | { 332 | $pools = $this->get(\sprintf('databases/%s/pools', $clusterId)); 333 | 334 | return \array_map(function ($pool) { 335 | return new DatabasePoolEntity($pool); 336 | }, $pools->pools); 337 | } 338 | 339 | /** 340 | * @throws ExceptionInterface 341 | */ 342 | public function getConnectionPoolByName(string $clusterId, string $name): DatabasePoolEntity 343 | { 344 | $pool = $this->get(\sprintf('databases/%s/pools/%s', $clusterId, $name)); 345 | 346 | return new DatabasePoolEntity($pool->pool); 347 | } 348 | 349 | /** 350 | * @throws ExceptionInterface 351 | */ 352 | public function createConnectionPool(string $clusterId, string $name, string $mode, int $size, string $db, string $user): DatabasePoolEntity 353 | { 354 | $pool = $this->post(\sprintf('databases/%s/pools', $clusterId), [ 355 | 'name' => $name, 356 | 'mode' => $mode, 357 | 'size' => $size, 358 | 'db' => $db, 359 | 'user' => $user, 360 | ]); 361 | 362 | return new DatabasePoolEntity($pool->pool); 363 | } 364 | 365 | /** 366 | * @throws ExceptionInterface 367 | */ 368 | public function removeConnectionPool(string $clusterId, string $name): void 369 | { 370 | $this->delete(\sprintf('databases/%s/pools/%s', $clusterId, $name)); 371 | } 372 | 373 | /** 374 | * @throws ExceptionInterface 375 | */ 376 | public function getEvictionPolicy(string $clusterId): object 377 | { 378 | $modes = $this->get(\sprintf('databases/%s/eviction_policy', $clusterId)); 379 | 380 | return (object) ['evictionPolicy' => $modes->eviction_policy]; 381 | } 382 | 383 | /** 384 | * @throws ExceptionInterface 385 | */ 386 | public function updateEvictionPolicy(string $clusterId, string $evictionPolicy): void 387 | { 388 | $this->put(\sprintf('databases/%s/eviction_policy', $clusterId), [ 389 | 'eviction_policy' => $evictionPolicy, 390 | ]); 391 | } 392 | 393 | /** 394 | * @throws ExceptionInterface 395 | */ 396 | public function getSqlMode(string $clusterId): object 397 | { 398 | $mode = $this->get(\sprintf('databases/%s/sql_mode', $clusterId)); 399 | 400 | return (object) ['sqlMode' => $mode->sql_mode]; 401 | } 402 | 403 | /** 404 | * @throws ExceptionInterface 405 | */ 406 | public function updateSqlModes(string $clusterId, string $sqlMode): void 407 | { 408 | $this->put(\sprintf('databases/%s/sql_mode', $clusterId), [ 409 | 'sql_mode' => $sqlMode, 410 | ]); 411 | } 412 | } 413 | -------------------------------------------------------------------------------- /src/Api/Domain.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Entity\Domain as DomainEntity; 18 | use DigitalOceanV2\Exception\ExceptionInterface; 19 | 20 | /** 21 | * @author Yassir Hannoun 22 | * @author Graham Campbell 23 | */ 24 | class Domain extends AbstractApi 25 | { 26 | /** 27 | * @throws ExceptionInterface 28 | * 29 | * @return DomainEntity[] 30 | */ 31 | public function getAll(): array 32 | { 33 | $domains = $this->get('domains'); 34 | 35 | return \array_map(function ($domain) { 36 | return new DomainEntity($domain); 37 | }, $domains->domains); 38 | } 39 | 40 | /** 41 | * @throws ExceptionInterface 42 | */ 43 | public function getByName(string $domainName): DomainEntity 44 | { 45 | $domain = $this->get(\sprintf('domains/%s', $domainName)); 46 | 47 | return new DomainEntity($domain->domain); 48 | } 49 | 50 | /** 51 | * @throws ExceptionInterface 52 | */ 53 | public function create(string $name, ?string $ipAddress = null): DomainEntity 54 | { 55 | $data = [ 56 | 'name' => $name, 57 | ]; 58 | 59 | if (null !== $ipAddress) { 60 | $data['ip_address'] = $ipAddress; 61 | } 62 | 63 | $domain = $this->post('domains', $data); 64 | 65 | return new DomainEntity($domain->domain); 66 | } 67 | 68 | /** 69 | * @throws ExceptionInterface 70 | */ 71 | public function remove(string $domain): void 72 | { 73 | $this->delete(\sprintf('domains/%s', $domain)); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Api/DomainRecord.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Entity\DomainRecord as DomainRecordEntity; 18 | use DigitalOceanV2\Exception\ExceptionInterface; 19 | use DigitalOceanV2\Exception\InvalidRecordException; 20 | 21 | /** 22 | * @author Yassir Hannoun 23 | * @author Graham Campbell 24 | */ 25 | class DomainRecord extends AbstractApi 26 | { 27 | /** 28 | * @throws ExceptionInterface 29 | * 30 | * @return DomainRecordEntity[] 31 | */ 32 | public function getAll(string $domainName): array 33 | { 34 | $domainRecords = $this->get(\sprintf('domains/%s/records', $domainName)); 35 | 36 | return \array_map(function ($domainRecord) { 37 | return new DomainRecordEntity($domainRecord); 38 | }, $domainRecords->domain_records); 39 | } 40 | 41 | /** 42 | * @throws ExceptionInterface 43 | */ 44 | public function getById(string $domainName, int $id): DomainRecordEntity 45 | { 46 | $domainRecords = $this->get(\sprintf('domains/%s/records/%d', $domainName, $id)); 47 | 48 | return new DomainRecordEntity($domainRecords->domain_record); 49 | } 50 | 51 | /** 52 | * @throws ExceptionInterface 53 | */ 54 | public function create(string $domainName, string $type, string $name, string $data, ?int $priority = null, ?int $port = null, ?int $weight = null, ?int $flags = null, ?string $tag = null, ?int $ttl = null): DomainRecordEntity 55 | { 56 | switch ($type = \strtoupper($type)) { 57 | case 'A': 58 | case 'AAAA': 59 | case 'CNAME': 60 | case 'TXT': 61 | case 'NS': 62 | $content = ['name' => $name, 'type' => $type, 'data' => $data]; 63 | 64 | break; 65 | 66 | case 'SRV': 67 | $content = [ 68 | 'name' => $name, 69 | 'type' => $type, 70 | 'data' => $data, 71 | 'priority' => (int) $priority, 72 | 'port' => (int) $port, 73 | 'weight' => (int) $weight, 74 | ]; 75 | 76 | break; 77 | 78 | case 'MX': 79 | $content = ['type' => $type, 'name' => $name, 'data' => $data, 'priority' => $priority]; 80 | 81 | break; 82 | 83 | case 'CAA': 84 | $content = ['type' => $type, 'name' => $name, 'data' => $data, 'flags' => $flags, 'tag' => $tag]; 85 | 86 | break; 87 | 88 | default: 89 | throw new InvalidRecordException('The domain record type is invalid.'); 90 | } 91 | 92 | if (null !== $ttl) { 93 | $content['ttl'] = $ttl; 94 | } 95 | 96 | $domainRecord = $this->post(\sprintf('domains/%s/records', $domainName), $content); 97 | 98 | return new DomainRecordEntity($domainRecord->domain_record); 99 | } 100 | 101 | /** 102 | * @throws ExceptionInterface 103 | */ 104 | public function update(string $domainName, int $recordId, ?string $name = null, ?string $data = null, ?int $priority = null, ?int $port = null, ?int $weight = null, ?int $flags = null, ?string $tag = null, ?int $ttl = null): DomainRecordEntity 105 | { 106 | $content = [ 107 | 'name' => $name, 108 | 'data' => $data, 109 | 'priority' => $priority, 110 | 'port' => $port, 111 | 'weight' => $weight, 112 | 'flags' => $flags, 113 | 'tag' => $tag, 114 | 'ttl' => $ttl, 115 | ]; 116 | 117 | $content = \array_filter($content, function ($val) { 118 | return null !== $val; 119 | }); 120 | 121 | return $this->updateFields($domainName, $recordId, $content); 122 | } 123 | 124 | /** 125 | * @throws ExceptionInterface 126 | */ 127 | public function updateData(string $domainName, int $recordId, string $data): DomainRecordEntity 128 | { 129 | return $this->updateFields($domainName, $recordId, ['data' => $data]); 130 | } 131 | 132 | /** 133 | * @throws ExceptionInterface 134 | */ 135 | public function updateFields(string $domainName, int $recordId, array $fields): DomainRecordEntity 136 | { 137 | $domainRecord = $this->put(\sprintf('domains/%s/records/%d', $domainName, $recordId), $fields); 138 | 139 | return new DomainRecordEntity($domainRecord->domain_record); 140 | } 141 | 142 | /** 143 | * @throws ExceptionInterface 144 | */ 145 | public function remove(string $domainName, int $recordId): void 146 | { 147 | $this->delete(\sprintf('domains/%s/records/%d', $domainName, $recordId)); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/Api/Droplet.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Entity\Action as ActionEntity; 18 | use DigitalOceanV2\Entity\Droplet as DropletEntity; 19 | use DigitalOceanV2\Entity\Image as ImageEntity; 20 | use DigitalOceanV2\Entity\Kernel as KernelEntity; 21 | use DigitalOceanV2\Exception\ExceptionInterface; 22 | 23 | /** 24 | * @author Yassir Hannoun 25 | * @author Graham Campbell 26 | */ 27 | class Droplet extends AbstractApi 28 | { 29 | /** 30 | * @throws ExceptionInterface 31 | * 32 | * @return DropletEntity[] 33 | */ 34 | public function getAll(?string $tag = null): array 35 | { 36 | $droplets = $this->get('droplets', null === $tag ? [] : ['tag_name' => $tag]); 37 | 38 | return \array_map(function ($droplet) { 39 | return new DropletEntity($droplet); 40 | }, $droplets->droplets); 41 | } 42 | 43 | /** 44 | * @throws ExceptionInterface 45 | * 46 | * @return DropletEntity[] 47 | */ 48 | public function getNeighborsById(int $id): array 49 | { 50 | $droplets = $this->get(\sprintf('droplets/%d/neighbors', $id)); 51 | 52 | return \array_map(function ($droplet) { 53 | return new DropletEntity($droplet); 54 | }, $droplets->droplets); 55 | } 56 | 57 | /** 58 | * @throws ExceptionInterface 59 | * 60 | * @return DropletEntity[] 61 | */ 62 | public function getAllNeighbors(): array 63 | { 64 | $neighbors = $this->get('reports/droplet_neighbors'); 65 | 66 | return \array_map(function ($neighbor) { 67 | return new DropletEntity($neighbor); 68 | }, $neighbors->neighbors); 69 | } 70 | 71 | /** 72 | * @throws ExceptionInterface 73 | */ 74 | public function getById(int $id): DropletEntity 75 | { 76 | $droplet = $this->get(\sprintf('droplets/%d', $id)); 77 | 78 | return new DropletEntity($droplet->droplet); 79 | } 80 | 81 | /** 82 | * @param int[] $sshKeys 83 | * 84 | * @throws ExceptionInterface 85 | * 86 | * @return DropletEntity|DropletEntity[]|null 87 | */ 88 | public function create(array|string $names, string $region, string $size, string|int $image, bool $backups = false, bool $ipv6 = false, string|bool $vpcUuid = false, array $sshKeys = [], string $userData = '', bool $monitoring = true, array $volumes = [], array $tags = [], bool $disableAgent = false): DropletEntity|array|null 89 | { 90 | $data = \is_array($names) ? ['names' => $names] : ['name' => $names]; 91 | 92 | $data = \array_merge($data, [ 93 | 'region' => $region, 94 | 'size' => $size, 95 | 'image' => $image, 96 | 'backups' => $backups ? 'true' : 'false', 97 | 'ipv6' => $ipv6 ? 'true' : 'false', 98 | 'monitoring' => $monitoring ? 'true' : 'false', 99 | ]); 100 | 101 | if ($disableAgent) { 102 | $data['with_droplet_agent'] = 'false'; 103 | } 104 | 105 | if (0 < \count($sshKeys)) { 106 | $data['ssh_keys'] = $sshKeys; 107 | } 108 | 109 | if ('' !== $userData) { 110 | $data['user_data'] = $userData; 111 | } 112 | 113 | if (\is_bool($vpcUuid)) { 114 | $data['private_networking'] = $vpcUuid ? 'true' : 'false'; 115 | } elseif ('' !== $vpcUuid) { 116 | $data['vpc_uuid'] = $vpcUuid; 117 | } 118 | 119 | if (0 < \count($volumes)) { 120 | $data['volumes'] = $volumes; 121 | } 122 | 123 | if (0 < \count($tags)) { 124 | $data['tags'] = $tags; 125 | } 126 | 127 | $droplet = $this->post('droplets', $data); 128 | 129 | if (\is_array($names)) { 130 | return \array_map(function ($droplet) { 131 | return new DropletEntity($droplet); 132 | }, $droplet->droplets); 133 | } 134 | 135 | return new DropletEntity($droplet->droplet); 136 | } 137 | 138 | /** 139 | * @throws ExceptionInterface 140 | */ 141 | public function remove(int $id): void 142 | { 143 | $this->delete(\sprintf('droplets/%d', $id)); 144 | } 145 | 146 | /** 147 | * @throws ExceptionInterface 148 | */ 149 | public function removeTagged(string $tag): void 150 | { 151 | $this->delete('droplets', [], [], ['tag_name' => $tag]); 152 | } 153 | 154 | /** 155 | * @throws ExceptionInterface 156 | * 157 | * @return KernelEntity[] 158 | */ 159 | public function getAvailableKernels(int $id): array 160 | { 161 | $kernels = $this->get(\sprintf('droplets/%d/kernels', $id)); 162 | 163 | return \array_map(function ($kernel) { 164 | return new KernelEntity($kernel); 165 | }, $kernels->kernels); 166 | } 167 | 168 | /** 169 | * @throws ExceptionInterface 170 | * 171 | * @return ImageEntity[] 172 | */ 173 | public function getSnapshots(int $id): array 174 | { 175 | $snapshots = $this->get(\sprintf('droplets/%d/snapshots', $id)); 176 | 177 | return \array_map(function ($snapshot) { 178 | return new ImageEntity($snapshot); 179 | }, $snapshots->snapshots); 180 | } 181 | 182 | /** 183 | * @throws ExceptionInterface 184 | * 185 | * @return ImageEntity[] 186 | */ 187 | public function getBackups(int $id): array 188 | { 189 | $backups = $this->get(\sprintf('droplets/%d/backups', $id)); 190 | 191 | return \array_map(function ($backup) { 192 | return new ImageEntity($backup); 193 | }, $backups->backups); 194 | } 195 | 196 | /** 197 | * @throws ExceptionInterface 198 | * 199 | * @return ActionEntity[] 200 | */ 201 | public function getActions(int $id): array 202 | { 203 | $actions = $this->get(\sprintf('droplets/%d/actions', $id)); 204 | 205 | return \array_map(function ($action) { 206 | return new ActionEntity($action); 207 | }, $actions->actions); 208 | } 209 | 210 | /** 211 | * @throws ExceptionInterface 212 | */ 213 | public function getActionById(int $id, int $actionId): ActionEntity 214 | { 215 | $action = $this->get(\sprintf('droplets/%d/actions/%d', $id, $actionId)); 216 | 217 | return new ActionEntity($action->action); 218 | } 219 | 220 | /** 221 | * @throws ExceptionInterface 222 | */ 223 | public function reboot(int $id): ActionEntity 224 | { 225 | return $this->executeAction($id, ['type' => 'reboot']); 226 | } 227 | 228 | /** 229 | * @throws ExceptionInterface 230 | */ 231 | public function powerCycle(int $id): ActionEntity 232 | { 233 | return $this->executeAction($id, ['type' => 'power_cycle']); 234 | } 235 | 236 | /** 237 | * @throws ExceptionInterface 238 | */ 239 | public function shutdown(int $id): ActionEntity 240 | { 241 | return $this->executeAction($id, ['type' => 'shutdown']); 242 | } 243 | 244 | /** 245 | * @throws ExceptionInterface 246 | */ 247 | public function powerOff(int $id): ActionEntity 248 | { 249 | return $this->executeAction($id, ['type' => 'power_off']); 250 | } 251 | 252 | /** 253 | * @throws ExceptionInterface 254 | */ 255 | public function powerOn(int $id): ActionEntity 256 | { 257 | return $this->executeAction($id, ['type' => 'power_on']); 258 | } 259 | 260 | /** 261 | * @throws ExceptionInterface 262 | */ 263 | public function passwordReset(int $id): ActionEntity 264 | { 265 | return $this->executeAction($id, ['type' => 'password_reset']); 266 | } 267 | 268 | /** 269 | * @throws ExceptionInterface 270 | */ 271 | public function resize(int $id, string $size, bool $disk = true): ActionEntity 272 | { 273 | return $this->executeAction($id, ['type' => 'resize', 'size' => $size, 'disk' => $disk ? 'true' : 'false']); 274 | } 275 | 276 | /** 277 | * @throws ExceptionInterface 278 | */ 279 | public function restore(int $id, int $image): ActionEntity 280 | { 281 | return $this->executeAction($id, ['type' => 'restore', 'image' => $image]); 282 | } 283 | 284 | /** 285 | * @throws ExceptionInterface 286 | */ 287 | public function rebuild(int $id, int|string $image): ActionEntity 288 | { 289 | return $this->executeAction($id, ['type' => 'rebuild', 'image' => $image]); 290 | } 291 | 292 | /** 293 | * @throws ExceptionInterface 294 | */ 295 | public function rename(int $id, string $name): ActionEntity 296 | { 297 | return $this->executeAction($id, ['type' => 'rename', 'name' => $name]); 298 | } 299 | 300 | /** 301 | * @throws ExceptionInterface 302 | */ 303 | public function changeKernel(int $id, int $kernel): ActionEntity 304 | { 305 | return $this->executeAction($id, ['type' => 'change_kernel', 'kernel' => $kernel]); 306 | } 307 | 308 | /** 309 | * @throws ExceptionInterface 310 | */ 311 | public function enableIpv6(int $id): ActionEntity 312 | { 313 | return $this->executeAction($id, ['type' => 'enable_ipv6']); 314 | } 315 | 316 | /** 317 | * @throws ExceptionInterface 318 | */ 319 | public function enableBackups(int $id): ActionEntity 320 | { 321 | return $this->executeAction($id, ['type' => 'enable_backups']); 322 | } 323 | 324 | /** 325 | * @throws ExceptionInterface 326 | */ 327 | public function disableBackups(int $id): ActionEntity 328 | { 329 | return $this->executeAction($id, ['type' => 'disable_backups']); 330 | } 331 | 332 | /** 333 | * @throws ExceptionInterface 334 | */ 335 | public function enablePrivateNetworking(int $id): ActionEntity 336 | { 337 | return $this->executeAction($id, ['type' => 'enable_private_networking']); 338 | } 339 | 340 | /** 341 | * @throws ExceptionInterface 342 | */ 343 | public function snapshot(int $id, string $name): ActionEntity 344 | { 345 | return $this->executeAction($id, ['type' => 'snapshot', 'name' => $name]); 346 | } 347 | 348 | /** 349 | * @throws ExceptionInterface 350 | */ 351 | private function executeAction(int $id, array $options): ActionEntity 352 | { 353 | $action = $this->post(\sprintf('droplets/%d/actions', $id), $options); 354 | 355 | return new ActionEntity($action->action); 356 | } 357 | } 358 | -------------------------------------------------------------------------------- /src/Api/Firewall.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Entity\Firewall as FirewallEntity; 18 | use DigitalOceanV2\Exception\ExceptionInterface; 19 | 20 | /** 21 | * @author Yassir Hannoun 22 | * @author Graham Campbell 23 | */ 24 | class Firewall extends AbstractApi 25 | { 26 | /** 27 | * @throws ExceptionInterface 28 | * 29 | * @return FirewallEntity[] 30 | */ 31 | public function getAll(): array 32 | { 33 | $firewalls = $this->get('firewalls'); 34 | 35 | return \array_map(function ($firewall) { 36 | return new FirewallEntity($firewall); 37 | }, $firewalls->firewalls); 38 | } 39 | 40 | /** 41 | * @throws ExceptionInterface 42 | */ 43 | public function getById(string $id): FirewallEntity 44 | { 45 | $firewall = $this->get(\sprintf('firewalls/%s', $id)); 46 | 47 | return new FirewallEntity($firewall->firewall); 48 | } 49 | 50 | public function create(string $name, array $inboundRules, array $outboundRules, array $dropletIds = [], array $tags = []): FirewallEntity 51 | { 52 | $data = [ 53 | 'name' => $name, 54 | 'inbound_rules' => $inboundRules, 55 | 'outbound_rules' => $outboundRules, 56 | ]; 57 | 58 | if (0 < \count($dropletIds)) { 59 | $data['droplet_ids'] = $dropletIds; 60 | } 61 | 62 | if (0 < \count($tags)) { 63 | $data['tags'] = $tags; 64 | } 65 | 66 | $firewall = $this->post('firewalls', $data); 67 | 68 | return new FirewallEntity($firewall->firewall); 69 | } 70 | 71 | /** 72 | * @throws ExceptionInterface 73 | */ 74 | public function remove(string $id): void 75 | { 76 | $this->delete(\sprintf('firewalls/%s', $id)); 77 | } 78 | 79 | /** 80 | * @throws ExceptionInterface 81 | */ 82 | public function update(string $id, array $data): FirewallEntity 83 | { 84 | $result = $this->put(\sprintf('firewalls/%s', $id), $data); 85 | 86 | return new FirewallEntity($result->firewall); 87 | } 88 | 89 | /** 90 | * @throws ExceptionInterface 91 | */ 92 | public function addRules(string $id, array $rules): void 93 | { 94 | $this->post(\sprintf('firewalls/%s/rules', $id), $rules); 95 | } 96 | 97 | /** 98 | * @throws ExceptionInterface 99 | */ 100 | public function removeRules(string $id, array $rules): void 101 | { 102 | $this->delete(\sprintf('firewalls/%s/rules', $id), $rules); 103 | } 104 | 105 | /** 106 | * @throws ExceptionInterface 107 | */ 108 | public function addDroplets(string $id, array $droplets): void 109 | { 110 | $this->post(\sprintf('firewalls/%s/droplets', $id), $droplets); 111 | } 112 | 113 | /** 114 | * @throws ExceptionInterface 115 | */ 116 | public function removeDroplets(string $id, array $droplets): void 117 | { 118 | $this->delete(\sprintf('firewalls/%s/droplets', $id), $droplets); 119 | } 120 | 121 | /** 122 | * @throws ExceptionInterface 123 | */ 124 | public function addTags(string $id, array $tags): void 125 | { 126 | $firewalls = $this->post(\sprintf('firewalls/%s/tags', $id), $tags); 127 | } 128 | 129 | /** 130 | * @throws ExceptionInterface 131 | */ 132 | public function removeTags(string $id, array $tags): void 133 | { 134 | $this->delete(\sprintf('firewalls/%s/tags', $id), $tags); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/Api/FloatingIp.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Entity\Action as ActionEntity; 18 | use DigitalOceanV2\Entity\FloatingIp as FloatingIpEntity; 19 | use DigitalOceanV2\Exception\ExceptionInterface; 20 | 21 | /** 22 | * @author Graham Campbell 23 | */ 24 | class FloatingIp extends AbstractApi 25 | { 26 | /** 27 | * @throws ExceptionInterface 28 | * 29 | * @return FloatingIpEntity[] 30 | */ 31 | public function getAll(): array 32 | { 33 | $ips = $this->get('floating_ips'); 34 | 35 | return \array_map(function ($ip) { 36 | return new FloatingIpEntity($ip); 37 | }, $ips->floating_ips); 38 | } 39 | 40 | /** 41 | * @throws ExceptionInterface 42 | */ 43 | public function getById(string $ipAddress): FloatingIpEntity 44 | { 45 | $ip = $this->get(\sprintf('floating_ips/%s', $ipAddress)); 46 | 47 | return new FloatingIpEntity($ip->floating_ip); 48 | } 49 | 50 | /** 51 | * @throws ExceptionInterface 52 | */ 53 | public function createAssigned(int $dropletId): FloatingIpEntity 54 | { 55 | $ip = $this->post('floating_ips', ['droplet_id' => $dropletId]); 56 | 57 | return new FloatingIpEntity($ip->floating_ip); 58 | } 59 | 60 | /** 61 | * @throws ExceptionInterface 62 | */ 63 | public function createReserved(string $regionSlug): FloatingIpEntity 64 | { 65 | $ip = $this->post('floating_ips', ['region' => $regionSlug]); 66 | 67 | return new FloatingIpEntity($ip->floating_ip); 68 | } 69 | 70 | /** 71 | * @throws ExceptionInterface 72 | */ 73 | public function remove(string $ipAddress): void 74 | { 75 | $this->delete(\sprintf('floating_ips/%s', $ipAddress)); 76 | } 77 | 78 | /** 79 | * @throws ExceptionInterface 80 | * 81 | * @return ActionEntity[] 82 | */ 83 | public function getActions(string $ipAddress): array 84 | { 85 | $actions = $this->get(\sprintf('floating_ips/%s/actions', $ipAddress)); 86 | 87 | return \array_map(function ($action) { 88 | return new ActionEntity($action); 89 | }, $actions->actions); 90 | } 91 | 92 | /** 93 | * @throws ExceptionInterface 94 | */ 95 | public function getActionById(string $ipAddress, int $actionId): ActionEntity 96 | { 97 | $action = $this->get(\sprintf('floating_ips/%s/actions/%d', $ipAddress, $actionId)); 98 | 99 | return new ActionEntity($action->action); 100 | } 101 | 102 | /** 103 | * @throws ExceptionInterface 104 | */ 105 | public function assign(string $ipAddress, int $dropletId): ActionEntity 106 | { 107 | return $this->executeAction($ipAddress, ['type' => 'assign', 'droplet_id' => $dropletId]); 108 | } 109 | 110 | /** 111 | * @throws ExceptionInterface 112 | */ 113 | public function unassign(string $ipAddress): ActionEntity 114 | { 115 | return $this->executeAction($ipAddress, ['type' => 'unassign']); 116 | } 117 | 118 | /** 119 | * @throws ExceptionInterface 120 | */ 121 | private function executeAction(string $ipAddress, array $options): ActionEntity 122 | { 123 | $action = $this->post(\sprintf('floating_ips/%s/actions', $ipAddress), $options); 124 | 125 | return new ActionEntity($action->action); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/Api/Image.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Entity\Action as ActionEntity; 18 | use DigitalOceanV2\Entity\Image as ImageEntity; 19 | use DigitalOceanV2\Exception\ExceptionInterface; 20 | 21 | /** 22 | * @author Yassir Hannoun 23 | * @author Graham Campbell 24 | */ 25 | class Image extends AbstractApi 26 | { 27 | /** 28 | * @throws ExceptionInterface 29 | * 30 | * @return ImageEntity[] 31 | */ 32 | public function getAll(array $criteria = []): array 33 | { 34 | $query = []; 35 | 36 | if (isset($criteria['type']) && \in_array($criteria['type'], ['application', 'custom', 'distribution', 'snapshot'], true)) { 37 | $query['type'] = $criteria['type']; 38 | } 39 | 40 | if (isset($criteria['private']) && (bool) $criteria['private']) { 41 | $query['private'] = 'true'; 42 | } 43 | 44 | $images = $this->get('images', $query); 45 | 46 | return \array_map(function ($image) { 47 | return new ImageEntity($image); 48 | }, $images->images); 49 | } 50 | 51 | /** 52 | * @throws ExceptionInterface 53 | */ 54 | public function getById(int $id): ImageEntity 55 | { 56 | $image = $this->get(\sprintf('images/%d', $id)); 57 | 58 | return new ImageEntity($image->image); 59 | } 60 | 61 | /** 62 | * @throws ExceptionInterface 63 | */ 64 | public function getBySlug(string $slug): ImageEntity 65 | { 66 | $image = $this->get(\sprintf('images/%s', $slug)); 67 | 68 | return new ImageEntity($image->image); 69 | } 70 | 71 | /** 72 | * @throws ExceptionInterface 73 | */ 74 | public function update(int $id, string $name): ImageEntity 75 | { 76 | $image = $this->put(\sprintf('images/%d', $id), ['name' => $name]); 77 | 78 | return new ImageEntity($image->image); 79 | } 80 | 81 | /** 82 | * @throws ExceptionInterface 83 | */ 84 | public function remove(int $id): void 85 | { 86 | $this->delete(\sprintf('images/%d', $id)); 87 | } 88 | 89 | /** 90 | * @throws ExceptionInterface 91 | */ 92 | public function transfer(int $id, string $regionSlug): ActionEntity 93 | { 94 | $action = $this->post(\sprintf('images/%d/actions', $id), ['type' => 'transfer', 'region' => $regionSlug]); 95 | 96 | return new ActionEntity($action->action); 97 | } 98 | 99 | /** 100 | * @throws ExceptionInterface 101 | */ 102 | public function convert(int $id): ActionEntity 103 | { 104 | $action = $this->post(\sprintf('images/%d/actions', $id), ['type' => 'convert']); 105 | 106 | return new ActionEntity($action->action); 107 | } 108 | 109 | /** 110 | * @throws ExceptionInterface 111 | */ 112 | public function getAction(int $id, int $actionId): ActionEntity 113 | { 114 | $action = $this->get(\sprintf('images/%d/actions/%d', $id, $actionId)); 115 | 116 | return new ActionEntity($action->action); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/Api/Key.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Entity\Key as KeyEntity; 18 | use DigitalOceanV2\Exception\ExceptionInterface; 19 | 20 | /** 21 | * @author Antoine Kirk 22 | * @author Graham Campbell 23 | */ 24 | class Key extends AbstractApi 25 | { 26 | /** 27 | * @throws ExceptionInterface 28 | * 29 | * @return KeyEntity[] 30 | */ 31 | public function getAll(): array 32 | { 33 | $keys = $this->get('account/keys'); 34 | 35 | return \array_map(function ($key) { 36 | return new KeyEntity($key); 37 | }, $keys->ssh_keys); 38 | } 39 | 40 | /** 41 | * @throws ExceptionInterface 42 | */ 43 | public function getById(int $id): KeyEntity 44 | { 45 | $key = $this->get(\sprintf('account/keys/%d', $id)); 46 | 47 | return new KeyEntity($key->ssh_key); 48 | } 49 | 50 | /** 51 | * @throws ExceptionInterface 52 | */ 53 | public function getByFingerprint(string $fingerprint): KeyEntity 54 | { 55 | $key = $this->get(\sprintf('account/keys/%s', $fingerprint)); 56 | 57 | return new KeyEntity($key->ssh_key); 58 | } 59 | 60 | /** 61 | * @throws ExceptionInterface 62 | */ 63 | public function create(string $name, string $publicKey): KeyEntity 64 | { 65 | $key = $this->post('account/keys', [ 66 | 'name' => $name, 67 | 'public_key' => $publicKey, 68 | ]); 69 | 70 | return new KeyEntity($key->ssh_key); 71 | } 72 | 73 | /** 74 | * @throws ExceptionInterface 75 | */ 76 | public function update(string $id, string $name): KeyEntity 77 | { 78 | $key = $this->put(\sprintf('account/keys/%s', $id), [ 79 | 'name' => $name, 80 | ]); 81 | 82 | return new KeyEntity($key->ssh_key); 83 | } 84 | 85 | /** 86 | * @throws ExceptionInterface 87 | */ 88 | public function remove(string $id): void 89 | { 90 | $this->delete(\sprintf('account/keys/%s', $id)); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/Api/LoadBalancer.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Entity\AbstractEntity; 18 | use DigitalOceanV2\Entity\ForwardingRule as ForwardRuleEntity; 19 | use DigitalOceanV2\Entity\HealthCheck as HealthCheckEntity; 20 | use DigitalOceanV2\Entity\LoadBalancer as LoadBalancerEntity; 21 | use DigitalOceanV2\Entity\StickySession as StickySessionEntity; 22 | use DigitalOceanV2\Exception\ExceptionInterface; 23 | 24 | /** 25 | * @author Jacob Holmes 26 | */ 27 | class LoadBalancer extends AbstractApi 28 | { 29 | /** 30 | * @throws ExceptionInterface 31 | * 32 | * @return LoadBalancerEntity[] 33 | */ 34 | public function getAll(): array 35 | { 36 | $loadBalancers = $this->get('load_balancers'); 37 | 38 | return \array_map(function ($key) { 39 | return new LoadBalancerEntity($key); 40 | }, $loadBalancers->load_balancers); 41 | } 42 | 43 | /** 44 | * @throws ExceptionInterface 45 | */ 46 | public function getById(string $id): LoadBalancerEntity 47 | { 48 | $loadBalancer = $this->get(\sprintf('load_balancers/%s', $id)); 49 | 50 | return new LoadBalancerEntity($loadBalancer->load_balancer); 51 | } 52 | 53 | /** 54 | * @param array|ForwardRuleEntity[] $forwardRules 55 | * @param array|HealthCheckEntity[] $healthCheck 56 | * @param array|StickySessionEntity[] $stickySessions 57 | * @param int<30, 600> $httpIdleTimeoutSeconds 58 | * 59 | * @throws ExceptionInterface 60 | */ 61 | public function create( 62 | string $name, 63 | string $region, 64 | ?array $forwardRules = null, 65 | string $algorithm = 'round_robin', 66 | array $healthCheck = [], 67 | array $stickySessions = [], 68 | array $dropletIds = [], 69 | bool $httpsRedirect = false, 70 | int $httpIdleTimeoutSeconds = 60 71 | ): LoadBalancerEntity { 72 | $loadBalancer = $this->post('load_balancers', [ 73 | 'name' => $name, 74 | 'algorithm' => $algorithm, 75 | 'region' => $region, 76 | 'forwarding_rules' => null === $forwardRules ? null : self::formatForwardRules($forwardRules), 77 | 'health_check' => self::formatConfigurationOptions($healthCheck), 78 | 'sticky_sessions' => self::formatConfigurationOptions($stickySessions), 79 | 'droplet_ids' => $dropletIds, 80 | 'redirect_http_to_https' => $httpsRedirect, 81 | 'http_idle_timeout_seconds' => $httpIdleTimeoutSeconds, 82 | ]); 83 | 84 | return new LoadBalancerEntity($loadBalancer->load_balancer); 85 | } 86 | 87 | /** 88 | * @throws ExceptionInterface 89 | */ 90 | public function update(string $id, array|LoadBalancerEntity $loadBalancerSpec): LoadBalancerEntity 91 | { 92 | $data = self::formatConfigurationOptions($loadBalancerSpec); 93 | 94 | $loadBalancer = $this->put(\sprintf('load_balancers/%s', $id), $data); 95 | 96 | return new LoadBalancerEntity($loadBalancer->load_balancer); 97 | } 98 | 99 | /** 100 | * @throws ExceptionInterface 101 | */ 102 | public function remove(string $id): void 103 | { 104 | $this->delete(\sprintf('load_balancers/%s', $id)); 105 | } 106 | 107 | private static function formatForwardRules(array|AbstractEntity $forwardRules): array 108 | { 109 | if (\is_array($forwardRules)) { 110 | return \array_map(function ($rule) { 111 | return self::formatConfigurationOptions($rule); 112 | }, $forwardRules); 113 | } 114 | 115 | return [ 116 | (new ForwardRuleEntity())->setStandardHttpRules()->toArray(), 117 | (new ForwardRuleEntity())->setStandardHttpsRules()->toArray(), 118 | ]; 119 | } 120 | 121 | private static function formatConfigurationOptions(array|AbstractEntity $config): array 122 | { 123 | return $config instanceof AbstractEntity ? $config->toArray() : $config; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/Api/Monitoring.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Entity\MonitoringAlert as MonitoringAlertEntity; 18 | use DigitalOceanV2\Entity\MonitoringMetric as MonitoringMetricEntity; 19 | use DigitalOceanV2\Exception\ExceptionInterface; 20 | 21 | /** 22 | * @author Michael Shihjay Chen 23 | * @author Graham Campbell 24 | */ 25 | class Monitoring extends AbstractApi 26 | { 27 | /** 28 | * @throws ExceptionInterface 29 | * 30 | * @return MonitoringAlertEntity[] 31 | */ 32 | public function getAlerts(): array 33 | { 34 | $alerts = $this->get('monitoring/alerts'); 35 | 36 | return \array_map(function ($alert) { 37 | return new MonitoringAlertEntity($alert); 38 | }, $alerts->policies); 39 | } 40 | 41 | /** 42 | * @throws ExceptionInterface 43 | */ 44 | public function getAlert(string $alertUUID): MonitoringAlertEntity 45 | { 46 | $alert = $this->get(\sprintf('monitoring/alerts/%s', $alertUUID)); 47 | 48 | return new MonitoringAlertEntity($alert); 49 | } 50 | 51 | /** 52 | * @throws ExceptionInterface 53 | */ 54 | public function getDropletBandwidth(string $hostId, string $start, string $end, string $direction = 'inbound', string $interface = 'public'): MonitoringMetricEntity 55 | { 56 | $metric = $this->get( 57 | 'monitoring/metrics/droplet/bandwidth', 58 | [ 59 | 'host_id' => $hostId, 60 | 'start' => $start, 61 | 'end' => $end, 62 | 'direction' => $direction, 63 | 'interface' => $interface, 64 | ] 65 | ); 66 | 67 | return new MonitoringMetricEntity($metric); 68 | } 69 | 70 | /** 71 | * @throws ExceptionInterface 72 | */ 73 | public function getDropletCpu(string $hostId, string $start, string $end): MonitoringMetricEntity 74 | { 75 | $metric = $this->get( 76 | 'monitoring/metrics/droplet/cpu', 77 | [ 78 | 'host_id' => $hostId, 79 | 'start' => $start, 80 | 'end' => $end, 81 | ] 82 | ); 83 | 84 | return new MonitoringMetricEntity($metric); 85 | } 86 | 87 | /** 88 | * @throws ExceptionInterface 89 | */ 90 | public function getDropletTotalMemory(string $hostId, string $start, string $end): MonitoringMetricEntity 91 | { 92 | $metric = $this->get( 93 | 'monitoring/metrics/droplet/memory_total', 94 | [ 95 | 'host_id' => $hostId, 96 | 'start' => $start, 97 | 'end' => $end, 98 | ] 99 | ); 100 | 101 | return new MonitoringMetricEntity($metric); 102 | } 103 | 104 | /** 105 | * @throws ExceptionInterface 106 | */ 107 | public function getDropletCachedMemory(string $hostId, string $start, string $end): MonitoringMetricEntity 108 | { 109 | $metric = $this->get( 110 | 'monitoring/metrics/droplet/memory_cached', 111 | [ 112 | 'host_id' => $hostId, 113 | 'start' => $start, 114 | 'end' => $end, 115 | ] 116 | ); 117 | 118 | return new MonitoringMetricEntity($metric); 119 | } 120 | 121 | /** 122 | * @throws ExceptionInterface 123 | */ 124 | public function getDropletFreeMemory(string $hostId, string $start, string $end): MonitoringMetricEntity 125 | { 126 | $metric = $this->get( 127 | 'monitoring/metrics/droplet/memory_free', 128 | [ 129 | 'host_id' => $hostId, 130 | 'start' => $start, 131 | 'end' => $end, 132 | ] 133 | ); 134 | 135 | return new MonitoringMetricEntity($metric); 136 | } 137 | 138 | /** 139 | * @throws ExceptionInterface 140 | */ 141 | public function getDropletAvailableMemory(string $hostId, string $start, string $end): MonitoringMetricEntity 142 | { 143 | $metric = $this->get( 144 | 'monitoring/metrics/droplet/memory_available', 145 | [ 146 | 'host_id' => $hostId, 147 | 'start' => $start, 148 | 'end' => $end, 149 | ] 150 | ); 151 | 152 | return new MonitoringMetricEntity($metric); 153 | } 154 | 155 | /** 156 | * @throws ExceptionInterface 157 | */ 158 | public function getDropletFilesystemFree(string $hostId, string $start, string $end): MonitoringMetricEntity 159 | { 160 | $metric = $this->get( 161 | 'monitoring/metrics/droplet/filesystem_free', 162 | [ 163 | 'host_id' => $hostId, 164 | 'start' => $start, 165 | 'end' => $end, 166 | ] 167 | ); 168 | 169 | return new MonitoringMetricEntity($metric); 170 | } 171 | 172 | /** 173 | * @throws ExceptionInterface 174 | */ 175 | public function getDropletFilesystemSize(string $hostId, string $start, string $end): MonitoringMetricEntity 176 | { 177 | $metric = $this->get( 178 | 'monitoring/metrics/droplet/filesystem_size', 179 | [ 180 | 'host_id' => $hostId, 181 | 'start' => $start, 182 | 'end' => $end, 183 | ] 184 | ); 185 | 186 | return new MonitoringMetricEntity($metric); 187 | } 188 | 189 | /** 190 | * @throws ExceptionInterface 191 | */ 192 | public function getDropletLoad1(string $hostId, string $start, string $end): MonitoringMetricEntity 193 | { 194 | $metric = $this->get( 195 | 'monitoring/metrics/droplet/load_1', 196 | [ 197 | 'host_id' => $hostId, 198 | 'start' => $start, 199 | 'end' => $end, 200 | ] 201 | ); 202 | 203 | return new MonitoringMetricEntity($metric); 204 | } 205 | 206 | /** 207 | * @throws ExceptionInterface 208 | */ 209 | public function getDropletLoad5(string $hostId, string $start, string $end): MonitoringMetricEntity 210 | { 211 | $metric = $this->get( 212 | 'monitoring/metrics/droplet/load_5', 213 | [ 214 | 'host_id' => $hostId, 215 | 'start' => $start, 216 | 'end' => $end, 217 | ] 218 | ); 219 | 220 | return new MonitoringMetricEntity($metric); 221 | } 222 | 223 | /** 224 | * @throws ExceptionInterface 225 | */ 226 | public function getDropletLoad15(string $hostId, string $start, string $end): MonitoringMetricEntity 227 | { 228 | $metric = $this->get( 229 | 'monitoring/metrics/droplet/load_15', 230 | [ 231 | 'host_id' => $hostId, 232 | 'start' => $start, 233 | 'end' => $end, 234 | ] 235 | ); 236 | 237 | return new MonitoringMetricEntity($metric); 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /src/Api/ProjectResource.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Entity\ProjectResource as ProjectResourceEntity; 18 | use DigitalOceanV2\Exception\ExceptionInterface; 19 | 20 | /** 21 | * @author Graham Campbell 22 | * @author Mohammad Salamat 23 | */ 24 | class ProjectResource extends AbstractApi 25 | { 26 | /** 27 | * @throws ExceptionInterface 28 | * 29 | * @return ProjectResourceEntity[] 30 | */ 31 | public function getProjectResources(string $id): array 32 | { 33 | $resources = $this->get(\sprintf('projects/%s/resources', $id)); 34 | 35 | return \array_map(function ($resource) { 36 | return new ProjectResourceEntity($resource); 37 | }, $resources->resources); 38 | } 39 | 40 | /** 41 | * @param array $resources 42 | * 43 | * @throws ExceptionInterface 44 | * 45 | * @return ProjectResourceEntity[] 46 | */ 47 | public function assignResources(string $id, array $resources): array 48 | { 49 | $resources = $this->post(\sprintf('projects/%s/resources', $id), [ 50 | 'resources' => $resources, 51 | ]); 52 | 53 | return \array_map(function ($resource) { 54 | return new ProjectResourceEntity($resource); 55 | }, $resources->resources); 56 | } 57 | 58 | /** 59 | * @throws ExceptionInterface 60 | * 61 | * @return ProjectResourceEntity[] 62 | */ 63 | public function getDefaultProjectResources(): array 64 | { 65 | $resources = $this->get('projects/default/resources'); 66 | 67 | return \array_map(function ($resource) { 68 | return new ProjectResourceEntity($resource); 69 | }, $resources->resources); 70 | } 71 | 72 | /** 73 | * @param array $resources 74 | * 75 | * @throws ExceptionInterface 76 | * 77 | * @return ProjectResourceEntity[] 78 | */ 79 | public function assignResourcesToDefaultProject(array $resources): array 80 | { 81 | $resources = $this->post('projects/default/resources', [ 82 | 'resources' => $resources, 83 | ]); 84 | 85 | return \array_map(function ($resource) { 86 | return new ProjectResourceEntity($resource); 87 | }, $resources->resources); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Api/Region.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Entity\Region as RegionEntity; 18 | use DigitalOceanV2\Exception\ExceptionInterface; 19 | 20 | /** 21 | * @author Yassir Hannoun 22 | * @author Graham Campbell 23 | */ 24 | class Region extends AbstractApi 25 | { 26 | /** 27 | * @throws ExceptionInterface 28 | * 29 | * @return RegionEntity[] 30 | */ 31 | public function getAll(): array 32 | { 33 | $regions = $this->get('regions'); 34 | 35 | return \array_map(function ($region) { 36 | return new RegionEntity($region); 37 | }, $regions->regions); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Api/ReservedIp.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Entity\Action as ActionEntity; 18 | use DigitalOceanV2\Entity\ReservedIp as ReservedIpEntity; 19 | use DigitalOceanV2\Exception\ExceptionInterface; 20 | 21 | /** 22 | * @author Graham Campbell 23 | * @author Manuel Christlieb 24 | */ 25 | class ReservedIp extends AbstractApi 26 | { 27 | /** 28 | * @throws ExceptionInterface 29 | * 30 | * @return ReservedIpEntity[] 31 | */ 32 | public function getAll(): array 33 | { 34 | $ips = $this->get('reserved_ips'); 35 | 36 | return \array_map(function ($ip) { 37 | return new ReservedIpEntity($ip); 38 | }, $ips->reserved_ips); 39 | } 40 | 41 | /** 42 | * @throws ExceptionInterface 43 | */ 44 | public function getById(string $ipAddress): ReservedIpEntity 45 | { 46 | $ip = $this->get(\sprintf('reserved_ips/%s', $ipAddress)); 47 | 48 | return new ReservedIpEntity($ip->reserved_ip); 49 | } 50 | 51 | /** 52 | * @throws ExceptionInterface 53 | */ 54 | public function createAssigned(int $dropletId): ReservedIpEntity 55 | { 56 | $ip = $this->post('reserved_ips', ['droplet_id' => $dropletId]); 57 | 58 | return new ReservedIpEntity($ip->reserved_ip); 59 | } 60 | 61 | /** 62 | * @throws ExceptionInterface 63 | */ 64 | public function createReserved(string $regionSlug): ReservedIpEntity 65 | { 66 | $ip = $this->post('reserved_ips', ['region' => $regionSlug]); 67 | 68 | return new ReservedIpEntity($ip->reserved_ip); 69 | } 70 | 71 | /** 72 | * @throws ExceptionInterface 73 | */ 74 | public function remove(string $ipAddress): void 75 | { 76 | $this->delete(\sprintf('reserved_ips/%s', $ipAddress)); 77 | } 78 | 79 | /** 80 | * @throws ExceptionInterface 81 | */ 82 | public function getActions(string $ipAddress): array 83 | { 84 | $actions = $this->get(\sprintf('reserved_ips/%s/actions', $ipAddress)); 85 | 86 | return \array_map(function ($action) { 87 | return new ActionEntity($action); 88 | }, $actions->actions); 89 | } 90 | 91 | /** 92 | * @throws ExceptionInterface 93 | */ 94 | public function getActionById(string $ipAddress, int $actionId): ActionEntity 95 | { 96 | $action = $this->get(\sprintf('reserved_ips/%s/actions/%d', $ipAddress, $actionId)); 97 | 98 | return new ActionEntity($action->action); 99 | } 100 | 101 | /** 102 | * @throws ExceptionInterface 103 | */ 104 | public function assign(string $ipAddress, int $dropletId): ActionEntity 105 | { 106 | return $this->executeAction($ipAddress, ['type' => 'assign', 'droplet_id' => $dropletId]); 107 | } 108 | 109 | /** 110 | * @throws ExceptionInterface 111 | */ 112 | public function unassign(string $ipAddress): ActionEntity 113 | { 114 | return $this->executeAction($ipAddress, ['type' => 'unassign']); 115 | } 116 | 117 | /** 118 | * @throws ExceptionInterface 119 | */ 120 | private function executeAction(string $ipAddress, array $options): ActionEntity 121 | { 122 | $action = $this->post(\sprintf('reserved_ips/%s/actions', $ipAddress), $options); 123 | 124 | return new ActionEntity($action->action); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/Api/Size.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Entity\Size as SizeEntity; 18 | use DigitalOceanV2\Exception\ExceptionInterface; 19 | 20 | /** 21 | * @author Yassir Hannoun 22 | * @author Graham Campbell 23 | */ 24 | class Size extends AbstractApi 25 | { 26 | /** 27 | * @throws ExceptionInterface 28 | * 29 | * @return SizeEntity[] 30 | */ 31 | public function getAll(): array 32 | { 33 | $sizes = $this->get('sizes'); 34 | 35 | return \array_map(function ($size) { 36 | return new SizeEntity($size); 37 | }, $sizes->sizes); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Api/Snapshot.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Entity\Snapshot as SnapshotEntity; 18 | use DigitalOceanV2\Exception\ExceptionInterface; 19 | 20 | /** 21 | * @author Yassir Hannoun 22 | */ 23 | class Snapshot extends AbstractApi 24 | { 25 | /** 26 | * @throws ExceptionInterface 27 | * 28 | * @return SnapshotEntity[] 29 | */ 30 | public function getAll(array $criteria = []): array 31 | { 32 | $query = []; 33 | 34 | if (isset($criteria['type']) && \in_array($criteria['type'], ['droplet', 'volume'], true)) { 35 | $query['resource_type'] = $criteria['type']; 36 | } 37 | 38 | $snapshots = $this->get('snapshots', $query); 39 | 40 | return \array_map(function ($snapshots) { 41 | return new SnapshotEntity($snapshots); 42 | }, $snapshots->snapshots); 43 | } 44 | 45 | /** 46 | * @throws ExceptionInterface 47 | */ 48 | public function getById(string $id): SnapshotEntity 49 | { 50 | $snapshot = $this->get(\sprintf('snapshots/%s', $id)); 51 | 52 | return new SnapshotEntity($snapshot->snapshot); 53 | } 54 | 55 | /** 56 | * @throws ExceptionInterface 57 | */ 58 | public function remove(string $id): void 59 | { 60 | $this->delete(\sprintf('snapshots/%s', $id)); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Api/Tag.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Entity\Tag as TagEntity; 18 | use DigitalOceanV2\Exception\ExceptionInterface; 19 | 20 | /** 21 | * @author Nicolas Beauvais 22 | */ 23 | class Tag extends AbstractApi 24 | { 25 | /** 26 | * @throws ExceptionInterface 27 | * 28 | * @return TagEntity[] 29 | */ 30 | public function getAll(): array 31 | { 32 | $tags = $this->get('tags'); 33 | 34 | return \array_map(function ($tag) { 35 | return new TagEntity($tag); 36 | }, $tags->tags); 37 | } 38 | 39 | /** 40 | * @throws ExceptionInterface 41 | */ 42 | public function getByName(string $name): TagEntity 43 | { 44 | $tag = $this->get(\sprintf('tags/%s', $name)); 45 | 46 | return new TagEntity($tag->tag); 47 | } 48 | 49 | /** 50 | * @throws ExceptionInterface 51 | */ 52 | public function create(string $name): TagEntity 53 | { 54 | $tag = $this->post('tags', ['name' => $name]); 55 | 56 | return new TagEntity($tag->tag); 57 | } 58 | 59 | /** 60 | * @throws ExceptionInterface 61 | */ 62 | public function tagResources(string $name, array $resources): void 63 | { 64 | $this->post(\sprintf('tags/%s/resources', $name), ['resources' => $resources]); 65 | } 66 | 67 | /** 68 | * @throws ExceptionInterface 69 | */ 70 | public function untagResources(string $name, array $resources): void 71 | { 72 | $this->delete(\sprintf('tags/%s/resources', $name), ['resources' => $resources]); 73 | } 74 | 75 | /** 76 | * @throws ExceptionInterface 77 | */ 78 | public function remove(string $name): void 79 | { 80 | $this->delete(\sprintf('tags/%s', $name)); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Api/Volume.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Entity\Action as ActionEntity; 18 | use DigitalOceanV2\Entity\Snapshot as SnapshotEntity; 19 | use DigitalOceanV2\Entity\Volume as VolumeEntity; 20 | use DigitalOceanV2\Exception\ExceptionInterface; 21 | 22 | /** 23 | * @author Yassir Hannoun 24 | */ 25 | class Volume extends AbstractApi 26 | { 27 | /** 28 | * @param string $regionSlug restricts results to volumes available in a specific region 29 | * 30 | * @throws ExceptionInterface 31 | * 32 | * @return VolumeEntity[] Lists all of the Block Storage volumes available 33 | */ 34 | public function getAll(?string $regionSlug = null): array 35 | { 36 | $query = null === $regionSlug ? [] : ['region' => $regionSlug]; 37 | 38 | $volumes = $this->get('volumes', $query); 39 | 40 | return \array_map(function ($volume) { 41 | return new VolumeEntity($volume); 42 | }, $volumes->volumes); 43 | } 44 | 45 | /** 46 | * @param string $driveName restricts results to volumes with the specified name 47 | * @param string $regionSlug restricts results to volumes available in a specific region 48 | * 49 | * @throws ExceptionInterface 50 | * 51 | * @return VolumeEntity[] Lists all of the Block Storage volumes available 52 | */ 53 | public function getByNameAndRegion(string $driveName, string $regionSlug): array 54 | { 55 | $volumes = $this->get(\sprintf('volumes®ion=%s&name=%s', $regionSlug, $driveName)); 56 | 57 | return \array_map(function ($volume) { 58 | return new VolumeEntity($volume); 59 | }, $volumes->volumes); 60 | } 61 | 62 | /** 63 | * @throws ExceptionInterface 64 | * 65 | * @return VolumeEntity the Block Storage volume with the specified id 66 | */ 67 | public function getById(string $id): VolumeEntity 68 | { 69 | $volume = $this->get(\sprintf('volumes/%s', $id)); 70 | 71 | return new VolumeEntity($volume->volume); 72 | } 73 | 74 | /** 75 | * Get all volume snapshots. 76 | * 77 | * @throws ExceptionInterface 78 | * 79 | * @return SnapshotEntity[] 80 | */ 81 | public function getSnapshots(string $id): array 82 | { 83 | $snapshots = $this->get(\sprintf('volumes/%s/snapshots', $id)); 84 | 85 | return \array_map(function ($snapshot) { 86 | return new SnapshotEntity($snapshot); 87 | }, $snapshots->snapshots); 88 | } 89 | 90 | /** 91 | * @param string $name A human-readable name for the Block Storage volume 92 | * @param string $description Free-form text field to describe a Block Storage volume 93 | * @param int $sizeInGigabytes The size of the Block Storage volume in GiB 94 | * @param string $regionSlug The region where the Block Storage volume will be created 95 | * @param string $snapshotId The unique identifier for the volume snapshot from which to create the volume. Should not be specified with a region_id. 96 | * @param string $filesystemType the name of the filesystem type to be used on the volume 97 | * @param string $filesystemLabel the label to be applied to the filesystem 98 | * 99 | * @throws ExceptionInterface 100 | */ 101 | public function create(string $name, string $description, int $sizeInGigabytes, string $regionSlug, ?string $snapshotId = null, ?string $filesystemType = null, ?string $filesystemLabel = null): VolumeEntity 102 | { 103 | $data = [ 104 | 'size_gigabytes' => $sizeInGigabytes, 105 | 'name' => $name, 106 | 'description' => $description, 107 | 'region' => $regionSlug, 108 | ]; 109 | 110 | if (null !== $snapshotId) { 111 | $data['snapshot_id'] = $snapshotId; 112 | } 113 | if (null !== $filesystemType) { 114 | $data['filesystem_type'] = $filesystemType; 115 | } 116 | if (null !== $filesystemLabel) { 117 | $data['filesystem_label'] = $filesystemLabel; 118 | } 119 | 120 | $volume = $this->post('volumes', $data); 121 | 122 | return new VolumeEntity($volume->volume); 123 | } 124 | 125 | /** 126 | * @throws ExceptionInterface 127 | */ 128 | public function remove(string $id): void 129 | { 130 | $this->delete(\sprintf('volumes/%s', $id)); 131 | } 132 | 133 | /** 134 | * @param string $driveName restricts the search to volumes with the specified name 135 | * @param string $regionSlug restricts the search to volumes available in a specific region 136 | * 137 | * @throws ExceptionInterface 138 | */ 139 | public function removeWithNameAndRegion(string $driveName, string $regionSlug): void 140 | { 141 | $this->delete('volumes', [ 142 | 'name' => $driveName, 143 | 'region' => $regionSlug, 144 | ]); 145 | } 146 | 147 | /** 148 | * @param string $id the id of the volume 149 | * @param int $dropletId the unique identifier for the Droplet the volume will be attached to 150 | * @param string $regionSlug the slug identifier for the region the volume is located in 151 | * 152 | * @throws ExceptionInterface 153 | */ 154 | public function attach(string $id, int $dropletId, string $regionSlug): ActionEntity 155 | { 156 | $action = $this->post(\sprintf('volumes/%s/actions', $id), [ 157 | 'type' => 'attach', 158 | 'droplet_id' => $dropletId, 159 | 'region' => $regionSlug, 160 | ]); 161 | 162 | return new ActionEntity($action->action); 163 | } 164 | 165 | /** 166 | * @param string $id the id of the volume 167 | * @param int $dropletId the unique identifier for the Droplet the volume will detach from 168 | * @param string $regionSlug the slug identifier for the region the volume is located in 169 | * 170 | * @throws ExceptionInterface 171 | */ 172 | public function detach(string $id, int $dropletId, string $regionSlug): ActionEntity 173 | { 174 | $action = $this->post(\sprintf('volumes/%s/actions', $id), [ 175 | 'type' => 'detach', 176 | 'droplet_id' => $dropletId, 177 | 'region' => $regionSlug, 178 | ]); 179 | 180 | return new ActionEntity($action->action); 181 | } 182 | 183 | /** 184 | * @param string $id the id of the volume 185 | * @param int $newSize the new size of the Block Storage volume in GiB 186 | * @param string $regionSlug the slug identifier for the region the volume is located in 187 | * 188 | * @throws ExceptionInterface 189 | */ 190 | public function resize(string $id, int $newSize, string $regionSlug): ActionEntity 191 | { 192 | $action = $this->post(\sprintf('volumes/%s/actions', $id), [ 193 | 'type' => 'resize', 194 | 'size_gigabytes' => $newSize, 195 | 'region' => $regionSlug, 196 | ]); 197 | 198 | return new ActionEntity($action->action); 199 | } 200 | 201 | /** 202 | * Create a new snapshot of the volume. 203 | * 204 | * @param string $id the id of the volume 205 | * @param string $name a human-readable name for the volume snapshot 206 | * 207 | * @throws ExceptionInterface 208 | */ 209 | public function snapshot(string $id, string $name): SnapshotEntity 210 | { 211 | $snapshot = $this->post(\sprintf('volumes/%s/snapshots', $id), ['name' => $name]); 212 | 213 | return new SnapshotEntity($snapshot->snapshot); 214 | } 215 | 216 | /** 217 | * @throws ExceptionInterface 218 | */ 219 | public function getActionById(string $id, int $actionId): ActionEntity 220 | { 221 | $action = $this->get(\sprintf('volumes/%s/actions/%d', $id, $actionId)); 222 | 223 | return new ActionEntity($action->action); 224 | } 225 | 226 | /** 227 | * @throws ExceptionInterface 228 | * 229 | * @return ActionEntity[] 230 | */ 231 | public function getActions(string $id): array 232 | { 233 | $actions = $this->get(\sprintf('volumes/%s/actions', $id)); 234 | 235 | return \array_map(function ($action) { 236 | return new ActionEntity($action); 237 | }, $actions->actions); 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /src/Api/Vpc.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Api; 16 | 17 | use DigitalOceanV2\Entity\Vpc as VpcEntity; 18 | use DigitalOceanV2\Exception\ExceptionInterface; 19 | 20 | /** 21 | * @author Manuel Christlieb 22 | */ 23 | class Vpc extends AbstractApi 24 | { 25 | /** 26 | * @throws ExceptionInterface 27 | * 28 | * @return VpcEntity[] 29 | */ 30 | public function getAll(): array 31 | { 32 | $vpcs = $this->get('vpcs'); 33 | 34 | return \array_map(static fn ($vpc) => new VpcEntity($vpc), $vpcs->vpcs); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Client.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2; 16 | 17 | use DigitalOceanV2\Api\Account; 18 | use DigitalOceanV2\Api\Action; 19 | use DigitalOceanV2\Api\App; 20 | use DigitalOceanV2\Api\CdnEndpoint; 21 | use DigitalOceanV2\Api\Certificate; 22 | use DigitalOceanV2\Api\Database; 23 | use DigitalOceanV2\Api\Domain; 24 | use DigitalOceanV2\Api\DomainRecord; 25 | use DigitalOceanV2\Api\Droplet; 26 | use DigitalOceanV2\Api\Firewall; 27 | use DigitalOceanV2\Api\FloatingIp; 28 | use DigitalOceanV2\Api\Image; 29 | use DigitalOceanV2\Api\Key; 30 | use DigitalOceanV2\Api\LoadBalancer; 31 | use DigitalOceanV2\Api\Monitoring; 32 | use DigitalOceanV2\Api\ProjectResource; 33 | use DigitalOceanV2\Api\Region; 34 | use DigitalOceanV2\Api\ReservedIp; 35 | use DigitalOceanV2\Api\Size; 36 | use DigitalOceanV2\Api\Snapshot; 37 | use DigitalOceanV2\Api\Tag; 38 | use DigitalOceanV2\Api\Volume; 39 | use DigitalOceanV2\Api\Vpc; 40 | use DigitalOceanV2\HttpClient\Builder; 41 | use DigitalOceanV2\HttpClient\Message\ResponseMediator; 42 | use DigitalOceanV2\HttpClient\Plugin\Authentication; 43 | use DigitalOceanV2\HttpClient\Plugin\ExceptionThrower; 44 | use DigitalOceanV2\HttpClient\Plugin\History; 45 | use Http\Client\Common\HttpMethodsClientInterface; 46 | use Http\Client\Common\Plugin\AddHostPlugin; 47 | use Http\Client\Common\Plugin\HeaderDefaultsPlugin; 48 | use Http\Client\Common\Plugin\HistoryPlugin; 49 | use Http\Client\Common\Plugin\RedirectPlugin; 50 | use Http\Discovery\Psr17FactoryDiscovery; 51 | use Psr\Http\Client\ClientInterface; 52 | use Psr\Http\Message\ResponseInterface; 53 | 54 | /** 55 | * @author Antoine Kirk 56 | * @author Graham Campbell 57 | */ 58 | class Client 59 | { 60 | /** 61 | * The default base URL. 62 | * 63 | * @var string 64 | */ 65 | private const BASE_URL = 'https://api.digitalocean.com'; 66 | 67 | /** 68 | * The default user agent header. 69 | * 70 | * @var string 71 | */ 72 | private const USER_AGENT = 'digitalocean-php-api-client/5.0'; 73 | 74 | private readonly Builder $httpClientBuilder; 75 | private readonly History $responseHistory; 76 | 77 | public function __construct(?Builder $httpClientBuilder = null) 78 | { 79 | $this->httpClientBuilder = $builder = $httpClientBuilder ?? new Builder(); 80 | $this->responseHistory = new History(); 81 | 82 | $builder->addPlugin(new ExceptionThrower()); 83 | $builder->addPlugin(new HistoryPlugin($this->responseHistory)); 84 | $builder->addPlugin(new RedirectPlugin()); 85 | 86 | $builder->addPlugin(new HeaderDefaultsPlugin([ 87 | 'Accept' => ResponseMediator::JSON_CONTENT_TYPE, 88 | 'User-Agent' => self::USER_AGENT, 89 | ])); 90 | 91 | $this->setUrl(self::BASE_URL); 92 | } 93 | 94 | public static function createWithHttpClient(ClientInterface $httpClient): self 95 | { 96 | $builder = new Builder($httpClient); 97 | 98 | return new self($builder); 99 | } 100 | 101 | public function account(): Account 102 | { 103 | return new Account($this); 104 | } 105 | 106 | public function action(): Action 107 | { 108 | return new Action($this); 109 | } 110 | 111 | public function app(): App 112 | { 113 | return new App($this); 114 | } 115 | 116 | public function cdnEndpoint(): CdnEndpoint 117 | { 118 | return new CdnEndpoint($this); 119 | } 120 | 121 | public function certificate(): Certificate 122 | { 123 | return new Certificate($this); 124 | } 125 | 126 | public function database(): Database 127 | { 128 | return new Database($this); 129 | } 130 | 131 | public function domain(): Domain 132 | { 133 | return new Domain($this); 134 | } 135 | 136 | public function domainRecord(): DomainRecord 137 | { 138 | return new DomainRecord($this); 139 | } 140 | 141 | public function droplet(): Droplet 142 | { 143 | return new Droplet($this); 144 | } 145 | 146 | public function firewall(): Firewall 147 | { 148 | return new Firewall($this); 149 | } 150 | 151 | public function floatingIp(): FloatingIp 152 | { 153 | return new FloatingIp($this); 154 | } 155 | 156 | public function image(): Image 157 | { 158 | return new Image($this); 159 | } 160 | 161 | public function key(): Key 162 | { 163 | return new Key($this); 164 | } 165 | 166 | public function loadBalancer(): LoadBalancer 167 | { 168 | return new LoadBalancer($this); 169 | } 170 | 171 | public function monitoring(): Monitoring 172 | { 173 | return new Monitoring($this); 174 | } 175 | 176 | public function projectResource(): ProjectResource 177 | { 178 | return new ProjectResource($this); 179 | } 180 | 181 | public function region(): Region 182 | { 183 | return new Region($this); 184 | } 185 | 186 | public function reservedIp(): ReservedIp 187 | { 188 | return new ReservedIp($this); 189 | } 190 | 191 | public function size(): Size 192 | { 193 | return new Size($this); 194 | } 195 | 196 | public function snapshot(): Snapshot 197 | { 198 | return new Snapshot($this); 199 | } 200 | 201 | public function tag(): Tag 202 | { 203 | return new Tag($this); 204 | } 205 | 206 | public function volume(): Volume 207 | { 208 | return new Volume($this); 209 | } 210 | 211 | public function vpc(): Vpc 212 | { 213 | return new Vpc($this); 214 | } 215 | 216 | public function authenticate(string $token): void 217 | { 218 | $this->getHttpClientBuilder()->addPlugin(new Authentication($token)); 219 | } 220 | 221 | /** 222 | * Set the base URL. 223 | */ 224 | public function setUrl(string $url): void 225 | { 226 | $this->httpClientBuilder->removePlugin(AddHostPlugin::class); 227 | $this->httpClientBuilder->addPlugin(new AddHostPlugin(Psr17FactoryDiscovery::findUriFactory()->createUri($url))); 228 | } 229 | 230 | /** 231 | * Get the last response. 232 | */ 233 | public function getLastResponse(): ?ResponseInterface 234 | { 235 | return $this->responseHistory->getLastResponse(); 236 | } 237 | 238 | /** 239 | * Get the HTTP client. 240 | */ 241 | public function getHttpClient(): HttpMethodsClientInterface 242 | { 243 | return $this->getHttpClientBuilder()->getHttpClient(); 244 | } 245 | 246 | /** 247 | * Get the HTTP client builder. 248 | */ 249 | protected function getHttpClientBuilder(): Builder 250 | { 251 | return $this->httpClientBuilder; 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /src/Entity/AbstractEntity.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | use DigitalOceanV2\Exception\RuntimeException; 18 | 19 | /** 20 | * @author Antoine Kirk 21 | * @author Graham Campbell 22 | */ 23 | abstract class AbstractEntity 24 | { 25 | public function __construct(object|array|null $parameters = null) 26 | { 27 | if (null === $parameters) { 28 | return; 29 | } 30 | 31 | if (\is_object($parameters)) { 32 | $parameters = \get_object_vars($parameters); 33 | } 34 | 35 | $this->build($parameters); 36 | } 37 | 38 | public function __get(string $property): mixed 39 | { 40 | $property = static::convertToCamelCase($property); 41 | if (\property_exists($this, $property)) { 42 | return $this->{$property}; 43 | } 44 | 45 | $trace = \debug_backtrace(); 46 | \trigger_error( 47 | 'Undefined property '.$property. 48 | ' in '.$trace[0]['file']. 49 | ' on line '.$trace[0]['line'], 50 | \E_USER_NOTICE 51 | ); 52 | 53 | return null; 54 | } 55 | 56 | public function build(array $parameters): void 57 | { 58 | foreach ($parameters as $property => $value) { 59 | $property = static::convertToCamelCase($property); 60 | 61 | if (\property_exists($this, $property)) { 62 | $this->$property = $value; 63 | } 64 | } 65 | } 66 | 67 | public function toArray(): array 68 | { 69 | $settings = []; 70 | $called = static::class; 71 | 72 | $reflection = new \ReflectionClass($called); 73 | $properties = $reflection->getProperties(\ReflectionProperty::IS_PUBLIC); 74 | 75 | foreach ($properties as $property) { 76 | $prop = $property->getName(); 77 | if (isset($this->$prop) && $property->class == $called) { 78 | $settings[self::convertToSnakeCase($prop)] = $this->$prop; 79 | } 80 | } 81 | 82 | return $settings; 83 | } 84 | 85 | protected static function convertToIso8601(string $date): string 86 | { 87 | $date = new \DateTime($date); 88 | $date->setTimezone(new \DateTimeZone(\date_default_timezone_get())); 89 | 90 | return $date->format(\DateTime::ISO8601); 91 | } 92 | 93 | protected static function convertToCamelCase(string $str): string 94 | { 95 | $callback = function ($match): string { 96 | return \strtoupper($match[2]); 97 | }; 98 | 99 | $replaced = \preg_replace_callback('/(^|_)([a-z])/', $callback, $str); 100 | 101 | if (null === $replaced) { 102 | throw new RuntimeException(\sprintf('preg_replace_callback error: %s', \preg_last_error_msg())); 103 | } 104 | 105 | return \lcfirst($replaced); 106 | } 107 | 108 | protected static function convertToSnakeCase(string $str): string 109 | { 110 | $replaced = \preg_split('/(?=[A-Z])/', $str); 111 | 112 | if (false === $replaced) { 113 | throw new RuntimeException(\sprintf('preg_split error: %s', \preg_last_error_msg())); 114 | } 115 | 116 | return \strtolower(\implode('_', $replaced)); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/Entity/Account.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Antoine Kirk 19 | * @author Graham Campbell 20 | */ 21 | final class Account extends AbstractEntity 22 | { 23 | public int $dropletLimit; 24 | 25 | public int $floatingIpLimit; 26 | 27 | public int $volumeLimit; 28 | 29 | public string $email; 30 | 31 | public string $uuid; 32 | 33 | public bool $emailVerified; 34 | 35 | public string $status; 36 | 37 | public string $statusMessage; 38 | 39 | public Team $team; 40 | 41 | public function build(array $parameters): void 42 | { 43 | foreach ($parameters as $property => $value) { 44 | $property = static::convertToCamelCase($property); 45 | 46 | if ('team' === $property) { 47 | $this->team = new Team($value); 48 | } elseif (\property_exists($this, $property)) { 49 | $this->$property = $value; 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Entity/Action.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Antoine Kirk 19 | * @author Graham Campbell 20 | */ 21 | final class Action extends AbstractEntity 22 | { 23 | public int $id; 24 | 25 | public string $status; 26 | 27 | public string $type; 28 | 29 | public ?string $startedAt; 30 | 31 | public ?string $completedAt; 32 | 33 | public int $resourceId; 34 | 35 | public string $resourceType; 36 | 37 | public Region $region; 38 | 39 | public string $regionSlug; 40 | 41 | public function build(array $parameters): void 42 | { 43 | foreach ($parameters as $property => $value) { 44 | $property = static::convertToCamelCase($property); 45 | 46 | if ('region' === $property) { 47 | $this->region = new Region($value); 48 | } elseif (\property_exists($this, $property)) { 49 | $this->$property = $value; 50 | } 51 | } 52 | } 53 | 54 | public function setStartedAt(string $startedAt): void 55 | { 56 | $this->startedAt = static::convertToIso8601($startedAt); 57 | } 58 | 59 | public function setCompletedAt(?string $completedAt): void 60 | { 61 | $this->completedAt = null === $completedAt ? null : static::convertToIso8601($completedAt); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Entity/App.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Michael Shihjay Chen 19 | * @author Graham Campbell 20 | */ 21 | final class App extends AbstractEntity 22 | { 23 | public string $id; 24 | 25 | public string $ownerUuid; 26 | 27 | public array $spec; 28 | 29 | public string $defaultIngress; 30 | 31 | public string $createdAt; 32 | 33 | public string $updatedAt; 34 | 35 | public array $activeDeployment; 36 | 37 | public array $inProgressDeployment; 38 | 39 | public string $lastDeploymentCreatedAt; 40 | 41 | public string $liveUrl; 42 | 43 | public array $region; 44 | 45 | public string $tierSlug; 46 | 47 | public string $liveUrlBase; 48 | 49 | public string $liveDomain; 50 | 51 | public array $domains; 52 | } 53 | -------------------------------------------------------------------------------- /src/Entity/AppDeployment.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Michael Shihjay Chen 19 | * @author Graham Campbell 20 | */ 21 | final class AppDeployment extends AbstractEntity 22 | { 23 | public string $id; 24 | 25 | public array $spec; 26 | 27 | public array $services; 28 | 29 | public array $staticSites; 30 | 31 | public array $workers; 32 | 33 | public array $jobs; 34 | 35 | public string $phaseLastUpdatedAt; 36 | 37 | public string $createdAt; 38 | 39 | public string $updatedAt; 40 | 41 | public string $cause; 42 | 43 | public string $clonedFrom; 44 | 45 | public array $progress; 46 | 47 | public string $phase; 48 | 49 | public string $tierSlug; 50 | } 51 | -------------------------------------------------------------------------------- /src/Entity/AppDeploymentLog.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Michael Shihjay Chen 19 | * @author Graham Campbell 20 | */ 21 | final class AppDeploymentLog extends AbstractEntity 22 | { 23 | public string $liveUrl; 24 | 25 | public array $historicUrls; 26 | } 27 | -------------------------------------------------------------------------------- /src/Entity/AppInstanceSize.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Michael Shihjay Chen 19 | * @author Graham Campbell 20 | */ 21 | final class AppInstanceSize extends AbstractEntity 22 | { 23 | public string $name; 24 | 25 | public string $slug; 26 | 27 | public string $cpuType; 28 | 29 | public string $cpus; 30 | 31 | public string $memoryBytes; 32 | 33 | public string $usdPerMonth; 34 | 35 | public string $usdPerSecond; 36 | 37 | public string $tierSlug; 38 | 39 | public string $tierUpgradeTo; 40 | 41 | public string $tierDowngradeTo; 42 | } 43 | -------------------------------------------------------------------------------- /src/Entity/AppRegion.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Michael Shihjay Chen 19 | * @author Graham Campbell 20 | */ 21 | final class AppRegion extends AbstractEntity 22 | { 23 | public string $slug; 24 | 25 | public string $label; 26 | 27 | public string $flag; 28 | 29 | public string $continent; 30 | 31 | public bool $disabled; 32 | 33 | public array $dataCenters; 34 | 35 | public string $reason; 36 | 37 | public bool $default; 38 | } 39 | -------------------------------------------------------------------------------- /src/Entity/AppTier.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Michael Shihjay Chen 19 | * @author Graham Campbell 20 | */ 21 | final class AppTier extends AbstractEntity 22 | { 23 | public string $name; 24 | 25 | public string $slug; 26 | 27 | public string $storageBytes; 28 | 29 | public string $egressBandwidthBytes; 30 | 31 | public string $buildSeconds; 32 | } 33 | -------------------------------------------------------------------------------- /src/Entity/CdnEndpoint.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Christian Fuentes 19 | */ 20 | final class CdnEndpoint extends AbstractEntity 21 | { 22 | public string $id; 23 | 24 | public string $origin; 25 | 26 | public string $endpoint; 27 | 28 | public string $createdAt; 29 | 30 | public int $ttl; 31 | 32 | public string $certificateId; 33 | 34 | public string $customDomain; 35 | 36 | public function setCreatedAt(string $createdAt): void 37 | { 38 | $this->createdAt = static::convertToIso8601($createdAt); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Entity/Certificate.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Jacob Holmes 19 | */ 20 | final class Certificate extends AbstractEntity 21 | { 22 | public string $id; 23 | 24 | public string $name; 25 | 26 | public string $notAfter; 27 | 28 | public string $sha1Fingerprint; 29 | 30 | public string $createdAt; 31 | 32 | public function setCreatedAt(string $createdAt): void 33 | { 34 | $this->createdAt = static::convertToIso8601($createdAt); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Entity/Database.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Filippo Fortino 19 | */ 20 | final class Database extends AbstractEntity 21 | { 22 | public string $name; 23 | } 24 | -------------------------------------------------------------------------------- /src/Entity/DatabaseBackup.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Filippo Fortino 19 | */ 20 | final class DatabaseBackup extends AbstractEntity 21 | { 22 | public string $createdAt; 23 | 24 | public float $sizeGigabytes; 25 | 26 | public function setCreatedAt(string $createdAt): void 27 | { 28 | $this->createdAt = static::convertToIso8601($createdAt); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Entity/DatabaseCluster.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Filippo Fortino 19 | */ 20 | final class DatabaseCluster extends AbstractEntity 21 | { 22 | public string $id; 23 | 24 | public string $name; 25 | 26 | public string $engine; 27 | 28 | public string $version; 29 | 30 | public DatabaseConnection $connection; 31 | 32 | public DatabaseConnection $privateConnection; 33 | 34 | /** 35 | * @var DatabaseUser[] 36 | */ 37 | public array $users = []; 38 | 39 | /** 40 | * @var string[] 41 | */ 42 | public array $dbNames = []; 43 | 44 | public int $numNodes; 45 | 46 | public string $size; 47 | 48 | public string $region; 49 | 50 | public string $status; 51 | 52 | public DatabaseMaintenanceWindow $maintenanceWindow; 53 | 54 | public string $createdAt; 55 | 56 | /** 57 | * @var string[] 58 | */ 59 | public array $tags = []; 60 | 61 | public string $privateNetworkUuid; 62 | 63 | public ?int $storageSizeMib; 64 | 65 | public function build(array $parameters): void 66 | { 67 | foreach ($parameters as $property => $value) { 68 | $property = static::convertToCamelCase($property); 69 | 70 | if ('connection' === $property) { 71 | $this->connection = new DatabaseConnection($value); 72 | } elseif ('privateConnection' === $property) { 73 | $this->privateConnection = new DatabaseConnection($value); 74 | } elseif ('users' === $property) { 75 | $this->users = \array_map(fn ($v) => new DatabaseUser($v), $value); 76 | } elseif ('maintenanceWindow' === $property) { 77 | $this->maintenanceWindow = new DatabaseMaintenanceWindow($value); 78 | } elseif (\property_exists($this, $property)) { 79 | $this->$property = $value; 80 | } 81 | } 82 | } 83 | 84 | public function setCreatedAt(string $createdAt): void 85 | { 86 | $this->createdAt = static::convertToIso8601($createdAt); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Entity/DatabaseConnection.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Filippo Fortino 19 | */ 20 | final class DatabaseConnection extends AbstractEntity 21 | { 22 | public string $uri; 23 | 24 | public string $database; 25 | 26 | public string $host; 27 | 28 | public int $port; 29 | 30 | public string $user; 31 | 32 | public string $password; 33 | 34 | public bool $ssl; 35 | } 36 | -------------------------------------------------------------------------------- /src/Entity/DatabaseMaintenanceWindow.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Filippo Fortino 19 | */ 20 | final class DatabaseMaintenanceWindow extends AbstractEntity 21 | { 22 | public string $day; 23 | 24 | public string $hour; 25 | 26 | public bool $pending; 27 | 28 | /** 29 | * @var string[] 30 | */ 31 | public array $description = []; 32 | } 33 | -------------------------------------------------------------------------------- /src/Entity/DatabaseMysqlSettings.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Filippo Fortino 19 | */ 20 | final class DatabaseMysqlSettings extends AbstractEntity 21 | { 22 | public string $authPlugin; 23 | } 24 | -------------------------------------------------------------------------------- /src/Entity/DatabasePool.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Filippo Fortino 19 | */ 20 | final class DatabasePool extends AbstractEntity 21 | { 22 | public string $name; 23 | 24 | public string $mode; 25 | 26 | public int $size; 27 | 28 | public string $db; 29 | 30 | public string $user; 31 | 32 | public DatabaseConnection $connection; 33 | 34 | public DatabaseConnection $privateConnection; 35 | 36 | public function build(array $parameters): void 37 | { 38 | foreach ($parameters as $property => $value) { 39 | $property = static::convertToCamelCase($property); 40 | 41 | if ('connection' === $property) { 42 | $this->connection = new DatabaseConnection($value); 43 | } elseif ('privateConnection' === $property) { 44 | $this->privateConnection = new DatabaseConnection($value); 45 | } elseif (\property_exists($this, $property)) { 46 | $this->$property = $value; 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Entity/DatabaseReplica.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Filippo Fortino 19 | */ 20 | final class DatabaseReplica extends AbstractEntity 21 | { 22 | public string $name; 23 | 24 | public DatabaseConnection $connection; 25 | 26 | public DatabaseConnection $privateConnection; 27 | 28 | public string $region; 29 | 30 | public string $status; 31 | 32 | public string $createdAt; 33 | 34 | /** 35 | * @var string[] 36 | */ 37 | public array $tags = []; 38 | 39 | public string $privateNetworkUuid; 40 | 41 | public function build(array $parameters): void 42 | { 43 | foreach ($parameters as $property => $value) { 44 | $property = static::convertToCamelCase($property); 45 | 46 | if ('connection' === $property) { 47 | $this->connection = new DatabaseConnection($value); 48 | } elseif ('privateConnection' === $property) { 49 | $this->privateConnection = new DatabaseConnection($value); 50 | } elseif (\property_exists($this, $property)) { 51 | $this->$property = $value; 52 | } 53 | } 54 | } 55 | 56 | public function setCreatedAt(string $createdAt): void 57 | { 58 | $this->createdAt = static::convertToIso8601($createdAt); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Entity/DatabaseRule.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Filippo Fortino 19 | */ 20 | final class DatabaseRule extends AbstractEntity 21 | { 22 | public string $uuid; 23 | 24 | public string $clusterUuid; 25 | 26 | public string $type; 27 | 28 | public string $value; 29 | 30 | public string $createdAt; 31 | 32 | public function setCreatedAt(string $createdAt): void 33 | { 34 | $this->createdAt = static::convertToIso8601($createdAt); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Entity/DatabaseUser.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Filippo Fortino 19 | */ 20 | final class DatabaseUser extends AbstractEntity 21 | { 22 | public string $name; 23 | 24 | public string $role; 25 | 26 | public string $password; 27 | 28 | public DatabaseMysqlSettings $mysqlSettings; 29 | 30 | public function build(array $parameters): void 31 | { 32 | foreach ($parameters as $property => $value) { 33 | $property = static::convertToCamelCase($property); 34 | 35 | if ('mysqlSettings' === $property) { 36 | $this->mysqlSettings = new DatabaseMysqlSettings($value); 37 | } elseif (\property_exists($this, $property)) { 38 | $this->$property = $value; 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Entity/Domain.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Yassir Hannoun 19 | * @author Graham Campbell 20 | */ 21 | final class Domain extends AbstractEntity 22 | { 23 | public string $name; 24 | 25 | public int $ttl; 26 | 27 | public string $zoneFile; 28 | } 29 | -------------------------------------------------------------------------------- /src/Entity/DomainRecord.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Yassir Hannoun 19 | * @author Graham Campbell 20 | */ 21 | final class DomainRecord extends AbstractEntity 22 | { 23 | public int $id; 24 | 25 | public string $type; 26 | 27 | public string $name; 28 | 29 | public ?string $data; 30 | 31 | public ?int $priority; 32 | 33 | public ?int $port; 34 | 35 | public int $ttl; 36 | 37 | public ?int $weight; 38 | 39 | public ?int $flags; 40 | 41 | public ?string $tag; 42 | } 43 | -------------------------------------------------------------------------------- /src/Entity/Droplet.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Yassir Hannoun 19 | * @author Graham Campbell 20 | */ 21 | final class Droplet extends AbstractEntity 22 | { 23 | public int $id; 24 | 25 | public string $name; 26 | 27 | public int $memory; 28 | 29 | public int $vcpus; 30 | 31 | public int $disk; 32 | 33 | public Region $region; 34 | 35 | public Image $image; 36 | 37 | public Kernel $kernel; 38 | 39 | public Size $size; 40 | 41 | public string $sizeSlug; 42 | 43 | public bool $locked; 44 | 45 | public string $createdAt; 46 | 47 | public string $status; 48 | 49 | /** 50 | * @var string[] 51 | */ 52 | public array $tags = []; 53 | 54 | /** 55 | * @var Network[] 56 | */ 57 | public array $networks = []; 58 | 59 | /** 60 | * @var int[] 61 | */ 62 | public array $backupIds = []; 63 | 64 | /** 65 | * @var string[] 66 | */ 67 | public array $volumeIds = []; 68 | 69 | /** 70 | * @var int[] 71 | */ 72 | public array $snapshotIds = []; 73 | 74 | /** 75 | * @var string[] 76 | */ 77 | public array $features = []; 78 | 79 | public bool $backupsEnabled; 80 | 81 | public bool $privateNetworkingEnabled; 82 | 83 | public bool $ipv6Enabled; 84 | 85 | public bool $virtIOEnabled; 86 | 87 | public NextBackupWindow $nextBackupWindow; 88 | 89 | public string $vpcUuid; 90 | 91 | public function build(array $parameters): void 92 | { 93 | foreach ($parameters as $property => $value) { 94 | $property = static::convertToCamelCase($property); 95 | 96 | if ('networks' === $property) { 97 | if (\property_exists($value, 'v4')) { 98 | foreach ($value->v4 as $subValue) { 99 | $subValue->version = 4; 100 | $this->networks[] = new Network($subValue); 101 | } 102 | } 103 | 104 | if (\property_exists($value, 'v6')) { 105 | foreach ($value->v6 as $subValue) { 106 | /** @var object{ip_address: string, netmask: int, gateway: string, type: string} $subValue */ 107 | $this->networks[] = new Network((object) [ 108 | 'ip_address' => $subValue->ip_address, 109 | 'netmask' => null, 110 | 'gateway' => $subValue->gateway, 111 | 'type' => $subValue->type, 112 | 'cidr' => \sprintf('%s/%d', $subValue->ip_address, $subValue->netmask), 113 | 'version' => 6, 114 | ]); 115 | } 116 | } 117 | } elseif ('kernel' === $property) { 118 | $this->kernel = new Kernel($value); 119 | } elseif ('size' === $property) { 120 | $this->size = new Size($value); 121 | } elseif ('region' === $property) { 122 | $this->region = new Region($value); 123 | } elseif ('image' === $property) { 124 | $this->image = new Image($value); 125 | } elseif ('nextBackupWindow' === $property) { 126 | $this->nextBackupWindow = new NextBackupWindow($value); 127 | } elseif (\property_exists($this, $property)) { 128 | $this->$property = $value; 129 | } 130 | } 131 | 132 | $this->backupsEnabled = \in_array('backups', $this->features, true); 133 | $this->virtIOEnabled = \in_array('virtio', $this->features, true); 134 | $this->privateNetworkingEnabled = \in_array('private_networking', $this->features, true); 135 | $this->ipv6Enabled = \in_array('ipv6', $this->features, true); 136 | } 137 | 138 | public function setCreatedAt(string $createdAt): void 139 | { 140 | $this->createdAt = static::convertToIso8601($createdAt); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/Entity/Firewall.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Yassir Hannoun 19 | * @author Graham Campbell 20 | */ 21 | final class Firewall extends AbstractEntity 22 | { 23 | public string $id; 24 | 25 | public string $status; 26 | 27 | public string $createdAt; 28 | 29 | public array $pendingChanges; 30 | 31 | public string $name; 32 | 33 | public array $inboundRules; 34 | 35 | public array $outboundRules; 36 | 37 | public array $dropletIds; 38 | 39 | public array $tags; 40 | 41 | public function build(array $parameters): void 42 | { 43 | foreach ($parameters as $property => $value) { 44 | $property = static::convertToCamelCase($property); 45 | 46 | if ('inboundRules' === $property) { 47 | $this->inboundRules = \array_map(fn ($v) => new FirewallRuleInbound($v), $value); 48 | } elseif ('outboundRules' === $property) { 49 | $this->outboundRules = \array_map(fn ($v) => new FirewallRuleOutbound($v), $value); 50 | } elseif (\property_exists($this, $property)) { 51 | $this->$property = $value; 52 | } 53 | } 54 | } 55 | 56 | public function setCreatedAt(string $createdAt): void 57 | { 58 | $this->createdAt = static::convertToIso8601($createdAt); 59 | } 60 | 61 | public function toArray(): array 62 | { 63 | return [ 64 | 'name' => $this->name, 65 | 'inbound_rules' => \array_map(function ($rule): array { 66 | return $rule->toArray(); 67 | }, $this->inboundRules), 68 | 'outbound_rules' => \array_map(function ($rule): array { 69 | return $rule->toArray(); 70 | }, $this->outboundRules), 71 | 'droplet_ids' => $this->dropletIds, 72 | 'tags' => $this->tags, 73 | ]; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Entity/FirewallLocations.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Yassir Hannoun 19 | * @author Graham Campbell 20 | */ 21 | final class FirewallLocations extends AbstractEntity 22 | { 23 | public array $addresses; 24 | 25 | public array $dropletIds; 26 | 27 | public array $loadBalancerUids; 28 | 29 | public array $tags; 30 | } 31 | -------------------------------------------------------------------------------- /src/Entity/FirewallRule.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Yassir Hannoun 19 | * @author Graham Campbell 20 | */ 21 | abstract class FirewallRule extends AbstractEntity 22 | { 23 | public string $protocol; 24 | 25 | public string $ports; 26 | 27 | public function toArray(): array 28 | { 29 | $data = [ 30 | 'protocol' => $this->protocol, 31 | ]; 32 | 33 | if ('icmp' != $this->protocol) { 34 | $data['ports'] = ('0' === $this->ports) ? 'all' : $this->ports; 35 | } 36 | 37 | return $data; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Entity/FirewallRuleInbound.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Yassir Hannoun 19 | * @author Graham Campbell 20 | */ 21 | final class FirewallRuleInbound extends FirewallRule 22 | { 23 | public FirewallLocations $sources; 24 | 25 | public function build(array $parameters): void 26 | { 27 | foreach ($parameters as $property => $value) { 28 | $property = static::convertToCamelCase($property); 29 | 30 | if ('sources' === $property) { 31 | $this->sources = new FirewallLocations($value); 32 | } elseif (\property_exists($this, $property)) { 33 | $this->$property = $value; 34 | } 35 | } 36 | } 37 | 38 | public function toArray(): array 39 | { 40 | $data = parent::toArray(); 41 | $data['sources'] = $this->sources->toArray(); 42 | 43 | return $data; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Entity/FirewallRuleOutbound.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Yassir Hannoun 19 | * @author Graham Campbell 20 | */ 21 | final class FirewallRuleOutbound extends FirewallRule 22 | { 23 | public FirewallLocations $destinations; 24 | 25 | public function build(array $parameters): void 26 | { 27 | foreach ($parameters as $property => $value) { 28 | $property = static::convertToCamelCase($property); 29 | 30 | if ('destinations' === $property) { 31 | $this->destinations = new FirewallLocations($value); 32 | } elseif (\property_exists($this, $property)) { 33 | $this->$property = $value; 34 | } 35 | } 36 | } 37 | 38 | public function toArray(): array 39 | { 40 | $data = parent::toArray(); 41 | $data['destinations'] = $this->destinations->toArray(); 42 | 43 | return $data; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Entity/FloatingIp.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Graham Campbell 19 | */ 20 | final class FloatingIp extends AbstractEntity 21 | { 22 | public string $ip; 23 | 24 | public ?Droplet $droplet; 25 | 26 | public Region $region; 27 | 28 | public function build(array $parameters): void 29 | { 30 | foreach ($parameters as $property => $value) { 31 | $property = static::convertToCamelCase($property); 32 | 33 | if ('droplet' === $property) { 34 | $this->droplet = new Droplet($value); 35 | } elseif ('region' === $property) { 36 | $this->region = new Region($value); 37 | } elseif (\property_exists($this, $property)) { 38 | $this->$property = $value; 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Entity/ForwardingRule.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Jacob Holmes 19 | */ 20 | class ForwardingRule extends AbstractEntity 21 | { 22 | public string $entryProtocol; 23 | 24 | public int $entryPort; 25 | 26 | public string $targetProtocol; 27 | 28 | public int $targetPort; 29 | 30 | public ?string $certificateId; 31 | 32 | public ?bool $tlsPassthrough; 33 | 34 | /** 35 | * @return $this 36 | */ 37 | public function setStandardHttpRules(): self 38 | { 39 | $this->entryProtocol = 'http'; 40 | $this->targetProtocol = 'http'; 41 | $this->entryPort = 80; 42 | $this->targetPort = 80; 43 | 44 | return $this; 45 | } 46 | 47 | /** 48 | * @return $this 49 | */ 50 | public function setStandardHttpsRules(): self 51 | { 52 | $this->entryProtocol = 'https'; 53 | $this->targetProtocol = 'https'; 54 | $this->entryPort = 443; 55 | $this->targetPort = 443; 56 | $this->tlsPassthrough = true; 57 | 58 | return $this; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Entity/HealthCheck.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Jacob Holmes 19 | */ 20 | class HealthCheck extends AbstractEntity 21 | { 22 | public string $protocol; 23 | 24 | public int $port; 25 | 26 | public string $path; 27 | 28 | public int $checkIntervalSeconds; 29 | 30 | public int $responseTimeoutSeconds; 31 | 32 | public int $healthyThreshold; 33 | 34 | public int $unhealthyThreshold; 35 | } 36 | -------------------------------------------------------------------------------- /src/Entity/Image.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Yassir Hannoun 19 | * @author Graham Campbell 20 | */ 21 | final class Image extends AbstractEntity 22 | { 23 | public int $id; 24 | 25 | public string $name; 26 | 27 | public string $type; 28 | 29 | public string $distribution; 30 | 31 | public ?string $slug; 32 | 33 | public int $minDiskSize; 34 | 35 | public float $sizeGigabytes; 36 | 37 | public string $createdAt; 38 | 39 | public bool $public; 40 | 41 | /** 42 | * @var string[] 43 | */ 44 | public array $regions = []; 45 | 46 | public string $description; 47 | 48 | /** 49 | * @var string[] 50 | */ 51 | public array $tags = []; 52 | 53 | public string $status; 54 | 55 | public string $error_message; 56 | 57 | public function setCreatedAt(string $createdAt): void 58 | { 59 | $this->createdAt = static::convertToIso8601($createdAt); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Entity/Kernel.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Yassir Hannoun 19 | * @author Graham Campbell 20 | */ 21 | final class Kernel extends AbstractEntity 22 | { 23 | public int $id; 24 | 25 | public string $name; 26 | 27 | public string $version; 28 | } 29 | -------------------------------------------------------------------------------- /src/Entity/Key.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Antoine Kirk 19 | * @author Graham Campbell 20 | */ 21 | final class Key extends AbstractEntity 22 | { 23 | public int $id; 24 | 25 | public string $name; 26 | 27 | public string $fingerprint; 28 | 29 | public string $publicKey; 30 | } 31 | -------------------------------------------------------------------------------- /src/Entity/LoadBalancer.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Jacob Holmes 19 | */ 20 | final class LoadBalancer extends AbstractEntity 21 | { 22 | public string $id; 23 | 24 | public int $name; 25 | 26 | public string $ip; 27 | 28 | public string $algorithm; 29 | 30 | public string $status; 31 | 32 | public string $createdAt; 33 | 34 | /** 35 | * @var ForwardingRule[] 36 | */ 37 | public array $forwardingRules; 38 | 39 | public HealthCheck $healthCheck; 40 | 41 | public StickySession $stickySessions; 42 | 43 | public Region $region; 44 | 45 | public string $tag; 46 | 47 | public array $dropletIds; 48 | 49 | public bool $redirectHttpToHttps; 50 | 51 | /** 52 | * @var int<30, 600> 53 | */ 54 | public int $httpIdleTimeoutSeconds; 55 | 56 | public function build(array $parameters): void 57 | { 58 | foreach ($parameters as $property => $value) { 59 | $property = static::convertToCamelCase($property); 60 | 61 | if ('forwardingRules' === $property) { 62 | $this->forwardingRules = \array_map(fn ($v) => new ForwardingRule($v), $value); 63 | } elseif ('healthCheck' === $property) { 64 | $this->healthCheck = new HealthCheck($value); 65 | } elseif ('stickySessions' === $property) { 66 | $this->stickySessions = new StickySession($value); 67 | } elseif ('region' === $property) { 68 | $this->region = new Region($value); 69 | } elseif (\property_exists($this, $property)) { 70 | $this->$property = $value; 71 | } 72 | } 73 | } 74 | 75 | public function toArray(): array 76 | { 77 | return [ 78 | 'name' => $this->name, 79 | 'region' => $this->region->slug, 80 | 'algorithm' => $this->algorithm, 81 | 'forwarding_rules' => \array_map(function ($rule): array { 82 | return $rule->toArray(); 83 | }, $this->forwardingRules), 84 | 'health_check' => $this->healthCheck->toArray(), 85 | 'sticky_sessions' => $this->stickySessions->toArray(), 86 | 'droplet_ids' => $this->dropletIds, 87 | 'redirect_http_to_https' => $this->redirectHttpToHttps, 88 | 'http_idle_timeout_seconds' => $this->httpIdleTimeoutSeconds, 89 | ]; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Entity/Meta.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Antoine Kirk 19 | * @author Graham Campbell 20 | */ 21 | final class Meta extends AbstractEntity 22 | { 23 | public int $total; 24 | } 25 | -------------------------------------------------------------------------------- /src/Entity/MonitoringAlert.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Michael Shihjay Chen 19 | * @author Graham Campbell 20 | */ 21 | final class MonitoringAlert extends AbstractEntity 22 | { 23 | public array $alerts; 24 | 25 | public string $compare; 26 | 27 | public string $description; 28 | 29 | public bool $enabled; 30 | 31 | public array $entities; 32 | 33 | public array $tags; 34 | 35 | public string $type; 36 | 37 | public int $value; 38 | 39 | public string $window; 40 | } 41 | -------------------------------------------------------------------------------- /src/Entity/MonitoringMetric.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Michael Shihjay Chen 19 | * @author Graham Campbell 20 | */ 21 | final class MonitoringMetric extends AbstractEntity 22 | { 23 | public array $data; 24 | 25 | public array $status; 26 | } 27 | -------------------------------------------------------------------------------- /src/Entity/Network.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Yassir Hannoun 19 | * @author Graham Campbell 20 | */ 21 | final class Network extends AbstractEntity 22 | { 23 | public string $ipAddress; 24 | 25 | public string $gateway; 26 | 27 | public string $type; 28 | 29 | /** 30 | * IPv4 or IPv6. 31 | */ 32 | public int $version; 33 | 34 | /** 35 | * IPv6 specific. 36 | */ 37 | public ?string $cidr; 38 | 39 | /** 40 | * IPv4 specific. 41 | */ 42 | public ?string $netmask; 43 | } 44 | -------------------------------------------------------------------------------- /src/Entity/NextBackupWindow.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Antoine Kirk 19 | * @author Graham Campbell 20 | */ 21 | final class NextBackupWindow extends AbstractEntity 22 | { 23 | public string $start; 24 | 25 | public string $end; 26 | } 27 | -------------------------------------------------------------------------------- /src/Entity/ProjectResource.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Graham Campbell 19 | * @author Mohammad Salamat 20 | */ 21 | final class ProjectResource extends AbstractEntity 22 | { 23 | public string $urn; 24 | 25 | public string $assignedAt; 26 | 27 | public array $links; 28 | 29 | public string $status; 30 | } 31 | -------------------------------------------------------------------------------- /src/Entity/RateLimit.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Yassir Hannoun 19 | * @author Graham Campbell 20 | */ 21 | final class RateLimit extends AbstractEntity 22 | { 23 | public int $limit; 24 | 25 | public int $remaining; 26 | 27 | public int $reset; 28 | } 29 | -------------------------------------------------------------------------------- /src/Entity/Region.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Yassir Hannoun 19 | * @author Graham Campbell 20 | */ 21 | final class Region extends AbstractEntity 22 | { 23 | public string $slug; 24 | 25 | public string $name; 26 | 27 | public bool $available; 28 | 29 | /** 30 | * @var string[] 31 | */ 32 | public array $sizes = []; 33 | 34 | /** 35 | * @var string[] 36 | */ 37 | public array $features = []; 38 | } 39 | -------------------------------------------------------------------------------- /src/Entity/ReservedIp.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Graham Campbell 19 | * @author Manuel Christlieb 20 | */ 21 | final class ReservedIp extends AbstractEntity 22 | { 23 | public string $ip; 24 | 25 | public ?Droplet $droplet = null; 26 | 27 | public Region $region; 28 | 29 | public bool $locked; 30 | 31 | public string $projectId; 32 | 33 | public function build(array $parameters): void 34 | { 35 | foreach ($parameters as $property => $value) { 36 | $property = static::convertToCamelCase($property); 37 | 38 | if ('droplet' === $property && null !== $value) { 39 | $this->droplet = new Droplet($value); 40 | } elseif ('region' === $property) { 41 | $this->region = new Region($value); 42 | } elseif (\property_exists($this, $property)) { 43 | $this->$property = $value; 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Entity/Size.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Yassir Hannoun 19 | * @author Graham Campbell 20 | */ 21 | final class Size extends AbstractEntity 22 | { 23 | public string $slug; 24 | 25 | public bool $available; 26 | 27 | public int $memory; 28 | 29 | public int $vcpus; 30 | 31 | public int $disk; 32 | 33 | public int|float $transfer; 34 | 35 | public int|float $priceMonthly; 36 | 37 | public int|float $priceHourly; 38 | 39 | /** 40 | * @var string[] 41 | */ 42 | public array $regions = []; 43 | 44 | public string $description; 45 | } 46 | -------------------------------------------------------------------------------- /src/Entity/Snapshot.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Yassir Hannoun 19 | */ 20 | final class Snapshot extends AbstractEntity 21 | { 22 | public string $id; 23 | 24 | public string $name; 25 | 26 | public string $createdAt; 27 | 28 | public string $resourceId; 29 | 30 | public string $resourceType; 31 | 32 | public int $minDiskSize; 33 | 34 | public float $sizeGigabytes; 35 | 36 | /** 37 | * @var string[] 38 | */ 39 | public array $regions = []; 40 | 41 | public function setCreatedAt(string $createdAt): void 42 | { 43 | $this->createdAt = static::convertToIso8601($createdAt); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Entity/StickySession.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Jacob Holmes 19 | */ 20 | class StickySession extends AbstractEntity 21 | { 22 | public string $type; 23 | 24 | public string $cookieName; 25 | 26 | public string $cookieTtlSeconds; 27 | } 28 | -------------------------------------------------------------------------------- /src/Entity/Tag.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Nicolas Beauvais 19 | */ 20 | final class Tag extends AbstractEntity 21 | { 22 | public string $name; 23 | 24 | public array $resources; 25 | } 26 | -------------------------------------------------------------------------------- /src/Entity/Team.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Roy de Jong 19 | */ 20 | final class Team extends AbstractEntity 21 | { 22 | public string $uuid; 23 | 24 | public string $name; 25 | } 26 | -------------------------------------------------------------------------------- /src/Entity/Volume.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Yassir Hannoun 19 | */ 20 | final class Volume extends AbstractEntity 21 | { 22 | public string $id; 23 | 24 | public Region $region; 25 | 26 | /** 27 | * @var int[] 28 | */ 29 | public array $dropletIds = []; 30 | 31 | public string $name; 32 | 33 | public string $description; 34 | 35 | public int $sizeGigabytes; 36 | 37 | public string $createdAt; 38 | 39 | public string $filesystemType; 40 | 41 | public string $filesystemLabel; 42 | 43 | /** 44 | * @var Tag[] 45 | */ 46 | public array $tags = []; 47 | 48 | public function build(array $parameters): void 49 | { 50 | foreach ($parameters as $property => $value) { 51 | $property = static::convertToCamelCase($property); 52 | 53 | if ('region' === $property) { 54 | $this->region = new Region($value); 55 | } elseif (\property_exists($this, $property)) { 56 | $this->$property = $value; 57 | } 58 | } 59 | } 60 | 61 | public function setCreatedAt(string $createdAt): void 62 | { 63 | $this->createdAt = static::convertToIso8601($createdAt); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Entity/Vpc.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Entity; 16 | 17 | /** 18 | * @author Manuel Christlieb 19 | */ 20 | final class Vpc extends AbstractEntity 21 | { 22 | public string $name; 23 | 24 | public string $description; 25 | 26 | public string $region; 27 | 28 | public string $ipRange; 29 | 30 | public string $id; 31 | 32 | public string $urn; 33 | 34 | public bool $default; 35 | 36 | public string $createdAt; 37 | } 38 | -------------------------------------------------------------------------------- /src/Exception/ApiLimitExceededException.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Exception; 16 | 17 | /** 18 | * @author Graham Campbell 19 | */ 20 | class ApiLimitExceededException extends RuntimeException 21 | { 22 | } 23 | -------------------------------------------------------------------------------- /src/Exception/DiscoveryFailedException.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Exception; 16 | 17 | /** 18 | * @author Graham Campbell 19 | */ 20 | class DiscoveryFailedException extends \Exception implements ExceptionInterface 21 | { 22 | } 23 | -------------------------------------------------------------------------------- /src/Exception/ErrorException.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Exception; 16 | 17 | /** 18 | * @author Graham Campbell 19 | */ 20 | class ErrorException extends \ErrorException implements ExceptionInterface 21 | { 22 | } 23 | -------------------------------------------------------------------------------- /src/Exception/ExceptionInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Exception; 16 | 17 | /** 18 | * @author Graham Campbell 19 | */ 20 | interface ExceptionInterface extends \Throwable 21 | { 22 | } 23 | -------------------------------------------------------------------------------- /src/Exception/InvalidArgumentException.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Exception; 16 | 17 | /** 18 | * @author Graham Campbell 19 | */ 20 | class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface 21 | { 22 | } 23 | -------------------------------------------------------------------------------- /src/Exception/InvalidRecordException.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Exception; 16 | 17 | /** 18 | * @author Graham Campbell 19 | */ 20 | class InvalidRecordException extends InvalidArgumentException 21 | { 22 | } 23 | -------------------------------------------------------------------------------- /src/Exception/ResourceNotFoundException.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Exception; 16 | 17 | /** 18 | * @author Graham Campbell 19 | */ 20 | class ResourceNotFoundException extends RuntimeException 21 | { 22 | } 23 | -------------------------------------------------------------------------------- /src/Exception/RuntimeException.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Exception; 16 | 17 | /** 18 | * @author Graham Campbell 19 | */ 20 | class RuntimeException extends \RuntimeException implements ExceptionInterface 21 | { 22 | } 23 | -------------------------------------------------------------------------------- /src/Exception/ValidationFailedException.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\Exception; 16 | 17 | /** 18 | * @author Graham Campbell 19 | */ 20 | class ValidationFailedException extends ErrorException 21 | { 22 | } 23 | -------------------------------------------------------------------------------- /src/HttpClient/Builder.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\HttpClient; 16 | 17 | use Http\Client\Common\HttpMethodsClient; 18 | use Http\Client\Common\HttpMethodsClientInterface; 19 | use Http\Client\Common\Plugin; 20 | use Http\Client\Common\PluginClientFactory; 21 | use Http\Discovery\Psr17FactoryDiscovery; 22 | use Http\Discovery\Psr18ClientDiscovery; 23 | use Psr\Http\Client\ClientInterface; 24 | use Psr\Http\Message\RequestFactoryInterface; 25 | use Psr\Http\Message\StreamFactoryInterface; 26 | use Psr\Http\Message\UriFactoryInterface; 27 | 28 | /** 29 | * The HTTP client builder class. 30 | * 31 | * This will allow you to fluently add and remove plugins. 32 | * 33 | * @author Tobias Nyholm 34 | * @author Graham Campbell 35 | */ 36 | final class Builder 37 | { 38 | private readonly ClientInterface $httpClient; 39 | private readonly RequestFactoryInterface $requestFactory; 40 | private readonly StreamFactoryInterface $streamFactory; 41 | private readonly UriFactoryInterface $uriFactory; 42 | 43 | /** 44 | * @var Plugin[] 45 | */ 46 | private array $plugins; 47 | 48 | private ?HttpMethodsClientInterface $pluginClient; 49 | 50 | public function __construct( 51 | ?ClientInterface $httpClient = null, 52 | ?RequestFactoryInterface $requestFactory = null, 53 | ?StreamFactoryInterface $streamFactory = null, 54 | ?UriFactoryInterface $uriFactory = null 55 | ) { 56 | $this->httpClient = $httpClient ?? Psr18ClientDiscovery::find(); 57 | $this->requestFactory = $requestFactory ?? Psr17FactoryDiscovery::findRequestFactory(); 58 | $this->streamFactory = $streamFactory ?? Psr17FactoryDiscovery::findStreamFactory(); 59 | $this->uriFactory = $uriFactory ?? Psr17FactoryDiscovery::findUriFactory(); 60 | 61 | $this->plugins = []; 62 | $this->pluginClient = null; 63 | } 64 | 65 | public function getHttpClient(): HttpMethodsClientInterface 66 | { 67 | if (null === $this->pluginClient) { 68 | $plugins = $this->plugins; 69 | 70 | $this->pluginClient = new HttpMethodsClient( 71 | (new PluginClientFactory())->createClient($this->httpClient, $plugins), 72 | $this->requestFactory, 73 | $this->streamFactory 74 | ); 75 | } 76 | 77 | return $this->pluginClient; 78 | } 79 | 80 | /** 81 | * Get the request factory. 82 | */ 83 | public function getRequestFactory(): RequestFactoryInterface 84 | { 85 | return $this->requestFactory; 86 | } 87 | 88 | /** 89 | * Get the stream factory. 90 | */ 91 | public function getStreamFactory(): StreamFactoryInterface 92 | { 93 | return $this->streamFactory; 94 | } 95 | 96 | /** 97 | * Get the URI factory. 98 | */ 99 | public function getUriFactory(): UriFactoryInterface 100 | { 101 | return $this->uriFactory; 102 | } 103 | 104 | /** 105 | * Add a new plugin to the end of the plugin chain. 106 | */ 107 | public function addPlugin(Plugin $plugin): void 108 | { 109 | $this->plugins[] = $plugin; 110 | $this->pluginClient = null; 111 | } 112 | 113 | /** 114 | * Remove a plugin by its fully qualified class name (FQCN). 115 | */ 116 | public function removePlugin(string $fqcn): void 117 | { 118 | foreach ($this->plugins as $idx => $plugin) { 119 | if ($plugin instanceof $fqcn) { 120 | unset($this->plugins[$idx]); 121 | $this->pluginClient = null; 122 | } 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/HttpClient/Message/ResponseMediator.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\HttpClient\Message; 16 | 17 | use DigitalOceanV2\Exception\RuntimeException; 18 | use DigitalOceanV2\HttpClient\Util\JsonObject; 19 | use Psr\Http\Message\ResponseInterface; 20 | use stdClass; 21 | 22 | /** 23 | * This is the response mediator class. 24 | * 25 | * @author Graham Campbell 26 | */ 27 | final class ResponseMediator 28 | { 29 | /** 30 | * The content type header. 31 | * 32 | * @var string 33 | */ 34 | public const CONTENT_TYPE_HEADER = 'Content-Type'; 35 | 36 | /** 37 | * The JSON content type identifier. 38 | * 39 | * @var string 40 | */ 41 | public const JSON_CONTENT_TYPE = 'application/json'; 42 | 43 | /** 44 | * @throws RuntimeException 45 | */ 46 | public static function getContent(ResponseInterface $response): stdClass 47 | { 48 | if (204 === $response->getStatusCode()) { 49 | return JsonObject::empty(); 50 | } 51 | 52 | $body = (string) $response->getBody(); 53 | 54 | if ('' === $body) { 55 | return JsonObject::empty(); 56 | } 57 | 58 | if (0 !== \strpos(self::getHeader($response, self::CONTENT_TYPE_HEADER) ?? '', self::JSON_CONTENT_TYPE)) { 59 | throw new RuntimeException(\sprintf('The content type was not %s.', self::JSON_CONTENT_TYPE)); 60 | } 61 | 62 | return JsonObject::decode($body); 63 | } 64 | 65 | /** 66 | * Get the error message from the response if present. 67 | */ 68 | public static function getErrorMessage(ResponseInterface $response): ?string 69 | { 70 | try { 71 | $content = self::getContent($response); 72 | } catch (RuntimeException $e) { 73 | return null; 74 | } 75 | 76 | return isset($content->message) && \is_string($content->message) ? $content->message : null; 77 | } 78 | 79 | /** 80 | * Get the pagination data from the response. 81 | * 82 | * @return array 83 | */ 84 | public static function getPagination(ResponseInterface $response): array 85 | { 86 | try { 87 | $content = self::getContent($response); 88 | } catch (RuntimeException $e) { 89 | return []; 90 | } 91 | 92 | if (!isset($content->links->pages) || !\is_object($content->links->pages)) { 93 | return []; 94 | } 95 | 96 | /** array */ 97 | return \array_filter(\get_object_vars($content->links->pages)); 98 | } 99 | 100 | /** 101 | * Get the rate limit data from the response. 102 | * 103 | * @return array 104 | */ 105 | public static function getRateLimit(ResponseInterface $response): array 106 | { 107 | $reset = self::getHeader($response, 'RateLimit-Reset'); 108 | $remaining = self::getHeader($response, 'RateLimit-Remaining'); 109 | $limit = self::getHeader($response, 'RateLimit-Limit'); 110 | 111 | if (null === $reset || null === $remaining || null === $limit) { 112 | return []; 113 | } 114 | 115 | return [ 116 | 'reset' => (int) $reset, 117 | 'remaining' => (int) $remaining, 118 | 'limit' => (int) $limit, 119 | ]; 120 | } 121 | 122 | private static function getHeader(ResponseInterface $response, string $name): ?string 123 | { 124 | $headers = $response->getHeader($name); 125 | 126 | return \array_shift($headers); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/HttpClient/Plugin/Authentication.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\HttpClient\Plugin; 16 | 17 | use Http\Client\Common\Plugin; 18 | use Http\Promise\Promise; 19 | use Psr\Http\Message\RequestInterface; 20 | use Psr\Http\Message\ResponseInterface; 21 | 22 | /** 23 | * A plugin to add authentication to the request. 24 | * 25 | * @internal 26 | * 27 | * @author Tobias Nyholm 28 | * @author Graham Campbell 29 | */ 30 | final class Authentication implements Plugin 31 | { 32 | /** 33 | * The authorization header. 34 | */ 35 | private readonly string $header; 36 | 37 | public function __construct(string $token) 38 | { 39 | $this->header = \sprintf('Bearer %s', $token); 40 | } 41 | 42 | /** 43 | * Handle the request and return the response coming from the next callable. 44 | * 45 | * @param callable(RequestInterface): Promise $next 46 | * @param callable(RequestInterface): Promise $first 47 | * 48 | * @return \Http\Promise\Promise 49 | */ 50 | public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise 51 | { 52 | $request = $request->withHeader('Authorization', $this->header); 53 | 54 | return $next($request); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/HttpClient/Plugin/ExceptionThrower.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\HttpClient\Plugin; 16 | 17 | use DigitalOceanV2\Exception\ApiLimitExceededException; 18 | use DigitalOceanV2\Exception\ErrorException; 19 | use DigitalOceanV2\Exception\ExceptionInterface; 20 | use DigitalOceanV2\Exception\ResourceNotFoundException; 21 | use DigitalOceanV2\Exception\RuntimeException; 22 | use DigitalOceanV2\Exception\ValidationFailedException; 23 | use DigitalOceanV2\HttpClient\Message\ResponseMediator; 24 | use Http\Client\Common\Plugin; 25 | use Http\Promise\Promise; 26 | use Psr\Http\Message\RequestInterface; 27 | use Psr\Http\Message\ResponseInterface; 28 | 29 | /** 30 | * A plugin to throw bitbucket exceptions. 31 | * 32 | * @internal 33 | * 34 | * @author Tobias Nyholm 35 | * @author Fabien Bourigault 36 | * @author Graham Campbell 37 | */ 38 | final class ExceptionThrower implements Plugin 39 | { 40 | /** 41 | * Handle the request and return the response coming from the next callable. 42 | * 43 | * @param callable(RequestInterface): Promise $next 44 | * @param callable(RequestInterface): Promise $first 45 | * 46 | * @return \Http\Promise\Promise 47 | */ 48 | public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise 49 | { 50 | return $next($request)->then(function (ResponseInterface $response): ResponseInterface { 51 | $status = $response->getStatusCode(); 52 | 53 | if ($status >= 400 && $status < 600) { 54 | throw self::createException($status, ResponseMediator::getErrorMessage($response) ?? $response->getReasonPhrase()); 55 | } 56 | 57 | return $response; 58 | }); 59 | } 60 | 61 | /** 62 | * Create an exception from a status code and error message. 63 | * 64 | * @return ErrorException|RuntimeException 65 | */ 66 | private static function createException(int $status, string $message): ExceptionInterface 67 | { 68 | if (400 === $status || 422 === $status) { 69 | return new ValidationFailedException($message, $status); 70 | } 71 | 72 | if (429 === $status) { 73 | return new ApiLimitExceededException($message, $status); 74 | } 75 | 76 | if (404 === $status) { 77 | return new ResourceNotFoundException($message, $status); 78 | } 79 | 80 | return new RuntimeException($message, $status); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/HttpClient/Plugin/History.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\HttpClient\Plugin; 16 | 17 | use Http\Client\Common\Plugin\Journal; 18 | use Psr\Http\Client\ClientExceptionInterface; 19 | use Psr\Http\Message\RequestInterface; 20 | use Psr\Http\Message\ResponseInterface; 21 | 22 | /** 23 | * A plugin to remember the last response. 24 | * 25 | * @internal 26 | * 27 | * @author Tobias Nyholm 28 | * @author Graham Campbell 29 | */ 30 | final class History implements Journal 31 | { 32 | private ?ResponseInterface $lastResponse; 33 | 34 | public function __construct() 35 | { 36 | $this->lastResponse = null; 37 | } 38 | 39 | /** 40 | * Get the last response. 41 | */ 42 | public function getLastResponse(): ?ResponseInterface 43 | { 44 | return $this->lastResponse; 45 | } 46 | 47 | /** 48 | * Record a successful call. 49 | */ 50 | public function addSuccess(RequestInterface $request, ResponseInterface $response): void 51 | { 52 | $this->lastResponse = $response; 53 | } 54 | 55 | /** 56 | * Record a failed call. 57 | */ 58 | public function addFailure(RequestInterface $request, ClientExceptionInterface $exception): void 59 | { 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/HttpClient/Util/JsonObject.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\HttpClient\Util; 16 | 17 | use DigitalOceanV2\Exception\RuntimeException; 18 | use stdClass; 19 | 20 | /** 21 | * The is the JSON object helper class. 22 | * 23 | * @internal 24 | * 25 | * @author Graham Campbell 26 | */ 27 | final class JsonObject 28 | { 29 | /** 30 | * Create an empty PHP object. 31 | */ 32 | public static function empty(): stdClass 33 | { 34 | return new stdClass(); 35 | } 36 | 37 | /** 38 | * Decode a JSON string into a PHP object. 39 | * 40 | * @throws RuntimeException 41 | */ 42 | public static function decode(string $json): stdClass 43 | { 44 | /** @var scalar|array|stdClass|null */ 45 | $data = \json_decode($json); 46 | 47 | if (\JSON_ERROR_NONE !== \json_last_error()) { 48 | throw new RuntimeException(\sprintf('json_decode error: %s', \json_last_error_msg())); 49 | } 50 | 51 | if (!$data instanceof stdClass) { 52 | throw new RuntimeException(\sprintf('json_decode error: Expected JSON of type object, %s given.', \get_debug_type($data))); 53 | } 54 | 55 | return $data; 56 | } 57 | 58 | /** 59 | * Encode a PHP array into a JSON string. 60 | * 61 | * @throws RuntimeException 62 | */ 63 | public static function encode(array $value): string 64 | { 65 | $json = \json_encode($value); 66 | 67 | if (\JSON_ERROR_NONE !== \json_last_error()) { 68 | throw new RuntimeException(\sprintf('json_encode error: %s', \json_last_error_msg())); 69 | } 70 | 71 | /** @var string */ 72 | return $json; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/HttpClient/Util/QueryStringBuilder.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2\HttpClient\Util; 16 | 17 | /** 18 | * The is the URI builder helper class. 19 | * 20 | * @internal 21 | * 22 | * @author Graham Campbell 23 | */ 24 | final class QueryStringBuilder 25 | { 26 | /** 27 | * Encode a query as a query string according to RFC 3986. 28 | */ 29 | public static function build(array $query): string 30 | { 31 | if (0 === \count($query)) { 32 | return ''; 33 | } 34 | 35 | return \sprintf('?%s', \http_build_query($query, '', '&', \PHP_QUERY_RFC3986)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/ResultPager.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2; 16 | 17 | use Closure; 18 | use DigitalOceanV2\Api\AbstractApi; 19 | use DigitalOceanV2\Exception\ExceptionInterface; 20 | use DigitalOceanV2\Exception\RuntimeException; 21 | use DigitalOceanV2\HttpClient\Message\ResponseMediator; 22 | use Generator; 23 | use ValueError; 24 | 25 | final class ResultPager implements ResultPagerInterface 26 | { 27 | /** 28 | * The default number of entries to request per page. 29 | * 30 | * @var int 31 | */ 32 | private const PER_PAGE = 100; 33 | 34 | private readonly Client $client; 35 | 36 | private readonly int $perPage; 37 | 38 | /** 39 | * @var array 40 | */ 41 | private array $pagination; 42 | 43 | public function __construct(Client $client, ?int $perPage = null) 44 | { 45 | if (null !== $perPage && ($perPage < 1 || $perPage > 200)) { 46 | throw new ValueError(\sprintf('%s::__construct(): Argument #2 ($perPage) must be between 1 and 200, or null', self::class)); 47 | } 48 | 49 | $this->client = $client; 50 | $this->perPage = $perPage ?? self::PER_PAGE; 51 | $this->pagination = []; 52 | } 53 | 54 | /** 55 | * Fetch a single result from an api call. 56 | * 57 | * @throws ExceptionInterface 58 | */ 59 | public function fetch(AbstractApi $api, string $method, array $parameters = []): array 60 | { 61 | $result = self::bindPerPage($api, $this->perPage)->$method(...$parameters); 62 | 63 | if (!\is_array($result)) { 64 | throw new RuntimeException('Pagination of this endpoint is not supported.'); 65 | } 66 | 67 | $this->postFetch(); 68 | 69 | return $result; 70 | } 71 | 72 | /** 73 | * Fetch all results from an api call. 74 | * 75 | * @throws ExceptionInterface 76 | */ 77 | public function fetchAll(AbstractApi $api, string $method, array $parameters = []): array 78 | { 79 | return \iterator_to_array($this->fetchAllLazy($api, $method, $parameters)); 80 | } 81 | 82 | /** 83 | * Lazily fetch all results from an api call. 84 | * 85 | * @throws ExceptionInterface 86 | */ 87 | public function fetchAllLazy(AbstractApi $api, string $method, array $parameters = []): Generator 88 | { 89 | $currentPage = 1; 90 | 91 | foreach ($this->fetch(self::bindPage($api, $currentPage), $method, $parameters) as $entry) { 92 | yield $entry; 93 | } 94 | 95 | while ($this->hasNext()) { 96 | foreach ($this->fetch(self::bindPage($api, ++$currentPage), $method, $parameters) as $entry) { 97 | yield $entry; 98 | } 99 | } 100 | } 101 | 102 | /** 103 | * Check to determine the availability of a next page. 104 | */ 105 | public function hasNext(): bool 106 | { 107 | return isset($this->pagination['next']); 108 | } 109 | 110 | /** 111 | * Refresh the pagination property. 112 | */ 113 | private function postFetch(): void 114 | { 115 | $response = $this->client->getLastResponse(); 116 | 117 | if (null === $response) { 118 | $this->pagination = []; 119 | } else { 120 | $this->pagination = ResponseMediator::getPagination($response); 121 | } 122 | } 123 | 124 | private static function bindPage(AbstractApi $api, int $page): AbstractApi 125 | { 126 | /** @var Closure(AbstractApi): AbstractApi */ 127 | $closure = Closure::bind(static function (AbstractApi $api) use ($page): AbstractApi { 128 | $clone = clone $api; 129 | 130 | $clone->page = $page; 131 | 132 | return $clone; 133 | }, null, AbstractApi::class); 134 | 135 | return $closure($api); 136 | } 137 | 138 | private static function bindPerPage(AbstractApi $api, int $perPage): AbstractApi 139 | { 140 | /** @var Closure(AbstractApi): AbstractApi */ 141 | $closure = Closure::bind(static function (AbstractApi $api) use ($perPage): AbstractApi { 142 | $clone = clone $api; 143 | 144 | $clone->perPage = $perPage; 145 | 146 | return $clone; 147 | }, null, AbstractApi::class); 148 | 149 | return $closure($api); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/ResultPagerInterface.php: -------------------------------------------------------------------------------- 1 | 9 | * (c) Graham Campbell 10 | * 11 | * For the full copyright and license information, please view the LICENSE 12 | * file that was distributed with this source code. 13 | */ 14 | 15 | namespace DigitalOceanV2; 16 | 17 | use DigitalOceanV2\Api\AbstractApi; 18 | use DigitalOceanV2\Exception\ExceptionInterface; 19 | use Generator; 20 | 21 | interface ResultPagerInterface 22 | { 23 | /** 24 | * Fetch a single result from an api call. 25 | * 26 | * @throws ExceptionInterface 27 | */ 28 | public function fetch(AbstractApi $api, string $method, array $parameters = []): array; 29 | 30 | /** 31 | * Fetch all results from an api call. 32 | * 33 | * @throws ExceptionInterface 34 | */ 35 | public function fetchAll(AbstractApi $api, string $method, array $parameters = []): array; 36 | 37 | /** 38 | * Lazily fetch all results from an api call. 39 | * 40 | * @throws ExceptionInterface 41 | */ 42 | public function fetchAllLazy(AbstractApi $api, string $method, array $parameters = []): Generator; 43 | 44 | /** 45 | * Check to determine the availability of a next page. 46 | */ 47 | public function hasNext(): bool; 48 | } 49 | --------------------------------------------------------------------------------