Completed
Branch master (b62fb8)
by Ventimiglia
06:10
created

Firebase::writeRules()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 2
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/Samuel18/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->gulleClientInit();
98
    }
99
100
    /**
101
     * Create new guzzle client
102
     */
103
    private function gulleClientInit()
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
            }
217
        }
218
        
219
        $path = ltrim($path, '/');
220
        
221
        return $path . '.json?auth=' . $auth . '&' . http_build_query($options) . '';
222
    }
223
224
    /**
225
     * DELETE - Removing Data FROM FIREBASE
226
     *
227
     * @param string $path
228
     * @param array $options
229
     *
230
     * {@inheritdoc}
231
     *
232
     * @see \Interfaces\FirebaseInterface::delete()
233
     */
234
    public function delete($path, $options = [])
235
    {
236
        $this->writeRequest('delete', $this->getJsonPath($path, $options), '');
237
    }
238
239
    /**
240
     * GET - Reading Data FROM FIREBASE
241
     *
242
     * @param string $path
243
     * @param array $options
244
     *
245
     * {@inheritdoc}
246
     *
247
     * @see \Interfaces\FirebaseInterface::get()
248
     */
249
    public function get($path, $options = [])
250
    {
251
        $this->writeRequest('get', $this->getJsonPath($path, $options), '');
252
    }
253
254
    /**
255
     * PATCH - Updating Data TO FIREBASE
256
     *
257
     * @param string $path
258
     * @param array $data
259
     * @param array $options
260
     *
261
     * {@inheritdoc}
262
     *
263
     * @see \Interfaces\FirebaseInterface::patch()
264
     */
265
    public function patch($path, array $data, $options = [])
266
    {
267
        $this->writeRequest('patch', $this->getJsonPath($path, $options), $data);
268
    }
269
270
    /**
271
     * POST - Pushing Data TO FIREBASE
272
     *
273
     * @param string $path
274
     * @param array $data
275
     * @param array $options
276
     *
277
     * {@inheritdoc}
278
     *
279
     * @see \Interfaces\FirebaseInterface::post()
280
     */
281
    public function post($path, array $data, $options = [])
282
    {
283
        $this->writeRequest('post', $this->getJsonPath($path, $options), $data);
284
    }
285
286
    /**
287
     * PUT - Writing Data TO FIREBASE
288
     *
289
     * @param string $path
290
     * @param array $data
291
     * @param array $options
292
     *
293
     * {@inheritdoc}
294
     *
295
     * @see \Interfaces\FirebaseInterface::put()
296
     */
297
    public function put($path, array $data, $options = [])
298
    {
299
        $this->writeRequest('put', $this->getJsonPath($path, $options), $data);
300
    }
301
302
    /**
303
     * READ RULES - Retrieve firebase rules
304
     *
305
     * @param string $path
306
     */
307
    public function getRules($path)
308
    {
309
        $this->writeRequest('get', $this->getJsonPath($path, []), []);
310
    }
311
    
312
    /**
313
     * WRITE RULES - Retrieve firebase rules
314
     *
315
     * @param string $path
316
     */
317
    public function writeRules($path,array $data)
318
    {
319
        $this->writeRequest('put', $this->getJsonPath($path, []), $data);
320
    }
321
322
    /**
323
     * Method to send request
324
     *
325
     * @param string $op
326
     * @param string $path
327
     * @param mixed $data
328
     */
329
    private function writeRequest($op, $path, $data)
330
    {
331
        $operation = \strtolower($op);
332
        
333
        switch ($operation) {
334
            case 'get':
335
                $response = $this->client->{$operation}($path);
336
                $bodyResponse = $response->getBody()->getContents();
337
                $this->setDataFromOperation('get', $response->getStatusCode());
338
                break;
339
            case 'delete':
340
                $response = $this->client->{$operation}($path);
341
                $bodyResponse = $response->getReasonPhrase(); // OK
342
                $this->setDataFromOperation('get', $response->getStatusCode());
343
                break;
344
            case 'post':
345
                $bodyResponse = $this->client->{$operation}($path, [
346
                    'body' => \json_encode($data)
347
                ]);
348
                
349
                // save auto-increment id created from Firebase after post operation
350
                $this->setLastIdStored(json_decode($bodyResponse->getBody()
351
                    ->getContents(), true)['name']);
352
                
353
                $this->setDataFromOperation($op, $bodyResponse->getStatusCode());
354
                break;
355
            
356
            default:
357
                $bodyResponse = $this->client->{$operation}($path, [
358
                    'body' => \json_encode($data)
359
                ]);
360
                
361
                $this->setDataFromOperation($op, $bodyResponse->getStatusCode());
362
                break;
363
        }
364
        
365
        $this->response = $bodyResponse;
366
        $this->makeResponce();
367
    }
368
369
    /**
370
     * This function set variables after operation
371
     *
372
     * @param string $operation
373
     * @param mixed $status
374
     */
375
    private function setDataFromOperation($operation, $status)
