Completed
Branch develop (7b6053)
by Ventimiglia
03:28
created

Firebase::guzzeClientInit()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 0
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 32 and the first side effect is on line 13.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
declare(strict_types = 1);
3
namespace ZendFirebase;
4
5
use Interfaces\FirebaseInterface;
6
use GuzzleHttp\Client;
7
use ZendFirebase\Stream\StreamClient;
8
use Monolog\Logger;
9
use Monolog\Handler\StreamHandler;
10
use Monolog\Handler\FirePHPHandler;
11
use Monolog\Formatter\LineFormatter;
12
13
require 'Interfaces/FirebaseInterface.php';
14
require 'Stream/StreamClient.php';
15
16
/**
17
 * PHP7 FIREBASE LIBRARY (http://samuelventimiglia.it/)
18
 *
19
 *
20
 * @link https://github.com/samuel20miglia/zend_Firebase
21
 * @copyright Copyright (c) 2016-now Ventimiglia Samuel - Biasin Davide
22
 * @license BSD 3-Clause License
23
 *
24
 */
25
26
/**
27
 * This class do rest operations to firebase server
28
 *
29
 * @author ghostbyte
30
 * @package ZendFirebase
31
 */
32
class Firebase extends FirebaseResponce implements FirebaseInterface
33
{
34
35
    /**
36
     * Default Timeout
37
     *
38
     * @var integer $timeout
39
     */
40
    private $timeout = 30;
41
42
    /**
43
     * Format of datetime of logs
44
     *
45
     * @var string $dateFormatLog
46
     */
47
    private $dateFormatLog = "Y n j, g:i a";
48
49
    /**
50
     * DateTime of log filename
51
     *
52
     * @var string $dateFormatLogFilename
53
     */
54
    private static $dateFormatLogFilename;
55
56
    /**
57
     * Authentication object
58
     *
59
     * @var Config\FirebaseAuth $auth
60
     */
61
    private $auth;
62
63
    /**
64
     * Create new Client
65
     *
66
     * @var $client
67
     */
68
    private $client;
69
70
    /**
71
     * Responce from firebase
72
     *
73
     * @var mixed $response
74
     */
75
    private $response;
76
77
    /**
78
     * Last Auto-Increment saved from post operation
79
     *
80
     * @var string $lastIdStored
81
     */
82
    protected $lastIdStored = '';
83
84
    /**
85
     * Create new Firebase client object
86
     * Remember to install PHP CURL extention
87
     *
88
     * @param Config\FirebaseAuth $auth
89
     */
90
    public function __construct(\ZendFirebase\Authentication\FirebaseAuth $auth)
91
    {
92
        $this->checkDipendenties($auth);
0 ignored issues
show
Documentation introduced by
$auth is of type object<ZendFirebase\Authentication\FirebaseAuth>, but the function expects a object<ZendFirebase\Config\FirebaseAuth>.

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...
93
        
94
        // store object into variable
95
        $this->auth = $auth;
0 ignored issues
show
Documentation Bug introduced by
It seems like $auth of type object<ZendFirebase\Authentication\FirebaseAuth> is incompatible with the declared type object<ZendFirebase\Config\FirebaseAuth> of property $auth.

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...
96
        
97
        $this->guzzeClientInit();
98
    }
99
100
    /**
101
     * Create new guzzle client
102
     */
103
    private function guzzeClientInit()
104
    {
105
        
106
        /* create new client */
107
        $this->client = new Client([
108
            'base_uri' => $this->auth->getBaseURI(),
109
            'timeout' => $this->getTimeout(),
110
            'headers' => $this->getRequestHeaders()
111
        ]);
112
    }
113
114
    /**
115
     * Controll of all dipendenties
116
     *
117
     * @param \ZendFirebase\Config\FirebaseAuth $auth
118
     */
119
    private function checkDipendenties($auth)
120
    {
121
        $authMessage = 'Forget credential or is not an object.';
122
        $curlMessage = 'Extension CURL is not loaded or not installed.';
123
        
124
        // check if auth is null
125
        if (! is_object($auth) || null == $auth) {
126
            trigger_error($authMessage, E_USER_ERROR);
127
        }
128
        
129
        // check if extension is installed
130
        if (! extension_loaded('curl')) {
131
            trigger_error($curlMessage, E_USER_ERROR);
132
        }
133
    }
134
135
    /**
136
     * Return Integer of Timeout
137
     * default 30 setted 10
138
     *
139
     * @return integer $timeout
140
     */
141
    public function getTimeout(): int
142
    {
143
        return $this->timeout;
144
    }
145
146
    /**
147
     * Default timeout is 10 seconds
148
     * is is not set switch to 30
149
     *
150
     * @param integer $timeout
151
     */
152
    public function setTimeout($timeout)
153
    {
154
        $this->timeout = $timeout;
155
    }
156
157
    /**
158
     * Return string of LastIdStored generated after post command
159
     *
160
     * @return string $lastIdStored
161
     */
162
    public function getLastIdStored(): string
163
    {
164
        return $this->lastIdStored;
165
    }
166
167
    /**
168
     * Set string of LastIdStored generated after post command
169
     *
170
     * @param string $lastIdStored
171
     */
172
    public function setLastIdStored($lastIdStored)
173
    {
174
        $this->lastIdStored = $lastIdStored;
175
    }
176
177
    /**
178
     * Method for get array headers for Guzzle client
179
     *
180
     * @throws \Exception
181
     * @return array
182
     */
183
    private function getRequestHeaders(): array
184
    {
185
        $headers = [];
186
        $headers['stream'] = true;
187
        $headers['Accept'] = 'application/json';
188
        $headers['Content-Type'] = 'application/json';
189
        
190
        // check if header is an array
191
        if (! is_array($headers)) {
192
            $str = "The guzzle client headers must be an array.";
193
            throw new \Exception($str);
194
        }
195
        
196
        return $headers;
197
    }
198
199
    /**
200
     * Returns with the normalized JSON absolute path
201
     *
202
     * @param string $path
203
     * @param array $options
204
     * @return string $path
205
     */
206
    private function getJsonPath($path, $options = []): string
207
    {
208
        /* autentication token */
209
        $auth = $this->auth->getServertoken();
210
        /* returns the data in a human-readable format */
211
        $options['print'] = 'pretty';
212
        
213
        foreach ($options as $opt => $optVal) {
214
            if (\is_string($optVal)) {
215
                $options[$opt] = '"' . $optVal . '"';
216
                \var_dump($optVal);
0 ignored issues
show
Security Debugging Code introduced by
\var_dump($optVal); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
217
            }
218
        }
219
        
220
        $path = ltrim($path, '/');
221
        
222
        return $path . '.json?auth=' . $auth . '&' . http_build_query($options) . '';
223
    }
224
225
    /**
226
     * DELETE - Removing Data FROM FIREBASE
227
     *
228
     * @param string $path
229
     * @param array $options
230
     *
231
     * {@inheritdoc}
232
     *
233
     * @see \Interfaces\FirebaseInterface::delete()
234
     */
235
    public function delete($path, $options = [])
236
    {
237
        $this->writeRequest('delete', $this->getJsonPath($path, $options), '');
238
    }
239
240
    /**
241
     * GET - Reading Data FROM FIREBASE
242
     *
243
     * @param string $path
244
     * @param array $options
245
     *
246
     * {@inheritdoc}
247
     *
248
     * @see \Interfaces\FirebaseInterface::get()
249
     */
250
    public function get($path, $options = [])
251
    {
252
        $this->writeRequest('get', $this->getJsonPath($path, $options), '');
253
    }
254
255
    /**
256
     * PATCH - Updating Data TO FIREBASE
257
     *
258
     * @param string $path
259
     * @param array $data
260
     * @param array $options
261
     *
262
     * {@inheritdoc}
263
     *
264
     * @see \Interfaces\FirebaseInterface::patch()
265
     */
266
    public function patch($path, array $data, $options = [])
267
    {
268
        $this->writeRequest('patch', $this->getJsonPath($path, $options), $data);
269
    }
270
271
    /**
272
     * POST - Pushing Data TO FIREBASE
273
     *
274
     * @param string $path
275
     * @param array $data
276
     * @param array $options
277
     *
278
     * {@inheritdoc}
279
     *
280
     * @see \Interfaces\FirebaseInterface::post()
281
     */
282
    public function post($path, array $data, $options = [])
283
    {
284
        $this->writeRequest('post', $this->getJsonPath($path, $options), $data);
285
    }
286
287
    /**
288
     * PUT - Writing Data TO FIREBASE
289
     *
290
     * @param string $path
291
     * @param array $data
292
     * @param array $options
293
     *
294
     * {@inheritdoc}
295
     *
296
     * @see \Interfaces\FirebaseInterface::put()
297
     */
298
    public function put($path, array $data, $options = [])
299
    {
300
        $this->writeRequest('put', $this->getJsonPath($path, $options), $data);
301
    }
302
303
    /**
304
     * READ RULES - Retrieve firebase rules
305
     *
306
     * @param string $path
307
     */
308
    public function getRules($path)
309
    {
310
        $this->writeRequest('get', $this->getJsonPath($path, []), []);
311
    }
312
    
313
    /**
314
     * WRITE RULES - Retrieve firebase rules
315
     *
316
     * @param string $path
317
     */
318
    public function writeRules($path,array $data)
319
    {
320
        $this->writeRequest('put', $this->getJsonPath($path, []), $data);
321
    }
322
323
    /**
324
     * Method to send request
325
     *
326
     * @param string $op
327
     * @param string $path
328
     * @param mixed $data
329
     */
330
    private function writeRequest($op, $path, $data)
331
    {
332
        $operation = \strtolower($op);
333
        
334
        switch ($operation) {
335
            case 'get':
336
                $response = $this->client->{$operation}($path);
337
                $bodyResponse = $response->getBody()->getContents();
338
                $this->setDataFromOperation('get', $response->getStatusCode());
339
                break;
340
            case 'delete':
341
                $response = $this->client->{$operation}($path);
342
                $bodyResponse = $response->getReasonPhrase(); // OK
343
                $this->setDataFromOperation('get', $response->getStatusCode());
344
                break;
345
            case 'post':
346
                $bodyResponse = $this->client->{$operation}($path, [
347
                    'body' => \json_encode($data)
348
                ]);
349
                
350
                // save auto-increment id created from Firebase after post operation
351
                $this->setLastIdStored(json_decode($bodyResponse->getBody()
352
                    ->getContents(), true)['name']);
353
                
354
                $this->setDataFromOperation($op, $bodyResponse->getStatusCode());
355
                break;
356
            
357
            default:
358
                $bodyResponse = $this->client->{$operation}($path, [
359
                    'body' => \json_encode($data)
360
                ]);
361
                
362
                $this->setDataFromOperation($op, $bodyResponse->getStatusCode());
363
                break;
364
        }
365
        
366
        $this->response = $bodyResponse;
367
        $this->makeResponce();
368
    }
369
370
    /**
371
     * This function set variables after operation
372
     *
373
     * @param string $operation
374
     * @param mixed $status
375
     */
376
    private function setDataFromOperation($operation, $status)
377
    {
378
        $oP = \strtoupper($operation);
379
        
380
        $this->status = $status; // 200
381
        $this->operation = $oP;
382
    }
383
384
    /**
385
     * Start stream with server and write log in choised folder
386
     *
387
     * @param string $path
388
     * @param string $folderToStoreLog
389
     * @param integer $requestDelay
390
     * @param string $callback
391
     * @param array $options
392
     * @param boolean $print
393
     * @example $requestDelay = 3000 -> 3 seconds between get request
394
     */
395
    public function startStream($path, $folderToStoreLog, $callback, $requestDelay = 5000, $options = [], $print = true)
396
    {
397
        $url = $this->auth->getBaseURI();
398
        
399
        $client = new StreamClient($url, $requestDelay, $this->getJsonPath($path, $options));
400
        
401
        // returns generator
402
        $events = $client->getEvents();
403
        
404
        // call method for create instance of logger
405
        $logger = $this->createLogger($this->formatFolderName($folderToStoreLog));
406
        
407
        // blocks until new event arrive
408
        foreach ($events as $event) {
409
            // decode json data arrived to php array
410
            $eventData = \json_decode($event->getData(), true);
411
            
412
            // callback to return
413
            $callback($eventData, $event->getEventType());
414
            
415
            if ($print) {
416
                // anyway print data in output
417
                $this->printEventData($eventData, $event);
418
            }
419
            
420
            // write logs
421
            $this->writeEventLogs($logger, $eventData, $event, $path);
422
        }
423
    }
424
425
    /**
426
     * Print on output datas
427
     *
428
     * @param mixed $eventData
429
     * @param mixed $event
430
     */
431
    private function printEventData($eventData, $event)
432
    {
433
        // pass event to callback function
434
        print_r($eventData);
435
        print_r("EVENT TYPE: " . $event->getEventType() . PHP_EOL . PHP_EOL);
436
    }
437
438
    /**
439
     * Write log of current event
440
     *
441
     * @param Logger $logger
442
     * @param array $eventData
443
     * @param mixed $event
444
     * @param string $path
445
     */
446 View Code Duplication
    private function writeEventLogs($logger, $eventData, $event, $path)
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...
447
    {
448
        if (! empty($eventData) || null != $eventData) {
449
            $logger->addDebug("path: {$path}", [
450
                'DATA' => $eventData,
451
                'EVENT TYPE' => $event->getEventType()
452
            ]);
453
        } else {
454
            $logger->addDebug("path: {$path}", [
455
                'EVENT TYPE' => $event->getEventType()
456
            ]);
457
        }
458
    }
459
460
    /**
461
     * Format folder name
462
     *
463
     * @param string $folderToStoreLog
464
     * @return string $folderName
465
     */
466
    private function formatFolderName($folderToStoreLog): string
467
    {
468
        /* search / in string */
469
        $folderName = substr(strrchr(trim($folderToStoreLog), "/"), 1);
470
        /* if not exsits add on path+/ */
471
        $folderName = empty($folderName) ? $folderToStoreLog . '/' : $folderToStoreLog;
472
        
473
        return $folderName;
474
    }
475
476
    /**
477
     *
478
     * Create logger instance for save stream log
479
     *
480
     * @param string $folderToStoreLog
481
     * @return Logger $logger
482
     */
483 View Code Duplication
    private function createLogger($folderToStoreLog)
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...
484
    {
485
        // the default output format is "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"
486
        $output = "%datetime% > %level_name% > %message% %context% %extra%\n";
487
        // finally, create a formatter
488
        $formatter = new LineFormatter($output, $this->dateFormatLog);
489
        self::$dateFormatLogFilename = date("Y-m-d_H:i:s");
490
        // Create the logger
491
        $logger = new Logger('stream_logger');
492
        
493
        // Now add some handlers
494
        $stream = new StreamHandler(trim($folderToStoreLog) . self::$dateFormatLogFilename . ".log", Logger::DEBUG);
495
        
496
        $stream->setFormatter($formatter);
497
        $logger->pushHandler($stream);
498
        $logger->pushHandler(new FirePHPHandler());
499
        
500
        // You can now use your logger
501
        $logger->addInfo('Stream logger is ready...');
502
        return $logger;
503
    }
504
505
    /**
506
     * This method return the responce from firebase
507
     *
508
     * @example set and validate data passed
509
     */
510
    private function makeResponce()
511
    {
512
        $jsonData = [];
513
        if ($this->operation === 'GET') {
514
            $jsonData = json_decode($this->response, true);
515
            
516
            if ($this->validateJson() !== false) {
517
                $jsonData[] = $this->validateJson();
518
            }
519
            if (empty($jsonData)) {
520
                $jsonData[] = '204 No Content';
521
            }
522
        } else {
523
            $jsonData[] = 'Success';
524
        }
525
        
526
        /* Set data after operations */
527
        $this->setOperation($this->operation);
528
        $this->setStatus($this->status);
529
        
530
        $this->setFirebaseData($jsonData);
531
        $this->validateResponce();
532
    }
533
534
    /**
535
     * Remove object from memory
536
     */
537
    public function __destruct()
538
    {
539
    }
540
}
541