Completed
Push — master ( bdd3b4...4b0c95 )
by Vítězslav
09:50
created

FlexiBeeRO::doCurlRequest()   B

Complexity

Conditions 5
Paths 12

Size

Total Lines 39
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 5
eloc 24
c 2
b 0
f 1
nc 12
nop 3
dl 0
loc 39
rs 8.439
1
<?php
2
/**
3
 * FlexiPeeHP - Read Only Access to FlexiBee class.
4
 *
5
 * @author     Vítězslav Dvořák <[email protected]>
6
 * @copyright  (C) 2015,2016 Spoje.Net
7
 */
8
9
namespace FlexiPeeHP;
10
11
class FlexiBeeRO extends \Ease\Brick
12
{
13
    /**
14
     * Základní namespace pro komunikaci s FlexiBEE.
15
     *
16
     * @var string Jmený prostor datového bloku odpovědi
17
     */
18
    public $nameSpace = 'winstrom';
19
20
    /**
21
     * Datový blok v poli odpovědi.
22
     *
23
     * @var string
24
     */
25
    public $resultField = 'results';
26
27
    /**
28
     * Verze protokolu použitého pro komunikaci.
29
     *
30
     * @var string Verze použitého API
31
     */
32
    public $protoVersion = '1.0';
33
34
    /**
35
     * Evidence užitá objektem.
36
     *
37
     * @link https://demo.flexibee.eu/c/demo/evidence-list Přehled evidencí
38
     * @var string
39
     */
40
    public $evidence = null;
41
42
    /**
43
     * Výchozí formát pro komunikaci.
44
     *
45
     * @link https://www.flexibee.eu/api/dokumentace/ref/format-types Přehled možných formátů
46
     *
47
     * @var string json|xml|...
48
     */
49
    public $format = 'json';
50
51
    /**
52
     * Curl Handle.
53
     *
54
     * @var resource
55
     */
56
    public $curl = null;
57
58
    /**
59
     * @link https://demo.flexibee.eu/devdoc/company-identifier Identifikátor firmy
60
     * @var string
61
     */
62
    public $company = null;
63
64
    /**
65
     * Server[:port]
66
     * @var string
67
     */
68
    public $url = null;
69
70
    /**
71
     * REST API Username
72
     * @var string
73
     */
74
    public $user = null;
75
76
    /**
77
     * REST API Password
78
     * @var string
79
     */
80
    public $password = null;
81
82
    /**
83
     * @var array Pole HTTP hlaviček odesílaných s každým požadavkem
84
     */
85
    public $defaultHttpHeaders = ['User-Agent' => 'FlexiPeeHP v1.4'];
86
87
    /**
88
     * Default additional request url parameters after question mark
89
     * 
90
     * @link https://www.flexibee.eu/api/dokumentace/ref/urls   Common params
91
     * @link https://www.flexibee.eu/api/dokumentace/ref/paging Paging params
92
     * @var array
93
     */
94
    public $defaultUrlParams = ['limit' => 0];
95
96
    /**
97
     * Identifikační řetězec.
98
     *
99
     * @var string
100
     */
101
    public $init = null;
102
103
    /**
104
     * Sloupeček s názvem.
105
     *
106
     * @var string
107
     */
108
    public $nameColumn = 'nazev';
109
110
    /**
111
     * Sloupeček obsahující datum vložení záznamu do shopu.
112
     *
113
     * @var string
114
     */
115
    public $myCreateColumn = 'false';
116
117
    /**
118
     * Slopecek obsahujici datum poslení modifikace záznamu do shopu.
119
     *
120
     * @var string
121
     */
122
    public $myLastModifiedColumn = 'lastUpdate';
123
124
    /**
125
     * Klíčový idendifikátor záznamu.
126
     *
127
     * @var string
128
     */
129
    public $fbKeyColumn = 'id';
130
131
    /**
132
     * Informace o posledním HTTP requestu.
133
     *
134
     * @var *
135
     */
136
    public $info;
137
138
    /**
139
     * Informace o poslední HTTP chybě.
140
     *
141
     * @var string
142
     */
143
    public $lastCurlError = null;
144
145
    /**
146
     * Used codes storage.
147
     *
148
     * @var array
149
     */
150
    public $codes = null;
151
152
    /**
153
     * Last Inserted ID.
154
     *
155
     * @var int
156
     */
157
    public $lastInsertedID = null;
158
159
    /**
160
     * Default Line Prefix.
161
     *
162
     * @var string
163
     */
164
    public $prefix = '/c/';
165
166
    /**
167
     * Raw Content of last curl response
168
     * 
169
     * @var string
170
     */
171
    public $lastCurlResponse;
172
173
    /**
174
     * HTTP Response code of last request
175
     *
176
     * @var int
177
     */
178
    public $lastResponseCode = null;
179
180
    /**
181
     * Array of fields for next curl POST operation
182
     *
183
     * @var array
184
     */
185
    protected $postFields = [];
186
187
    /**
188
     * Last operation result data or message(s)
189
     *
190
     * @var array
191
     */
192
    public $lastResult = null;
193
194
    /**
195
     * Nuber from  @rowCount
196
     * @var int
197
     */
198
    public $rowCount = null;
199
200
    /**
201
     * @link https://demo.flexibee.eu/devdoc/actions Provádění akcí
202
     * @var string
203
     */
204
    protected $action;
205
206
    /**
207
     * Pole akcí které podporuje ta která evidence
208
     * @link https://demo.flexibee.eu/c/demo/faktura-vydana/actions.json Např. Akce faktury
209
     * @var array
210
     */
211
    public $actionsAvailable = null;
212
213
    /**
214
     * Parmetry pro URL
215
     * @link https://www.flexibee.eu/api/dokumentace/ref/urls/ Všechny podporované parametry
216
     * @var array
217
     */
218
    public $urlParams = [
219
        'dry-run',
220
        'fail-on-warning',
221
        'report-name',
222
        'report-lang',
223
        'report-sign',
224
        'detail',
225
        'mode',
226
        'limit',
227
        'start',
228
        'order',
229
        'sort',
230
        'add-row-count',
231
        'relations',
232
        'includes',
233
        'use-ext-id',
234
        'use-internal-id',
235
        'stitky-as-ids',
236
        'only-ext-ids',
237
        'no-ext-ids',
238
        'no-ids',
239
        'code-as-id',
240
        'no-http-errors',
241
        'export-settings',
242
        'as-gui',
243
        'code-in-response',
244
        'add-global-version',
245
        'encoding',
246
        'delimeter',
247
        'format',
248
        'auth',
249
        'skupina-stitku',
250
        'dir',
251
        'xpath', // See: https://www.flexibee.eu/api/dokumentace/ref/xpath/
252
        'dry-run', // See: https://www.flexibee.eu/api/dokumentace/ref/dry-run/
253
        'inDesktopApp' // Note: Undocumented function (html only)
254
    ];
255
256
    /**
257
     * Class for read only interaction with FlexiBee.
258
     *
259
     * @param mixed $init default record id or initial data
260
     * @param array $options Connection settings override
261
     */
262
    public function __construct($init = null, $options = [])
263
    {
264
        $this->init = $init;
265
266
        parent::__construct();
267
        $this->setUp($options);
268
        $this->curlInit();
269
        if (!is_null($init)) {
270
            $this->processInit($init);
271
        }
272
    }
273
274
    /**
275
     * SetUp Object to be ready for connect
276
     *
277
     * @param array $options Object Options
278
     */
279
    public function setUp($options = [])
280
    {
281 View Code Duplication
        if (isset($options['company'])) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
282
            $this->company = $options['company'];
283
        } else {
284
            if (is_null($this->company) && defined('FLEXIBEE_COMPANY')) {
285
                $this->company = constant('FLEXIBEE_COMPANY');
286
            }
287
        }
288 View Code Duplication
        if (isset($options['url'])) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
289
            $this->url = $options['url'];
290
        } else {
291
            if (is_null($this->url) && defined('FLEXIBEE_URL')) {
292
                $this->url = constant('FLEXIBEE_URL');
293
            }
294
        }
