hostfog / hostfog-php
Official PHP client for the HostFog Panel REST API: manage sites, databases, SSL, DNS, backups and server stats.
Requires
- php: >=8.1
- ext-curl: *
- ext-json: *
Requires (Dev)
- phpunit/phpunit: ^10.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
HostFog API client for PHP
The official PHP SDK for the HostFog Panel REST API.
Manage sites, databases, SSL certificates, DNS, backups and server stats from your own code. No dependencies.
Install
composer require hostfog/hostfog-php
Quick start
<?php require 'vendor/autoload.php'; use HostFog\Client; $client = new Client(token: getenv('HOSTFOG_TOKEN'), host: 'panel.example.com'); $site = $client->sites->create(['domain' => 'example.com', 'phpVersion' => '8.3', 'ssl' => true]); $client->ssl->issue('example.com'); foreach ($client->sites->list() as $site) { echo "{$site['domain']} is {$site['status']}\n"; }
Contents
Connecting
use HostFog\Client; // Builds https://panel.example.com:2087/api/v1 $client = new Client(token: 'hf_...', host: 'panel.example.com'); // Or give the full base URL (takes precedence over host) $client = new Client(token: 'hf_...', baseUrl: 'https://panel.example.com/api/v1'); // All options $client = new Client( token: 'hf_...', host: '203.0.113.10', timeout: 60.0, // seconds, default 30 verifySsl: false, // default true transport: null, // custom transport, see "Testing your code" );
Every request sends Authorization: Bearer {token}, Accept: application/json and User-Agent: hostfog-php/0.1.0, plus Content-Type: application/json when there's a body.
A freshly installed panel uses a self-signed certificate until you attach a hostname. Set verifySsl: false for that first connection only, then switch it back on.
Successful calls return the data from the response as a PHP array. Calls that get 204 No Content return null. Path segments such as domains and IDs are URL-encoded for you.
Sites
Site: {domain, status: "active"|"suspended", php_version, ssl, created_at}
// GET /sites $sites = $client->sites->list(); // GET /sites/{domain} $site = $client->sites->get('example.com'); // ['domain' => 'example.com', 'status' => 'active', 'php_version' => '8.3', 'ssl' => true, 'created_at' => '2026-10-16T09:00:00Z'] // POST /sites (sent as {"domain", "php_version", "ssl"}; unset keys are left out) $site = $client->sites->create([ 'domain' => 'example.com', 'phpVersion' => '8.3', // optional, 'php_version' also works 'ssl' => true, // optional ]); // DELETE /sites/{domain} (returns null) $client->sites->delete('example.com'); // POST /sites/{domain}/suspend $client->sites->suspend('example.com'); // POST /sites/{domain}/unsuspend $client->sites->unsuspend('example.com');
Databases
Database: {name, user, size_bytes}
// GET /databases $databases = $client->databases->list(); // POST /databases (the panel generates a password if you leave it out) $db = $client->databases->create([ 'name' => 'shop', 'user' => 'shop_user', 'password' => 'a-strong-password', // optional ]); // DELETE /databases/{name} $client->databases->delete('shop');
SSL
SslStatus: {domain, issued, issuer, valid_to, auto_renew}
// POST /ssl/{domain} (issue or reissue a Let's Encrypt certificate) $status = $client->ssl->issue('example.com'); // GET /ssl/{domain} $status = $client->ssl->status('example.com'); // ['domain' => 'example.com', 'issued' => true, 'issuer' => "Let's Encrypt", 'valid_to' => '2027-01-14T00:00:00Z', 'auto_renew' => true]
DNS
DnsRecord: {id, type, name, value, ttl, priority}
// GET /dns/{domain}/records $records = $client->dns->records('example.com'); // POST /dns/{domain}/records (ttl and priority are optional) $record = $client->dns->addRecord('example.com', [ 'type' => 'MX', 'name' => '@', 'value' => 'mail.example.com', 'ttl' => 3600, 'priority' => 10, ]); $client->dns->addRecord('example.com', ['type' => 'A', 'name' => 'www', 'value' => '203.0.113.10']); // DELETE /dns/{domain}/records/{id} $client->dns->deleteRecord('example.com', $record['id']);
Backups
Backup: {id, domain|null, size_bytes, created_at, status}
// GET /backups $backups = $client->backups->list(); // POST /backups (whole server) $backup = $client->backups->create(); // POST /backups (one site) $backup = $client->backups->create(['domain' => 'example.com']); // POST /backups/{id}/restore $client->backups->restore($backup['id']);
Server
// GET /server/info $info = $client->server->info(); // ['hostname' => 'srv1', 'os' => 'Ubuntu 24.04', 'panel_version' => '1.0.0', 'ip' => '203.0.113.10'] // GET /server/stats $stats = $client->server->stats(); // ['cpu_percent' => 12.5, 'memory_used' => ..., 'memory_total' => ..., 'disk_used' => ..., 'disk_total' => ..., 'load' => [0.1, 0.2, 0.3]]
Error handling
| Exception | When |
|---|---|
HostFog\Exception\HostFogException |
Base class of everything below. Also thrown for a missing token or host. |
HostFog\Exception\ApiException |
The panel answered with a non-2xx status. |
HostFog\Exception\ConnectionException |
The panel couldn't be reached: DNS, refused connection, timeout or TLS failure. |
ApiException exposes getStatus() (HTTP status), getErrorCode() (for example site_exists) and getMessage(). If the error body isn't valid JSON, the code is http_{status}, for example http_502.
use HostFog\Exception\ApiException; use HostFog\Exception\ConnectionException; use HostFog\Exception\HostFogException; try { $client->sites->create(['domain' => 'example.com']); } catch (ApiException $e) { if ($e->getErrorCode() === 'site_exists') { // already there, carry on } else { echo "API error {$e->getStatus()}: {$e->getMessage()}\n"; } } catch (ConnectionException $e) { echo "Panel unreachable: {$e->getMessage()}\n"; } catch (HostFogException $e) { // anything else from the SDK }
Testing your code
Pass a transport callable to run without a network. It receives (string $method, string $url, array $headers, ?string $body) and returns ['status' => int, 'body' => string]. Throw any exception to simulate a network failure; it's rethrown as ConnectionException.
$client = new Client( token: 'test', host: 'panel.test', transport: function (string $method, string $url, array $headers, ?string $body): array { return ['status' => 200, 'body' => '{"data":[{"domain":"example.com","status":"active"}]}']; }, ); $client->sites->list(); // [['domain' => 'example.com', 'status' => 'active']]
API specification
openapi.yaml is the OpenAPI 3.1 contract for the HostFog Panel API v1. It's shared by the PHP, JavaScript and Python SDKs.
Requirements
- PHP 8.1 or newer
- ext-curl and ext-json
There are no Composer runtime dependencies.
Development
composer install composer test # or: vendor/bin/phpunit
The tests run fully offline. Every endpoint is checked through a recording fake transport, and the default cURL transport is tested against local HTTP and HTTPS servers on 127.0.0.1.
Publishing
This package has no publish workflow. Publishing to Packagist is a one-time step: submit https://github.com/hostfog/hostfog-php at packagist.org/packages/submit. After that, the Packagist GitHub hook picks up every new tag automatically, so releasing is just:
git tag v0.1.0 && git push origin v0.1.0
Other languages
| Language | API client | Toolkit |
|---|---|---|
| PHP | hostfog/hostfog-php (you are here) | hostfog/toolkit-php |
| JavaScript | hostfog/hostfog-js | hostfog/toolkit-js |
| Python | hostfog/hostfog-python | hostfog/toolkit-python |
Licence
MIT. Copyright (c) 2026 HostFog. See LICENSE.