376
    {
377
        $oP = \strtoupper($operation);
378
        
379
        $this->status = $status; // 200
380
        $this->operation = $oP;
381
    }
382
383
    /**
384
     * Start stream with server and write log in choised folder
385
     *
386
     * @param string $path
387
     * @param string $folderToStoreLog
388
     * @param integer $requestDelay
389
     * @param string $callback
390
     * @param array $options
391
     * @param boolean $print
392
     * @example $requestDelay = 3000 -> 3 seconds between get request
393
     */
394
    public function startStream($path, $folderToStoreLog, $callback, $requestDelay = 5000, $options = [], $print = true)
395
    {
396
        $url = $this->auth->getBaseURI();
397
        
398
        $client = new StreamClient($url, $requestDelay, $this->getJsonPath($path, $options));
399
        
400
        // returns generator
401
        $events = $client->getEvents();
402
        
403
        // call method for create instance of logger
404
        $logger = $this->createLogger($this->formatFolderName($folderToStoreLog));
405
        
406
        // blocks until new event arrive
407
        foreach ($events as $event) {
408
            // decode json data arrived to php array
409
            $eventData = \json_decode($event->getData(), true);
410
            
411
            // callback to return
412
            $callback($eventData, $event->getEventType());
413
            
414
            if ($print) {
415
                // anyway print data in output
416
                $this->printEventData($eventData, $event);
417
            }
418
            
419
            // write logs
420
            $this->writeEventLogs($logger, $eventData, $event, $path);
421
        }
422
    }
423
424
    /**
425
     * Print on output datas
426
     *
427
     * @param mixed $eventData
428
     * @param mixed $event
429
     */
430
    private function printEventData($eventData, $event)
431
    {
432
        // pass event to callback function
433
        print_r($eventData);
434
        print_r("EVENT TYPE: " . $event->getEventType() . PHP_EOL . PHP_EOL);
435
    }
436
437
    /**
438
     * Write log of current event
439
     *
440
     * @param Logger $logger
441
     * @param array $eventData
442
     * @param mixed $event
443
     * @param string $path
444
     */
445
    private function writeEventLogs($logger, $eventData, $event, $path)
446
    {
447
        if (! empty($eventData) || null != $eventData) {
448
            $logger->addDebug("path: {$path}", [
449
                'DATA' => $eventData,
450
                'EVENT TYPE' => $event->getEventType()
451
            ]);
452
        } else {
453
            $logger->addDebug("path: {$path}", [
454
                'EVENT TYPE' => $event->getEventType()
455
            ]);
456
        }
457
    }
458
459
    /**
460
     * Format folder name
461
     *
462
     * @param string $folderToStoreLog
463
     * @return string $folderName
464
     */
465
    private function formatFolderName($folderToStoreLog): string
466
    {
467
        /* search / in string */
468
        $folderName = substr(strrchr(trim($folderToStoreLog), "/"), 1);
469
        /* if not exsits add on path+/ */
470
        $folderName = empty($folderName) ? $folderToStoreLog . '/' : $folderToStoreLog;
471
        
472
        return $folderName;
473
    }
474
475
    /**
476
     *
477
     * Create logger instance for save stream log
478
     *
479
     * @param string $folderToStoreLog
480
     * @return Logger $logger
481
     */
482
    private function createLogger($folderToStoreLog)
483
    {
484
        // the default output format is "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"
485
        $output = "%datetime% > %level_name% > %message% %context% %extra%\n";
486
        // finally, create a formatter
487
        $formatter = new LineFormatter($output, $this->dateFormatLog);
488
        self::$dateFormatLogFilename = date("Y-m-d_H:i:s");
489
        // Create the logger
490
        $logger = new Logger('stream_logger');
491
        
492
        // Now add some handlers
493
        $stream = new StreamHandler(trim($folderToStoreLog) . self::$dateFormatLogFilename . ".log", Logger::DEBUG);
494
        
495
        $stream->setFormatter($formatter);
496
        $logger->pushHandler($stream);
497
        $logger->pushHandler(new FirePHPHandler());
498
        
499
        // You can now use your logger
500
        $logger->addInfo('Stream logger is ready...');
501
        return $logger;
502
    }
503
504
    /**
505
     * This method return the responce from firebase
506
     *
507
     * @example set and validate data passed
508
     */
509
    private function makeResponce()
510
    {
511
        $jsonData = [];
512
        if ($this->operation === 'GET') {
513
            $jsonData = json_decode($this->response, true);
514
            
515
            if ($this->validateJson() !== false) {
516
                $jsonData[] = $this->validateJson();
517
            }
518
            if (empty($jsonData)) {
519
                $jsonData[] = '204 No Content';
520
            }
521
        } else {
522
            $jsonData[] = 'Success';
523
        }
524
        
525
        /* Set data after operations */
526
        $this->setOperation($this->operation);
527
        $this->setStatus($this->status);
528
        
529
        $this->setFirebaseData($jsonData);
530
        $this->validateResponce();
531
    }
532
533
    /**
534
     * Remove object from memory
535
     */
536
    public function __destruct()
537
    {
538
    }
539
}
540