-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfsapi-simple.php
More file actions
137 lines (117 loc) · 4.48 KB
/
Copy pathfsapi-simple.php
File metadata and controls
137 lines (117 loc) · 4.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
<?php
declare(strict_types=1);
/**
* footballsoccerapi.com — the simple version.
*
* One file, two functions, no classes, no namespaces, no autoloader, nothing
* to install. Drop it next to your script, require it, and go.
*
* require 'fsapi-simple.php';
* $matches = fsapi_get('/v1/matches', ['country' => 'England', 'limit' => 5]);
*
* The full client in src/ does more — typed exceptions, cursor walking, batch
* chunking — and you should use it if you are building something that has to
* keep working. This is for the other case: a script, a cron job, a quick
* check of whether the data is what you need before you commit to anything.
*
* Set your key once at the top, or in the environment as FSAPI_KEY.
*/
/** Your key. Or leave it and set FSAPI_KEY in the environment. */
const FSAPI_KEY = '';
/**
* Ask the API for something. Returns the data, or throws with a plain reason.
*
* @param string $path e.g. '/v1/matches'
* @param array<string,mixed> $params e.g. ['country' => 'England']
* @return mixed The data. Arrays for lists, an array for one thing.
*/
function fsapi_get(string $path, array $params = [])
{
$result = fsapi_call($path, $params);
return $result['data'];
}
/**
* The same, but keeping the meta.
*
* The meta says how old the archive is and how many rows matched, which is
* worth having when you are about to quote a number at somebody.
*
* @return array{data:mixed,meta:array<string,mixed>}
*/
function fsapi_call(string $path, array $params = []): array
{
$key = FSAPI_KEY !== '' ? FSAPI_KEY : (string) getenv('FSAPI_KEY');
if ($key === '') {
throw new RuntimeException(
'No API key. Either set FSAPI_KEY at the top of ' . basename(__FILE__)
. ', or put it in the environment:' . PHP_EOL
. ' FSAPI_KEY=fsa_live_... php your-script.php' . PHP_EOL
. 'A free key takes a minute: https://footballsoccerapi.com/free-key'
);
}
$clean = [];
foreach ($params as $k => $v) {
if ($v === null || $v === '') {
continue;
}
$clean[$k] = is_bool($v) ? ($v ? 'true' : 'false') : $v;
}
$url = 'https://api.footballsoccerapi.com/' . ltrim($path, '/')
. ($clean ? '?' . http_build_query($clean) : '');
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['X-API-Key: ' . $key, 'Accept: application/json'],
]);
$body = curl_exec($ch);
$status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$err = curl_error($ch);
curl_close($ch);
if ($body === false) {
throw new RuntimeException('Could not reach the API: ' . $err);
}
$json = json_decode((string) $body, true);
/* Not JSON means something other than the API answered — a proxy, or a
challenge page. Worth saying, because it sends you to the right layer. */
if (!is_array($json)) {
throw new RuntimeException(
'The API returned ' . $status . ' without a JSON body. Something between you '
. 'and it may have intercepted the request.'
);
}
if ($status >= 400) {
$e = $json['error'] ?? [];
$message = (string) ($e['message'] ?? 'Request failed with ' . $status);
// The API says what plan a refusal needs, so repeat it rather than
// leaving a bare 403.
if (isset($e['needs_plan'])) {
$message .= PHP_EOL . 'Needs the ' . $e['needs_plan'] . ' plan; you have '
. ($e['your_plan'] ?? 'none') . '.';
}
throw new RuntimeException($message, $status);
}
return ['data' => $json['data'] ?? null, 'meta' => $json['meta'] ?? []];
}
/**
* Every row matching the filters, paging for you.
*
* Uses the cursor, so a whole season streams without holding it all in memory
* and without you counting pages.
*
* @param array<string,mixed> $params
* @return Generator<int,array<string,mixed>>
*/
function fsapi_walk(string $path, array $params = []): Generator
{
$cursor = null;
do {
$res = fsapi_call($path, $params + ['limit' => $params['limit'] ?? 500]
+ ($cursor !== null ? ['cursor' => $cursor] : []));
foreach ((array) $res['data'] as $row) {
yield $row;
}
$cursor = $res['meta']['next_cursor'] ?? null;
} while ($cursor !== null && $cursor !== '');
}