abstract class AbstractResponse implements ResponseInterface (View source)

Shared Response foundation

Brand-neutral base for every registrar Response. It owns the machinery that is identical across brands — the constructor skeleton (template method), command sanitisation, column/record bookkeeping, record-cursor navigation and the derived pagination getters — and leaves the parts that genuinely differ to the concrete subclasses:

  • wire hooks: {\CNIC\translate()} / {\CNIC\populate()} (protected),
  • factories: {\CNIC\newRecord()} and {\CNIC\newResponseParser()} (protected),
  • the status/code accessors and addColumn declared on {\CNIC\ResponseInterface} (getCode/getDescription/isError/isSuccess, addColumn) — each reads a different wire shape, and addColumn additionally has to build its brand's own correctly-typed Column (see registerColumn()),
  • the pagination primitives, likewise declared on {\CNIC\ResponseInterface} (getCurrentPageNumber, getFirstRecordIndex, getLastRecordIndex, getRecordsTotalCount, getRecordsLimitation, hasNextPage, hasPreviousPage), which this base deliberately does NOT implement — not even as single-page defaults — so a brand that forgets pagination fails at declaration time instead of silently answering "one page, no next page".

None of the members in those last two groups is declared abstract here: they are interface methods this base simply never implements, so every concrete brand must supply them. Do not add base defaults for the pagination primitives — see docs/agents/architecture.md for why the seam is drawn there, and tests/ResponsePaginationSeamTest.php, which refuses it.

CNR\Response and IBS\Response both extend this as siblings — mirroring the AbstractClient / AbstractSocketConfig / AbstractResponseTemplateManager / AbstractResponseTranslator pattern — so neither brand is-a the other. The CNR-only capabilities (telemetry, transient/pending status, list-hash) live on CNR\Response via {\CNIC\ExtendedResponseInterface} and are deliberately NOT part of this base, so brands like IBS/Moniker never inherit methods they cannot support.

Properties

protected array<string, string> $command

The API Command used within this request

protected string[] $sensitiveFields

Command parameter keys that carry sensitive data for this brand (account password, domain authorization code, ...). Their values are masked before the command is stored so they can never be read back (e.g. by custom loggers). Matching is case-insensitive (see sanitizeCommand()), so only the names matter, not their casing. Brand-specific by design: each brand declares the keys it uses (CNR upper-case, IBS lower-/camel-case); the neutral default masks nothing.

protected string $raw

plain API response

protected array<string, mixed> $hash

hash representation of plain API response.

protected non-empty-string $paginationKeys

Regex for pagination related column keys, stripped in getColumnKeys(true).

protected string[] $columnKeys

Column names available in this response

protected ColumnInterface[] $columns

Container of Column Instances

protected array<string, int> $columnIndex

Map of column name to its index in the column/columnKeys lists.

protected int $recordIndex

Record Index we currently point to in record list

protected RecordInterface[] $records

Record List (List of rows)

protected array<string, mixed> $context

Context data for the response

protected string $requestUrl

API request url

protected ResponseParserInterface $parser

The parser turning the translated raw response into {$hash}.

Methods

__construct(string $raw, array $cmd = [], array $placeholders = [], array $context = [], ResponseParserInterface|null $parser = null)

Constructor

string
translate(string $raw, array $cmd, array $placeholders)

Translate the raw API response into its canonical form.

void
populate()

Parse the translated response into the hash and build the column/record lists from it. Brand-specific because each brand's parser returns a different hash shape (CNR nests columns under PROPERTY, IBS is a flat key => value map). The sanitized command is available as $this->command, and the parser — the brand default or the injected substitute — as $this->parser.

newResponseParser()

Instantiate the response parser for this brand.

newRecord(array $row)

Instantiate the record type for this brand.

array
sanitizeCommand(array $cmd)

Mask the brand's sensitive command keys (see $sensitiveFields) so their values can never be read back from the response (e.g. by custom loggers).

void
assembleRecords()

