Completed
Push — master ( 5edd48...19a6ea )
by Vítězslav
03:08
created

FlexiBeeRO   D

Complexity

Total Complexity 129

Size/Duplication

Total Lines 897
Duplicated Lines 4.91 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 3
Bugs 0 Features 1
Metric Value
c 3
b 0
f 1
dl 44
loc 897
rs 4.4444
wmc 129
lcom 1
cbo 2

24 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A curlInit() 0 12 1
A setEvidence() 0 4 1
B object2array() 9 20 6
A objectToID() 9 17 4
F performRequest() 0 124 24
A processRequest() 0 4 1
A xml2array() 0 19 4
A disconnect() 0 7 2
A __destruct() 0 4 1
A loadFlexiData() 0 4 1
A getFlexiRow() 0 10 2
F getFlexiData() 0 46 11
A loadFromFlexiBee() 0 8 2
A jsonizeData() 0 11 1
A idExists() 0 10 2
A recordExists() 0 11 2
A getAllFromFlexibee() 7 19 4
C getColumnsFromFlexibee() 7 32 7
D getKod() 0 53 13
C searchString() 12 52 15
C logResult() 0 44 13
C flexiUrl() 0 23 8
A __toString() 0 13 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like FlexiBeeRO often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FlexiBeeRO, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * FlexiPeeHP - Třída pro čtení z FlexiBee.
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
     * @var type
60
     */
61
    public $company = FLEXIBEE_COMPANY;
62
63
    /**
64
     * @var string
65
     */
66
    public $url = FLEXIBEE_URL;
67
68
    /**
69
     * @var string
70
     */
71
    public $user = FLEXIBEE_LOGIN;
72
73
    /**
74
     * @var string
75
     */
76
    public $password = FLEXIBEE_PASSWORD;
77
78
    /**
79
     * @var array Pole HTTP hlaviček odesílaných s každým požadavkem
80
     */
81
    public $defaultHttpHeaders = ['User-Agent: FlexiPeeHP'];
82
83
    /**
84
     * Default additional request url parameters after question mark
85
     * 
86
     * @link https://www.flexibee.eu/api/dokumentace/ref/urls   Common params
87
     * @link https://www.flexibee.eu/api/dokumentace/ref/paging Paging params
88
     * @var array
89
     */
90
    public $defaultUrlParams = ['limit' => 0];
91
92
    /**
93
     * Identifikační řetězec.
94
     *
95
     * @var string
96
     */
97
    public $init = null;
98
99
    /**
100
     * Sloupeček s názvem.
101
     *
102
     * @var string
103
     */
104
    public $nameColumn = 'nazev';
105
106
    /**
107
     * Sloupeček obsahující datum vložení záznamu do shopu.
108
     *
109
     * @var string
110
     */
111
    public $myCreateColumn = 'false';
112
113
    /**
114
     * Slopecek obsahujici datum poslení modifikace záznamu do shopu.
115
     *
116
     * @var string
117
     */
118
    public $myLastModifiedColumn = 'lastUpdate';
119
120
    /**
121
     * Klíčový idendifikátor záznamu.
122
     *
123
     * @var string
124
     */
125
    public $fbKeyColumn = 'id';
126
127
    /**
128
     * Informace o posledním HTTP requestu.
129
     *
130
     * @var array
131
     */
132
    public $info;
133
134
    /**
135
     * Informace o poslední HTTP chybě.
136
     *
137
     * @var array
138
     */
139
    public $error;
140
141
    /**
142
     * Used codes storage.
143
     *
144
     * @var array
145
     */
146
    public $codes = null;
147
148
    /**
149
     * Last Inserted ID.
150
     *
151
     * @var int
152
     */
153
    public $lastInsertedID = null;
154
155
    /**
156
     * Default Line Prefix.
157
     *
158
     * @var string
159
     */
160
    public $prefix = '/c/';
161
162
    /**
163
     * HTTP Response code of last request
164
     *
165
     * @var int
166
     */
167
    public $lastResponseCode = null;
168
169
    /**
170
     * Array of fields for next curl POST operation
171
     *
172
     * @var array
173
     */
