Completed
Pull Request — master (#137)
by
unknown
23:45
created

Aliyun_MNSAgent::request()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
c 0
b 0
f 0
rs 9.6666
cc 1
eloc 6
nc 1
nop 0
1
<?php
2
3
namespace Toplan\PhpSms;
4
5
6
class Constants
7
{
8
    const GMT_DATE_FORMAT = "D, d M Y H:i:s \\G\\M\\T";
9
10
    const MNS_VERSION_HEADER = "x-mns-version";
11
    const MNS_HEADER_PREFIX = "x-mns";
12
    const MNS_XML_NAMESPACE = "http://mns.aliyuncs.com/doc/v1/";
13
14
    const MNS_VERSION = "2015-06-06";
15
    const AUTHORIZATION = "Authorization";
16
    const MNS = "MNS";
17
18
    const CONTENT_LENGTH = "Content-Length";
19
    const CONTENT_TYPE = "Content-Type";
20
    const SECURITY_TOKEN = "security-token";
21
    const DIRECT_MAIL = "DirectMail";
22
    const DIRECT_SMS = "DirectSMS";
23
    const WEBSOCKET = "WebSocket";
24
25
    // XML Tag
26
    const ERROR = "Error";
27
    const ERRORS = "Errors";
28
    const DELAY_SECONDS = "DelaySeconds";
29
    const MAXIMUM_MESSAGE_SIZE = "MaximumMessageSize";
30
    const MESSAGE_RETENTION_PERIOD = "MessageRetentionPeriod";
31
    const VISIBILITY_TIMEOUT = "VisibilityTimeout";
32
    const POLLING_WAIT_SECONDS = "PollingWaitSeconds";
33
    const MESSAGE_BODY = "MessageBody";
34
    const PRIORITY = "Priority";
35
    const MESSAGE_ID = "MessageId";
36
    const MESSAGE_BODY_MD5 = "MessageBodyMD5";
37
    const ENQUEUE_TIME = "EnqueueTime";
38
    const NEXT_VISIBLE_TIME = "NextVisibleTime";
39
    const FIRST_DEQUEUE_TIME = "FirstDequeueTime";
40
    const RECEIPT_HANDLE = "ReceiptHandle";
41
    const RECEIPT_HANDLES = "ReceiptHandles";
42
    const DEQUEUE_COUNT = "DequeueCount";
43
    const ERROR_CODE = "ErrorCode";
44
    const ERROR_MESSAGE = "ErrorMessage";
45
    const ENDPOINT = "Endpoint";
46
    const STRATEGY = "NotifyStrategy";
47
    const CONTENT_FORMAT = "NotifyContentFormat";
48
    const LOGGING_BUCKET = "LoggingBucket";
49
    const LOGGING_ENABLED = "LoggingEnabled";
50
    const MESSAGE_ATTRIBUTES = "MessageAttributes";
51
    const SUBJECT = "Subject";
52
    const ACCOUNT_NAME = "AccountName";
53
    const ADDRESS_TYPE = "AddressType";
54
    const REPLY_TO_ADDRESS = "ReplyToAddress";
55
    const IS_HTML = "IsHtml";
56
    const FREE_SIGN_NAME = "FreeSignName";
57
    const TEMPLATE_CODE = "TemplateCode";
58
    const RECEIVER = "Receiver";
59
    const SMS_PARAMS = "SmsParams";
60
    const IMPORTANCE_LEVEL = "ImportanceLevel";
61
62
    // some MNS ErrorCodes
63
    const INVALID_ARGUMENT = "InvalidArgument";
64
    const QUEUE_ALREADY_EXIST = "QueueAlreadyExist";
65
    const QUEUE_NOT_EXIST = "QueueNotExist";
66
    const MALFORMED_XML = "MalformedXML";
67
    const MESSAGE_NOT_EXIST = "MessageNotExist";
68
    const RECEIPT_HANDLE_ERROR = "ReceiptHandleError";
69
    const BATCH_SEND_FAIL = "BatchSendFail";
70
    const BATCH_DELETE_FAIL = "BatchDeleteFail";
71
72
    const TOPIC_ALREADY_EXIST = "TopicAlreadyExist";
73
    const TOPIC_NOT_EXIST = "TopicNotExist";
74
    const SUBSCRIPTION_ALREADY_EXIST = "SubscriptionAlreadyExist";
75
    const SUBSCRIPTION_NOT_EXIST = "SubscriptionNotExist";
76
}
77
78
79
/**
80
 * Class AliyunAgent
81
 *
82
 * @property string $accessKeyId
83
 * @property string $accessKeySecret
84
 * @property string $signName
85
 * @property string $endPoint
86
 * @property string $topicName
87
 */
88
class Aliyun_MNSAgent extends Agent implements TemplateSms
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
89
{
90
91
    private $tempId, $smsParams;
0 ignored issues
show
Coding Style introduced by
It is generally advisable to only define one property per statement.

Only declaring a single property per statement allows you to later on add doc comments more easily.

It is also recommended by PSR2, so it is a common style that many people expect.

Loading history...
92
93
    public function sendTemplateSms($to, $tempId, array $data)
94
    {
95
        $this->smsParams = [$to=>$data];
96
        $this->tempId = $tempId;
97
        $this->request();
98
99
    }
100
101
    protected function request()
102
    {
103
        $sendurl = $this->endPoint . '/topics/' . $this->topicName . '/messages';
104
        $result = $this->curlPost($sendurl, [], [
105
            CURLOPT_HTTPHEADER => $this->createHeaders(),
106
            CURLOPT_POSTFIELDS => $this->generateBody(),
107
        ]);
108
        $this->setResult($result);
109
    }
110
111
    protected function writeMessagePropertiesForPublishXML(\XMLWriter $xmlWriter)
112
    {
113
114
        $xmlWriter->writeElement(Constants::MESSAGE_BODY, 'smsmessage');
115
        $xmlWriter->startElement(Constants::MESSAGE_ATTRIBUTES);
116
        $jsonArray = array("Type" => "multiContent");
117
        $jsonArray[Constants::FREE_SIGN_NAME] = $this->signName;
118
        $jsonArray[Constants::TEMPLATE_CODE] = $this->tempId;
119
120
        if ($this->smsParams != null) {
121
            if (!is_array($this->smsParams)) {
122
                throw new PhpSmsException("SmsParams should be an array!",400);
123
            }
124
            if (!empty($this->smsParams)) {
125
                $jsonArray['SmsParams'] = json_encode($this->smsParams,JSON_FORCE_OBJECT);
126
            }
127
        }
128
129
        if (!empty($jsonArray)) {
130
            $xmlWriter->writeElement(Constants::DIRECT_SMS, json_encode($jsonArray));
131
        }
132
        $xmlWriter->endElement();
133
    }
134
135
136
    protected function generateBody()
137
    {
138
        $xmlWriter = new \XMLWriter;
139
        $xmlWriter->openMemory();
140
        $xmlWriter->startDocument("1.0", "UTF-8");
141
        $xmlWriter->startElementNS(NULL, "Message", Constants::MNS_XML_NAMESPACE);
142
        $this->writeMessagePropertiesForPublishXML($xmlWriter);
143
        $xmlWriter->endElement();
144
        $xmlWriter->endDocument();
145
146
      return $xmlWriter->outputMemory();
147
    }
148
149
    protected function createHeaders()
150
    {
151
        $params = [
152
            Constants::CONTENT_TYPE => 'text/xml',
153
            Constants::MNS_VERSION_HEADER => Constants::MNS_VERSION,
154
            'Date' => gmdate(Constants::GMT_DATE_FORMAT),
155
        ];
156
        $params[Constants::AUTHORIZATION] = Constants::MNS . " " . $this->accessKeyId . ":" . $this->computeSignature($params);
157
158
        $p = [];
159
        foreach ($params as $k => $v) {
160
            $p[] = "$k:$v";
161
        }
162
        return $p;
163
    }
164
165
166
    private function computeSignature($parameters)
167
    {
168
        $contentMd5 = '';
169
        $contentType = $parameters[Constants::CONTENT_TYPE];
170
        $date = $parameters['Date'];
171
        $canonicalizedMNSHeaders = Constants::MNS_VERSION_HEADER.':' . $parameters[Constants::MNS_VERSION_HEADER];
172
        $canonicalizedResource = '/topics/' . $this->topicName . '/messages';
173
        $stringToSign = strtoupper('POST') . "\n" . $contentMd5 . "\n" . $contentType . "\n" . $date . "\n" . $canonicalizedMNSHeaders . "\n" . $canonicalizedResource;
174
175
        return base64_encode(hash_hmac('sha1', $stringToSign, $this->accessKeySecret, true));
176
    }
177
178
179
    protected function loadXmlContent($content)
180
    {
181
        $xmlReader = new \XMLReader();
182
        $isXml = $xmlReader->XML($content);
183
        if ($isXml === FALSE) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
184
185
        }
186
        try {
187
            while ($xmlReader->read()) {}
0 ignored issues
show
Unused Code introduced by
This while loop is empty and can be removed.

This check looks for while loops that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

Consider removing the loop.

Loading history...
188
        } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
189
190
        }
191
        $xmlReader->XML($content);
192
        return $xmlReader;
193
    }
