Passed
Push — master ( eba474...e75eb9 )
by Kylian
01:49 queued 12s
created

BeatSaverAPI::getMapsByName()   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 2
dl 0
loc 3
rs 10
1
<?php
2
3
namespace KriKrixs;
4
5
class BeatSaverAPI
6
{
7
    const BEATSAVER_URL = "https://api.beatmaps.io/"; // 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 ID
68
     * @param int $id
69
     * @return array
70
     */
71
    public function getMapByID(int $id): array
72
    {
73
        return $this->get("/maps/id/" . $id);
74
    }
75
76
    /**
77
     * Get map by BSR Key
78
     * @param string $bsrKey BSR Key of the map
79
     * @return array
80
     */
81
    public function getMapByKey(string $bsrKey): array
82
    {
83
        return $this->get("/maps/beatsaver/" . $bsrKey);
84
    }
85
86
    /**
87
     * Get map by Hash
88
     * @param string $hash Hash of the map
89
     * @return array
90
     */
91
    public function getMapByHash(string $hash): array
92
    {
93
        return $this->get("/maps/hash/" . $hash);
94
    }
95
96
    ////////////////
97
    /// Get Maps ///
98
    ////////////////
99
100
    /**
101
     * Private building response functions
102
     * @param string $endpoint
103
     * @param int $limit
104
     * @return array
105
     */
106
    private function getMaps(string $endpoint, int $limit): array
107
    {
108
        $response = [
109
            "error" => false,
110
            "maps" => []
111
        ];
112
113
        for($page = 0; $page <= floor(($limit - 1) / self::MAPS_NUMBERS_PER_PAGE); $page++) {
114
            $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

114
            $apiResult = $this->callAPI(/** @scrutinizer ignore-type */ str_ireplace("page", $page, $endpoint));
Loading history...
115
116
            if($apiResult === false || $apiResult == "Not Found") {
117
                $response["error"] = true;
118
119
                if($apiResult == "Not Found")
120
                    break;
121
            } else {
122
                $apiResult = json_decode($apiResult, true);
123
124
                if(count($apiResult["docs"]) === 0)
125
                    break;
126
127
                if(($page + 1) * self::MAPS_NUMBERS_PER_PAGE <= $limit) {
128
                    $response["maps"] = array_merge($response["maps"], $apiResult["docs"]);
129
                } else {
130
                    $max = $limit <= self::MAPS_NUMBERS_PER_PAGE ? $limit : $limit - ($page * self::MAPS_NUMBERS_PER_PAGE);
131
132
                    for($i = 0; $i < $max; $i++) {
133
                        array_push($response["maps"], $apiResult["docs"][$i]);
134
                    }
135
                }
136
            }
137
138
            if(count($apiResult["docs"]) < ($page + 1) * self::MAPS_NUMBERS_PER_PAGE)
139
                break;
140
        }
141
142
        return $response;
143
    }
144
145
    /**
146
     * Get maps by Uploader ID! Not the uploader name!
147
     * @param int $uploaderID Uploader ID on BeatSaver
148
     * @param int $limit How many maps do you want to be returned
149
     * @return string|bool
150
     */
151
    public function getMapsByUploaderID(int $uploaderID, int $limit): array
152
    {
153
        return $this->getMaps("/maps/uploader/" . $uploaderID . "/page", $limit);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getMaps('/...erID . '/page', $limit) returns the type array<string,array|false> which is incompatible with the documented return type boolean|string.
Loading history...
154
    }
155
156
    /**
157
     * Get 20 latest maps
158
     * @param bool $autoMapper Do you want automapper or not ?
159
     * @return array
160
     */
161
    public function getMapsSortedByLatest(bool $autoMapper): array
0 ignored issues
show
Unused Code introduced by
The parameter $autoMapper 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

161
    public function getMapsSortedByLatest(/** @scrutinizer ignore-unused */ bool $autoMapper): 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...
162
    {
163
        return $this->getMaps("/maps/latest?automapper=false", self::MAPS_NUMBERS_PER_PAGE);
164
    }
165
166
    /**
167
     * Get maps sorted by plays numbers
168
     * @param int $limit How many maps do you want to be returned
169
     * @return array
170
     */
171
    public function getMapsSortedByPlays(int $limit): array
172
    {
173
        return $this->getMaps("/maps/plays/page", $limit);
174
    }
175
176
    /**
177
     * Search a map (Set null to a parameter to not use it)
178
     * @param int $limit Limit of map you want
179
     * @param int $sortOrder (Default 1) 1 = Latest | 2 = Relevance | 3 = Rating
180
     * @param string|null $mapName (Optional) Map name
181
     * @param \DateTime|null $startDate (Optional) Map made from this date
182
     * @param \DateTime|null $endDate (Optional) Map made to this date
183
     * @param bool $ranked (Optional) Want ranked or not ?
184
     * @param bool $automapper (Optional) Want automapper or not ?
185
     * @param bool $chroma (Optional) Want chroma or not ?
186
     * @param bool $noodle (Optional) Want noodle or not ?
187
     * @param bool $cinema (Optional) Want cinema or not ?
188
     * @param bool $fullSpread (Optional) Want fullSpread or not ?
189
     * @param float|null $minBpm (Optional) Minimum BPM
190
     * @param float|null $maxBpm (Optional) Maximum BPM
191
     * @param float|null $minNps (Optional) Minimum NPS
192
     * @param float|null $maxNps (Optional) Maximum NPS
193
     * @param float|null $minRating (Optional) Minimum Rating
194
     * @param float|null $maxRating (Optional) Maximum Rating
195
     * @param int|null $minDuration (Optional) Minimum Duration
196
     * @param int|null $maxDuration (Optional) Maximum Duration
197
     * @return array
198
     */
199
    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
200
    {
201
        $sort = [
202
            1 => "Latest",
203
            2 => "Relevance",
204
            3 => "Rating"
205
        ];
206
207
        $endpoint = "/search/text/page?sortOrder=" . $sort[$sortOrder];
208
209
        if($mapName)        $endpoint .= "&q=" . urlencode($mapName);
210
        if($startDate)      $endpoint .= "&from=" . $startDate->format("Y-m-d");
211
        if($endDate)        $endpoint .= "&to=" . $endDate->format("Y-m-d");
212
        if($ranked)         $endpoint .= "&ranked=" . $ranked;
0 ignored issues
show
Bug introduced by
Are you sure $ranked of type true can be used in concatenation? ( Ignorable by Annotation )

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

212
        if($ranked)         $endpoint .= "&ranked=" . /** @scrutinizer ignore-type */ $ranked;
Loading history...
213
        if($automapper)     $endpoint .= "&automapper=" . $automapper;
0 ignored issues
show
Bug introduced by
Are you sure $automapper of type true can be used in concatenation? ( Ignorable by Annotation )

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

213
        if($automapper)     $endpoint .= "&automapper=" . /** @scrutinizer ignore-type */ $automapper;
Loading history...
214
        if($chroma)         $endpoint .= "&chroma=" . $chroma;
0 ignored issues
show
Bug introduced by
Are you sure $chroma of type true can be used in concatenation? ( Ignorable by Annotation )

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

214
        if($chroma)         $endpoint .= "&chroma=" . /** @scrutinizer ignore-type */ $chroma;
Loading history...
215
        if($noodle)         $endpoint .= "&noodle=" . $noodle;
0 ignored issues
show
Bug introduced by
Are you sure $noodle of type true can be used in concatenation? ( Ignorable by Annotation )

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

215
        if($noodle)         $endpoint .= "&noodle=" . /** @scrutinizer ignore-type */ $noodle;
Loading history...
216
        if($cinema)         $endpoint .= "&cinema=" . $cinema;
0 ignored issues
show
Bug introduced by
Are you sure $cinema of type true can be used in concatenation? ( Ignorable by Annotation )

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

216
        if($cinema)         $endpoint .= "&cinema=" . /** @scrutinizer ignore-type */ $cinema;
Loading history...
217
        if($fullSpread)     $endpoint .= "&fullSpread=" . $fullSpread;
0 ignored issues
show
Bug introduced by
Are you sure $fullSpread of type true can be used in concatenation? ( Ignorable by Annotation )

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

217
        if($fullSpread)     $endpoint .= "&fullSpread=" . /** @scrutinizer ignore-type */ $fullSpread;
Loading history...
218
        if($minBpm)         $endpoint .= "&minBpm=" . $minBpm;
219
        if($maxBpm)         $endpoint .= "&maxBpm=" . $maxBpm;
220
        if($minNps)         $endpoint .= "&minNps=" . $minNps;
221
        if($maxNps)         $endpoint .= "&maxNps=" . $maxNps;
222
        if($minRating)      $endpoint .= "&minRating=" . $minRating;
223
        if($maxRating)      $endpoint .= "&maxRating=" . $maxRating;
224
        if($minDuration)    $endpoint .= "&minDuration=" . $minDuration;
0 ignored issues
show
Bug Best Practice introduced by
The expression $minDuration of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
225
        if($maxDuration)    $endpoint .= "&maxDuration=" . $maxDuration;
0 ignored issues
show
Bug Best Practice introduced by
The expression $maxDuration of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
226
227
        return $this->getMaps($endpoint, $limit);
228
    }
229
230
    ////////////////
231
    /// Get User ///
232
    ////////////////
233
234
    /**
235
     * Get user's infos by UserID
236
     * @param int $id User ID
237
     * @return array
238
     */
239
    public function getUserByID(int $id): array
240
    {
241
        return $this->get("/users/id/" . $id);
242
    }
243
}