174
    protected $postFields = [];
175
176
    /**
177
     * Last operation result data or message(s)
178
     *
179
     * @var array
180
     */
181
    public $lastResult = null;
182
183
    /**
184
     * Třída pro práci s FlexiBee.
185
     *
186
     * @param string $init výchozí selektor dat
187
     */
188
    public function __construct($init = null)
189
    {
190
        $this->init = $init;
191
192
        parent::__construct();
193
        $this->curlInit();
194
    }
195
196
    public function curlInit()
197
    {
198
        $this->curl = \curl_init(); // create curl resource
199
        curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true); // return content as a string from curl_exec
200
        curl_setopt($this->curl, CURLOPT_FOLLOWLOCATION, true); // follow redirects (compatibility for future changes in FlexiBee)
201
        curl_setopt($this->curl, CURLOPT_HTTPAUTH, true);       // HTTP authentication
202
        curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER, false); // FlexiBee by default uses Self-Signed certificates
203
        curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST, false);
204
        curl_setopt($this->curl, CURLOPT_VERBOSE, true); // For debugging
205
        curl_setopt($this->curl, CURLOPT_USERPWD,
206
            $this->user.':'.$this->password); // set username and password
207
    }
208
209
    /**
210
     * Nastaví Agendu pro Komunikaci.
211
     *
212
     * @param string $evidence
213
     */
214
    public function setEvidence($evidence)
215
    {
216
        $this->evidence = $evidence;
217
    }
218
219
    /**
220
     * Převede rekurzivně Objekt na pole.
221
     *
222
     * @param object|array $object
223
     *
224
     * @return array
225
     */
226
    public static function object2array($object)
227
    {
228
        $result = null;
229
        if (is_object($object)) {
230
            $objectData = get_object_vars($object);
231
            if (is_array($objectData) && count($objectData)) {
232
                $result = array_map('self::object2array', $objectData);
233
            }
234 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...
235
            if (is_array($object)) {
236
                foreach ($object as $item => $value) {
237
                    $result[$item] = self::object2array($value);
238
                }
239
            } else {
240
                $result = $object;
241
            }
242
        }
243
244
        return $result;
245
    }
246
247
    /**
248
     * Převede rekurzivně Objekt na pole.
249
     *
250
     * @param object|array $object
251
     *
252
     * @return array
253
     */
254
    public static function objectToID($object)
255
    {
256
        $result = null;
257
        if (is_object($object)) {
258
            $result = $object->__toString();
259 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...
260
            if (is_array($object)) {
261
                foreach ($object as $item => $value) {
262
                    $result[$item] = self::objectToID($value);
263
                }
264
            } else {
265
                $result = $object;
266
            }
267
        }
268
269
        return $result;
270
    }
271
272
    /**
273
     * Funkce, která provede I/O operaci a vyhodnotí výsledek.
274
     *
275
     * @param string $urlSuffix část URL za identifikátorem firmy.
276
     * @param string $method    HTTP/REST metoda
277
     * @param string $format    Requested format
278
     * @return array Výsledek operace
279
     */
280
    public function performRequest($urlSuffix = null, $method = 'GET',
281
                                   $format = null)
282
    {
283
        if (is_null($format)) {
284
            $format = $this->format;
285
        }
286
        if (is_null($urlSuffix)) {
287
            $urlSuffix = $this->evidence.'.'.$format;
288
        }
289
        $url = $this->url.$this->prefix.$this->company.'/'.$urlSuffix;
290
        curl_setopt($this->curl, CURLOPT_URL, $url);
291
// Nastavení samotné operace
292
        curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $method);
293
//Vždy nastavíme byť i prázná postdata jako ochranu před chybou 411
294
        curl_setopt($this->curl, CURLOPT_POSTFIELDS, $this->postFields);
295
296
        $httpHeaders = $this->defaultHttpHeaders;
297
        switch ($format) {
298
            case 'json':
299
                $httpHeaders[] = 'Accept: application/json';
300
                break;
301
            case 'xml':
302
                $httpHeaders[] = 'Accept: application/xml';
303
                break;
304
305
            default:
306
                break;
307
        }