295 View Code Duplication
        if (isset($options['user'])) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
296
            $this->user = $options['user'];
297
        } else {
298
            if (is_null($this->user) && defined('FLEXIBEE_LOGIN')) {
299
                $this->user = constant('FLEXIBEE_LOGIN');
300
            }
301
        }
302 View Code Duplication
        if (isset($options['password'])) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
303
            $this->password = $options['password'];
304
        } else {
305
            if (is_null($this->password) && defined('FLEXIBEE_PASSWORD')) {
306
                $this->password = constant('FLEXIBEE_PASSWORD');
307
            }
308
        }
309
        if (isset($options['evidence'])) {
310
            $this->setEvidence($options['evidence']);
311
        }
312
    }
313
314
    /**
315
     * Inicializace CURL
316
     */
317
    public function curlInit()
318
    {
319
        $this->curl = \curl_init(); // create curl resource
320
        curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true); // return content as a string from curl_exec
321
        curl_setopt($this->curl, CURLOPT_FOLLOWLOCATION, true); // follow redirects (compatibility for future changes in FlexiBee)
322
        curl_setopt($this->curl, CURLOPT_HTTPAUTH, true);       // HTTP authentication
323
        curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER, false); // FlexiBee by default uses Self-Signed certificates
324
        curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST, false);
325
        curl_setopt($this->curl, CURLOPT_VERBOSE, true); // For debugging
326
        curl_setopt($this->curl, CURLOPT_USERPWD,
327
            $this->user.':'.$this->password); // set username and password
