Passed
Pull Request — develop (#52)
by Peter
04:52
created

Tiqr_Message_FCM::_sendFirebase()   B

Complexity

Conditions 8
Paths 7

Size

Total Lines 59
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 6
Bugs 0 Features 0
Metric Value
cc 8
eloc 38
c 6
b 0
f 0
nc 7
nop 6
dl 0
loc 59
ccs 0
cts 49
cp 0
crap 72
rs 8.0675

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * This file is part of the tiqr project.
4
 *
5
 * The tiqr project aims to provide an open implementation for
6
 * authentication using mobile devices. It was initiated by
7
 * SURFnet and developed by Egeniq.
8
 *
9
 * More information: http://www.tiqr.org
10
 *
11
 * @author Joost van Dijk <[email protected]>
12
 *
13
 * @package tiqr
14
 *
15
 * @license New BSD License - See LICENSE file for details.
16
 *
17
 * @copyright (C) 2010-2024 SURF BV
18
 */
19
20
/**
21
 * Android Cloud To Device Messaging message.
22
 * @author peter
23
 */
24
class Tiqr_Message_FCM extends Tiqr_Message_Abstract
25
{
26
    /**
27
     * Send message.
28
     *
29
     * @throws Tiqr_Message_Exception_AuthFailure
30
     * @throws Tiqr_Message_Exception_SendFailure
31
     * @throws \Google\Exception
32
     */
33
    public function send()
34
    {
35
        $options = $this->getOptions();
36
        $projectId = $options['firebase.projectId'];
37
        $credentialsFile = $options['firebase.credentialsFile'];
38
39
        $translatedAddress = $this->getAddress();
40
        $alertText = $this->getText();
41
        $url = $this->getCustomProperty('challenge');
42
43
        $this->_sendFirebase($translatedAddress, $alertText, $url, $projectId, $credentialsFile);
44
    }
45
46
    /**
47
     * @throws \Google\Exception
48
     */
49
    private function getGoogleAccessToken($credentialsFile){
50
        $options = $this->getOptions();
0 ignored issues
show
Unused Code introduced by
The assignment to $options is dead and can be removed.
Loading history...
51
        $client = new \Google_Client();
52
        $client->setAuthConfig($credentialsFile);
53
        $client->addScope('https://www.googleapis.com/auth/firebase.messaging');
54
        $client->fetchAccessTokenWithAssertion();
55
        $token = $client->getAccessToken();
56
        return $token['access_token'];
57
    }
58
59
    /**
60
     * Send a message to a device using the firebase API key.
61
     *
62
     * @param $deviceToken string device ID
63
     * @param $alert string alert message
64
     * @param $challenge string tiqr challenge url
65
     * @param $projectId string the id of the firebase project
66
     * @param $credentialsFile string The location of the firebase secret json
67
     * @param $retry boolean is this a 2nd attempt
68
     * @throws Tiqr_Message_Exception_SendFailure
69
     * @throws \Google\Exception
70
     */
71
    private function _sendFirebase(string $deviceToken, string $alert, string $challenge, string $projectId, string $credentialsFile, bool $retry=false)
72
    {
73
        $options = $this->getOptions();
0 ignored issues
show
Unused Code introduced by
The assignment to $options is dead and can be removed.
Loading history...
74
        $apiurl = 'https://fcm.googleapis.com/v1/projects/'.$projectId.'/messages:send';
75
76
        $fields = [
77
            'message' => [
78
                'token' => $deviceToken,
79
                'data' => [
80
                    'challenge' => $challenge,
81
                    'text'      => $alert,
82
                ],
83
                "android" => [
84
                    "ttl" => "300s",
85
                ],
86
            ],
87
        ];
88
89
        $headers = array(
90
            'Authorization: Bearer ' . $this->getGoogleAccessToken($credentialsFile),
91
            'Content-Type: application/json',
92
        );
93
94
        $ch = curl_init();
95
        curl_setopt($ch, CURLOPT_URL, $apiurl);
96
        curl_setopt($ch, CURLOPT_POST, true);
97
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
98
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
99
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($fields));
100
        $result = curl_exec($ch);
101
        $errors = curl_error($ch);
102
        $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
103
        $remoteip = curl_getinfo($ch,CURLINFO_PRIMARY_IP);
104
        curl_close($ch);
105
106
        if ($result === false) {
107
            throw new Tiqr_Message_Exception_SendFailure("Server unavailable", true);
108
        }
109
110
        if (!empty($errors)) {
111
            throw new Tiqr_Message_Exception_SendFailure("Http error occurred: ". $errors, true);
112
        }
113
114
        // Wait and retry once in case of a 502 Bad Gateway error
115
        if ($statusCode === 502 && !($retry)) {
116
          sleep(2);
117
          $this->_sendFirebase($deviceToken, $alert, $challenge, $projectId, $credentialsFile, true);
118
          return;
119
        }
120
121
        if ($statusCode !== 200) {
122
            throw new Tiqr_Message_Exception_SendFailure(sprintf('Invalid status code : %s. Server : %s. Response : "%s".', $statusCode, $remoteip, $result), true);
0 ignored issues
show
Bug introduced by
It seems like $result can also be of type true; however, parameter $values of sprintf() does only seem to accept double|integer|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

122
            throw new Tiqr_Message_Exception_SendFailure(sprintf('Invalid status code : %s. Server : %s. Response : "%s".', $statusCode, $remoteip, /** @scrutinizer ignore-type */ $result), true);
Loading history...
123
        }
124
125
        // handle errors, ignoring registration_id's
126
        $response = json_decode($result, true);
0 ignored issues
show
Bug introduced by
It seems like $result can also be of type true; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

126
        $response = json_decode(/** @scrutinizer ignore-type */ $result, true);
Loading history...
127
        foreach ($response['results'] as $k => $v) {
128
            if (isset($v['error'])) {
129
                throw new Tiqr_Message_Exception_SendFailure("Error in FCM response: " . $v['error'], true);
130
            }
131
        }
132
    }
133
}
134