Passed
Push — master ( 197018...b7719c )
by Kylian
01:27
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
class BeatSaverAPI
6
{
7
    const BEATSAVER_URL = "https://api.beatsaver.com/"; // Might Change when BeatSaver is up
8
    const MAPS_NUMBERS_PER_PAGE = 20;
9
10
    private string $userAgent;
11
12
    /**
13
     * BeatSaverAPI constructor
14
     * @param string $userAgent User Agent to provide to Beat Saver API
15
     */
16
    public function __construct(string $userAgent)
17
    {
18
        $this->userAgent = $userAgent;
19
    }
20
21
    /**
22
     * Private calling API function
23
     * @param string $endpoint
24
     * @return string|null
25
     */
26
    private function callAPI(string $endpoint): ?string
27
    {
28
        $curl = curl_init();
29
30
        curl_setopt_array($curl, [
31
            CURLOPT_URL => self::BEATSAVER_URL . $endpoint,
32
            CURLOPT_USERAGENT => $this->userAgent,
33
            CURLOPT_RETURNTRANSFER => true
34
        ]);
35
36
        $result = curl_exec($curl);
37
38
        curl_close($curl);
39
40
        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...
41
    }
42
43
    ////////////
44
    /// CALL ///
45
    ///////////
46
47
    /**
48
     * Private building response functions
49
     * @param string $endpoint
50
     * @return array
51
     */
52
    private function get(string $endpoint): array
53
    {
54
        $apiResult = $this->callAPI($endpoint);
55
56
        return [
57
            "error" => $apiResult === false,
58
            "map" => json_decode($apiResult, true)
59
        ];
60
    }
61
62
    ///////////////
63
    /// Get Map ///
64
    ///////////////
65
66
    /**
67
     * Get map by BSR Key
68
     * @param string $bsrKey BSR Key of the map
69
     * @return array
70
     */
71
    public function getMapByKey(string $bsrKey): array
72
    {
73
        return $this->get("/maps/id/" . $bsrKey);
74
    }
75
76
    /**
77
     * Get map by Hash
78
     * @param string $hash Hash of the map
79
     * @return array
80
     */
81
    public function getMapByHash(string $hash): array
82
    {
83
        return $this->get("/maps/hash/" . $hash);
84
    }
85
86
    ////////////////
87
    /// Get Maps ///
88
    ////////////////
89
90
    /**
91
     * Private building response functions
92
     * @param string $endpoint
93
     * @param int $limit
94
     * @return array
95
     */
96
    private function getMaps(string $endpoint, int $limit): array
97
    {
98
        $response = [
99
            "error" => false,
100
            "maps" => []
101
        ];
102
103
        for($page = 0; $page <= floor(($limit - 1) / self::MAPS_NUMBERS_PER_PAGE); $page++) {
104
            $apiResult = $this->callAPI(str_ireplace("page", $page, $endpoint));
0 ignored issues
show
Bug introduced by
It seems like str_ireplace('page', $page, $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

104
            $apiResult = $this->callAPI(/** @scrutinizer ignore-type */ str_ireplace("page", $page, $endpoint));
Loading history...
105
106
            if($apiResult === false || $apiResult == "Not Found") {
107
                $response["error"] = true;
108
109
                if($apiResult == "Not Found")
110
                    break;
111
            } else {
112
                $apiResult = json_decode($apiResult, true);
113
114
                if(count($apiResult["docs"]) === 0)
115
                    break;
116
117
                if(($page + 1) * self::MAPS_NUMBERS_PER_PAGE <= $limit) {
118
                    $response["maps"] = array_merge($response["maps"], $apiResult["docs"]);
119
                } else {
120
                    $max = $limit <= self::MAPS_NUMBERS_PER_PAGE ? $limit : $limit - ($page * self::MAPS_NUMBERS_PER_PAGE);
121
122
                    for($i = 0; $i < $max; $i++) {
123
                        array_push($response["maps"], $apiResult["docs"][$i]);
124
                    }
125
                }
126
            }
127
128
            if(count($apiResult["docs"]) < ($page + 1) * self::MAPS_NUMBERS_PER_PAGE)
129
                break;
130
        }
131
132
        return $response;
133
    }
134
135
    /**
136
     * Get maps by Uploader ID! Not the uploader name!
137
     * @param int $uploaderID Uploader ID on BeatSaver
138
     * @param int $limit How many maps do you want to be returned
139
     * @return array
140
     */
141
    public function getMapsByUploaderID(int $uploaderID, int $limit): array
142
    {
143
        return $this->getMaps("/maps/uploader/" . $uploaderID . "/page", $limit);
144
    }
145
146
    /**
147
     * Get 20 latest maps
148
     * @param bool $autoMapper Do you want automapper or not ?
149
     * @return array
150
     */
151
    public function getMapsSortedByLatest(bool $autoMapper): array
152
    {
153
        return $this->getMaps("/maps/latest?automapper=" . $autoMapper, self::MAPS_NUMBERS_PER_PAGE);
154
    }
155
156
    /**
157
     * Get maps sorted by plays numbers
158
     * @param int $limit How many maps do you want to be returned
159
     * @return array
160
     */
161
    public function getMapsSortedByPlays(int $limit): array
162
    {
163
        return $this->getMaps("/maps/plays/page", $limit);
164
    }
165
166
    /**
167
     * Search a map (Set null to a parameter to not use it)
168
     * @param int $limit Limit of map you want
169
     * @param int $sortOrder (Default 1) 1 = Latest | 2 = Relevance | 3 = Rating
170
     * @param string|null $mapName (Optional) Map name
171
     * @param \DateTime|null $startDate (Optional) Map made from this date
172
     * @param \DateTime|null $endDate (Optional) Map made to this date
173
     * @param bool $ranked (Optional) Want ranked or not ?
174
     * @param bool $automapper (Optional) Want automapper or not ?
175
     * @param bool $chroma (Optional) Want chroma or not ?
176
     * @param bool $noodle (Optional) Want noodle or not ?
177
     * @param bool $cinema (Optional) Want cinema or not ?
178
     * @param bool $fullSpread (Optional) Want fullSpread or not ?
179
     * @param float|null $minBpm (Optional) Minimum BPM
180
     * @param float|null $maxBpm (Optional) Maximum BPM
181
     * @param float|null $minNps (Optional) Minimum NPS
182
     * @param float|null $maxNps (Optional) Maximum NPS
183
     * @param float|null $minRating (Optional) Minimum Rating
184
     * @param float|null $maxRating (Optional) Maximum Rating
185
     * @param int|null $minDuration (Optional) Minimum Duration
186
     * @param int|null $maxDuration (Optional) Maximum Duration
187
     * @return array
188
     */
189
    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): array
190
    {
191
        $sort = [
192
            1 => "Latest",
193
            2 => "Relevance",
194
            3 => "Rating"
195
        ];
196
197
        $endpoint = "/search/text/page?sortOrder=" . $sort[$sortOrder];
198
199
        if($mapName)        $endpoint .= "&q=" . urlencode($mapName);
200
        if($startDate)      $endpoint .= "&from=" . $startDate->format("Y-m-d");
201
        if($endDate)        $endpoint .= "&to=" . $endDate->format("Y-m-d");
202
        if($ranked)         $endpoint .= "&ranked=" . /** @scrutinizer ignore-type */ $ranked;
203
        if($automapper)     $endpoint .= "&automapper=" . /** @scrutinizer ignore-type */ $automapper;
204
        if($chroma)         $endpoint .= "&chroma=" . /** @scrutinizer ignore-type */ $chroma;
205
        if($noodle)         $endpoint .= "&noodle=" . /** @scrutinizer ignore-type */ $noodle;
206
        if($cinema)         $endpoint .= "&cinema=" . /** @scrutinizer ignore-type */ $cinema;
207
        if($fullSpread)     $endpoint .= "&fullSpread=" . /** @scrutinizer ignore-type */ $fullSpread;
208
        if($minBpm)         $endpoint .= "&minBpm=" . /** @scrutinizer ignore-type */ $minBpm;
209
        if($maxBpm)         $endpoint .= "&maxBpm=" . /** @scrutinizer ignore-type */ $maxBpm;
210
        if($minNps)         $endpoint .= "&minNps=" . /** @scrutinizer ignore-type */ $minNps;
211
        if($maxNps)         $endpoint .= "&maxNps=" . /** @scrutinizer ignore-type */ $maxNps;
212
        if($minRating)      $endpoint .= "&minRating=" . /** @scrutinizer ignore-type */ $minRating;
213
        if($maxRating)      $endpoint .= "&maxRating=" . /** @scrutinizer ignore-type */ $maxRating;
214
        if($minDuration !== null)    $endpoint .= "&minDuration=" . /** @scrutinizer ignore-type */ $minDuration;
215
        if($maxDuration !== null)    $endpoint .= "&maxDuration=" . /** @scrutinizer ignore-type */ $maxDuration;
216
217
        return $this->getMaps($endpoint, $limit);
218
    }
219
220
    ////////////////
221
    /// Get User ///
222
    ////////////////
223
224
    /**
225
     * Get user's infos by UserID
226
     * @param int $id User ID
227
     * @return array
228
     */
229
    public function getUserByID(int $id): array
230
    {
231
        return $this->get("/users/id/" . $id);
232
    }
233
}