194
195
    protected function setResult($result)
196
    {
197
198
        if ($result['request']) {
199
            $this->result(Agent::INFO, $result['response']);
200
201
202
            $messageId = NULL;
203
            $messageBodyMD5 = NULL;
0 ignored issues
show
Unused Code introduced by
$messageBodyMD5 is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
204
            $errorCode = NULL;
205
            $errorMessage = NULL;
0 ignored issues
show
Unused Code introduced by
$errorMessage is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
206
207
            $xmlReader=  $this->loadXmlContent($result['response']);
208
            while ($xmlReader->read())
209
            {
210
                switch ($xmlReader->nodeType)
211
                {
212
                    case \XMLReader::ELEMENT:
213
                        switch ($xmlReader->name) {
214 View Code Duplication
                            case Constants::MESSAGE_ID:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
215
                                $xmlReader->read();
216
                                if ($xmlReader->nodeType == \XMLReader::TEXT)
217
                                {
218
                                    $messageId = $xmlReader->value;
219
                                }
220
                                break;
221 View Code Duplication
                            case Constants::MESSAGE_BODY_MD5:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
222
                                $xmlReader->read();
223
                                if ($xmlReader->nodeType == \XMLReader::TEXT)
224
                                {
225
                                    $messageBodyMD5 = $xmlReader->value;
0 ignored issues
show
Unused Code introduced by
$messageBodyMD5 is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
226
                                }
227
                                break;
228 View Code Duplication
                            case Constants::ERROR_CODE:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
229
                                $xmlReader->read();
230
                                if ($xmlReader->nodeType == \XMLReader::TEXT)
231
                                {
232
                                    $errorCode = $xmlReader->value;
233
                                }
234
                                break;
235 View Code Duplication
                            case Constants::ERROR_MESSAGE:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
236
                                $xmlReader->read();
237
                                if ($xmlReader->nodeType == \XMLReader::TEXT)
238
                                {
239
                                    $errorMessage = $xmlReader->value;
0 ignored issues
show
Unused Code introduced by
$errorMessage is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
240
                                }
241
                                break;
242
                        }
243
                        break;
244
                    case \XMLReader::END_ELEMENT:
245
                        if ($xmlReader->name == 'Message')
246
                        {
247 View Code Duplication
                            if ($messageId != NULL)
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $messageId of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
Duplication introduced by
This code seems to be duplicated across 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...
248
                            {
249
                                $this->result(Agent::SUCCESS, true);
250
                            }
251
                            else
252
                            {
253
                                $this->result(Agent::SUCCESS, false);
254
                            }
255
                        }
256
                        break;
257
                }
258
            }
259
260 View Code Duplication
            if ($messageId != NULL)
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $messageId of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
Duplication introduced by
This code seems to be duplicated across 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...
261
            {
262
                $this->result(Agent::SUCCESS, true);
263
            }
264
            else
265
            {
266
                $this->result(Agent::SUCCESS, false);
267
            }
268
269
            if ($errorCode!=NULL)
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $errorCode of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
270
            $this->result(Agent::CODE, $errorCode);
271
272
273
        } else {
274
            $this->result(Agent::INFO, 'request failed');
275
        }
276
    }
277
278
279
}
280