class Response extends AbstractResponse implements ExtendedResponseInterface (View source)

CNR Response

Extends the shared AbstractResponse with the CNR wire specifics — the translate()/populate() hooks, the CODE/DESCRIPTION status accessors and the column-driven pagination primitives — and adds the richer CNR-only capabilities declared on {\CNIC\ExtendedResponseInterface} (telemetry, transient/pending status and the list-hash projection) that flat platforms like IBS/Moniker do not provide.

Properties

protected array<string, string> $command

The API Command used within this request

from  AbstractResponse
protected string[] $sensitiveFields

Command parameter keys carrying sensitive data (masked before storage).

protected string $raw

plain API response

from  AbstractResponse
protected array<string, mixed> $hash

hash representation of plain API response.

from  AbstractResponse
protected non-empty-string $metaKeys

Regex for CNR's pagination metadata keys — the five counters the API returns inside PROPERTY, interleaved with the data columns (PROPERTY[TOTAL][0] next to PROPERTY[DOMAIN][0..n]).

protected string[] $columnKeys

Column names available in this response

from  AbstractResponse
protected ColumnInterface[] $columns

Container of Column Instances

from  AbstractResponse
protected array<string, int> $columnIndex

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

from  AbstractResponse
protected RecordInterface[] $records

Record List (List of rows)

from  AbstractResponse
protected array<string, mixed> $context

Context data for the response

from  AbstractResponse
protected string $requestUrl

API request url

from  AbstractResponse

Methods

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

Constructor

string
translate(string $raw, array $cmd, array $placeholders, string|null $error = null, ResponseTemplateManagerInterface|null $templates = null)

Translate the raw API response into its canonical form using the CNR translator. $cmd is already sanitized.

void
populate(string $raw, ResponseParserInterface $parser, array $cmd)

Parse the translated response into the hash and build the column/record lists from it. CNR exposes its columns under the PROPERTY sub-array and assembles records only when properties are present.

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()

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

getPagination()

Get the paginator for this response's list window.

getRecord(int $recordIndex)

Get Record at given index

array
getRecords()

Get all Records

int
getRecordsCount()

Get count of rows in this response

getIterator()

Iterate the record list, keyed by record index.

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).

bool
isMetaKey(string $key)

Is this wire key response metadata rather than data?

int
getCode()

Get API response code

string
getDescription()

Get API response description

float
getQueuetime()

Get Queuetime of API response

float
getRuntime()

Get Runtime of API response

bool
isError()

Check if current API response represents an error case API response code is an 5xx code

bool
isSuccess()

Check if current API response represents a success case API response code is an 2xx code

bool
isTmpError()

Check if current API response represents a temporary error case API response code is an 4xx code

bool
isPending()

Check if current operation is returned as pending

addColumn(string $columnName, array $data)

Add a column to the column list

int|null
getFirstRecordIndex()

Get Index of first row in this response — the offset the current window starts at, or null when this response carries no FIRST counter (a non-list response).

int|null
getLastRecordIndex()

Get last record index of the current list query, or null when this response carries no LAST counter.

array
getListHash()

Get Response as List Hash including useful meta data for tables

int|null
getRecordsTotalCount()

Get total count of records available for the list query, or null when this response carries no TOTAL counter (a non-list response).

int|null
getRecordsLimitation()

Get limit(ation) setting of the current list query — the count of requested rows — or null when this response carries no LIMIT counter.

Details

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

Constructor

Assembles the response completely: every column and record exists by the time this returns, and nothing afterwards can add one (RSRMID-2939) — see the sealing note on {\CNIC\ResponseInterface}.

The parser is a constructor local, not a property, and reaches {\CNIC\populate()} as an argument. So does the translated raw response and the sanitized command. That is deliberate: while populate() read them off $this, the order of the assignments above it was load-bearing and enforced by nothing but a comment — moving the $this->command assignment below the populate() call silently switched the IBS parser to its other wire branch, because that parser reads the command to choose JSON vs plain text. Passing them in makes the dependency a signature, so there is no order left to get wrong. Do not reintroduce a $parser property: nothing after construction has any use for it.

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())

string|null $error

transport error, if any; non-null means $raw is unusable and the brand's "httperror" template is substituted instead (see {\CNIC\AbstractResponseTranslator::translate()})

ResponseTemplateManagerInterface|null $templates

registry the translator resolves template ids against; null uses the brand's built-ins. Supplying one is how a caller scopes a registered template to this response instead of to the whole process (RSRMID-2941)