328
    }
329
330
    /**
331
     * Zinicializuje objekt dle daných dat
332
     * 
333
     * @param mixed $init
334
     */
335
    public function processInit($init)
336
    {
337
        if (is_integer($init)) {
338
            $this->loadFromFlexiBee($init);
339
        } elseif (is_array($init)) {
340
            $this->takeData($init);
341
        } elseif (strstr($init, 'code:')) {
342
            $this->loadFromFlexiBee($init);
343
        }
344
    }
345
346
    /**
347
     * Nastaví Evidenci pro Komunikaci.
348
     *
349
     * @param string $evidence
350
     */
351
    public function setEvidence($evidence)
352
    {
353
        $this->evidence = $evidence;
354
    }
355
356
    /**
357
     * Vrací právě používanou evidenci pro komunikaci
358
     * 
359
     * @return string
360
     */
361
    public function getEvidence()
362
    {
363
        return $this->evidence;
364
    }
365
366
    /**
367
     * Vrací název evidence použité v odpovědích z FlexiBee
368
     *
369
     * @return string
370
     */
371
    public function getResponseEvidence()
372
    {
373
        switch ($this->evidence) {
374
            case 'c':
375
                $evidence = 'companies';
376
                break;
377
            default:
378
                $evidence = $this->getEvidence();
379
                break;
380
        }
381
        return $evidence;
382
    }
383
384
    /**
385
     * Převede rekurzivně Objekt na pole.
386
     *
387
     * @param object|array $object
388
     *
389
     * @return array
390
     */
391
    public static function object2array($object)
392
    {
393
        $result = null;
394
        if (is_object($object)) {
395
            $objectData = get_object_vars($object);
396
            if (is_array($objectData) && count($objectData)) {
397
                $result = array_map('self::object2array', $objectData);
398
            }
399 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
400
            if (is_array($object)) {
401
                foreach ($object as $item => $value) {
402
                    $result[$item] = self::object2array($value);
403
                }
404
            } else {
405
                $result = $object;
406
            }
407
        }
408
409
        return $result;
410
    }
411
412
    /**
413
     * Převede rekurzivně v poli všechny objekty na jejich identifikátory.
414
     *
415
     * @param object|array $object
416
     *
417
     * @return array
418
     */
419
    public static function objectToID($object)
420
    {
421
        $result = null;
422
        if (is_object($object)) {
423
            $result = $object->__toString();
424 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
425
            if (is_array($object)) {
426
                foreach ($object as $item => $value) {
427
                    $result[$item] = self::objectToID($value);
428
                }
429
            } else { //String
430
                $result = $object;
431
            }
432
        }
433
434
        return $result;
435
    }
436
437
    /**
438
     * Return basic URL for used Evidence
439
     *
440
     * @link https://www.flexibee.eu/api/dokumentace/ref/urls/ Sestavování URL
441
     * @param string $urlSuffix
442
     */
443
    public function getEvidenceURL($urlSuffix = null)
444
    {
445
        if (is_null($urlSuffix)) {
446
            $urlSuffix = $this->evidence;
447
        } elseif ($urlSuffix[0] == ';') {
448
            $urlSuffix = $this->evidence.$urlSuffix;
449
        }
450
        return $this->url.$this->prefix.$this->company.'/'.$urlSuffix;
451
    }
452
453
    /**
454
     * Funkce, která provede I/O operaci a vyhodnotí výsledek.
455
     *
456
     * @param string $urlSuffix část URL za identifikátorem firmy.
457
     * @param string $method    HTTP/REST metoda
458
     * @param string $format    Requested format
459
     * @return array|boolean Výsledek operace
460
     */
461
    public function performRequest($urlSuffix = null, $method = 'GET',
462
                                   $format = null)
