GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 94f336...bbca31 )
by Juan
8s
created

Amplitude::sendEvent()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 37
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 1
Metric Value
c 3
b 0
f 1
dl 0
loc 37
rs 8.439
cc 6
eloc 26
nc 4
nop 0
1
<?php
2
namespace Zumba\Amplitude;
3
4
use Psr\Log;
5
6
class Amplitude
7
{
8
    use Log\LoggerAwareTrait;
9
10
    const AMPLITUDE_API_URL = 'https://api.amplitude.com/httpapi';
11
12
    const EXCEPTION_MSG_NO_API_KEY = 'API Key is required to log an event';
13
    const EXCEPTION_MSG_NO_EVENT_TYPE = 'Event Type is required to log or queue an event';
14
    const EXCEPTION_MSG_NO_USER_OR_DEVICE = 'Either user_id or device_id required to log an event';
15
16
    /**
17
     * The API key to use for all events generated by this instance
18
     *
19
     * @var string
20
     */
21
    protected $apiKey;
22
23
    /**
24
     * The event that will be used for the next event being tracked
25
     *
26
     * @var \Zumba\Amplitude\Event
27
     */
28
    protected $event;
29
30
    /**
31
     * The user ID to use for events generated by this instance
32
     *
33
     * @var string
34
     */
35
    protected $userId;
36
37
    /**
38
     * The user data to set on the next event logged to Amplitude
39
     *
40
     * @var array
41
     */
42
    protected $userProperties = [];
43
44
    /**
45
     * The device ID to use for events generated by this instance
46
     *
47
     * @var string
48
     */
49
    protected $deviceId;
50
51
    /**
52
     * Queue of events, used to allow generating events that might happen prior to amplitude being fully initialized
53
     *
54
     * @var \Zumba\Amplitude\Event[]
55
     */
56
    protected $queue = [];
57
58
    /**
59
     * Flag for if user is opted out of tracking
60
     *
61
     * @var boolean
62
     */
63
    protected $optOut = false;
64
65
    /**
66
     * Flag for if should save the last HTTP response for debugging purposes
67
     *
68
     * @var boolean True to enable saving the last response
69
     */
70
    protected $debugResponse = false;
71
72
    /**
73
     * Last response from logging event
74
     *
75
     * @var array|null
76
     */
77
    protected $lastHttpResponse;
78
79
    /**
80
     * Array of Amplitude instances
81
     *
82
     * @var \Zumba\Amplitude\Amplitude[]
83
     */
84
    private static $instances = [];
85
86
    /**
87
     * Singleton to get named instance
88
     *
89
     * Using this is optional, it depends on the use-case if it is better to use a singleton instance or just create
90
     * a new object directly.
91
     *
92
     * Useful if want to possibly send multiple events for the same user in a single page load, or even keep track
93
     * of multiple named instances, each could track to it's own api key and/or user/device ID.
94
     *
95
     * Each instance maintains it's own:
96
     * - API Key
97
     * - User ID
98
     * - Device ID
99
     * - User Properties
100
     * - Event Queue (if events are queued before the amplitude instance is initialized)
101
     * - Event object - for the next event that will be sent or queued
102
     * - Logger
103
     * - Opt out status
104
     *
105
     * @param string $instanceName Optional, can use to maintain multiple singleton instances of amplitude, each with
106
     *   it's own API key set
107
     * @return \Zumba\Amplitude\Amplitude
108
     */
109
    public static function getInstance($instanceName = 'default')
110
    {
111
        if (empty(self::$instances[$instanceName])) {
112
            self::$instances[$instanceName] = new static();
113
        }
114
        return self::$instances[$instanceName];
115
    }
116
117
    /**
118
     * Constructor, optionally sets the api key
119
     *
120
     * @param string $apiKey
121
     * @param \Psr\Log $logger
0 ignored issues
show
Bug introduced by
There is no parameter named $logger. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
122
     */
123
    public function __construct($apiKey = null)
124
    {
125
        if (!empty($apiKey)) {
126
            $this->apiKey = (string)$apiKey;
127
        }
128
        // Initialize logger to be null logger
129
        $this->setLogger(new Log\NullLogger());
130
    }
131
132
    /**
133
     * Initialize amplitude
134
     *
135
     * This lets you set the api key, and optionally the user ID.
136
     *
137
     * @param string $apiKey Amplitude API key
138
     * @param string $userId
139
     * @return \Zumba\Amplitude\Amplitude
140
     */
141
    public function init($apiKey, $userId = null)
142
    {
143
        $this->apiKey = (string)$apiKey;
144
        if ($userId !== null) {
145
            $this->setUserId($userId);
146
        }
147
        return $this;
148
    }
149
150
    /**
151
     * Log any events that were queued before amplitude was initialized
152
     *
153
     * Note that api key, and either the user ID or device ID need to be set prior to calling this.
154
     *
155
     * @return \Zumba\Amplitude\Amplitude
156
     * @throws \LogicException
157
     */
158
    public function logQueuedEvents()
159
    {
160
        if (empty($this->queue)) {
161
            return $this;
162
        }
163
        foreach ($this->queue as $event) {
164
            $this->event = $event;
165
            $this->logEvent();
166
        }
167
        return $this->resetEvent()
168
            ->resetQueue();
169
    }
170
171
    /**
172
     * Clear out all events in the queue, without sending them to amplitude
173
     *
174
     * @return \Zumba\Amplitude\Amplitude
175
     */
176
    public function resetQueue()
177
    {
178
        $this->queue = [];
179
        return $this;
180
    }
181
182
    /**
183
     * Gets the event that will be used for the next event logged by call to logEvent() or queueEvent()
184
     *
185
     * You can also pass in an event or array of event properties.  If you pass in an event, it will be set as the
186
     * event to be used for the next call to queueEvent() or logEvent()
187
     *
188
     * @param null|array|\Zumba\Amplitude\Event Can pass in an event to set as the next event to run, or array to set
189
     *   properties on that event
190
     * @return \Zumba\Amplitude\Event
191
     */
192
    public function event($event = null)
193
    {
194
        if (!empty($event) && $event instanceof \Zumba\Amplitude\Event) {
195
            $this->event = $event;
196
        } elseif (empty($this->event)) {
197
            // Set the values that persist between tracking events
198
            $this->event = new Event();
199
        }
200
        if (!empty($event) && is_array($event)) {
201
            // Set properties on the event
202
            $this->event->set($event);
203
        }
204
        return $this->event;
205
    }
206
207
    /**
208
     * Resets the event currently in the process of being set up (what is returned by event())
209
     *
210
     * @return \Zumba\Amplitude\Amplitude
211
     */
212
    public function resetEvent()
213
    {
214
        $this->event = null;
215
        return $this;
216
    }
217
218
    /**
219
     * Log an event immediately
220
     *
221
     * Requires amplitude is already initialized and user ID or device ID is set.  If need to wait until amplitude
222
     * is initialized, use queueEvent() method instead.
223
     *
224
     * Can either pass in information to be logged, or can set up the Event object before hand, see the event()
225
     * method for more information
226
     *
227
     * @param string $eventType Required if not set on event object prior to calling this
228
     * @param array $eventProperties Optional, properties to set on event
229
     * @return \Zumba\Amplitude\Amplitude
230
     * @throws \LogicException Thorws exception if any of the requirments are not met, such as api key set
231
     */
232
    public function logEvent($eventType = '', array $eventProperties = [])
233
    {
234
        if ($this->optOut) {
235
            return $this;
236
        }
237
        // Sanity checking
238
        if (empty($this->apiKey)) {
239
            throw new \LogicException(static::EXCEPTION_MSG_NO_API_KEY);
240
        }
241
        $event = $this->event();
242
        $event->set($eventProperties);
243
        $event->eventType = $eventType ?: $event->eventType;
244
        // Set the persistent options on the event
245
        $this->setPersistentEventData();
246
247
        if (empty($event->eventType)) {
248
            throw new \LogicException(static::EXCEPTION_MSG_NO_EVENT_TYPE);
249
        }
250
        if (empty($event->userId) && empty($event->deviceId)) {
251
            throw new \LogicException(static::EXCEPTION_MSG_NO_USER_OR_DEVICE);
252
        }
253
254
        $this->sendEvent();
255
256
        // Reset the event for next call
257
        $this->resetEvent();
258
259
        return $this;
260
    }
261
262
    /**
263
     * Set the persistent data on the event object
264
     *
265
     * @return void
266
     */
267
    protected function setPersistentEventData()
268
    {
269
        $event = $this->event();
270
        if (!empty($this->userId)) {
271
            $event->userId = $this->userId;
272
        }
273
        if (!empty($this->deviceId)) {
274
            $event->deviceId = $this->deviceId;
275
        }
276
        if (!empty($this->userProperties)) {
277
            $event->setUserProperties($this->userProperties);
278
            $this->resetUserProperties();
279
        }
280
    }
281
282
    /**
283
     * Log or queue the event, depending on if amplitude instance is already set up or not
284
     *
285
     * Note that this is an internal queue, the queue is lost between page loads.
286
     *
287
     * This functions identically to logEvent, with the exception that if Amplitude is not yet set up, it queues the
288
     * event to be logged later (during same page load).
289
     *
290
     * If the API key, and either user ID or device ID are already set in the amplitude instance, and there is not
291
     * already events in the queue that have not been run, this will log the event immediately.  Note that having
292
     * the userId or deviceId set on the event itself does not affect if it queues the event or not, only if set on
293
     * the Amplitude instance.
294
     *
295
     * Otherwise it will queue the event, and will be run after the amplitude instance is initialized and
296
     * logQueuedEvents() method is run
297
     *
298
     * @param string $eventType
299
     * @param array $eventProperties
300
     * @return \Zumba\Amplitude\Amplitude
301
     * @throws \LogicException
302
     */
303
    public function queueEvent($eventType = '', array $eventProperties = [])
304
    {
305
        if ($this->optOut) {
306
            return $this;
307
        }
308
        $event = $this->event();
309
        $event->set($eventProperties);
310
        $event->eventType = $eventType ?: $event->eventType;
311
312
        // Sanity checking
313
        if (empty($event->eventType)) {
314
            throw new \LogicException(static::EXCEPTION_MSG_NO_EVENT_TYPE);
315
        }
316
        if (empty($this->queue) && !empty($this->apiKey) && (!empty($this->userId) || !empty($this->deviceId))) {
317
            // No need to queue, everything seems to be initialized already and queue has already been processed
318
            return $this->logEvent();
319
        }
320
        $this->queue[] = $event;
321
        $this->resetEvent();
322
323
        return $this;
324
    }
325
326
    /**
327
     * Set the user ID for future events logged
328
     *
329
     * Any set with this will take precedence over any set on the Event object
330
     *
331
     * @param string $userId
332
     * @return \Zumba\Amplitude\Amplitude
333
     */
334
    public function setUserId($userId)
335
    {
336
        $this->userId = (string)$userId;
337
        return $this;
338
    }
339
340
    /**
341
     * Set the device ID for future events logged
342
     *
343
     * Any set with this will take precedence over any set on the Event object
344
     *
345
     * @param string $deviceId
346
     * @return \Zumba\Amplitude\Amplitude
347
     */
348
    public function setDeviceId($deviceId)
349
    {
350
        $this->deviceId = (string)$deviceId;
351
        return $this;
352
    }
353
354
    /**
355
     * Set the user properties, will be sent with the next event sent to Amplitude
356
     *
357
     * Any set with this will take precedence over any set on the Event object
358
     *
359
     * If no events are logged, it will not get sent to Amplitude
360
     *
361
     * @param array $userProperties
362
     */
363
    public function setUserProperties(array $userProperties)
364
    {
365
        $this->userProperties = array_merge($this->userProperties, $userProperties);
366
        return $this;
367
    }
368
369
    /**
370
     * Resets user properties added with setUserProperties() if they have not already been sent in an event to Amplitude
371
     *
372
     * @return \Zumba\Amplitude\Amplitude
373
     */
374
    public function resetUserProperties()
375
    {
376
        $this->userProperties = [];
377
        return $this;
378
    }
379
380
    /**
381
     * Check if there are events in the queue that have not been sent
382
     *
383
     * @return boolean
384
     */
385
    public function hasQueuedEvents()
386
    {
387
        return !empty($this->queue);
388
    }
389
390
    /**
391
     * Resets all user information
392
     *
393
     * This resets the user ID, device ID previously set using setUserId or setDeviceId.
394
     *
395
     * If additional information was previously set using setUserProperties() method, and the event has not already
396
     * been sent to Amplitude, it will reset that information as well.
397
     *
398
     * Does not reset user information if set manually on an individual event in the queue.
399
     *
400
     * @return \Zumba\Amplitude\Amplitude
401
     */
402
    public function resetUser()
403
    {
404
        $this->setUserId(null);
405
        $this->setDeviceId(null);
406
        $this->resetUserProperties();
407
        return $this;
408
    }
409
410
    /**
411
     * Set opt out for the current user.
412
     *
413
     * If set to true, will not send any future events to amplitude for this amplitude instance.
414
     *
415
     * @param boolean $optOut
416
     * @return \Zumba\Amplitude\Amplitude
417
     */
418
    public function setOptOut($optOut)
419
    {
420
        $this->optOut = (bool)$optOut;
421
        return $this;
422
    }
423
424
    /**
425
     * Getter for currently set api key
426
     *
427
     * @return string|null
428
     */
429
    public function getApiKey()
430
    {
431
        return $this->apiKey;
432
    }
433
434
    /**
435
     * Getter for currently set user ID
436
     *
437
     * @return string|null
438
     */
439
    public function getUserId()
440
    {
441
        return $this->userId;
442
    }
443
444
    /**
445
     * Getter for currently set device ID
446
     *
447
     * @return string|null
448
     */
449
    public function getDeviceId()
450
    {
451
        return $this->deviceId;
452
    }
453
454
    /**
455
     * Getter for all currently set user properties, that will be automatically sent on next Amplitude event
456
     *
457
     * Once the properties have been sent in an Amplitude event, they will be cleared.
458
     *
459
     * @return array
460
     */
461
    public function getUserProperties()
462
    {
463
        return $this->userProperties;
464
    }
465
466
    /**
467
     * Get the current value for opt out.
468
     *
469
     * @return boolean
470
     */
471
    public function getOptOut()
472
    {
473
        return $this->optOut;
474
    }
475
476
    /**
477
     * Send the event currently set in $this->event to amplitude
478
     *
479
     * Requres $this->event and $this->apiKey to be set, otherwise it throws an exception.
480
     *
481
     * @return void
482
     * @throws \InternalErrorException If event or api key not set
483
     */
484
    protected function sendEvent()
485
    {
486
        if (empty($this->event) || empty($this->apiKey)) {
487
            throw new \InternalErrorException('Event or api key not set, cannot send event');
488
        }
489
        $ch = curl_init(static::AMPLITUDE_API_URL);
490
        if (!$ch) {
491
            // Could be a number of PHP environment problems, log a critical error
492
            $this->logger->critical(
493
                'Call to curl_init(' . static::AMPLITUDE_API_URL . ') failed, unable to send Amplitude event'
494
            );
495
            return;
496
        }
497
        $postFields = [
498
            'api_key' => $this->apiKey,
499
            'event' => json_encode($this->event),
500
        ];
501
        curl_setopt($ch, \CURLOPT_POSTFIELDS, $postFields);
502
        // Always return instead of outputting response!
503
        curl_setopt($ch, \CURLOPT_RETURNTRANSFER, true);
504
        $response = curl_exec($ch);
505
        $curlErrno = curl_errno($ch);
506
        if ($curlErrno) {
507
            $this->logger->critical(
508
                'Curl error: ' . curl_error($ch),
509
                compact('curlErrno', 'response', 'postFields')
510
            );
511
        } else {
512
            $httpCode = curl_getinfo($ch, \CURLINFO_HTTP_CODE);
513
            $this->logger->log(
514
                $httpCode === 200 ? Log\LogLevel::INFO : Log\LogLevel::ERROR,
515
                'Amplitude HTTP API response: ' . $response,
516
                compact('httpCode', 'response', 'postFields')
517
            );
518
        }
519
        curl_close($ch);
520
    }
521
}
522