protected string translate(string $raw, array $cmd, array $placeholders, string|null $error = null, ResponseTemplateManagerInterface|null $templates = null)

Translate the raw API response into its canonical form using the CNR translator. $cmd is already sanitized.

Parameters

string $raw
array $cmd

API command used within this request

array $placeholders
string|null $error

transport error, if any; non-null means $raw is unusable (see {\CNIC\AbstractResponseTranslator::translate()})

ResponseTemplateManagerInterface|null $templates

registry to resolve template ids against; null uses the brand's built-ins

Return Value

string

protected void populate(string $raw, ResponseParserInterface $parser, array $cmd)

Parse the translated response into the hash and build the column/record lists from it. CNR exposes its columns under the PROPERTY sub-array and assembles records only when properties are present.

The five pagination counters arrive in that same PROPERTY block, and are deliberately skipped rather than registered (RSRMID-2965): they are response metadata, and a one-cell TOTAL "column" beside a 200-cell DOMAIN one made {\CNIC\AbstractResponse::assembleRecords()} count the metadata as a row. The primitives below read them back off the hash. Their cells are still validated by {\CNIC\CNR\stringCells()} before being dropped, so a parser handing CNR a non-string keeps failing at construction wherever it put it — the guarantee does not get quietly narrower for the keys that stopped being columns.

$cmd is forwarded to keep the parse call uniform across brands even though the CNR parser ignores it — see ResponseParserInterface::parse().

Parameters

string $raw

the translated response, as returned by {\CNIC\translate()}

ResponseParserInterface $parser

the brand default or the injected substitute

array $cmd

API command used within this request, already sanitized

Return Value

void

protected ResponseParserInterface newResponseParser()

Instantiate the response parser for this brand.

protected RecordInterface newRecord(array $row)

Instantiate the record type for this brand.

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).

Delegates the actual matching/masking to {\CNIC\CommandRedactor::redact()}, which is shared with {\CNIC\AbstractSocketConfig::maskSensitiveCommand()}. 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.

Replaces the record list rather than appending to it, so calling it twice yields the same rows instead of doubling them (RSRMID-2939). No caller does — each brand's populate() calls it once, at the end — but "assembles the records" is what the name promises, and an append-only version made that promise conditional on a call count nothing enforced.

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, and both brands build the same shared CNIC\Column: CNR responses are plaintext (always strings) and IBS/Moniker responses are JSON (arbitrary values, nested arrays and objects included), a difference expressed as a native return type on ColumnInterface::getStringByIndex() rather than a per-brand Column subclass. Each brand's addColumn() still builds its Column locally and hands the finished instance here, so this shared helper never has to construct one itself — see IBS\Response::addColumn()/CNR\Response::addColumn().

A repeated column name is refused rather than half-registered (RSRMID-2939). The three lists are one data structure: $columns/$columnKeys are positional while $columnIndex maps a name to one position, so a second column under an existing name used to append to the first two while the ??= kept the index pointing at the first — leaving getColumns() holding a column getColumn() could never return, and getColumnKeys() listing a name twice.

Neither shipped brand can reach the throw, and neither can a substitute parser: both brands derive their column names from array_keys() of the parsed hash, and two distinct PHP array keys cannot stringify to the same name. It guards the invariant against a future brand whose populate() builds its columns some other way — there the collision is a programming error and says so instead of silently desynchronising the three lists.

Parameters

ColumnInterface $col

Return Value

AbstractResponse

Exceptions

DuplicateColumnException

protected AbstractResponse addRecord(array $row)

Add a record to the record list.

Protected since RSRMID-2939: a record added after construction changed getRecordsCount() and, through it, the pagination getters IBS derives from it (getRecordsTotalCount/getRecordsLimitation/getLastRecordIndex/ getNumberOfPages) — so a caller could silently repaginate a finished response. Only {\CNIC\assembleRecords()} calls this.

Parameters

array $row

Return Value

AbstractResponse

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()

Get Column Names

Data columns only. There is nothing left to filter here since RSRMID-2965: a brand's populate() never registers a metadata key as a column, so the list this returns is already free of them and the former getColumnKeys(bool $filterPaginationKeys) — with its preg_grep over every call — has no work to do. Do not re-add the flag: it existed only because metadata was mixed into the column pool, and a boolean parameter on a public interface is the cost that modelling error was charging every consumer.

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

Paginator getPagination()

Get the paginator for this response's list window.