463
    {
464
        $this->rowCount = null;
465
        $url            = $this->getEvidenceURL($urlSuffix);
466
467
        $responseCode = $this->doCurlRequest($url, $method, $format);
0 ignored issues
show
Documentation introduced by
$method is of type string, but the function expects a object<FlexiPeeHP\strinf>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
468
469
        if (is_null($format)) {
470
            $format = $this->format;
471
        }
472
473
        switch ($responseCode) {
474
            case 200:
475
            case 201:
476
                // Parse response
477
                $responseDecoded = [];
478
479
                switch ($format) {
480
                    case 'json':
481
                        $responseDecoded = json_decode($this->lastCurlResponse,
482
                            true, 10);
483
                        if (($method == 'PUT') && isset($responseDecoded[$this->nameSpace][$this->resultField][0]['id'])) {
484
                            $this->lastInsertedID = $responseDecoded[$this->nameSpace][$this->resultField][0]['id'];
485
                            $this->setMyKey($this->lastInsertedID);
486
                        } else {
487
                            $this->lastInsertedID = null;
488
                            if (isset($responseDecoded[$this->nameSpace]['@rowCount'])) {
489
                                $this->rowCount = (int) $responseDecoded[$this->nameSpace]['@rowCount'];
490
                            }
491
                        }
492
                        $decodeError = json_last_error_msg();
493
                        if ($decodeError != 'No error') {
494
                            $this->addStatusMessage($decodeError, 'error');
495
                        }
496
                        break;
497
                    case 'xml':
498
                        if (strlen($this->lastCurlResponse)) {
499
                            $responseDecoded = self::xml2array($this->lastCurlResponse);
500
                        } else {
501
                            $responseDecoded = null;
502
                        }
503
                        break;
504
                }
505
506
507
                $response         = $this->lastResult = $this->unifyResponseFormat($responseDecoded);
508
509
                break;
510
511
            default: //Some goes wrong
512
                $this->lastCurlError = curl_error($this->curl);
513
                switch ($format) {
514
                    case 'json':
515
                        $response = preg_replace_callback('/\\\\u([0-9a-fA-F]{4})/',
516
                            function ($match) {
517
                            return mb_convert_encoding(pack('H*', $match[1]),
518
                                'UTF-8', 'UCS-2BE');
519
                        }, $this->lastCurlResponse);
520
                        $response = (json_encode(json_decode($response, true, 10),
521
                                JSON_PRETTY_PRINT));
522
                        break;
523
                    case 'xml':
524
                        if (strlen($this->lastCurlResponse)) {
525
                            $response = self::xml2array($this->lastCurlResponse);
526
                        }
527
                        break;
528
                }
529
530
                if (is_array($response)) {
531
                    $result = urldecode(http_build_query($response));
0 ignored issues
show
Bug introduced by
The variable $response does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
532
                } elseif (strlen($response) && ($response != 'null')) {
533
                    $result = urldecode(http_build_query(self::object2array(current(json_decode($response)))));
534
                } else {
535
                    $result = null;
536
                }
537
538
                if ($response == 'null') {
539
                    if ($this->lastResponseCode == 200) {
540
                        $response = true;
541
                    } else {
542
                        $response = null;
543
                    }
544
                } else {
545
                    if (is_string($response)) {
546
                        $response = self::object2array(current(json_decode($response)));
547
                    }
548
                }
549
550
                if (is_array($response) && ($this->lastResponseCode == 400)) {
551
                    $this->logResult($response, $url);
552
                } else {
553
                    $this->addStatusMessage(sprintf('Error (HTTP %d): <pre>%s</pre> %s',
554
                            curl_getinfo($this->curl, CURLINFO_HTTP_CODE),
555
                            $result, $this->lastCurlError), 'error');
556
                    $this->addStatusMessage($url, 'info');
557
                    if (count($this->postFields)) {
558
                        if (is_array($result)) {
559
                            $this->addStatusMessage(urldecode(http_build_query($this->postFields)),
560
                                'debug');
561
                        } else {
562
                            $this->addStatusMessage(urldecode(http_build_query($this->getData())),
563
                                'debug');
564
                        }
565
                    }
566
                }
567
568
                break;
569
        }
570
        return $response;
571
    }
572
573
    /**
574
     * Vykonej HTTP požadavek
575
     *
576
     * @link https://www.flexibee.eu/api/dokumentace/ref/urls/ Sestavování URL
577
     * @param string $url    URL požadavku
578
     * @param strinf $method HTTP Method GET|POST|PUT|OPTIONS|DELETE
579
     * @param string $format požadovaný formát komunikace
580
     * @return int HTTP Response CODE
581
     */
582
    public function doCurlRequest($url, $method, $format = null)
583
    {
584
        if (is_null($format)) {
585
            $format = $this->format;
586
        }
587
        curl_setopt($this->curl, CURLOPT_URL, $url);
588
// Nastavení samotné operace
589
        curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, strtoupper($method));
590
//Vždy nastavíme byť i prázná postdata jako ochranu před chybou 411
591
        curl_setopt($this->curl, CURLOPT_POSTFIELDS, $this->postFields);
592
593
        $httpHeaders = $this->defaultHttpHeaders;
594
        switch ($format) {
595
            case 'json':
596
                $httpHeaders['Accept']       = 'application/json';
597
                $httpHeaders['Content-Type'] = 'application/json';
598
599
                break;
600
            case 'xml':
601
                $httpHeaders['Accept']       = 'application/xml';
602
                $httpHeaders['Content-Type'] = 'application/xml';
603
                break;
604
        }
605
606
        $httpHeadersFinal = [];
607
        foreach ($httpHeaders as $key => $value) {
608
            $httpHeadersFinal[] = $key.': '.$value;
609
        }
610
611
        curl_setopt($this->curl, CURLOPT_HTTPHEADER, $httpHeadersFinal);
