Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
1 | <?php |
||
11 | class Client |
||
12 | { |
||
13 | const THUMBNAIL_FORMAT_JPEG = 'jpeg'; |
||
14 | const THUMBNAIL_FORMAT_PNG = 'png'; |
||
15 | |||
16 | const THUMBNAIL_SIZE_XS = 'w32h32'; |
||
17 | const THUMBNAIL_SIZE_S = 'w64h64'; |
||
18 | const THUMBNAIL_SIZE_M = 'w128h128'; |
||
19 | const THUMBNAIL_SIZE_L = 'w640h480'; |
||
20 | const THUMBNAIL_SIZE_XL = 'w1024h768'; |
||
21 | |||
22 | const HTTP_BAD_REQUEST = 400 |
||
23 | const HTTP_CONFLICT = 409 |
||
|
|||
24 | |||
25 | protected $accessToken; |
||
26 | |||
27 | protected $client; |
||
28 | |||
29 | public function __construct(string $accessToken) |
||
30 | { |
||
31 | $this->accessToken = $accessToken; |
||
32 | |||
33 | $this->client = new GuzzleClient([ |
||
34 | 'headers' => [ |
||
35 | 'Authorization' => "Bearer {$this->accessToken}", |
||
36 | ], |
||
37 | ]); |
||
38 | } |
||
39 | |||
40 | /** |
||
41 | * Copy a file or folder to a different location in the user's Dropbox. |
||
42 | * |
||
43 | * If the source path is a folder all its contents will be copied. |
||
44 | * |
||
45 | * @link https://www.dropbox.com/developers/documentation/http/documentation#files-copy |
||
46 | */ |
||
47 | public function copy(string $path, string $newPath): array |
||
48 | { |
||
49 | $parameters = [ |
||
50 | 'from_path' => $this->normalizePath($path), |
||
51 | 'to_path' => $this->normalizePath($newPath), |
||
52 | ]; |
||
53 | |||
54 | return $this->rpcEndpointRequest('files/copy', $parameters); |
||
55 | } |
||
56 | |||
57 | /** |
||
58 | * Create a folder at a given path.Create a folder at a given path. |
||
59 | * |
||
60 | * @link https://www.dropbox.com/developers/documentation/http/documentation#files-create_folder |
||
61 | */ |
||
62 | public function createFolder(string $path): array |
||
63 | { |
||
64 | $parameters = [ |
||
65 | 'path' => $this->normalizePath($path), |
||
66 | ]; |
||
67 | |||
68 | $object = $this->rpcEndpointRequest('files/create_folder', $parameters); |
||
69 | |||
70 | $object['.tag'] = 'folder'; |
||
71 | |||
72 | return $object; |
||
73 | } |
||
74 | |||
75 | /** |
||
76 | * Delete the file or folder at a given path. |
||
77 | * |
||
78 | * If the path is a folder, all its contents will be deleted too. |
||
79 | * A successful response indicates that the file or folder was deleted. |
||
80 | * |
||
81 | * @link https://www.dropbox.com/developers/documentation/http/documentation#files-delete |
||
82 | */ |
||
83 | public function delete(string $path): array |
||
84 | { |
||
85 | $parameters = [ |
||
86 | 'path' => $this->normalizePath($path), |
||
87 | ]; |
||
88 | |||
89 | return $this->rpcEndpointRequest('files/delete', $parameters); |
||
90 | } |
||
91 | |||
92 | /** |
||
93 | * Download a file from a user's Dropbox. |
||
94 | * |
||
95 | * @param string $path |
||
96 | * |
||
97 | * @return resource |
||
98 | * |
||
99 | * @link https://www.dropbox.com/developers/documentation/http/documentation#files-download |
||
100 | */ |
||
101 | public function download(string $path) |
||
102 | { |
||
103 | $arguments = [ |
||
104 | 'path' => $this->normalizePath($path), |
||
105 | ]; |
||
106 | |||
107 | $response = $this->contentEndpointRequest('files/download', $arguments); |
||
108 | |||
109 | return StreamWrapper::getResource($response->getBody()); |
||
110 | } |
||
111 | |||
112 | /** |
||
113 | * Returns the metadata for a file or folder. |
||
114 | * |
||
115 | * Note: Metadata for the root folder is unsupported. |
||
116 | * |
||
117 | * @link https://www.dropbox.com/developers/documentation/http/documentation#files-get_metadata |
||
118 | */ |
||
119 | public function getMetadata(string $path): array |
||
120 | { |
||
121 | $parameters = [ |
||
122 | 'path' => $this->normalizePath($path), |
||
123 | ]; |
||
124 | |||
125 | return $this->rpcEndpointRequest('files/get_metadata', $parameters); |
||
126 | } |
||
127 | |||
128 | /** |
||
129 | * Get a temporary link to stream content of a file. |
||
130 | * |
||
131 | * This link will expire in four hours and afterwards you will get 410 Gone. |
||
132 | * Content-Type of the link is determined automatically by the file's mime type. |
||
133 | * |
||
134 | * @link https://www.dropbox.com/developers/documentation/http/documentation#files-get_temporary_link |
||
135 | */ |
||
136 | public function getTemporaryLink(string $path): string |
||
137 | { |
||
138 | $parameters = [ |
||
139 | 'path' => $this->normalizePath($path), |
||
140 | ]; |
||
141 | |||
142 | $body = $this->rpcEndpointRequest('files/get_temporary_link', $parameters); |
||
143 | |||
144 | return $body['link']; |
||
145 | } |
||
146 | |||
147 | /** |
||
148 | * Get a thumbnail for an image. |
||
149 | * |
||
150 | * This method currently supports files with the following file extensions:jpg, jpeg, |
||
151 | * png, tiff, tif, gif and bmp. |
||
152 | * Photos that are larger than 20MB in size won't be converted to a thumbnail. |
||
153 | * |
||
154 | * @link https://www.dropbox.com/developers/documentation/http/documentation#files-get_thumbnail |
||
155 | */ |
||
156 | public function getThumbnail(string $path, string $format = 'jpeg', string $size = 'w64h64'): string |
||
157 | { |
||
158 | $arguments = [ |
||
159 | 'path' => $this->normalizePath($path), |
||
160 | 'format' => $format, |
||
161 | 'size' => $size |
||
162 | ]; |
||
163 | |||
164 | $response = $this->contentEndpointRequest('files/get_thumbnail', $arguments); |
||
165 | |||
166 | return (string)$response->getBody(); |
||
167 | } |
||
168 | |||
169 | /** |
||
170 | * Starts returning the contents of a folder. |
||
171 | * |
||
172 | * If the result's ListFolderResult.has_more field is true, call |
||
173 | * list_folder/continue with the returned ListFolderResult.cursor to retrieve more entries. |
||
174 | * |
||
175 | * Note: auth.RateLimitError may be returned if multiple list_folder or list_folder/continue calls |
||
176 | * with same parameters are made simultaneously by same API app for same user. If your app implements |
||
177 | * retry logic, please hold off the retry until the previous request finishes. |
||
178 | * |
||
179 | * @link https://www.dropbox.com/developers/documentation/http/documentation#files-list_folder |
||
180 | */ |
||
181 | public function listFolder(string $path = '', bool $recursive = false): array |
||
182 | { |
||
183 | $parameters = [ |
||
184 | 'path' => $this->normalizePath($path), |
||
185 | 'recursive' => $recursive, |
||
186 | ]; |
||
187 | |||
188 | return $this->rpcEndpointRequest('files/list_folder', $parameters); |
||
189 | } |
||
190 | |||
191 | /** |
||
192 | * Move a file or folder to a different location in the user's Dropbox. |
||
193 | * |
||
194 | * If the source path is a folder all its contents will be moved. |
||
195 | * |
||
196 | * @link https://www.dropbox.com/developers/documentation/http/documentation#files-move |
||
197 | */ |
||
198 | public function move(string $path, string $newPath): array |
||
199 | { |
||
200 | $parameters = [ |
||
201 | 'from_path' => $this->normalizePath($path), |
||
202 | 'to_path' => $this->normalizePath($newPath), |
||
203 | ]; |
||
204 | |||
205 | return $this->rpcEndpointRequest('files/move', $parameters); |
||
206 | } |
||
207 | |||
208 | /** |
||
209 | * Create a new file with the contents provided in the request. |
||
210 | * |
||
211 | * Do not use this to upload a file larger than 150 MB. Instead, create an upload session with upload_session/start. |
||
212 | * |
||
213 | * @link https://www.dropbox.com/developers/documentation/http/documentation#files-upload |
||
214 | * |
||
215 | * @param string $path |
||
216 | * @param string $mode |
||
217 | * @param string|resource $contents |
||
218 | * |
||
219 | * @return array |
||
220 | */ |
||
221 | public function upload(string $path, string $mode, $contents): array |
||
222 | { |
||
223 | $arguments = [ |
||
224 | 'path' => $this->normalizePath($path), |
||
225 | 'mode' => $mode, |
||
226 | ]; |
||
227 | |||
228 | $response = $this->contentEndpointRequest('files/upload', $arguments, $contents); |
||
229 | |||
230 | $metadata = json_decode($response->getBody(), true); |
||
231 | |||
232 | $metadata['.tag'] = 'file'; |
||
233 | |||
234 | return $metadata; |
||
235 | } |
||
236 | |||
237 | protected function normalizePath(string $path): string |
||
238 | { |
||
239 | $path = trim($path, '/'); |
||
240 | |||
241 | if ($path === '') { |
||
242 | return ''; |
||
243 | } |
||
244 | |||
245 | return '/' . $path; |
||
246 | } |
||
247 | |||
248 | /** |
||
249 | * @param string $endpoint |
||
250 | * @param array $arguments |
||
251 | * @param string|resource $body |
||
252 | * |
||
253 | * @return \Psr\Http\Message\ResponseInterface |
||
254 | * |
||
255 | * @throws \Spatie\Dropbox\Exceptions\BadRequest |
||
256 | */ |
||
257 | protected function contentEndpointRequest(string $endpoint, array $arguments, $body = ''): ResponseInterface |
||
258 | { |
||
259 | $headers['Dropbox-API-Arg'] = json_encode($arguments); |
||
260 | |||
261 | if ($body !== '') { |
||
262 | $headers['Content-Type'] = 'application/octet-stream'; |
||
263 | } |
||
264 | |||
265 | try { |
||
266 | $response = $this->client->post("https://content.dropboxapi.com/2/{$endpoint}", [ |
||
267 | 'headers' => $headers, |
||
268 | 'body' => $body, |
||
269 | ]); |
||
270 | |||
271 | } catch (ClientException $exception) { |
||
272 | if (in_array($exception->getResponse()->getStatusCode(), [ |
||
273 | static::HTTP_BAD_REQUEST, |
||
274 | static::HTTP_CONFLICT, |
||
275 | ])) { |
||
276 | throw new BadRequest($exception->getResponse()); |
||
277 | } |
||
278 | |||
279 | throw $exception; |
||
280 | } |
||
281 | |||
282 | return $response; |
||
283 | } |
||
284 | |||
285 | protected function rpcEndpointRequest(string $endpoint, array $parameters): array |
||
305 |