The one place the four brand primitives meet the shared arithmetic. Every derivation from them — page numbers, the page count, the has-next/ has-previous predicates — lives on {\CNIC\Paginator} since RSRMID-2965, because none of it reads a column, holds state or needs a wire payload: keeping it here meant an offset grid could only be exercised by hand-authoring an API response that carried four integers.

A fresh Paginator per call, over numbers that can no longer change (a response is sealed once constructed), so two callers cannot observe each other and there is no cache to invalidate.

{\CNIC\getRecordsCount()} supplies the fifth member deliberately: it counts the rows this response holds and is {\CNIC\getRecord()}'s bounds authority, so it is the reading that cannot be made to lie by a wire that miscounts.

Return Value

Paginator

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

Traversable getIterator()

Iterate the record list, keyed by record index.

A fresh ArrayIterator per call, over a list that can no longer change: two foreach loops over one response therefore see identical rows, in either order, without a rewind step between them, and neither is observable to the other. That is the property the removed record cursor could not offer — see {\CNIC\ResponseInterface} for the full account.

Return Value

Traversable

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

protected bool isMetaKey(string $key)

Is this wire key response metadata rather than data?

The one place {$metaKeys} is matched, called from each brand's populate() before it registers a column. Shared so that "which keys are metadata" is answered identically for every brand while what those keys are stays brand-specific — see {$metaKeys} for why the two sets must not be merged.

Parameters

string $key

Return Value

bool

int getCode()

Get API response code

Return Value

int

string getDescription()

Get API response description

Return Value

string

float getQueuetime()

Get Queuetime of API response

Return Value

float

float getRuntime()

Get Runtime of API response

Return Value

float

bool isError()

Check if current API response represents an error case API response code is an 5xx code

Return Value

bool

bool isSuccess()

Check if current API response represents a success case API response code is an 2xx code

Return Value

bool

bool isTmpError()

Check if current API response represents a temporary error case API response code is an 4xx code

Return Value

bool

bool isPending()

Check if current operation is returned as pending

Return Value

bool

protected Response addColumn(string $columnName, array $data)

Add a column to the column list

CNR responses are plaintext, so column values are always strings — stringCells() guarantees it before this is called. The shared CNIC\Column is nonetheless used as-is, exactly like IBS\Response::addColumn(): the value type is expressed to consumers by getStringByIndex(), not by a column subclass (RSRMID-2942). See IBS\Response::addColumn() for why each brand builds its Column locally and hands the finished instance to the shared registerColumn() bookkeeping rather than to a shared factory.

Protected since RSRMID-2939, and called only from populate(): records are assembled from the columns once, at the end of construction, so a column added afterwards was absent from every record — present in getColumns() and getColumnKeys(), invisible to getRecord()/getRecords() and to iteration.

Parameters

string $columnName
array $data

array of column data, already narrowed by stringCells()

Return Value

Response

int|null getFirstRecordIndex()

Get Index of first row in this response — the offset the current window starts at, or null when this response carries no FIRST counter (a non-list response).

No "0 because there are rows" fallback (RSRMID-2965): that stand-in made every response that happened to hold a row claim to be the first page of a list.

Return Value

int|null

int|null getLastRecordIndex()

Get last record index of the current list query, or null when this response carries no LAST counter.

Reported exactly as CNR reports it, including the one shape that is not a row index: an empty window echoes LAST = FIRST (with COUNT = 0) rather than omitting LAST. Callers must not read it as "the offset of a row that exists" — hasNextPage() is where that shape is accounted for. The former getRecordsCount() - 1 fallback is gone (RSRMID-2965): it answered a record index to a question about a result-set offset, which agreed with the wire on the first page only.

Return Value

int|null

array getListHash()

Get Response as List Hash including useful meta data for tables

Return Value

array

hash including list meta data and array of rows in hash notation

int|null getRecordsTotalCount()

Get total count of records available for the list query, or null when this response carries no TOTAL counter (a non-list response).

No getRecordsCount() fallback (RSRMID-2943): a non-list response reports "no total" honestly instead of a count that only happened to equal the record count.

Return Value

int|null

int|null getRecordsLimitation()

Get limit(ation) setting of the current list query — the count of requested rows — or null when this response carries no LIMIT counter.

No getRecordsCount() fallback (RSRMID-2943), for the same reason as {\CNIC\CNR\getRecordsTotalCount()}: 0 is a real, requested limit and must stay distinguishable from "this response carries no LIMIT counter at all".

Return Value

int|null