612
613
// Proveď samotnou operaci
614
        $this->lastCurlResponse = curl_exec($this->curl);
615
616
        $this->info = curl_getinfo($this->curl);
617
618
        $this->lastResponseCode = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
619
        return $this->lastResponseCode;
620
    }
621
622
    /**
623
     * Nastaví druh prováděné akce.
624
     *
625
     * @link https://demo.flexibee.eu/devdoc/actions Provádění akcí
626
     * @param string $action
627
     * @return boolean
628
     */
629
    public function setAction($action)
630
    {
631
        $result = false;
632
        if (is_null($this->actionsAvailable)) {
633
            $this->action = $action;
634
            $result       = true;
635
        } else {
636
            if (array_search($action, $this->actionsAvailable)) {
637
                $this->action = $action;
638
                $result       = true;
639
            }
640
        }
641
        return $result;
642
    }
643
644
    /**
645
     * Convert XML to array.
646
     *
647
     * @param string $xml
648
     *
649
     * @return array
650
     */
651
    public static function xml2array($xml)
652
    {
653
        $arr = [];
654
655
        if (is_string($xml)) {
656
            $xml = simplexml_load_string($xml);
657
        }
658
659
        foreach ($xml->children() as $r) {
660
            if (count($r->children()) == 0) {
661
                $arr[$r->getName()] = strval($r);
662
            } else {
663
                $arr[$r->getName()][] = self::xml2array($r);
664
            }
665
        }
666
667
        return $arr;
668
    }
669
670
    /**
671
     * Odpojení od FlexiBee.
672
     */
673
    public function disconnect()
674
    {
675
        if (is_resource($this->curl)) {
676
            curl_close($this->curl);
677
        }
678
        $this->curl = null;
679
    }
680
681
    public function __destruct()
682
    {
683
        $this->disconnect();
684
    }
685
686
    /**
687
     * Načte řádek dat z FlexiBee.
688
     *
689
     * @param int $recordID id požadovaného záznamu
690
     *
691
     * @return array
692
     */
693
    public function getFlexiRow($recordID)
694
    {
695
        $record   = null;
696
        $response = $this->performRequest($this->evidence.'/'.$recordID.'.json');
697
        if (isset($response[$this->evidence])) {
698
            $record = $response[$this->evidence][0];
699
        }
700
701
        return $record;
702
    }
703
704
    /**
705
     * Oddělí z pole podmínek ty jenž patří za ? v URL požadavku
706
     *
707
     * @link https://www.flexibee.eu/api/dokumentace/ref/urls/ Sestavování URL
708
     * @param array $conditions pole podmínek   - rendrují se do ()
709
     * @param array $urlParams  pole parametrů  - rendrují za ?
710
     */
711
    public function extractUrlParams(&$conditions, &$urlParams)
712
    {
713
        foreach ($this->urlParams as $urlParam) {
714
            if (isset($conditions[$urlParam])) {
715
                \Ease\Sand::divDataArray($conditions, $urlParams, $urlParam);
716
            }
717
        }
718
    }
719
720
    /**
721
     * Načte data z FlexiBee.
722
     *
723
     * @param string $suffix     dotaz
724
     * @param string|array $conditions Volitelný filtrovací výraz
725
     */
726
    public function getFlexiData($suffix = null, $conditions = null)
727
    {
728
        $urlParams = $this->defaultUrlParams;
729
        if (!is_null($conditions)) {
730
            if (is_array($conditions)) {
731
                $this->extractUrlParams($conditions, $urlParams);
732
                $conditions = $this->flexiUrl($conditions);
733
            }
734
735
            if (strlen($conditions) && ($conditions[0] != '/')) {
736
                $conditions = '/'.rawurlencode('('.($conditions).')');
737
            }
738
        } else {
739
            $conditions = '';
740
        }
741
        if (strlen($suffix)) {
742
            $transactions = $this->performRequest($this->evidence.$conditions.'.'.$this->format.'?'.$suffix.'&'.http_build_query($urlParams),
743
                'GET');
744
        } else {
745
            $transactions = $this->performRequest($this->evidence.$conditions.'.'.$this->format.'?'.http_build_query($urlParams),
746
                'GET');
747
        }
748
        if (isset($transactions[$this->evidence])) {
749
            $result = $transactions[$this->evidence];
750
        } else {
751
            $result = $transactions;
752
        }
753
754
        return $result;
755
    }
756
757
    /**
758
     * Načte záznam z FlexiBee.
759
     *
760
     * @param int $id ID záznamu
761
     *
762
     * @return int počet načtených položek
763
     */
