Passed
Pull Request — develop (#42)
by Pieter van der
03:31 queued 40s
created

Tiqr_Message_APNS2::send()   B

Complexity

Conditions 10
Paths 22

Size

Total Lines 89
Code Lines 57

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 110

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 57
c 1
b 0
f 0
dl 0
loc 89
ccs 0
cts 72
cp 0
rs 7.0715
cc 10
nc 22
nop 0
crap 110

How to fix   Long Method    Complexity   

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
use Pushok\AuthProvider;
4
use Pushok\Client;
5
use Pushok\Notification;
6
use Pushok\Payload;
7
use Pushok\Payload\Alert;
8
9
/** @internal includes */
10
require_once('Tiqr/Message/Abstract.php');
11
12
/**
13
 * Apple Push Notification Service message class for the HTTP/2 api.push.apple.com APNs API.
14
 */
15
class Tiqr_Message_APNS2 extends Tiqr_Message_Abstract
16
{
17
    /**
18
     * Send message.
19
     */
20
    public function send()
21
    {
22
        $version_info = curl_version();
23
        if ($version_info['features'] & CURL_VERSION_HTTP2 == 0) {
24
            throw new RuntimeException('APNS2 requires HTTP/2 support in curl');
25
        }
26
27
        // Get the UID from the client certificate we use for authentication, this
28
        // is set to the bundle ID.
29
        $options=$this->getOptions();
30
        $cert_filename = $options['apns.certificate'];
31
        $cert_file_contents = file_get_contents($cert_filename);
32
        if (false === $cert_file_contents) {
33
            throw new RuntimeException(
34
                sprintf('Error reading APNS client certificate file: "%s"', $cert_filename)
35
            );
36
        }
37
38
        $cert=openssl_x509_parse( $cert_file_contents );
39
        if (false === $cert) {
40
            throw new RuntimeException('Error parsing APNS client certificate');
41
        }
42
        $bundle_id = $cert['subject']['UID'] ?? NULL;
43
        if (NULL === $bundle_id) {
44
            throw new RuntimeException('No uid found in the certificate subject');
45
        }
46
        $this->logger->info(sprintf('Setting bundle_id to "%s" based on UID from certificate', $bundle_id));
47
        $this->logger->info(
48
            sprintf('Authenticating using certificate with subject "%s" valid until "%s"',
49
                $cert['name'],
50
                date(DATE_RFC2822, $cert['validTo_time_t'])
51
            )
52
        );
53
54
        $authProviderOptions = [
55
            'app_bundle_id' => $bundle_id, // The bundle ID for app obtained from Apple developer account
56
            'certificate_path' => $cert_filename,
57
            'certificate_secret' => null // Private key secret
58
        ];
59
60
        $authProvider = AuthProvider\Certificate::create($authProviderOptions);
61
62
        // Create the push message
63
        $alert=Alert::create();
64
        $alert->setBody($this->getText());
65
        // TODO: Set title?
66
        $payload=Payload::create()->setAlert($alert);
67
        $payload->setSound('default');
68
        // TODO: Can we set an expiry time for the message?
69
        foreach ($this->getCustomProperties() as $name => $value) {
70
            $payload->setCustomValue($name, $value);
71
        }
72
        $this->logger->debug(sprintf('JSON Payload: %s', $payload->toJson()));
73
        $notification=new Notification($payload, $this->getAddress());
74
75
        // Send the push message
76
        $client = new Client($authProvider, $options['apns.environment'] == 'production');
77
        $client->addNotification($notification);
78
        $responses=$client->push();
79
        if ( sizeof($responses) != 1) {
80
            $this->logger->warning('Unexpected number responses. Expected 1, got %n', sizeof($responses) );
0 ignored issues
show
Bug introduced by
sizeof($responses) of type integer is incompatible with the type array expected by parameter $context of Psr\Log\LoggerInterface::warning(). ( Ignorable by Annotation )

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

80
            $this->logger->warning('Unexpected number responses. Expected 1, got %n', /** @scrutinizer ignore-type */ sizeof($responses) );
Loading history...
81
            if (sizeof($responses) == 0) {
82
                $this->logger->warning('Could not determine whether the notification was sent');
83
                return;
84
            }
85
        }
86
        /** @var \Pushok\Response $response */
87
        $response = reset($responses);  // Get first response from the array
88
        $deviceToken=$response->getDeviceToken() ?? '';
89
        // A canonical UUID that is the unique ID for the notification. E.g. 123e4567-e89b-12d3-a456-4266554400a0
90
        $apnsId=$response->getApnsId() ?? '';
91
        // Status code. E.g. 200 (Success), 410 (The device token is no longer active for the topic.)
92
        $statusCode=$response->getStatusCode();
93
        $this->logger->info(sprintf('Got response with ApnsId "%s", status %s for deviceToken "%s"', $apnsId, $statusCode, $deviceToken));
94
        if ( strcasecmp($deviceToken, $this->getAddress()) ) {
95
        $this->logger->warning(sprintf('Unexpected deviceToken in response. Expected: "%s"; got: "%s"', $this->getAddress(), $deviceToken));
96
        }
97
        if ($statusCode == 200) {
98
            $this->logger->notice(sprintf('Successfully sent APNS2 push notification. APNS ID: "%s"; deviceToken: "%s"', $apnsId, $deviceToken));
99
            return;
100
        }
101
102
        $reasonPhrase=$response->getReasonPhrase(); // E.g. The device token is no longer active for the topic.
103
        $errorReason=$response->getErrorReason(); // E.g. Unregistered
104
        $errorDescription=$response->getErrorDescription(); // E.g. The device token is inactive for the specified topic.
105
106
        $this->logger->error(sprintf('Error sending APNS2 push notification. APNS ID: "%s"; deviceToken: "%s"; Error: "%s" "%s" "%s"', $apnsId, $deviceToken, $reasonPhrase, $errorReason, $errorDescription));
107
        throw new RuntimeException(
108
            sprintf('Error sending APNS2 push notification. Status: %s. Error: "%s" "%s" "%s"', $statusCode, $reasonPhrase, $errorReason, $errorDescription)
109
        );
110
    }
111
}
112