AbstractClient
abstract class AbstractClient (View source)
Shared foundation for all registrar API clients.
Concrete subclasses provide the request() implementation, the default logger, and the appropriate SocketConfig subtype.
Where configuration lives
Not here. Connection configuration has one home — {\CNIC\AbstractSocketConfig} —
reachable through {\CNIC\getSocketConfig()}. What lives here is client
behaviour: the logger and debug flag, the response context, the transport
instance, and the SDK's own identity (VERSION/$userAgent, versioned with this class
and released from it). Do not add a copy of a config-owned value; guarded by
tests/ClientConfigSeamTest.php.
The configuration methods below are forwarders, and deliberately kept: they are
the documented ergonomic surface ($cl->useOTESystem()->setCredentials(...))
and they read and write the config's state rather than a copy of it, so a
forwarder cannot disagree with the config. A new setting needs no forwarder —
getSocketConfig() is the accessor whose absence let ~26 of these accumulate.
Only capabilities every brand can actually honour live here. In particular
getSession()/setSession() do not — API sessions are a CNR concept and
live on {\CNIC\CNR\Client} beside the state they read. Do not hoist them,
or role credentials ({\CNIC\RoleCredentialsInterface}), back up: a
capability a brand cannot honour belongs off the shared surface, not on it
returning a constant. Full decision record: docs/agents/architecture.md.
Constants
| private VERSION |
Current module version. Kept in sync automatically by semantic-release — see .releaserc.json. |
Properties
| protected array<string, mixed> | $context | context data for the client |
|
| protected AbstractSocketConfig | $socketConfig | Object covering API connection data — the one home for connection configuration; see {getSocketConfig()}. |
|
| protected bool | $debugMode | activity flag for debug mode |
|
| protected string | $userAgent | User agent sent with every request. Empty until {setUserAgent()} is called; while it is empty {getUserAgent()} derives the SDK default. |
|
| protected LoggerInterface | $logger | logger instance for debug mode |
|
| protected TransportInterface | $transport | HTTP transport layer |
Methods
Constructor
The connection configuration this client uses — the accessor that means a new setting needs no forwarder, and the seam that lets configuration be built and asserted without constructing a client.
Perform API request using the given command.
Shared request lifecycle (template method). Never reimplement it in a brand; vary it through exactly two hooks — {buildCommand()} (command flattening) and {newResponse()} (covariant Response factory) — plus the {newSocketConfig()} subtype, which is where a brand-mandatory cURL option would go. No brand declares one.
Flatten and normalise the given command into wire form.
Instantiate the brand Response for the given raw payload.
Instantiate the SocketConfig for this client.
Instantiate the HTTP transport for this client. Mirrors {newSocketConfig()} — the default is the production cURL transport; override or {setTransport()} to inject a test double so the request() lifecycle can run offline.
Inject a custom HTTP transport (e.g. a record/replay cassette transport for offline tests) in place of the default {HttpTransport}.
Instantiate the brand's logger, writing to the given sink. Mirrors
{newTransport()}/{@see newSocketConfig()}, and exists so a sink can be
chosen without the brand's Logger class being named at the call site. An
override must honour $sink — that is what makes {setLogSink()} work
for a subclass.
Route debug output somewhere other than standard output, keeping this brand's format.
Set custom logger to use instead of the default one — use this to replace the format as well as the destination. Create your own class extending \CNIC\AbstractLogger (format only) or implementing \CNIC\LoggerInterface (format and destination).
Enable debug output to STDOUT
Disable debug output
Serialize given command for POST request including connection configuration data
Get the API connection url that is currently set
Set the request timeout in seconds (default 300).
Get the request timeout in seconds currently configured.
Set a custom user agent (for platforms that use this SDK)
Get the user agent string — the one set via {setUserAgent()}, or the SDK default when none was.
Merge additional cURL options into the bag, overriding existing values on key collision. Forwards to {AbstractSocketConfig::setExtraCurlOptions()}, whose docblock carries the detail: what reaches the wire, and the two sets of keys that are refused — the SDK-managed settings ({AbstractSocketConfig::MANAGED_OPTIONS}, rejected here and now) and the transport's own ({HttpTransport::PROTECTED_OPTIONS}, rejected on the next request).
Restore the cURL option bag to the brand defaults, discarding anything previously handed to {setExtraCurlOptions()}. Options only — the proxy and referer are separate state and survive; see {AbstractSocketConfig::resetCurlOptions()}.
Set proxy to use for API communication
Get proxy configuration for API communication
Set Referer to use for API communication
Get Referer configuration for API communication
Get the current module version
Set another connection url to be used for API communication
Set Credentials to be used for API communication.
Activate High Performance Setup — route requests through the co-located proxy on loopback.
Convert domain names to idn + punycode.
Delegate cURL execution to the transport layer.
Close all cURL connections
Get LIVE system URL.
Get the API system in use, or null when the configured URL is neither of the brand's two known endpoints.
Check whether the OT&E system is in use
Set OT&E System for API communication
Set LIVE System for API communication (this is the default setting)
Set context data for the client
Details
__construct()
Constructor
AbstractSocketConfig
getSocketConfig()
The connection configuration this client uses — the accessor that means a new setting needs no forwarder, and the seam that lets configuration be built and asserted without constructing a client.
Brands narrow the return type covariantly where they have their own config capabilities — {\CNIC\CNR\Client::getSocketConfig()} returns the CNR config, which is the one place the invariant property type is narrowed.
abstract ResponseInterface
request(array $cmd = [], string $path = "")
Perform API request using the given command.
The shared request lifecycle lives in {\CNIC\performRequest()}; each brand's
public request() is a thin wrapper that pins its default $path and
declares a concrete Response return type. Every brand accepts an optional
$path appended to the configured base URL to select the endpoint: for
IBS/Moniker the path selects the operation (e.g. Domain/Create); for CNR
it defaults to the single fixed script path (api/call.cgi) and rarely
varies. The signature is symmetric across all brands.
protected ResponseInterface
performRequest(array $cmd, string $path = "")
Shared request lifecycle (template method). Never reimplement it in a brand; vary it through exactly two hooks — {buildCommand()} (command flattening) and {newResponse()} (covariant Response factory) — plus the {newSocketConfig()} subtype, which is where a brand-mandatory cURL option would go. No brand declares one.
Brand-specific command rewriting belongs behind buildCommand(), not here:
CNR's IDN conversion lives in {\CNIC\CNR\IDNCommandRewriter}, and a
shared step gated by a flag only one brand sets is the shape that replaced.
abstract protected array
buildCommand(array $cmd)
Flatten and normalise the given command into wire form.
Brand-specific: CNR flattens as-is; IBS injects ResponseFormat=JSON.
abstract protected ResponseInterface
newResponse(string $raw, array $cmd, array $cfg)
Instantiate the brand Response for the given raw payload.
Return type is covariant so each brand pins its concrete Response.
abstract protected AbstractSocketConfig
newSocketConfig()
Instantiate the SocketConfig for this client.
Subclasses return their own SocketConfig subtype.
protected TransportInterface
newTransport()
Instantiate the HTTP transport for this client. Mirrors {newSocketConfig()} — the default is the production cURL transport; override or {setTransport()} to inject a test double so the request() lifecycle can run offline.
AbstractClient
setTransport(TransportInterface $transport)
Inject a custom HTTP transport (e.g. a record/replay cassette transport for offline tests) in place of the default {HttpTransport}.
abstract protected LoggerInterface
newLogger(LogSinkInterface $sink)
Instantiate the brand's logger, writing to the given sink. Mirrors
{newTransport()}/{@see newSocketConfig()}, and exists so a sink can be
chosen without the brand's Logger class being named at the call site. An
override must honour $sink — that is what makes {setLogSink()} work
for a subclass.
AbstractClient
setLogSink(LogSinkInterface $sink)
Route debug output somewhere other than standard output, keeping this brand's format.
This is the seam integrators want: the brand formatter is the part with the logic, the destination is the part that varies per host application. Passing a fresh {\CNIC\EchoSink} restores the shipped default, discarding any logger set via {\CNIC\setCustomLogger()}.
AbstractClient
setCustomLogger(LoggerInterface $customLogger)
Set custom logger to use instead of the default one — use this to replace the format as well as the destination. Create your own class extending \CNIC\AbstractLogger (format only) or implementing \CNIC\LoggerInterface (format and destination).
AbstractClient
enableDebugMode()
Enable debug output to STDOUT
AbstractClient
disableDebugMode()
Disable debug output
string
getPOSTData(array $cmd, bool $maskSecrets = false)
Serialize given command for POST request including connection configuration data
string
getURL()
Get the API connection url that is currently set
AbstractClient
setSocketTimeout(int $timeoutSeconds)
Set the request timeout in seconds (default 300).
The only way to change the timeout: CURLOPT_TIMEOUT in the option bag
is rejected ({\CNIC\setExtraCurlOptions()}) rather than quietly overriding
what {\CNIC\getSocketTimeout()} reports.
int
getSocketTimeout()
Get the request timeout in seconds currently configured.
AbstractClient
setUserAgent(string $label, string $revision, array $modules = [])
Set a custom user agent (for platforms that use this SDK)
string
getUserAgent()
Get the user agent string — the one set via {setUserAgent()}, or the SDK default when none was.
A pure read — keep it that way. Memoising the default into {$userAgent} would
make a getter write during a request, and there is nothing worth memoising:
the value is a handful of constants and one php_uname() call.
AbstractClient
setExtraCurlOptions(array $opts)
Merge additional cURL options into the bag, overriding existing values on key collision. Forwards to {AbstractSocketConfig::setExtraCurlOptions()}, whose docblock carries the detail: what reaches the wire, and the two sets of keys that are refused — the SDK-managed settings ({AbstractSocketConfig::MANAGED_OPTIONS}, rejected here and now) and the transport's own ({HttpTransport::PROTECTED_OPTIONS}, rejected on the next request).
AbstractClient
resetCurlOptions()
Restore the cURL option bag to the brand defaults, discarding anything previously handed to {setExtraCurlOptions()}. Options only — the proxy and referer are separate state and survive; see {AbstractSocketConfig::resetCurlOptions()}.
AbstractClient
setProxy(string $proxy = "")
Set proxy to use for API communication
string|null
getProxy()
Get proxy configuration for API communication
AbstractClient
setReferer(string $referer = "")
Set Referer to use for API communication
string|null
getReferer()
Get Referer configuration for API communication
string
getVersion()
Get the current module version
AbstractClient
setURL(string $url)
Set another connection url to be used for API communication
AbstractClient
setCredentials(string $login = "", string $password = "")
Set Credentials to be used for API communication.
On CNR this discards any active API session: CNR\SocketConfig::setLogin()/
setPassword() clear the session id, because a session and a password are
alternative credentials on the wire and the newer one is authoritative. The
invariant is deliberate and pinned by a test —
CNR\SessionCapable::reuseSession() depends on it, restoring the login first
and the session second. Set the session after the credentials, never before.
AbstractClient
useHighPerformanceConnectionSetup()
Activate High Performance Setup — route requests through the co-located proxy on loopback.
Brand-agnostic and therefore shared — the caller supplies the local proxy, so IBS/Moniker may opt in too. It records a flag on the config rather than rewriting the URL, so the selected system survives it; see {\CNIC\AbstractSocketConfig::useHighPerformanceConnectionSetup()}.
array
IDNConvert(array $domains)
Convert domain names to idn + punycode.
Brand-agnostic and therefore shared: a thin pass-through to the vendor converter for callers who want to normalise a name explicitly. The automatic rewrite of an outbound command is a different thing and is deliberately not here — which parameters carry a domain name is CNR knowledge, and it lives in {\CNIC\CNR\IDNCommandRewriter}.
protected array
executeCurl(string $postData, array $cfg)
Delegate cURL execution to the transport layer.
The configured options are handed over as they are, and beat the
transport's own defaults in {\CNIC\HttpTransport::post()} (PHP's + keeps
the left operand on a duplicate key).
Do not re-add a per-request options argument here: it would be a route into
the option set that skips {\CNIC\AbstractSocketConfig::MANAGED_OPTIONS}, and so
a way for a subclass to put a second answer behind getProxy(). A
per-request option belongs on the config before the request, or on a
transport the caller drives themselves.
void
close()
Close all cURL connections
string
getLiveUrl()
Get LIVE system URL.
There is deliberately no matching getOTEUrl() here: a configuration value
no longer needs a hand-written forwarder, so read the OT&E endpoint from
{\CNIC\getSocketConfig()}.
System|null
getSystem()
Get the API system in use, or null when the configured URL is neither of the brand's two known endpoints.
Derived from the URL rather than stored beside it, which is what makes it
impossible for the two to disagree — and why the return type is nullable:
after a setURL() to some other host there is no honest OT&E-or-LIVE answer
to give. See {\CNIC\AbstractSocketConfig::getSystem()}.
bool
isOTE()
Check whether the OT&E system is in use
AbstractClient
useOTESystem()
Set OT&E System for API communication
AbstractClient
useLIVESystem()
Set LIVE System for API communication (this is the default setting)
AbstractClient
setContext(array $context)
Set context data for the client