308
309
        curl_setopt($this->curl, CURLOPT_HTTPHEADER, $httpHeaders);
310
311
// Proveď samotnou operaci
312
        $response = curl_exec($this->curl);
313
314
        $this->info = curl_getinfo($this->curl);
0 ignored issues
show
Documentation Bug introduced by
It seems like curl_getinfo($this->curl) of type * is incompatible with the declared type array of property $info.

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...
315
316
        $this->lastResponseCode = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
317
318
        if ($this->lastResponseCode != 200 && $this->lastResponseCode != 201) {
319
            $this->error = curl_error($this->curl);
0 ignored issues
show
Documentation Bug introduced by
It seems like curl_error($this->curl) of type string is incompatible with the declared type array of property $error.

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...
320
            switch ($format) {
321
                case 'json':
322
                    $response = preg_replace_callback('/\\\\u([0-9a-fA-F]{4})/',
323
                        function ($match) {
324
                        return mb_convert_encoding(pack('H*', $match[1]),
325
                            'UTF-8', 'UCS-2BE');
326
                    }, $response);
327
                    $response = (json_encode(json_decode($response, true, 10),
328
                            JSON_PRETTY_PRINT));
329
                    break;
330
                case 'xml':
331
                    if (strlen($response)) {
332
                        $response = self::xml2array($response);
333
                    }
334
                    break;
335
            }
336
337
            if (is_array($response)) {
338
                $result = urldecode(http_build_query($response));
339
            } elseif (strlen($response) && ($response != 'null')) {
340
                $result = urldecode(http_build_query(self::object2array(current(json_decode($response)))));
341
            } else {
342
                $result = null;
343
            }
344
345
            if ($response == 'null') {
346
                if ($this->lastResponseCode == 200) {
347
                    $response = true;
348
                } else {
349
                    $response = null;
350
                }
351
            } else {
352
                if (is_string($response)) {
353
                    $response = self::object2array(current(json_decode($response)));
354
                }
355
            }
356
357
            if ($this->lastResponseCode == 400) {
358
                $this->logResult($response);
359
            } else {
360
                $this->addStatusMessage(sprintf('Error (HTTP %d): <pre>%s</pre> %s',
361
                        curl_getinfo($this->curl, CURLINFO_HTTP_CODE), $result,
362
                        $this->error), 'error');
363
                $this->addStatusMessage($url, 'info');
364
                if ($this->postFields) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->postFields of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
365
                    $this->addStatusMessage(urldecode(http_build_query($this->postFields)),
366
                        'debug');
367
                }
368
            }
369
370
371
            return $response;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $response; (boolean|null|array|object|integer|double) is incompatible with the return type documented by FlexiPeeHP\FlexiBeeRO::performRequest of type array.

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...
372
        }
373
374
        // Parse response
375
        switch ($format) {
376
            case 'json':
377
                $decoded = json_decode($response, true, 10);
378
                if (($method == 'PUT') && isset($decoded[$this->nameSpace][$this->resultField][0]['id'])) {
379
                    $this->lastInsertedID = $decoded[$this->nameSpace][$this->resultField][0]['id'];
380
                } else {
381
                    $this->lastInsertedID = null;
382
                }
383
//                $decodeError = json_last_error_msg();
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
384
//                $this->addStatusMessage($decodeError);
385
386
                break;
387
            case 'xml':
388
                if (strlen($response)) {
389
                    $decoded = self::xml2array($response);
390
                } else {
391
                    $decoded = null;
392
                }
393
                break;
394
        }
395
396
        // Get response body root automatically
397
        if (isset($decoded[$this->nameSpace])) {
398
            $decoded = $decoded[$this->nameSpace];
399
        }
400
401
        $this->lastResult = $decoded;
0 ignored issues
show
Bug introduced by
The variable $decoded 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...
Documentation Bug introduced by
It seems like $decoded of type * is incompatible with the declared type array of property $lastResult.

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...
402
        return $decoded;
403
    }
404
405
    /**
406
     * Todo: Sem vyčlenit zpracování výsledku requestu
407
     */
