• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ringcentral / ringcentral-php / 10247615018

05 Aug 2024 11:04AM UTC coverage: 77.524% (+0.1%) from 77.389%
10247615018

Pull #139

github

web-flow
Merge 085079492 into 67c9a424d
Pull Request #139: Extract Truncated error response

9 of 9 new or added lines in 1 file covered. (100.0%)

2 existing lines in 2 files now uncovered.

576 of 743 relevant lines covered (77.52%)

7.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.36
/src/Http/ApiResponse.php
1
<?php
2

3
namespace RingCentral\SDK\Http;
4

5
use Exception;
6
use Psr\Http\Message\RequestInterface;
7
use Psr\Http\Message\ResponseInterface;
8
use RingCentral\SDK\Core\Utils;
9
use stdClass;
10

11
/**
12
 * FIXME Support streams
13
 * @package RingCentral\SDK\Http
14
 * @see     http://www.opensource.apple.com/source/apache_mod_php/apache_mod_php-7/php/pear/Mail/mimeDecode.php
15
 * @see     https://github.com/php-mime-mail-parser/php-mime-mail-parser
16
 */
17
class ApiResponse
18
{
19

20
    /** @var array */
21
    protected $_jsonAsArray;
22

23
    /** @var stdClass */
24
    protected $_jsonAsObject;
25

26
    /** @var ApiResponse[] */
27
    protected $_multiparts;
28

29
    /** @var ResponseInterface */
30
    protected $_response;
31

32
    /** @var RequestInterface */
33
    protected $_request;
34

35
    /**
36
     * TODO Support strams
37
     * @param RequestInterface  $request  Reqeuest used to get the response
38
     * @param ResponseInterface $response Response
39
     */
40
    public function __construct(RequestInterface $request = null, ResponseInterface $response = null)
41
    {
42

43
        $this->_request = $request;
33✔
44
        $this->_response = $response;
33✔
45

46
    }
47

48
    /**
49
     * @return string
50
     */
51
    public function text()
52
    {
53
        return (string)$this->body();
30✔
54
    }
55

56
    /**
57
     * @return \Psr\Http\Message\StreamInterface
58
     */
59
    public function body()
60
    {
61
        return $this->_response->getBody();
30✔
62
    }
63

64
    /**
65
     * @return mixed
66
     */
67
    public function raw()
68
    {
69
        return $this->body();
×
70
    }
71

72
    /**
73
     * Parses response body as JSON
74
     * Result is cached internally
75
     * @return stdClass
76
     * @throws Exception
77
     */
78
    public function json()
79
    {
80

81
        if (!$this->isContentType('application/json')) {
15✔
82
            throw new Exception('Response is not JSON');
1✔
83
        }
84

85
        if (empty($this->_jsonAsObject)) {
14✔
86
            $this->_jsonAsObject = Utils::json_parse($this->text(), false);
14✔
87
        }
88

89
        return $this->_jsonAsObject;
11✔
90

91
    }
92

93
    /**
94
     * Parses response body as JSON and returns an array
95
     * Result is cached internally
96
     *
97
     * @throws Exception
98
     *
99
     * @return array
100
     */
101
    public function jsonArray()
102
    {
103

104
        if (!$this->isContentType('application/json')) {
24✔
105
            throw new Exception('Response is not JSON');
×
106
        }
107

108
        if (empty($this->_jsonAsArray)) {
24✔
109
            $this->_jsonAsArray = Utils::json_parse($this->text(), true);
24✔
110
        }
111

112
        return $this->_jsonAsArray;
24✔
113

114
    }
115

116
    /**
117
     * Parses multipart response body as an array of ApiResponse objects
118
     * @return ApiResponse[]
119
     * @throws Exception
120
     */
121
    public function multipart()
122
    {
123

124
        if (empty($this->_multiparts)) {
5✔
125

126
            $this->_multiparts = [];
5✔
127

128
            if (!$this->isContentType('multipart/mixed')) {
5✔
129
                throw new Exception('Response is not multipart');
1✔
130
            }
131

132
            // Step 1. Get boundary
133

134
            preg_match('/boundary=([^";]+)/i', $this->getContentType(), $matches);
4✔
135

136
            if (empty($matches[1])) {
4✔
137
                throw new Exception('Boundary not found');
1✔
138
            }
139

140
            $boundary = $matches[1];
3✔
141

142
            // Step 2. Split by boundary and remove first and last parts if needed
143

144
            $parts = explode('--' . $boundary . '', $this->text()); //TODO Handle as stream
3✔
145

146
            if (empty(trim($parts[0]))) {
3✔
147
                array_shift($parts);
3✔
148
            }
149

150
            if (trim($parts[count($parts) - 1]) == '--') {
3✔
151
                array_pop($parts);
3✔
152
            }
153

154
            if (count($parts) == 0) {
3✔
155
                throw new Exception('No parts found');
×
156
            }
157

158
            // Step 3. Create status info object
159

160
            $statusInfoPart = array_shift($parts);
3✔
161
            $statusInfoObj = new self(null,
3✔
162
                self::createResponse(trim($statusInfoPart), $this->response()->getStatusCode())
3✔
163
            );
3✔
164
            $statusInfo = $statusInfoObj->json()->response;
3✔
165

166
            // Step 4. Parse all parts into Response objects
167

168
            foreach ($parts as $i => $part) {
2✔
169

170
                $partInfo = $statusInfo[$i];
2✔
171

172
                $this->_multiparts[] = new self(null, self::createResponse(trim($part), $partInfo->status));
2✔
173

174
            }
175

176
        }
177

178
        return $this->_multiparts;
2✔
179

180
    }
181

182
    /**
183
     * @return bool
184
     */
185
    public function ok()
186
    {
187
        $status = $this->response()->getStatusCode();
25✔
188
        return $status >= 200 && $status < 300;
25✔
189
    }
190

191
    /**
192
     * Returns a meaningful error message
193
     * @return string
194
     */
195
    public function error()
196
    {
197

198
        $res = $this->response();
2✔
199

200
        if (!$res) {
2✔
UNCOV
201
            return null;
×
202
        }
203

204
        if ($this->ok()) {
2✔
205
            return null;
×
206
        }
207

208
        $message = ($res->getStatusCode() ? $res->getStatusCode() . ' ' : '') .
2✔
209
                   ($res->getReasonPhrase() ? $res->getReasonPhrase() : 'Unknown response reason phrase');
2✔
210

211
        try {
212

213
            $data = $this->json();
2✔
214

215
            if (!empty($data->message)) {
2✔
216
                $message = $data->message;
2✔
217
            }
218

219
            if (!empty($data->error_description)) {
2✔
220
                $message = $data->error_description;
×
221
            }
222

223
            if (!empty($data->description)) {
2✔
224
                $message = $data->description;
2✔
225
            }
226

227
        } catch (Exception $e) {
×
228
            // This should never happen
229
            $message .= ' (and additional error happened during JSON parse: ' . $e->getMessage() . ')';
×
230
        }
231

232
        return $message;
2✔
233

234
    }
235

236
    /**
237
     * @return RequestInterface
238
     */
239
    public function request()
240
    {
241
        return $this->_request;
×
242
    }
243

244
    /**
245
     * @return ResponseInterface
246
     */
247
    public function response()
248
    {
249
        return $this->_response;
33✔
250
    }
251

252
    protected function isContentType($type)
253
    {
254
        return !!stristr(strtolower($this->getContentType()), strtolower($type));
33✔
255
    }
256

257
    protected function getContentType()
258
    {
259
        return $this->response()->getHeaderLine('content-type');
33✔
260
    }
261

262
    static function createResponse($body = '', $status = 200)
263
    {
264

265
        // Make the HTTP message complete
266
        if (substr($body, 0, 5) !== 'HTTP/') {
9✔
267
            $body = "HTTP/1.1 " . $status . " Foo\r\n" . $body;
9✔
268
        }
269

270
        $response = \GuzzleHttp\Psr7\Message::parseResponse((string)$body);
9✔
271

272
        return $response->withStatus($status);
9✔
273

274
    }
275

276
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc