Passed
Push — master ( 197018...b7719c )
by Kylian
01:27
created

BeatSaverAPI::getMapByID()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
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
}