408
    public function processRequest()
409
    {
410
411
    }
412
413
    /**
414
     * Convert XML to array.
415
     *
416
     * @param string $xml
417
     *
418
     * @return array
419
     */
420
    public static function xml2array($xml)
421
    {
422
        $arr = [];
423
424
        if (is_string($xml)) {
425
            $xml = simplexml_load_string($xml);
426
        }
427
428
        foreach ($xml->children() as $r) {
429
            $t = [];
0 ignored issues
show
Unused Code introduced by
$t 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...
430
            if (count($r->children()) == 0) {
431
                $arr[$r->getName()] = strval($r);
432
            } else {
433
                $arr[$r->getName()][] = self::xml2array($r);
434
            }
435
        }
436
437
        return $arr;
438
    }
439
440
    /**
441
     * Odpojení od FlexiBee.
442
     */
443
    public function disconnect()
444
    {
445
        if (is_resource($this->curl)) {
446
            curl_close($this->curl);
447
        }
448
        $this->curl = null;
449
    }
450
451
    public function __destruct()
452
    {
453
        $this->disconnect();
454
    }
455
456
    /**
457
     * Načte data z FlexiBee.
458
     *
459
     * @param string $suffix dotaz
460
     */
461
    public function loadFlexiData($suffix = null)
462
    {
463
        return $this->takeData($this->getFlexiData($suffix));
464
    }
465
466
    /**
467
     * Načte řádek dat z FlexiBee.
468
     *
469
     * @param int $recordID id požadovaného záznamu
470
     *
471
     * @return array
472
     */
473
    public function getFlexiRow($recordID)
474
    {
475
        $record   = null;
476
        $response = $this->performRequest($this->evidence.'/'.$recordID.'.json');
477
        if (isset($response[$this->evidence])) {
478
            $record = $response[$this->evidence][0];
479
        }
480
481
        return $record;
482
    }
483
484
    /**
485
     * Načte data z FlexiBee.
486
     *
487
     * @param string $suffix     dotaz
488
     * @param string|array $conditions Volitelný filtrovací výraz
489
     */
490
    public function getFlexiData($suffix = null, $conditions = null)
491
    {
492
        $urlParams = $this->defaultUrlParams;
493
        if (!is_null($conditions)) {
494
            if (is_array($conditions)) {
495
496
                if (isset($conditions['sort'])) {
497
                    \Ease\Sand::divDataArray($conditions, $urlParams, 'sort');
498
                }
499
                if (isset($conditions['order'])) {
500
                    \Ease\Sand::divDataArray($conditions, $urlParams, 'order');
501
                }
502
                if (isset($conditions['limit'])) {
503
                    \Ease\Sand::divDataArray($conditions, $urlParams, 'limit');
504
                }
505
                if (isset($conditions['start'])) {
506
                    \Ease\Sand::divDataArray($conditions, $urlParams, 'start');
507
                }
508
                if (isset($conditions['add-row-count'])) {
509
                    \Ease\Sand::divDataArray($conditions, $urlParams,
510
                        'add-row-count');
511
                }
512
513
                $conditions = $this->flexiUrl($conditions);
514
            }
515
            if ($conditions[0] != '/') {
516
                $conditions = '/'.rawurlencode('('.($conditions).')');
517
            }
518
        } else {
519
            $conditions = '';
520
        }
521
        if ($suffix) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $suffix of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
522
            $transactions = $this->performRequest($this->evidence.$conditions.'.'.$this->format.'?'.$suffix.'&'.http_build_query($urlParams),
523
                'GET');
524
        } else {
525
            $transactions = $this->performRequest($this->evidence.$conditions.'.'.$this->format.'?'.http_build_query($urlParams),
526
                'GET');
527
        }
528
        if (isset($transactions[$this->evidence])) {
529
            $result = $transactions[$this->evidence];
530
        } else {
531
            $result = $transactions;
532
        }
533
534
        return $result;
535
    }
536
537
    /**
538
     * Načte záznam z FlexiBee.
539
     *
540
     * @param int $id ID záznamu
541
     *
542
     * @return int počet načtených položek
543
     */