Assemble the record (row) list from the columns already added via addColumn(). Shared by all brands: each subclass populates the columns with its own Column type beforehand, while the row assembly is identical.

array
getContext()

Get context data for the response

string
getRequestURL()

Get Request URL

string
getPlain()

Get Plain API response

array
getHash()

Get API response as Hash

registerColumn(ColumnInterface $col)

Register an already-constructed column into the list bookkeeping.

addRecord(array $row)

Add a record to the record list

getColumn(string $columnName)

Get column by column name

mixed
getColumnIndex(string $columnName, int $recordIndex)

Get Data by Column Name and Index

array
getColumnKeys(bool $filterPaginationKeys = false)

Get Column Names

array
getColumns()

Get List of Columns

array
getCommand()

Get Command used in this request

string
getCommandPlain()

Get Command used in this request in plain text format

getCurrentRecord()

Get Record of current record index

getNextRecord()

Get next record in record list

int|null
getNextPageNumber()

Get Page Number of next list query

int
getNumberOfPages()

Get the number of pages available for this list query

array
getPagination()

Get object containing all paging data

int|null
getPreviousPageNumber()

Get Page Number of previous list query

getPreviousRecord()

Get previous record in record list

getRecord(int $recordIndex)

Get Record at given index

array
getRecords()

Get all Records

int
getRecordsCount()

Get count of rows in this response

rewindRecordList()

Reset index in record list back to zero

bool
hasCurrentRecord()

Check if the record list contains a record for the current record index in use

bool
hasNextRecord()

Check if the record list contains a next record for the current record index in use

bool
hasPreviousRecord()

Check if the record list contains a previous record for the current record index in use

string
getHashString(string $key, string $default = "")

Get a string value from the hash by key, returning a default if not found or not a string

array
getHashArray(string $key)

Get an array value from the hash by key, returning an empty array if not found or not an array. The twin of {getHashString()} for the nested blocks a brand's populate() reads (e.g. CNR's PROPERTY).

Details

__construct(string $raw, array $cmd = [], array $placeholders = [], array $context = [], ResponseParserInterface|null $parser = null)

Constructor

Parameters

string $raw

API plain response

array $cmd

API command used within this request

array $placeholders

vars the response description has dynamically replaced

array $context

context data for the response (for use in custom loggers etc., optional, has no impact on SDK behaviour)

ResponseParserInterface|null $parser

parser to use instead of the brand default (see newResponseParser())

abstract protected string translate(string $raw, array $cmd, array $placeholders)

Translate the raw API response into its canonical form.

Brand-specific by the ResponseTranslator each subclass imports; $cmd is already sanitized.

Parameters

string $raw
array $cmd

API command used within this request

array $placeholders

Return Value

string

abstract protected void populate()

Parse the translated response into the hash and build the column/record lists from it. Brand-specific because each brand's parser returns a different hash shape (CNR nests columns under PROPERTY, IBS is a flat key => value map). The sanitized command is available as $this->command, and the parser — the brand default or the injected substitute — as $this->parser.

Return Value

void

abstract protected ResponseParserInterface newResponseParser()

Instantiate the response parser for this brand.

Factory hook mirroring {\CNIC\newRecord()} and {\CNIC\AbstractClient::newTransport()}: it supplies the default, and the constructor's $parser argument overrides it — so a substitute parser needs neither reflection nor a subclass. populate() must parse through $this->parser; instantiating a parser inline there behaves identically and silently closes the seam, which is why the guard is structural (tests/ResponseParserSeamTest.php).

abstract protected RecordInterface newRecord(array $row)

Instantiate the record type for this brand.

Factory hook for addRecord(). Records share one shape across brands (array<string,mixed>), so every brand currently returns the same shared CNIC\Record — the hook stays abstract nonetheless, because it is the seam a brand needing genuinely different row behaviour would implement, and hard-coding the shared Record here would close it. (Unlike columns, whose value types diverge and so cannot use a param-typed factory at all — see registerColumn().)

Parameters

