Passed
Push — master ( 60b6ca...163cac )
by Kylian
06:19
created

BeatSaverAPI::searchMap()   F

Complexity

Conditions 18
Paths > 20000

Size

Total Lines 29
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 18
eloc 23
c 3
b 0
f 0
nc 131072
nop 19
dl 0
loc 29
rs 0.7

How to fix   Complexity    Many Parameters   

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:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace KriKrixs;
4
5
use KriKrixs\functions\MultiQuery;
6
use KriKrixs\object\beatmap\BeatMap;
7
use KriKrixs\object\response\ResponseDownload;
8
use KriKrixs\object\response\ResponseMap;
9
use KriKrixs\object\response\ResponseMaps;
10
use KriKrixs\object\response\ResponseUser;
11
use KriKrixs\object\user\User;
12
13
class BeatSaverAPI
14
{
15
    const BEATSAVER_URL = "https://api.beatsaver.com/";
16
17
    private string $userAgent;
18
    private MultiQuery $multiQuery;
19
20
    /**
21
     * BeatSaverAPI constructor
22
     * @param string $userAgent User Agent to provide to Beat Saver API
23
     */
24
    public function __construct(string $userAgent)
25
    {
26
        $this->autoload("./");
27
28
        $this->userAgent = $userAgent;
29
        $this->multiQuery = new MultiQuery(self::BEATSAVER_URL, $userAgent);
30
    }
31
32
    private function autoload($directory) {
33
        if(is_dir($directory)) {
34
            $scan = scandir($directory);
35
            unset($scan[0], $scan[1]); //unset . and ..
36
            foreach($scan as $file) {
37
                if(is_dir($directory."/".$file)) {
38
                    $this->autoload($directory."/".$file);
39
                } else {
40
                    if(strpos($file, '.php') !== false) {
41
                        include_once($directory."/".$file);
42
                    }
43
                }
44
            }
45
        }
46
    }
47
48
    /**
49
     * Private calling API function
50
     * @param string $endpoint
51
     * @return string|null
52
     */
53
    private function callAPI(string $endpoint): ?string
54
    {
55
        $curl = curl_init();
56
57
        curl_setopt_array($curl, [
58
            CURLOPT_URL => self::BEATSAVER_URL . $endpoint,
59
            CURLOPT_USERAGENT => $this->userAgent,
60
            CURLOPT_RETURNTRANSFER => true
61
        ]);
62
63
        $result = curl_exec($curl);
64
65
        curl_close($curl);
66
67
        return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result could return the type true which is incompatible with the type-hinted return null|string. Consider adding an additional type-check to rule them out.
Loading history...
68
    }
69
70
    ////////////
71
    /// CALL ///
72
    ///////////
73
74
    /**
75
     * Private building response functions
76
     * @param string $endpoint
77
     * @return ResponseMap
78
     */
79
    private function getMap(string $endpoint): ResponseMap
80
    {
81
        $response = new ResponseMap();
82
83
        $apiResult = $this->callAPI($endpoint);
84
85
        if($apiResult === false || $apiResult == "Not Found") {
86
            $response->setErrorStatus(true)->setErrorStatus("[getMap] Something went wrong with the API call.");
0 ignored issues
show
Bug introduced by
'[getMap] Something went...ong with the API call.' of type string is incompatible with the type boolean expected by parameter $errorStatus of KriKrixs\object\response...ponse::setErrorStatus(). ( Ignorable by Annotation )

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

86
            $response->setErrorStatus(true)->setErrorStatus(/** @scrutinizer ignore-type */ "[getMap] Something went wrong with the API call.");
Loading history...
87
            return $response;
88
        }
89
90
        $response->setBeatMap(new BeatMap(json_decode($apiResult)));
91
92
        return $response;
93
    }
94
95
    ///////////////
96
    /// Get Map ///
97
    ///////////////
98
99
    /**
100
     * Get map by ID (Same as BSR Key)
101
     * @param string $id Map ID
102
     * @return ResponseMap
103
     */
104
    public function getMapByID(string $id): ResponseMap
105
    {
106
        return $this->getMap("/maps/id/" . $id);
107
    }
108
109
    /**
110
     * Get map by BSR Key (Same as ID)
111
     * @param string $bsrKey Map BSR key
112
     * @return ResponseMap
113
     */
114
    public function getMapByKey(string $bsrKey): ResponseMap
115
    {
116
        return $this->getMap("/maps/id/" . $bsrKey);
117
    }
118
119
    /**
120
     * Get map by Hash
121
     * @param string $hash Hash of the map
122
     * @return ResponseMap
123
     */
124
    public function getMapByHash(string $hash): ResponseMap
125
    {
126
        return $this->getMap("/maps/hash/" . $hash);
127
    }
128
129
    ////////////////
130
    /// Get Maps ///
131
    ////////////////
132
133
    /**
134
     * Private building response functions
135
     * @param string $endpoint
136
     * @param int $limit
137
     * @return array
138
     */
139
    private function getMaps(string $endpoint, int $limit): array
0 ignored issues
show
Unused Code introduced by
The parameter $limit is not used and could be removed. ( Ignorable by Annotation )

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

139
    private function getMaps(string $endpoint, /** @scrutinizer ignore-unused */ int $limit): array

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
140
    {
141
        $response = new ResponseMaps();
142
        $maps = [];
143
144
        // Latest
145
        if($numberOfPage === 0 && $startPage === 0){
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $startPage seems to be never defined.
Loading history...
Comprehensibility Best Practice introduced by
The variable $numberOfPage seems to be never defined.
Loading history...
146
            $apiResult = json_decode($this->callAPI($endpoint));
147
148
            if($apiResult === false || $apiResult == "Not Found") {
149
                $response->setErrorStatus(true)->setErrorMessage("[getMaps] Something went wrong with the API call while calling the first page.");
150
                return $response;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response returns the type KriKrixs\object\response\ResponseMaps which is incompatible with the type-hinted return array.
Loading history...
151
            } else{
152
                foreach ($apiResult->docs as $beatmap) {
153
                    $maps[] = new BeatMap($beatmap);
154
                }
155
            }
156
        } else {
157
            for($i = $startPage; $i < ($i + $numberOfPage); $i++){
158
                $apiResult = json_decode($this->callAPI(str_ireplace("page", $i, $endpoint)));
0 ignored issues
show
Bug introduced by
It seems like str_ireplace('page', $i, $endpoint) can also be of type array; however, parameter $endpoint of KriKrixs\BeatSaverAPI::callAPI() 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

158
                $apiResult = json_decode($this->callAPI(/** @scrutinizer ignore-type */ str_ireplace("page", $i, $endpoint)));
Loading history...
159
160
                if($apiResult === false || $apiResult == "Not Found") {
161
                    $response->setErrorStatus(true)->setErrorMessage("[getMaps] Something went wrong with the API call while calling page number " . $i . ".");
162
163
                    if($apiResult == "Not Found")
164
                        return $response;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response returns the type KriKrixs\object\response\ResponseMaps which is incompatible with the type-hinted return array.
Loading history...
165
                }
166
167
                foreach ($apiResult->docs as $beatmap) {
168
                    $maps[] = new BeatMap($beatmap);
169
                }
170
            }
171
        }
172
173
        $response->setBeatMaps($maps);
174
175
        return $response;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response returns the type KriKrixs\object\response\ResponseMaps which is incompatible with the type-hinted return array.
Loading history...
176
    }
177
178
    /**
179
     * Get maps by Uploader ID! Not the uploader name!
180
     * @param int $uploaderID Uploader ID on BeatSaver
181
     * @param int $limit How many maps do you want to be returned
182
     * @return array
183
     */
184
    public function getMapsByUploaderID(int $uploaderID, int $limit): array
185
    {
186
        return $this->getMaps("/maps/uploader/" . $uploaderID . "/page", $limit);
187
    }
188
189
    /**
190
     * Get 20 latest maps
191
     * @param bool $autoMapper Do you want automapper or not ?
192
     * @return ResponseMaps
193
     */
194
    public function getMapsSortedByLatest(bool $autoMapper): ResponseMaps
195
    {
196
        return $this->getMaps("/maps/latest?automapper=" . $autoMapper, self::MAPS_NUMBERS_PER_PAGE);
0 ignored issues
show
Bug introduced by
The constant KriKrixs\BeatSaverAPI::MAPS_NUMBERS_PER_PAGE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
Bug Best Practice introduced by
The expression return $this->getMaps('/...:MAPS_NUMBERS_PER_PAGE) returns the type array which is incompatible with the type-hinted return KriKrixs\object\response\ResponseMaps.
Loading history...
197
    }
198
199
    /**
200
     * Get maps sorted by plays numbers
201
     * @param int $limit How many maps do you want to be returned
202
     * @return array
203
     */
204
    public function getMapsSortedByPlays(int $limit): array
205
    {
206
        return $this->getMaps("/maps/plays/page", $limit);
207
    }
208
209
    /**
210
     * Search a map (Set null to a parameter to not use it)
211
     * @param int $limit Limit of map you want
212
     * @param int $sortOrder (Default 1) 1 = Latest | 2 = Relevance | 3 = Rating
213
     * @param string|null $mapName (Optional) Map name
214
     * @param \DateTime|null $startDate (Optional) Map made from this date
215
     * @param \DateTime|null $endDate (Optional) Map made to this date
216
     * @param bool $ranked (Optional) Want ranked or not ?
217
     * @param bool $automapper (Optional) Want automapper or not ?
218
     * @param bool $chroma (Optional) Want chroma or not ?
219
     * @param bool $noodle (Optional) Want noodle or not ?
220
     * @param bool $cinema (Optional) Want cinema or not ?
221
     * @param bool $fullSpread (Optional) Want fullSpread or not ?
222
     * @param float|null $minBpm (Optional) Minimum BPM
223
     * @param float|null $maxBpm (Optional) Maximum BPM
224
     * @param float|null $minNps (Optional) Minimum NPS
225
     * @param float|null $maxNps (Optional) Maximum NPS
226
     * @param float|null $minRating (Optional) Minimum Rating
227
     * @param float|null $maxRating (Optional) Maximum Rating
228
     * @param int|null $minDuration (Optional) Minimum Duration
229
     * @param int|null $maxDuration (Optional) Maximum Duration
230
     * @return ResponseMaps
231
     */
232
    public function searchMap(int $limit, int $sortOrder = 1, string $mapName = null, \DateTime $startDate = null, \DateTime $endDate = null, bool $ranked = false, bool $automapper = false, bool $chroma = false, bool $noodle = false, bool $cinema = false, bool $fullSpread = false, float $minBpm = null, float $maxBpm = null, float $minNps = null, float $maxNps = null, float $minRating = null, float $maxRating = null, int $minDuration = null, int $maxDuration = null): ResponseMaps
233
    {
234
        $sort = [
235
            1 => "Latest",
236
            2 => "Relevance",
237
            3 => "Rating"
238
        ];
239
240
        $endpoint = "/search/text/page?sortOrder=" . $sort[$sortOrder];
241
242
        if($mapName)                $endpoint .= "&q=" . urlencode($mapName);
243
        if($startDate)              $endpoint .= "&from=" . $startDate->format("Y-m-d");
244
        if($endDate)                $endpoint .= "&to=" . $endDate->format("Y-m-d");
245
        if($ranked)                 $endpoint .= "&ranked=" . /** @scrutinizer ignore-type */ var_export($ranked, true);
246
        if($automapper)             $endpoint .= "&automapper=" . /** @scrutinizer ignore-type */ var_export($automapper, true);
247
        if($chroma)                 $endpoint .= "&chroma=" . /** @scrutinizer ignore-type */ var_export($chroma, true);
248
        if($noodle)                 $endpoint .= "&noodle=" . /** @scrutinizer ignore-type */ var_export($noodle, true);
249
        if($cinema)                 $endpoint .= "&cinema=" . /** @scrutinizer ignore-type */ var_export($cinema, true);
250
        if($fullSpread)             $endpoint .= "&fullSpread=" . /** @scrutinizer ignore-type */ var_export($fullSpread, true);
251
        if($minBpm)                 $endpoint .= "&minBpm=" . /** @scrutinizer ignore-type */ $minBpm;
252
        if($maxBpm)                 $endpoint .= "&maxBpm=" . /** @scrutinizer ignore-type */ $maxBpm;
253
        if($minNps)                 $endpoint .= "&minNps=" . /** @scrutinizer ignore-type */ $minNps;
254
        if($maxNps)                 $endpoint .= "&maxNps=" . /** @scrutinizer ignore-type */ $maxNps;
255
        if($minRating)              $endpoint .= "&minRating=" . /** @scrutinizer ignore-type */ $minRating;
256
        if($maxRating)              $endpoint .= "&maxRating=" . /** @scrutinizer ignore-type */ $maxRating;
257
        if($minDuration !== null)   $endpoint .= "&minDuration=" . /** @scrutinizer ignore-type */ $minDuration;
258
        if($maxDuration !== null)   $endpoint .= "&maxDuration=" . /** @scrutinizer ignore-type */ $maxDuration;
259
260
        return $this->getMaps($endpoint, $limit);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getMaps($endpoint, $limit) returns the type array which is incompatible with the type-hinted return KriKrixs\object\response\ResponseMaps.
Loading history...
261
    }
262
263
    ////////////////
264
    /// Get User ///
265
    ////////////////
266
267
    /**
268
     * Private building response functions
269
     * @param string $endpoint
270
     * @return ResponseUser
271
     */
272
    private function getUser(string $endpoint): ResponseUser
273
    {
274
        $response = new ResponseUser();
275
276
        $apiResult = $this->callAPI($endpoint);
277
278
        if($apiResult === false || $apiResult == "Not Found") {
279
            $response->setErrorStatus(true)->setErrorStatus("[getMap] Something went wrong with the API call.");
0 ignored issues
show
Bug introduced by
'[getMap] Something went...ong with the API call.' of type string is incompatible with the type boolean expected by parameter $errorStatus of KriKrixs\object\response...ponse::setErrorStatus(). ( Ignorable by Annotation )

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

279
            $response->setErrorStatus(true)->setErrorStatus(/** @scrutinizer ignore-type */ "[getMap] Something went wrong with the API call.");
Loading history...
280
            return $response;
281
        }
282
283
        $response->setUser(new User(json_decode($apiResult)));
284
285
        return $response;
286
    }
287
288
    /**
289
     * Get user's infos by UserID
290
     * @param int $id User ID
291
     * @return array
292
     */
293
    public function getUserByID(int $id): array
294
    {
295
        return $this->getUser("/users/id/" . $id);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getUser('/users/id/' . $id) returns the type KriKrixs\object\response\ResponseUser which is incompatible with the type-hinted return array.
Loading history...
296
    }
297
}