AbstractResponse
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
Constructor
Translate the raw API response into its canonical form.
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.
Instantiate the response parser for this brand.
Instantiate the record type for this brand.
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).
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.
Get context data for the response
Get Request URL
Get Plain API response
Get API response as Hash
Register an already-constructed column into the list bookkeeping.
Add a record to the record list
Get column by column name
Get Data by Column Name and Index
Get Column Names
Get List of Columns
Get Command used in this request
Get Command used in this request in plain text format
Get Record of current record index
Get next record in record list
Get Page Number of next list query
Get the number of pages available for this list query
Get object containing all paging data
Get Page Number of previous list query
Get previous record in record list
Get Record at given index
Get all Records
Get count of rows in this response
Reset index in record list back to zero
Check if the record list contains a record for the current record index in use
Check if the record list contains a next record for the current record index in use
Check if the record list contains a previous record for the current record index in use
Get a string value from the hash by key, returning a default if not found or not a string
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
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.
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.
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().)
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.
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.
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
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.
ResponseInterface
addRecord(array $row)
Add a record to the record list
ColumnInterface|null
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
RecordInterface|null
getCurrentRecord()
Get Record of current record index
RecordInterface|null
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
RecordInterface|null
getPreviousRecord()
Get previous record in record list
RecordInterface|null
getRecord(int $recordIndex)
Get Record at given index
array
getRecords()
Get all Records
int
getRecordsCount()
Get count of rows in this response
ResponseInterface
rewindRecordList()
Reset index in record list back to zero
protected bool
hasCurrentRecord()
Check if the record list contains a record for the current record index in use
protected bool
hasNextRecord()
Check if the record list contains a next record for the current record index in use
protected bool
hasPreviousRecord()
Check if the record list contains a previous record for the current record index in use
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
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).