544
    public function loadFromFlexiBee($id = null)
545
    {
546
        if (is_null($id)) {
547
            $id = $this->getMyKey();
548
        }
549
550
        return $this->takeData($this->getFlexiData('/'.$id));
551
    }
552
553
    /**
554
     * Převede data do Json formátu pro FlexiBee.
555
     *
556
     * @param array $data
557
     *
558
     * @return string
559
     */
560
    public function jsonizeData($data)
561
    {
562
        $jsonize = [
563
            $this->nameSpace => [
564
                '@version' => $this->protoVersion,
565
                $this->evidence => $this->objectToID($data),
566
            ],
567
        ];
568
569
        return json_encode($jsonize);
570
    }
571
572
    /**
573
     * Test if given record ID exists in FlexiBee.
574
     *
575
     * @param string|int $identifer
576
     */
577
    public function idExists($identifer = null)
578
    {
579
        if (is_null($identifer)) {
580
            $identifer = $this->getMyKey();
581
        }
582
        $flexiData = $this->getFlexiData(
583
            'detail=custom:'.$this->getmyKeyColumn(), $identifer);
584
585
        return $flexiData;
586
    }
587
588
    /**
589
     * Test if given record exists in FlexiBee.
590
     *
591
     * @param array $data
592
     */
593
    public function recordExists($data = null)
594
    {
595
        if (is_null($data)) {
596
            $data = $this->getData();
597
        }
598
599
        $res = $this->getColumnsFromFlexibee([$this->myKeyColumn],
600
            self::flexiUrl($data));
601
602
        return $res;
603
    }
604
605
    /**
606
     * Vrací z FlexiBee sloupečky podle podmínek.
607
     *
608
     * @param array|int|string $conditions pole podmínek nebo ID záznamu
609
     * @param array|string     $orderBy    třídit dle
610
     * @param string           $indexBy    klice vysledku naplnit hodnotou ze
611
     *                                     sloupečku
612
     * @return array
613
     */
614
    public function getAllFromFlexibee($conditions = null, $orderBy = null,
0 ignored issues
show
Unused Code introduced by
The parameter $orderBy is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
615
                                       $indexBy = null)
616
    {
617
        if (is_int($conditions)) {
618
            $conditions = [$this->getmyKeyColumn() => $conditions];
619
        }
620
621
        $flexiData = $this->getFlexiData('', $conditions);
622
623 View Code Duplication
        if ($indexBy) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $indexBy of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
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...
624
            $flexiData2 = [];
625
            foreach ($flexiData as $dataID => $data) {
626
                $flexiData2[$data[$indexBy]] = $data;
627
            }
628
            $flexiData = $flexiData2;
629
        }
630
631
        return $flexiData;
632
    }
633
634
    /**
635
     * Vrací z FlexiBee sloupečky podle podmínek.
636
     *
637
     * @param string[]         $columnsList seznam položek
638
     * @param array|int|string $conditions  pole podmínek nebo ID záznamu
639
     * @param array|string     $orderBy     třídit dle
640
     * @param string           $indexBy     klice vysledku naplnit hodnotou ze
641
     *                                      sloupečku
642
     * @param int              $limit       maximální počet vrácených záznamů
643
     *
644
     * @return array
645
     */
646
    public function getColumnsFromFlexibee($columnsList, $conditions = null,
647
                                           $orderBy = null, $indexBy = null,
0 ignored issues
show
Unused Code introduced by
The parameter $orderBy is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
648
                                           $limit = null)
0 ignored issues
show
Unused Code introduced by
The parameter $limit is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
649
    {
650
        if (($columnsList != '*') && !count($columnsList)) {
651
            $this->error('getColumnsFromFlexiBee: Missing ColumnList');
652
653
            return;
654
        }
655
656
        if (is_int($conditions)) {
657
            $conditions = [$this->getmyKeyColumn() => $conditions];
658
        }
659
660
        if (is_array($columnsList)) {
661
            $columns = implode(',', array_unique($columnsList));
662
        } else {
663
            $columns = $columnsList;
664
        }
665
666
        $flexiData = $this->getFlexiData('detail=custom:'.$columns, $conditions);
667
668 View Code Duplication
        if ($indexBy) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $indexBy of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
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...
669
            $flexiData2 = [];
670
            foreach ($flexiData as $dataID => $data) {
671
                $flexiData2[$data[$indexBy]] = $data;
672
            }
673
            $flexiData = $flexiData2;
674
        }