764 View Code Duplication
    public function loadFromFlexiBee($id = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
765
    {
766
        $data = [];
767
        if (is_null($id)) {
768
            $id = $this->getMyKey();
769
        }
770
771
        $flexidata = $this->getFlexiData(null, '/'.$id);
772
        if (count($flexidata) == 1) {
773
            $data = current($flexidata);
774
        }
775
        return $this->takeData($data);
776
    }
777
778
    /**
779
     * Převede data do Json formátu pro FlexiBee.
780
     *
781
     * @param array $data
782
     *
783
     * @return string
784
     */
785
    public function jsonizeData($data)
786
    {
787
        $jsonize = [
788
            $this->nameSpace => [
789
                '@version' => $this->protoVersion,
790
                $this->evidence => $this->objectToID($data),
791
            ],
792
        ];
793
794
        if (!is_null($this->action)) {
795
            $jsonize[$this->nameSpace][$this->evidence.'@action'] = $this->action;
796
            $this->action                                         = null;
797
        }
798
799
        return json_encode($jsonize);
800
    }
801
802
    /**
803
     * Test if given record ID exists in FlexiBee.
804
     *
805
     * @param string|int $identifer
806
     */
807
    public function idExists($identifer = null)
808
    {
809
        if (is_null($identifer)) {
810
            $identifer = $this->getMyKey();
811
        }
812
        $flexiData = $this->getFlexiData(
813
            'detail=custom:'.$this->getmyKeyColumn(), $identifer);
814
815
        return $flexiData;
816
    }
817
818
    /**
819
     * Test if given record exists in FlexiBee.
820
     *
821
     * @param array $data
822
     * @return boolean Record presence status
823
     */
824
    public function recordExists($data = null)
825
    {
826
        $found = null;
0 ignored issues
show
Unused Code introduced by
$found is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
827
        if (is_null($data)) {
828
            $data = $this->getData();
829
        }
830
831
        $res = $this->getColumnsFromFlexibee([$this->myKeyColumn],
832
            self::flexiUrl($data));
0 ignored issues
show
Documentation introduced by
self::flexiUrl($data) is of type string, but the function expects a array|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
833
834
        if (!count($res) || (isset($res['success']) && ($res['success'] == 'false'))
835
            || !count($res[0])) {
836
            $found = false;
837
        } else {
838
            $found = true;
839
        }
840
        return $found;
841
    }
842
843
    /**
844
     * Vrací z FlexiBee sloupečky podle podmínek.
845
     *
846
     * @param array|int|string $conditions pole podmínek nebo ID záznamu
847
     * @param string           $indexBy    klice vysledku naplnit hodnotou ze
848
     *                                     sloupečku
849
     * @return array
850
     */
851 View Code Duplication
    public function getAllFromFlexibee($conditions = null, $indexBy = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
852
    {
853
        if (is_int($conditions)) {
854
            $conditions = [$this->getmyKeyColumn() => $conditions];
855
        }
856
857
        $flexiData = $this->getFlexiData('', $conditions);
858
859
        if (!is_null($indexBy)) {
860
            $flexiData = $this->reindexArrayBy($flexiData);
861
        }
862
863
        return $flexiData;
864
    }
865
866
    /**
867
     * Vrací z FlexiBee sloupečky podle podmínek.
868
     *
869
     * @param string[] $columnsList seznam položek
870
     * @param array    $conditions  pole podmínek nebo ID záznamu
871
     * @param string   $indexBy     Sloupeček podle kterého indexovat záznamy
872
     *
873
     * @return array
874
     */
875
    public function getColumnsFromFlexibee($columnsList, $conditions = null,
876
                                           $indexBy = null)
877
    {
878
        $detail = 'full';
879
880
        if (is_int($conditions)) {
881
            $conditions = [$this->getmyKeyColumn() => $conditions];
882
        }
883
884
        if ($columnsList != '*') {
885
            if (is_array($columnsList)) {
886
            $columns = implode(',', array_unique($columnsList));
887
        } else {
888
            $columns = $columnsList;
889
        }
890
            $detail = 'custom:'.$columns;
891
        }
892
893
        $flexiData = $this->getFlexiData('detail='.$detail, $conditions);
894
895
        if (!is_null($indexBy)) {
896
            $flexiData = $this->reindexArrayBy($flexiData, $indexBy);
897
        }
898
899
        return $flexiData;
900
    }
901
902
    /**
903
     * Vrací kód záznamu.
904
     *
905
     * @param mixed $data
906
     *
907
     * @return string
908
     */
909
    public function getKod($data = null, $unique = true)
910
    {
911
        $kod = null;
912
913
        if (is_null($data)) {
914
            $data = $this->getData();
915
        }
916
917
        if (is_string($data)) {
918
            $data = [$this->nameColumn => $data];
919
        }
920
921
        if (isset($data['kod'])) {
922
            $kod = $data['kod'];
923
        } else {
924
            if (isset($data[$this->nameColumn])) {
925
                $kod = preg_replace('/[^a-zA-Z0-9]/', '',
926
                    \Ease\Sand::rip($data[$this->nameColumn]));
927
            } else {
928
                if (isset($data[$this->myKeyColumn])) {
929
                    $kod = \Ease\Sand::rip($data[$this->myKeyColumn]);
930
                }
931
            }
932
        }
933
934
        if (!strlen($kod)) {
935
            $kod = 'NOTSET';
936
        }
937
938
        if (strlen($kod) > 18) {
939
            $kodfinal = strtoupper(substr($kod, 0, 18));
940
        } else {
941
            $kodfinal = strtoupper($kod);
942
        }
943
944
        if ($unique) {
945
            $counter = 0;
946
            if (count($this->codes)) {
947
                foreach ($this->codes as $codesearch => $keystring) {
948
                    if (strstr($codesearch, $kodfinal)) {
949
                        ++$counter;
950
                    }
951
                }
952
            }
953
            if ($counter) {
954
                $kodfinal = $kodfinal.$counter;
955
            }
956
957
            $this->codes[$kodfinal] = $kod;
958
        }
959
960
        return $kodfinal;
961
    }
962
963
    /**
964
     * Write Operation Result.
965
     *
966
     * @param array  $resultData
967
     * @param string $url        URL
968
     * @return boolean Log save success
969
     */
970
    public function logResult($resultData = null, $url = null)
971
    {
972
        $logResult = false;
973
        if (isset($resultData['success']) && ($resultData['success'] == 'false')) {
974
            if (isset($resultData['message'])) {
975
                $this->addStatusMessage($resultData['message'], 'warning');
976
            }
977
            $this->addStatusMessage('Error '.$this->lastResponseCode.': '.urldecode($url),
978
                'warning');
979
            unset($url);
980
        }
981
        if (is_null($resultData)) {
982
            $resultData = $this->lastResult;
983
        }
984
        if (isset($url)) {
985
            $this->logger->addStatusMessage(urldecode($url));
986
        }
987
988
        if (isset($resultData['results'])) {
989
            $status = null;
0 ignored issues
show
Unused Code introduced by
$status is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
990
            if ($resultData['success'] == 'false') {
991
                $status = 'error';
992
            } else {
993
                $status = 'success';
994
            }
995
            foreach ($resultData['results'] as $result) {
996
                if (isset($result['request-id'])) {
997
                    $rid = $result['request-id'];
998
                } else {
999
                    $rid = '';
1000
                }
1001
                if (isset($result['errors'])) {
1002
                    foreach ($result['errors'] as $error) {
1003
                        $message = $error['message'];
1004
                        if (isset($error['for'])) {
1005
                            $message .= ' for: '.$error['for'];
1006
                        }
1007
                        if (isset($error['value'])) {
1008
                            $message .= ' value:'.$error['value'];
1009
                        }
1010
                        if (isset($error['code'])) {
1011
                            $message .= ' code:'.$error['code'];
1012
                        }
1013
                        $this->addStatusMessage($rid.': '.$message, $status);
1014
                    }
1015
                }
1016
            }
1017
        }
1018
1019
        if (is_object($this->logger)) {
1020
            $logResult = $this->logger->flush(get_class($this));
1021
        }
1022
        return $logResult;
1023
    }
1024
1025
    /**
1026
     * Save RAW Curl Request & Response to files in Temp directory
1027
     */
1028
    public function saveDebugFiles()
1029
    {
1030
        $tmpdir = sys_get_temp_dir();
1031
        file_put_contents($tmpdir.'/request-'.$this->evidence.'-'.microtime().'.'.$this->format,
1032
            $this->postFields);
1033
        file_put_contents($tmpdir.'/response-'.$this->evidence.'-'.microtime().'.'.$this->format,
1034
            $this->lastCurlResponse);
1035
    }
1036
1037
    /**
1038
     * Připraví data pro odeslání do FlexiBee
1039
     * 
1040
     * @param string $data
1041
     */
1042
    public function setPostFields($data)
1043
    {
1044
        $this->postFields = $data;
0 ignored issues
show
Documentation Bug introduced by
It seems like $data of type string is incompatible with the declared type array of property $postFields.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1045
    }
1046
1047
    /**
1048
     * Generuje fragment url pro filtrování.
1049
     *
1050
     * @see https://www.flexibee.eu/api/dokumentace/ref/filters
1051
     *
1052
     * @param array  $data
1053
     * @param string $joiner default and/or
1054
     * @param string $defop  default operator
1055
     *
1056
     * @return string
1057
     */
1058
    public static function flexiUrl(array $data, $joiner = 'and', $defop = 'eq')
1059
    {
1060
        $flexiUrl = '';
0 ignored issues
show
Unused Code introduced by
$flexiUrl is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1061
        $parts    = [];
1062
1063
        foreach ($data as $column => $value) {
1064
            if (is_integer($data[$column]) || is_float($data[$column])) {
1065
                $parts[$column] = $column.' eq \''.$data[$column].'\'';
1066
            } elseif (is_bool($data[$column])) {
1067
                $parts[$column] = $data[$column] ? $column.' eq true' : $column.' eq false';
1068
            } elseif (is_null($data[$column])) {
1069
                $parts[$column] = $column." is null";
1070
            } elseif ($value == '!null') {
1071
                $parts[$column] = $column." is not null";
1072
            } else {
1073
                $parts[$column] = $column." $defop '".$data[$column]."'";
1074
            }
1075
        }
1076
1077
        $flexiUrl = implode(' '.$joiner.' ', $parts);
1078
1079
        return $flexiUrl;
1080
    }
1081
1082
    /**
1083
     * Vrací identifikátor objektu code: nebo id:
1084
     *
1085
     * @link https://demo.flexibee.eu/devdoc/identifiers Identifikátory záznamů
1086
     * @return string indentifikátor záznamu reprezentovaného objektem
1087
     */
1088
    public function __toString()
1089
    {
1090
        $myCode = $this->getDataValue('kod');
1091
        if ($myCode) {
1092
            $id = 'code:'.$myCode;
1093
        } else {
1094
            $id = $this->getDataValue('id');
1095
            if (is_null($id)) {
1096
                $this->addToLog('Object Data does not contain code: or id: cannot match with statement!',
1097
                    'warning');
1098
            }
1099
        }
1100
        return $id;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $id; (object|integer|double|string|array|boolean|null) is incompatible with the return type of the parent method Ease\Sand::__toString of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1101
    }
1102
1103
    /**
1104
     * Gives you FlexiPeeHP class name for Given Evidence
1105
     *
1106
     * @param string $evidence
1107
     * @return string Class name
1108
     */
1109
    static public function evidenceToClassName($evidence)
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
1110
    {
1111
        return str_replace(' ', '', ucwords(str_replace('-', ' ', $evidence)));
1112
    }
1113
1114
    /**
1115
     * Vrací hodnotu daného externího ID
1116
     *
1117
     * @param string $want Which ? If empty,you obtain the first one.
1118
     * @return string
1119
     */
1120
    public function getExternalID($want = null)
1121
    {
1122
        $extid = null;
1123
        $ids   = $this->getDataValue('external-ids');
1124
        if (is_null($want)) {
1125
            if (count($ids)) {
1126
                $extid = current($ids);
1127
            }
1128
        } else {
1129
            if (!is_null($ids)) {
1130
                foreach ($ids as $id) {
0 ignored issues
show
Bug introduced by
The expression $ids of type object|integer|double|string|array|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1131
                    if (strstr($id, 'ext:'.$want)) {
1132
                        $extid = str_replace('ext:'.$want.':', '', $id);
1133
                    }
1134
                }
1135
            }
1136
        }
1137
        return $extid;
1138
    }
1139
1140
    /**
1141
     * Vrací aktuální globální verzi změn
1142
     *
1143
     * @link https://www.flexibee.eu/api/dokumentace/ref/changes-api#globalVersion Globální Verze
1144
     * @return type
1145
     */
1146
    public function getGlobalVersion()
1147
    {
1148
        $globalVersion = null;
1149
        if (!count($this->lastResult) || !isset($this->lastResult['@globalVersion'])) {
1150
            $this->getFlexiData(null,
1151
                ['add-global-version' => 'true', 'limit' => 1]);
1152
        }
1153
1154
        if (isset($this->lastResult['@globalVersion'])) {
1155
            $globalVersion = intval($this->lastResult['@globalVersion']);
1156
        }
1157
1158
        return $globalVersion;
1159
    }
1160
1161
    /**
1162
     * Return the same response format for one and multiplete results
1163
     * 
1164
     * @param array $responseRaw
1165
     * @return array
1166
     */
1167
    public function unifyResponseFormat($responseRaw)
1168
    {
1169
        $response = null;
1170
        $evidence = $this->getResponseEvidence();
1171
        if (is_array($responseRaw)) {
1172
            // Get response body root automatically
1173
            if (array_key_exists($this->nameSpace, $responseRaw)) { //Unifi response format
1174
                $responseBody = $responseRaw[$this->nameSpace];
1175
                if (array_key_exists($evidence, $responseBody)) {
1176
                    $evidenceContent = $responseBody[$evidence];
1177
                    if (array_key_exists(0, $evidenceContent)) {
1178
                        $response[$evidence] = $evidenceContent; //Multiplete Results
1179
                    } else {
1180
                        $response[$evidence][0] = $evidenceContent; //One result
1181
                    }
1182
                } else {
1183
                    $response = $responseBody;
1184
                }
1185
            } else {
1186
                $response = $responseRaw;
1187
            }
1188
        }
1189
        return $response;
1190
    }
1191
1192
}
1193