array $row

Return Value

RecordInterface

protected array sanitizeCommand(array $cmd)

Mask the brand's sensitive command keys (see $sensitiveFields) so their values can never be read back from the response (e.g. by custom loggers).

Matching is case-insensitive to stay robust against casing differences between what a brand documents and what it actually sends.

Parameters

array $cmd

API command used within this request

Return Value

array

protected void assembleRecords()

Assemble the record (row) list from the columns already added via addColumn(). Shared by all brands: each subclass populates the columns with its own Column type beforehand, while the row assembly is identical.

Return Value

void

array getContext()

Get context data for the response

Return Value

array

context data

string getRequestURL()

Get Request URL

Return Value

string

string getPlain()

Get Plain API response

Return Value

string

array getHash()

Get API response as Hash

Return Value

array

API response hash

protected AbstractResponse registerColumn(ColumnInterface $col)

Register an already-constructed column into the list bookkeeping.

The bookkeeping ($columns/$columnKeys/$columnIndex) is identical for every brand; what differs is the column's value type. Rather than a param-typed newColumn() factory — which cannot stay type-clean under PHPStan L9 / Psalm L1, because CNR columns take string[] while IBS columns take mixed[] and a shared factory would have to narrow one into the other — each brand's addColumn() builds its own correctly-typed Column locally and hands the finished instance here, so this shared helper never sees the brand types.

Parameters

ColumnInterface $col

Return Value

AbstractResponse

ResponseInterface addRecord(array $row)

Add a record to the record list

Parameters

array $row

Return Value

ResponseInterface

ColumnInterface|null getColumn(string $columnName)

Get column by column name

Parameters

string $columnName

Return Value

ColumnInterface|null

mixed getColumnIndex(string $columnName, int $recordIndex)

Get Data by Column Name and Index

Parameters

string $columnName
int $recordIndex

Return Value

mixed

array getColumnKeys(bool $filterPaginationKeys = false)

Get Column Names

Parameters

bool $filterPaginationKeys

Return Value

array

Array of Column Names

array getColumns()

Get List of Columns

Return Value

array

Array of Columns

array getCommand()

Get Command used in this request

Return Value

array command

string getCommandPlain()

Get Command used in this request in plain text format

Return Value

string

RecordInterface|null getCurrentRecord()

Get Record of current record index

Return Value

RecordInterface|null

RecordInterface|null getNextRecord()

Get next record in record list

Return Value

RecordInterface|null

int|null getNextPageNumber()

Get Page Number of next list query

Return Value

int|null

int getNumberOfPages()

Get the number of pages available for this list query

Return Value

int

array getPagination()

Get object containing all paging data

Return Value

array

paginator data

int|null getPreviousPageNumber()

Get Page Number of previous list query

Return Value

int|null

RecordInterface|null getPreviousRecord()

Get previous record in record list

Return Value

RecordInterface|null

RecordInterface|null getRecord(int $recordIndex)

Get Record at given index

Parameters

int $recordIndex

Return Value

RecordInterface|null

array getRecords()

Get all Records

Return Value

array

array of records

int getRecordsCount()

Get count of rows in this response

Return Value

int

ResponseInterface rewindRecordList()

Reset index in record list back to zero

Return Value

ResponseInterface

protected bool hasCurrentRecord()

Check if the record list contains a record for the current record index in use

Return Value

bool

protected bool hasNextRecord()

Check if the record list contains a next record for the current record index in use

Return Value

bool

protected bool hasPreviousRecord()

Check if the record list contains a previous record for the current record index in use

Return Value

bool

protected string getHashString(string $key, string $default = "")

Get a string value from the hash by key, returning a default if not found or not a string

Parameters

string $key
string $default

Return Value

string

protected array getHashArray(string $key)

Get an array value from the hash by key, returning an empty array if not found or not an array. The twin of {getHashString()} for the nested blocks a brand's populate() reads (e.g. CNR's PROPERTY).

Parameters

string $key

Return Value

array