675
676
        return $flexiData;
677
    }
678
679
    /**
680
     * Vrací kód záznamu.
681
     *
682
     * @param mixed $data
683
     *
684
     * @todo papat i string
685
     *
686
     * @return string
687
     */
688
    public function getKod($data = null, $unique = true)
689
    {
690
        $kod = null;
691
692
        if (is_null($data)) {
693
            $data = $this->getData();
694
        }
695
696
        if (is_string($data)) {
697
            $data = [$this->nameColumn => $data];
698
        }
699
700
        if (isset($data['kod'])) {
701
            $kod = $data['kod'];
702
        } else {
703
            if (isset($data[$this->nameColumn])) {
704
                $kod = preg_replace('/[^a-zA-Z0-9]/', '',
705
                    \Ease\Sand::rip($data[$this->nameColumn]));
706
            } else {
707
                if (isset($data[$this->myKeyColumn])) {
708
                    $kod = \Ease\Sand::rip($data[$this->myKeyColumn]);
709
                }
710
            }
711
        }
712
713
        if (!strlen($kod)) {
714
            $kod = 'NOTSET';
715
        }
716
717
        if (strlen($kod) > 18) {
718
            $kodfinal = strtoupper(substr($kod, 0, 18));
719
        } else {
720
            $kodfinal = strtoupper($kod);
721
        }
722
723
        if ($unique) {
724
            $counter = 0;
725
            if ($this->codes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->codes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
726
                foreach ($this->codes as $codesearch => $keystring) {
727
                    if (strstr($codesearch, $kodfinal)) {
728
                        ++$counter;
729
                    }
730
                }
731
            }
732
            if ($counter) {
733
                $kodfinal = $kodfinal.$counter;
734
            }
735
736
            $this->codes[$kodfinal] = $kod;
737
        }
738
739
        return $kodfinal;
740
    }
741
742
    /**
743
     * Vyhledavani v záznamech objektu FlexiBee.
744
     *
745
     * @param string $what hledaný výraz
746
     *
747
     * @return array pole výsledků
748
     */
749
    public function searchString($what)
750
    {
751
        $results   = [];
752
        $conds     = [];
753
        $columns[] = $this->myKeyColumn;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$columns was never initialized. Although not strictly required by PHP, it is generally a good practice to add $columns = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
754
        foreach ($this->useKeywords as $keyword => $keywordInfo) {
0 ignored issues
show
Bug introduced by
The property useKeywords does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
755
            if (isset($this->keywordsInfo[$keyword]['virtual']) && ($this->keywordsInfo[$keyword]['virtual']
0 ignored issues
show
Bug introduced by
The property keywordsInfo does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
756
                == true)) {
757
                if ($keyword == $this->nameColumn) {
758
                    $this->nameColumn = $this->myKeyColumn;
759
                }
760
                continue;
761
            }
762
            switch ($keywordInfo) {
763
                case 'INT':
764 View Code Duplication
                case 'FLOAT':
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...
765
                    if (is_numeric($what)) {
766
                        $conds[]   = "($keyword = ".$what.')';
767
                        $columns[] = "$keyword";
768
                    }
769
                    break;
770
                case 'TEXT':
771 View Code Duplication
                case 'STRING':
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...
772
                    if (is_string($what)) {
773
                        $conds[]   = "( $keyword like '".$what."')";
774
                        $columns[] = "$keyword";
775
                    }
776
                    break;
777
                default:
778
                    break;
779
            }
780
        }
781
782
//        $res = \Ease\Shared::db()->queryToArray('SELECT ' . implode(',', $columns) . ',' . $this->nameColumn . ' FROM ' . $this->myTable . ' WHERE ' . implode(' OR ', $conds) . ' ORDER BY ' . $this->nameColumn, $this->myKeyColumn);
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
783
784
        $res = $this->getColumnsFromFlexibee($columns, implode(' or ', $conds));
785
786
        foreach ($res as $result) {
787
            $occurences = '';
788
            foreach ($result as $key => $value) {
789
                if (is_array($value)) {
790
                    continue;
791
                }
792
                if (mb_stristr($value, $what)) {
793
                    $occurences .= '('.$key.': '.$value.')';
794
                }
795
            }
796
            $results[$result[$this->myKeyColumn]] = [$this->nameColumn => $result[$this->nameColumn],
797
                'what' => $occurences,];
798
        }
799
        return $results;
800
    }
