• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

api-platform / core / 15955912273

29 Jun 2025 01:51PM UTC coverage: 22.057% (-0.03%) from 22.082%
15955912273

Pull #7249

github

web-flow
Merge d9904d788 into a42034dc3
Pull Request #7249: chore: solve some phpstan issues

0 of 9 new or added lines in 8 files covered. (0.0%)

11540 existing lines in 372 files now uncovered.

11522 of 52237 relevant lines covered (22.06%)

11.08 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.89
/src/State/Pagination/Pagination.php
1
<?php
2

3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <dunglas@gmail.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11

12
declare(strict_types=1);
13

14
namespace ApiPlatform\State\Pagination;
15

16
use ApiPlatform\Metadata\Exception\InvalidArgumentException;
17
use ApiPlatform\Metadata\Operation;
18

19
/**
20
 * Pagination configuration.
21
 *
22
 * @author Baptiste Meyer <baptiste.meyer@gmail.com>
23
 */
24
final class Pagination
25
{
26
    private readonly array $options;
27
    private readonly array $graphQlOptions;
28

29
    public function __construct(array $options = [], array $graphQlOptions = [])
30
    {
UNCOV
31
        $this->options = array_merge([
138✔
UNCOV
32
            'enabled' => true,
138✔
UNCOV
33
            'client_enabled' => false,
138✔
UNCOV
34
            'client_items_per_page' => false,
138✔
UNCOV
35
            'items_per_page' => 30,
138✔
UNCOV
36
            'page_default' => 1,
138✔
UNCOV
37
            'page_parameter_name' => 'page',
138✔
UNCOV
38
            'enabled_parameter_name' => 'pagination',
138✔
UNCOV
39
            'items_per_page_parameter_name' => 'itemsPerPage',
138✔
UNCOV
40
            'maximum_items_per_page' => null,
138✔
UNCOV
41
            'partial' => false,
138✔
UNCOV
42
            'client_partial' => false,
138✔
UNCOV
43
            'partial_parameter_name' => 'partial',
138✔
UNCOV
44
        ], $options);
138✔
UNCOV
45
        $this->graphQlOptions = array_merge([
138✔
UNCOV
46
            'enabled' => true,
138✔
UNCOV
47
        ], $graphQlOptions);
138✔
48
    }
49

50
    /**
51
     * Gets the current page.
52
     *
53
     * @throws InvalidArgumentException
54
     */
55
    public function getPage(array $context = []): int
56
    {
UNCOV
57
        $page = (int) $this->getParameterFromContext(
106✔
UNCOV
58
            $context,
106✔
UNCOV
59
            $this->options['page_parameter_name'],
106✔
UNCOV
60
            $this->options['page_default']
106✔
UNCOV
61
        );
106✔
62

UNCOV
63
        if (1 > $page) {
106✔
64
            throw new InvalidArgumentException('Page should not be less than 1');
×
65
        }
66

UNCOV
67
        return $page;
106✔
68
    }
69

70
    /**
71
     * Gets the current offset.
72
     */
73
    public function getOffset(?Operation $operation = null, array $context = []): int
74
    {
UNCOV
75
        $graphql = (bool) ($context['graphql_operation_name'] ?? false);
106✔
76

UNCOV
77
        $limit = $this->getLimit($operation, $context);
106✔
78

UNCOV
79
        if ($graphql && null !== ($after = $this->getParameterFromContext($context, 'after'))) {
106✔
80
            return false === ($after = base64_decode($after, true)) ? 0 : (int) $after + 1;
×
81
        }
82

UNCOV
83
        if ($graphql && null !== ($before = $this->getParameterFromContext($context, 'before'))) {
106✔
84
            return ($offset = (false === ($before = base64_decode($before, true)) ? 0 : (int) $before - $limit)) < 0 ? 0 : $offset;
×
85
        }
86

UNCOV
87
        if ($graphql && null !== ($last = $this->getParameterFromContext($context, 'last'))) {
106✔
88
            return ($offset = ($context['count'] ?? 0) - $last) < 0 ? 0 : $offset;
×
89
        }
90

91
        /** @var int|float $offset */
UNCOV
92
        $offset = ($this->getPage($context) - 1) * $limit;
106✔
93

UNCOV
94
        if (!\is_int($offset)) {
106✔
95
            throw new InvalidArgumentException('Page parameter is too large.');
×
96
        }
97

UNCOV
98
        return $offset;
106✔
99
    }
100

101
    /**
102
     * Gets the current limit.
103
     *
104
     * @throws InvalidArgumentException
105
     */
106
    public function getLimit(?Operation $operation = null, array $context = []): int
107
    {
UNCOV
108
        $graphql = (bool) ($context['graphql_operation_name'] ?? false);
106✔
109

UNCOV
110
        $limit = $operation?->getPaginationItemsPerPage() ?? $this->options['items_per_page'];
106✔
UNCOV
111
        $clientLimit = $operation?->getPaginationClientItemsPerPage() ?? $this->options['client_items_per_page'];
106✔
112

UNCOV
113
        if ($graphql && null !== ($first = $this->getParameterFromContext($context, 'first'))) {
106✔
114
            $limit = $first;
×
115
        }
116

UNCOV
117
        if ($graphql && null !== ($last = $this->getParameterFromContext($context, 'last'))) {
106✔
118
            $limit = $last;
×
119
        }
120

UNCOV
121
        if ($graphql && null !== ($before = $this->getParameterFromContext($context, 'before'))
106✔
UNCOV
122
            && (false === ($before = base64_decode($before, true)) ? 0 : (int) $before - $limit) < 0) {
106✔
123
            $limit = (int) $before;
×
124
        }
125

UNCOV
126
        if ($clientLimit) {
106✔
UNCOV
127
            $limit = (int) $this->getParameterFromContext($context, $this->options['items_per_page_parameter_name'], $limit);
106✔
UNCOV
128
            $maxItemsPerPage = $operation?->getPaginationMaximumItemsPerPage() ?? $this->options['maximum_items_per_page'];
106✔
129

UNCOV
130
            if (null !== $maxItemsPerPage && $limit > $maxItemsPerPage) {
106✔
131
                $limit = $maxItemsPerPage;
×
132
            }
133
        }
134

UNCOV
135
        if (0 > $limit) {
106✔
136
            throw new InvalidArgumentException('Limit should not be less than 0');
×
137
        }
138

UNCOV
139
        return $limit;
106✔
140
    }
141

142
    /**
143
     * Gets info about the pagination.
144
     *
145
     * Returns an array with the following info as values:
146
     *   - the page {@see Pagination::getPage}
147
     *   - the offset {@see Pagination::getOffset}
148
     *   - the limit {@see Pagination::getLimit}
149
     *
150
     * @throws InvalidArgumentException
151
     */
152
    public function getPagination(?Operation $operation = null, array $context = []): array
153
    {
UNCOV
154
        $page = $this->getPage($context);
106✔
UNCOV
155
        $limit = $this->getLimit($operation, $context);
106✔
156

UNCOV
157
        if (0 === $limit && 1 < $page) {
106✔
158
            throw new InvalidArgumentException('Page should not be greater than 1 if limit is equal to 0');
×
159
        }
160

UNCOV
161
        return [$page, $this->getOffset($operation, $context), $limit];
106✔
162
    }
163

164
    /**
165
     * Is the pagination enabled?
166
     */
167
    public function isEnabled(?Operation $operation = null, array $context = []): bool
168
    {
UNCOV
169
        return $this->getEnabled($context, $operation);
104✔
170
    }
171

172
    /**
173
     * Is the pagination enabled for GraphQL?
174
     */
175
    public function isGraphQlEnabled(?Operation $operation = null, array $context = []): bool
176
    {
UNCOV
177
        return $this->getGraphQlEnabled($operation);
7✔
178
    }
179

180
    /**
181
     * Is the partial pagination enabled?
182
     */
183
    public function isPartialEnabled(?Operation $operation = null, array $context = []): bool
184
    {
UNCOV
185
        return $this->getEnabled($context, $operation, true);
106✔
186
    }
187

188
    public function getOptions(): array
189
    {
190
        return $this->options;
×
191
    }
192

193
    public function getGraphQlPaginationType(Operation $operation): string
194
    {
UNCOV
195
        return $operation->getPaginationType() ?? 'cursor';
6✔
196
    }
197

198
    /**
199
     * Is the classic or partial pagination enabled?
200
     */
201
    private function getEnabled(array $context, ?Operation $operation = null, bool $partial = false): bool
202
    {
UNCOV
203
        $enabled = $this->options[$partial ? 'partial' : 'enabled'];
106✔
UNCOV
204
        $clientEnabled = $this->options[$partial ? 'client_partial' : 'client_enabled'];
106✔
205

UNCOV
206
        $enabled = ($partial ? $operation?->getPaginationPartial() : $operation?->getPaginationEnabled()) ?? $enabled;
106✔
UNCOV
207
        $clientEnabled = ($partial ? $operation?->getPaginationClientPartial() : $operation?->getPaginationClientEnabled()) ?? $clientEnabled;
106✔
208

UNCOV
209
        if ($clientEnabled) {
106✔
UNCOV
210
            return filter_var($this->getParameterFromContext($context, $this->options[$partial ? 'partial_parameter_name' : 'enabled_parameter_name'], $enabled), \FILTER_VALIDATE_BOOLEAN);
106✔
211
        }
212

213
        return (bool) $enabled;
×
214
    }
215

216
    private function getGraphQlEnabled(?Operation $operation): bool
217
    {
UNCOV
218
        $enabled = $this->graphQlOptions['enabled'];
7✔
219

UNCOV
220
        return $operation?->getPaginationEnabled() ?? $enabled;
7✔
221
    }
222

223
    /**
224
     * Gets the given pagination parameter name from the given context.
225
     */
226
    private function getParameterFromContext(array $context, string $parameterName, mixed $default = null)
227
    {
UNCOV
228
        $filters = $context['filters'] ?? [];
106✔
229

UNCOV
230
        return \array_key_exists($parameterName, $filters) ? $filters[$parameterName] : $default;
106✔
231
    }
232
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc