Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.12% covered (success)
98.12%
157 / 160
92.86% covered (success)
92.86%
39 / 42
CRAP
0.00% covered (danger)
0.00%
0 / 1
Response
98.12% covered (success)
98.12%
157 / 160
92.86% covered (success)
92.86%
39 / 42
90
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
1 / 1
10
 getCode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDescription
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPlain
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getQueuetime
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getHash
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRuntime
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 isError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isSuccess
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isTmpError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isPending
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 addColumn
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 addRecord
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getColumn
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 getColumnIndex
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 getColumnKeys
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getColumns
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCommand
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCommandPlain
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getCurrentPageNumber
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 getCurrentRecord
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 getFirstRecordIndex
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 getLastRecordIndex
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
4
 getListHash
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
4
 getNextRecord
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getNextPageNumber
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 getNumberOfPages
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 getPagination
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 getPreviousPageNumber
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
3.03
 getPreviousRecord
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getRecord
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
3.33
 getRecords
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getRecordsCount
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getRecordsTotalCount
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 getRecordsLimitation
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 hasNextPage
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 hasPreviousPage
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 rewindRecordList
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 hasColumn
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasCurrentRecord
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 hasNextRecord
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 hasPreviousRecord
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3#declare(strict_types=1);
4
5/**
6 * CNIC\HEXONET
7 * Copyright © CentralNic Group PLC
8 */
9
10namespace CNIC\HEXONET;
11
12use CNIC\HEXONET\ResponseParser as RP;
13use CNIC\HEXONET\ResponseTranslator as RT;
14
15/**
16 * HEXONET Response
17 *
18 * @package CNIC\HEXONET
19 */
20class Response // implements \CNIC\ResponseInterface
21{
22    /**
23     * The API Command used within this request
24     * @var array
25     */
26    private $command;
27
28     /**
29     * plain API response
30     * @var string
31     */
32    private $raw;
33
34    /**
35     * hash representation of plain API response
36     * @var array
37     */
38    private $hash;
39
40    /**
41     * Regex for pagination related column keys
42     * @var string
43     */
44    private $paginationkeys = "/^TOTAL|COUNT|LIMIT|FIRST|LAST$/";
45
46    /**
47     * Column names available in this responsse
48     * @var string[]
49     */
50    private $columnkeys;
51    /**
52     * Container of Column Instances
53     * @var Column[]
54     */
55    private $columns;
56    /**
57     * Record Index we currently point to in record list
58     * @var int
59     */
60    private $recordIndex;
61    /**
62     * Record List (List of rows)
63     * @var Record[]
64     */
65    private $records;
66
67    /**
68     * Constructor
69     * @param string $raw API plain response
70     * @param array $cmd API command used within this request
71     * @param array $ph placeholder array to get vars in response description dynamically replaced
72     */
73    public function __construct($raw, $cmd = [], $ph = [])
74    {
75        if (isset($cmd["PASSWORD"])) { // make password no longer accessible
76            $cmd["PASSWORD"] = "***";
77        }
78
79        $this->raw = RT::translate($raw, $cmd, $ph);
80        $this->hash = RP::parse($this->raw);
81        $this->command = $cmd;
82        $this->columnkeys = [];
83        $this->columns = [];
84        $this->recordIndex = 0;
85        $this->records = [];
86
87        if (array_key_exists("PROPERTY", $this->hash)) {
88            $colKeys = array_map("strval", array_keys($this->hash["PROPERTY"]));
89            $count = 0;
90            foreach ($colKeys as $k) {
91                $this->addColumn($k, $this->hash["PROPERTY"][$k]);
92                $col = $this->getColumn($k);
93                if (!is_null($col)) {
94                    $count2 = $col->length;
95                    if ($count2 > $count) {
96                        $count = $count2;
97                    }
98                }
99            }
100            for ($i = 0; $i < $count; $i++) {
101                $d = [];
102                foreach ($colKeys as $k) {
103                    $col = $this->getColumn($k);
104                    if ($col) {
105                        $v = $col->getDataByIndex($i);
106                        if ($v !== null) {
107                            $d[$k] = $v;
108                        }
109                    }
110                }
111                $this->addRecord($d);
112            }
113        }
114    }
115
116    /**
117     * Get API response code
118     * @return int API response code
119     */
120    public function getCode()
121    {
122        return intval($this->hash["CODE"], 10);
123    }
124
125    /**
126     * Get API response description
127     * @return string API response description
128     */
129    public function getDescription()
130    {
131        return $this->hash["DESCRIPTION"];
132    }
133
134    /**
135     * Get Plain API response
136     * @return string Plain API response
137     */
138    public function getPlain()
139    {
140        return $this->raw;
141    }
142
143    /**
144     * Get Queuetime of API response
145     * @return float Queuetime of API response
146     */
147    public function getQueuetime()
148    {
149        if (array_key_exists("QUEUETIME", $this->hash)) {
150            return floatval($this->hash["QUEUETIME"]);
151        }
152        return 0.00;
153    }
154
155    /**
156     * Get API response as Hash
157     * @return array API response hash
158     */
159    public function getHash()
160    {
161        return $this->hash;
162    }
163
164    /**
165     * Get Runtime of API response
166     * @return float Runtime of API response
167     */
168    public function getRuntime()
169    {
170        if (array_key_exists("RUNTIME", $this->hash)) {
171            return floatval($this->hash["RUNTIME"]);
172        }
173        return 0.00;
174    }
175
176    /**
177     * Check if current API response represents an error case
178     * API response code is an 5xx code
179     * @return bool boolean result
180     */
181    public function isError()
182    {
183        return substr($this->hash["CODE"], 0, 1) === "5";
184    }
185
186    /**
187     * Check if current API response represents a success case
188     * API response code is an 2xx code
189     * @return bool boolean result
190     */
191    public function isSuccess()
192    {
193        return substr($this->hash["CODE"], 0, 1) === "2";
194    }
195
196    /**
197     * Check if current API response represents a temporary error case
198     * API response code is an 4xx code
199     * @return bool boolean result
200     */
201    public function isTmpError()
202    {
203        return substr($this->hash["CODE"], 0, 1) === "4";
204    }
205
206    /**
207     * Check if current operation is returned as pending
208     * @return bool bool result
209     */
210    public function isPending()
211    {
212        return isset($this->hash["PENDING"]) ? $this->hash["PENDING"] === "1" : false;
213    }
214
215    /**
216     * Add a column to the column list
217     * @param string $key column name
218     * @param string[] $data array of column data
219     * @return $this
220     */
221    public function addColumn($key, $data)
222    {
223        $col = new Column($key, $data);
224        $this->columns[] = $col;
225        $this->columnkeys[] = $key;
226        return $this;
227    }
228
229    /**
230     * Add a record to the record list
231     * @param array $h row hash data
232     * @return $this
233     */
234    public function addRecord($h)
235    {
236        $this->records[] = new Record($h);
237        return $this;
238    }
239
240    /**
241     * Get column by column name
242     * @param string $key column name
243     * @return Column|null column instance or null if column does not exist
244     */
245    public function getColumn($key)
246    {
247        return ($this->hasColumn($key) ? $this->columns[array_search($key, $this->columnkeys)] : null);
248    }
249
250    /**
251     * Get Data by Column Name and Index
252     * @param string $colkey column name
253     * @param int $index column data index
254     * @return string|null column data at index or null if not found
255     */
256    public function getColumnIndex($colkey, $index)
257    {
258        $col = $this->getColumn($colkey);
259        return $col ? $col->getDataByIndex($index) : null;
260    }
261
262    /**
263     * Get Column Names
264     * @param bool $filterPaginationKeys strip pagination columns
265     * @return string[] Array of Column Names
266     */
267    public function getColumnKeys($filterPaginationKeys = false)
268    {
269        if ($filterPaginationKeys) {
270            return preg_grep($this->paginationkeys, $this->columnkeys, PREG_GREP_INVERT);
271        }
272        return $this->columnkeys;
273    }
274
275    /**
276     * Get List of Columns
277     * @return Column[] Array of Columns
278     */
279    public function getColumns()
280    {
281        return $this->columns;
282    }
283
284    /**
285     * Get Command used in this request
286     * @return array command
287     */
288    public function getCommand()
289    {
290        return $this->command;
291    }
292
293    /**
294     * Get Command used in this request in plain text format
295     * @return string command
296     */
297    public function getCommandPlain()
298    {
299        $tmp = "";
300        foreach ($this->command as $key => $val) {
301            $tmp .= "$key = $val\n";
302        }
303        return $tmp;
304    }
305
306    /**
307     * Get Page Number of current List Query
308     * @return int|null page number or null in case of a non-list response
309     */
310    public function getCurrentPageNumber()
311    {
312        $first = $this->getFirstRecordIndex();
313        $limit = $this->getRecordsLimitation();
314        if ($first !== null && $limit) {
315            return (int)(floor($first / $limit) + 1);
316        }
317        return null;
318    }
319
320    /**
321     * Get Record of current record index
322     * @return Record|null Record or null in case of a non-list response
323     */
324    public function getCurrentRecord()
325    {
326        return $this->hasCurrentRecord() ? $this->records[$this->recordIndex] : null;
327    }
328
329    /**
330     * Get Index of first row in this response
331     * @return int|null first row index
332     */
333    public function getFirstRecordIndex()
334    {
335        $col = $this->getColumn("FIRST");
336        if ($col) {
337            $f = $col->getDataByIndex(0);
338            return $f === null ? 0 : intval($f, 10);
339        }
340        if ($this->getRecordsCount()) {
341            return 0;
342        }
343        return null;
344    }
345
346    /**
347     * Get last record index of the current list query
348     * @return int|null record index or null for a non-list response
349     */
350    public function getLastRecordIndex()
351    {
352        $col = $this->getColumn("LAST");
353        if ($col) {
354            $l = $col->getDataByIndex(0);
355            if ($l !== null) {
356                return intval($l, 10);
357            }
358        }
359        $c = $this->getRecordsCount();
360        if ($c) {
361            return $c - 1;
362        }
363        return null;
364    }
365
366    /**
367     * Get Response as List Hash including useful meta data for tables
368     * @return array hash including list meta data and array of rows in hash notation
369     */
370    public function getListHash()
371    {
372        $lh = [];
373        foreach ($this->records as $rec) {
374            $data = $rec->getData();
375            foreach($data as $col => $val) {
376                if ((bool)preg_match($this->paginationkeys, $col)) {
377                    unset($data[$col]);
378                }
379            }
380            $lh[] = $data;
381        }
382        return [
383            "LIST" => $lh,
384            "meta" => [
385                "columns" => $this->getColumnKeys(true),
386                "pg" => $this->getPagination()
387            ]
388        ];
389    }
390
391    /**
392     * Get next record in record list
393     * @return Record|null Record or null in case there's no further record
394     */
395    public function getNextRecord()
396    {
397        if ($this->hasNextRecord()) {
398            return $this->records[++$this->recordIndex];
399        }
400        return null;
401    }
402
403    /**
404     * Get Page Number of next list query
405     * @return int|null page number or null if there's no next page
406     */
407    public function getNextPageNumber()
408    {
409        $cp = $this->getCurrentPageNumber();
410        if ($cp === null) {
411            return null;
412        }
413        $page = $cp + 1;
414        $pages = $this->getNumberOfPages();
415        return ($page <= $pages ? $page : $pages);
416    }
417
418    /**
419     * Get the number of pages available for this list query
420     * @return int number of pages
421     */
422    public function getNumberOfPages()
423    {
424        $t = $this->getRecordsTotalCount();
425        $limit = $this->getRecordsLimitation();
426        if ($t && $limit) {
427            return (int)ceil($t / $this->getRecordsLimitation());
428        }
429        return 0;
430    }
431
432    /**
433     * Get object containing all paging data
434     * @return array paginator data
435     */
436    public function getPagination()
437    {
438        return [
439            "COUNT" => $this->getRecordsCount(),
440            "CURRENTPAGE" => $this->getCurrentPageNumber(),
441            "FIRST" => $this->getFirstRecordIndex(),
442            "LAST" => $this->getLastRecordIndex(),
443            "LIMIT" => $this->getRecordsLimitation(),
444            "NEXTPAGE" => $this->getNextPageNumber(),
445            "PAGES" => $this->getNumberOfPages(),
446            "PREVIOUSPAGE" => $this->getPreviousPageNumber(),
447            "TOTAL" => $this->getRecordsTotalCount()
448        ];
449    }
450
451    /**
452     * Get Page Number of previous list query
453     * @return int|null page number or null if there's no previous page
454     */
455    public function getPreviousPageNumber()
456    {
457        $cp = $this->getCurrentPageNumber();
458        if ($cp === null) {
459            return null;
460        }
461        $cp -= 1;
462        if ($cp === 0) {
463            return null;
464        }
465        return $cp;
466    }
467
468    /**
469     * Get previous record in record list
470     * @return Record|null Record or null if there's no previous record
471     */
472    public function getPreviousRecord()
473    {
474        if ($this->hasPreviousRecord()) {
475            return $this->records[--$this->recordIndex];
476        }
477        return null;
478    }
479
480    /**
481     * Get Record at given index
482     * @param int $idx record index
483     * @return Record|null Record or null if index does not exist
484     */
485    public function getRecord($idx)
486    {
487        if ($idx >= 0 && $this->getRecordsCount() > $idx) {
488            return $this->records[$idx];
489        }
490        return null;
491    }
492
493    /**
494     * Get all Records
495     * @return Record[] array of records
496     */
497    public function getRecords()
498    {
499        return $this->records;
500    }
501
502    /**
503     * Get count of rows in this response
504     * @return int count of rows
505     */
506    public function getRecordsCount()
507    {
508        return count($this->records);
509    }
510
511    /**
512     * Get total count of records available for the list query
513     * @return int total count of records or count of records for a non-list response
514     */
515    public function getRecordsTotalCount()
516    {
517        $col = $this->getColumn("TOTAL");
518        if ($col) {
519            $t = $col->getDataByIndex(0);
520            if ($t !== null) {
521                return intval($t, 10);
522            }
523        }
524        return $this->getRecordsCount();
525    }
526
527    /**
528     * Get limit(ation) setting of the current list query
529     * This is the count of requested rows
530     * @return int limit setting or count requested rows
531     */
532    public function getRecordsLimitation()
533    {
534        $col = $this->getColumn("LIMIT");
535        if ($col) {
536            $l = $col->getDataByIndex(0);
537            if ($l !== null) {
538                return intval($l, 10);
539            }
540        }
541        return $this->getRecordsCount();
542    }
543
544    /**
545     * Check if this list query has a next page
546     * @return bool boolean result
547     */
548    public function hasNextPage()
549    {
550        $cp = $this->getCurrentPageNumber();
551        if ($cp === null) {
552            return false;
553        }
554        return ($cp + 1 <= $this->getNumberOfPages());
555    }
556
557    /**
558     * Check if this list query has a previous page
559     * @return bool boolean result
560     */
561    public function hasPreviousPage()
562    {
563        $cp = $this->getCurrentPageNumber();
564        if ($cp === null) {
565            return false;
566        }
567        return (($cp - 1) > 0);
568    }
569
570    /**
571     * Reset index in record list back to zero
572     * @return $this
573     */
574    public function rewindRecordList()
575    {
576        $this->recordIndex = 0;
577        return $this;
578    }
579
580    /**
581     * Check if column exists in response
582     * @param string $key column name
583     * @return bool boolean result
584     */
585    private function hasColumn($key)
586    {
587        return (array_search($key, $this->columnkeys) !== false);
588    }
589
590    /**
591     * Check if the record list contains a record for the
592     * current record index in use
593     * @return bool boolean result
594     */
595    private function hasCurrentRecord()
596    {
597        $len = $this->getRecordsCount();
598        return (
599            $len > 0 &&
600            $this->recordIndex >= 0 &&
601            $this->recordIndex < $len
602        );
603    }
604
605    /**
606     * Check if the record list contains a next record for the
607     * current record index in use
608     * @return bool boolean result
609     */
610    private function hasNextRecord()
611    {
612        $next = $this->recordIndex + 1;
613        return ($this->hasCurrentRecord() && ($next < $this->getRecordsCount()));
614    }
615
616    /**
617     * Check if the record list contains a previous record for the
618     * current record index in use
619     * @return bool boolean result
620     */
621    private function hasPreviousRecord()
622    {
623        return ($this->recordIndex > 0 && $this->hasCurrentRecord());
624    }
625}