801
802
    /**
803
     * Write Operation Result.
804
     *
805
     * @param array  $resultData
806
     * @param string $url        URL
807
     */
808
    public function logResult($resultData = null, $url = null)
809
    {
810
        if (is_null($resultData)) {
811
            $resultData = $this->lastResult;
812
        }
813
        if ($url) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $url of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
814
            $this->logger->addStatusMessage($url);
815
        }
816
817
        if (isset($resultData['results'])) {
818
            $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...
819
            if ($resultData['success'] == 'false') {
820
                $status = 'error';
821
            } else {
822
                $status = 'success';
823
            }
824
            foreach ($resultData['results'] as $result) {
825
                if (isset($result['request-id'])) {
826
                    $rid = $result['request-id'];
827
                } else {
828
                    $rid = '';
829
                }
830
                if (isset($result['errors'])) {
831
                    foreach ($result['errors'] as $error) {
832
                        $message = $error['message'];
833
                        if (isset($error['for'])) {
834
                            $message .= ' for: '.$error['for'];
835
                        }
836
                        if (isset($error['value'])) {
837
                            $message .= ' value:'.$error['value'];
838
                        }
839
                        if (isset($error['code'])) {
840
                            $message .= ' code:'.$error['code'];
841
                        }
842
                        $this->logger->addStatusMessage($rid.': '.$message,
843
                            $status);
844
                    }
845
                }
846
            }
847
        }
848
        if (is_object($this->logger)) {
849
            $this->logger->flush(get_class($this));
850
        }
851
    }
852
853
    /**
854
     * Generuje fragment url pro filtrování.
855
     *
856
     * @see https://www.flexibee.eu/api/dokumentace/ref/filters
857
     *
858
     * @param array  $data
859
     * @param string $operator default and/or
860
     *
861
     * @return string
862
     */
863
    public static function flexiUrl(array $data, $operator = 'and')
864
    {
865
        $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...
866
        $parts    = [];
867
868
        foreach ($data as $column => $value) {
869
            if (is_integer($data[$column]) || is_float($data[$column])) {
870
                $parts[$column] = $column.' eq '.$data[$column];
871
            } elseif (is_bool($data[$column])) {
872
                $parts[$column] = $data[$column] ? $column.' eq true' : $column.' eq false';
873
            } elseif (is_null($data[$column])) {
874
                $parts[$column] = $column." is null";
875
            } elseif ($value == '!null') {
876
                $parts[$column] = $column." is not null";
877
            } else {
878
                $parts[$column] = $column." eq '".$data[$column]."'";
879
            }
880
        }
881
882
        $flexiUrl = implode(' '.$operator.' ', $parts);
883
884
        return $flexiUrl;
885
    }
886
887
    /**
888
     * Vrací identifikátor objektu code: nebo id:
889
     *
890
     * @link https://demo.flexibee.eu/devdoc/identifiers Identifikátory záznamů
891
     * @return string indentifikátor záznamu reprezentovaného objektem
892
     * @throws Exception data objektu neobsahují kód nebo id
893
     */
894
    public function __toString()
895
    {
896
        $myCode = $this->getDataValue('kod');
897
        if ($myCode) {
898
            $id = 'code:'.$myCode;
899
        } else {
900
            $id = $this->getDataValue('id');
901
            if (!$id) {
902
                throw new \Exception(_('invoice without loaded code: or id: cannot match with statement!'));
903
            }
904
        }
905
        return $id;
906
    }
907
}