This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | namespace Doctrine\ODM\MongoDB\Aggregation; |
||
6 | |||
7 | use Doctrine\ODM\MongoDB\DocumentManager; |
||
8 | use Doctrine\ODM\MongoDB\Iterator\CachingIterator; |
||
9 | use Doctrine\ODM\MongoDB\Iterator\HydratingIterator; |
||
10 | use Doctrine\ODM\MongoDB\Iterator\Iterator; |
||
11 | use Doctrine\ODM\MongoDB\Iterator\UnrewindableIterator; |
||
12 | use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; |
||
13 | use Doctrine\ODM\MongoDB\Persisters\DocumentPersister; |
||
14 | use Doctrine\ODM\MongoDB\Query\Expr as QueryExpr; |
||
15 | use GeoJson\Geometry\Point; |
||
16 | use MongoDB\Collection; |
||
17 | use MongoDB\Driver\Cursor; |
||
18 | use OutOfRangeException; |
||
19 | use function array_map; |
||
20 | use function array_merge; |
||
21 | use function array_unshift; |
||
22 | use function assert; |
||
23 | use function is_array; |
||
24 | use function sprintf; |
||
25 | |||
26 | /** |
||
27 | * Fluent interface for building aggregation pipelines. |
||
28 | */ |
||
29 | class Builder |
||
30 | { |
||
31 | /** |
||
32 | * The DocumentManager instance for this query |
||
33 | * |
||
34 | * @var DocumentManager |
||
35 | */ |
||
36 | private $dm; |
||
37 | |||
38 | /** |
||
39 | * The ClassMetadata instance. |
||
40 | * |
||
41 | * @var ClassMetadata |
||
42 | */ |
||
43 | private $class; |
||
44 | |||
45 | /** @var string */ |
||
46 | private $hydrationClass; |
||
47 | |||
48 | /** |
||
49 | * The Collection instance. |
||
50 | * |
||
51 | * @var Collection |
||
52 | */ |
||
53 | private $collection; |
||
54 | |||
55 | /** @var Stage[] */ |
||
56 | private $stages = []; |
||
57 | |||
58 | /** @var bool */ |
||
59 | private $rewindable = true; |
||
60 | |||
61 | /** |
||
62 | * Create a new aggregation builder. |
||
63 | */ |
||
64 | 269 | public function __construct(DocumentManager $dm, string $documentName) |
|
65 | { |
||
66 | 269 | $this->dm = $dm; |
|
67 | 269 | $this->class = $this->dm->getClassMetadata($documentName); |
|
68 | 269 | $this->collection = $this->dm->getDocumentCollection($documentName); |
|
69 | 269 | } |
|
70 | |||
71 | /** |
||
72 | * Adds new fields to documents. $addFields outputs documents that contain all |
||
73 | * existing fields from the input documents and newly added fields. |
||
74 | * |
||
75 | * The $addFields stage is equivalent to a $project stage that explicitly specifies |
||
76 | * all existing fields in the input documents and adds the new fields. |
||
77 | * |
||
78 | * If the name of the new field is the same as an existing field name (including _id), |
||
79 | * $addFields overwrites the existing value of that field with the value of the |
||
80 | * specified expression. |
||
81 | * |
||
82 | * @see http://docs.mongodb.com/manual/reference/operator/aggregation/addFields/ |
||
83 | */ |
||
84 | 1 | public function addFields() : Stage\AddFields |
|
85 | { |
||
86 | 1 | $stage = new Stage\AddFields($this); |
|
87 | 1 | $this->addStage($stage); |
|
88 | |||
89 | 1 | return $stage; |
|
90 | } |
||
91 | |||
92 | /** |
||
93 | * Categorizes incoming documents into groups, called buckets, based on a |
||
94 | * specified expression and bucket boundaries. |
||
95 | * |
||
96 | * Each bucket is represented as a document in the output. The document for |
||
97 | * each bucket contains an _id field, whose value specifies the inclusive |
||
98 | * lower bound of the bucket and a count field that contains the number of |
||
99 | * documents in the bucket. The count field is included by default when the |
||
100 | * output is not specified. |
||
101 | * |
||
102 | * @see https://docs.mongodb.com/manual/reference/operator/aggregation/bucket/ |
||
103 | */ |
||
104 | 2 | public function bucket() : Stage\Bucket |
|
105 | { |
||
106 | 2 | $stage = new Stage\Bucket($this, $this->dm, $this->class); |
|
107 | 2 | $this->addStage($stage); |
|
108 | |||
109 | 2 | return $stage; |
|
110 | } |
||
111 | |||
112 | /** |
||
113 | * Categorizes incoming documents into a specific number of groups, called |
||
114 | * buckets, based on a specified expression. |
||
115 | * |
||
116 | * Bucket boundaries are automatically determined in an attempt to evenly |
||
117 | * distribute the documents into the specified number of buckets. Each |
||
118 | * bucket is represented as a document in the output. The document for each |
||
119 | * bucket contains an _id field, whose value specifies the inclusive lower |
||
120 | * bound and the exclusive upper bound for the bucket, and a count field |
||
121 | * that contains the number of documents in the bucket. The count field is |
||
122 | * included by default when the output is not specified. |
||
123 | * |
||
124 | * @see https://docs.mongodb.com/manual/reference/operator/aggregation/bucketAuto/ |
||
125 | */ |
||
126 | 2 | public function bucketAuto() : Stage\BucketAuto |
|
127 | { |
||
128 | 2 | $stage = new Stage\BucketAuto($this, $this->dm, $this->class); |
|
129 | 2 | $this->addStage($stage); |
|
130 | |||
131 | 2 | return $stage; |
|
132 | } |
||
133 | |||
134 | /** |
||
135 | * Returns statistics regarding a collection or view. |
||
136 | * |
||
137 | * $collStats must be the first stage in an aggregation pipeline, or else |
||
138 | * the pipeline returns an error. |
||
139 | * |
||
140 | * @see http://docs.mongodb.org/manual/reference/operator/aggregation/collStats/ |
||
141 | */ |
||
142 | 1 | public function collStats() : Stage\CollStats |
|
143 | { |
||
144 | 1 | $stage = new Stage\CollStats($this); |
|
145 | 1 | $this->addStage($stage); |
|
146 | |||
147 | 1 | return $stage; |
|
148 | } |
||
149 | |||
150 | /** |
||
151 | * Returns a document that contains a count of the number of documents input |
||
152 | * to the stage. |
||
153 | * |
||
154 | * @see https://docs.mongodb.com/manual/reference/operator/aggregation/count/ |
||
155 | */ |
||
156 | 1 | public function count(string $fieldName) : Stage\Count |
|
157 | { |
||
158 | 1 | $stage = new Stage\Count($this, $fieldName); |
|
159 | 1 | $this->addStage($stage); |
|
160 | |||
161 | 1 | return $stage; |
|
162 | } |
||
163 | |||
164 | /** |
||
165 | * Executes the aggregation pipeline |
||
166 | */ |
||
167 | 20 | public function execute(array $options = []) : Iterator |
|
168 | { |
||
169 | // Force cursor to be used |
||
170 | 20 | $options = array_merge($options, ['cursor' => true]); |
|
171 | |||
172 | 20 | $cursor = $this->collection->aggregate($this->getPipeline(), $options); |
|
173 | 20 | assert($cursor instanceof Cursor); |
|
0 ignored issues
–
show
|
|||
174 | |||
175 | 20 | return $this->prepareIterator($cursor); |
|
176 | } |
||
177 | |||
178 | 158 | public function expr() : Expr |
|
179 | { |
||
180 | 158 | return new Expr($this->dm, $this->class); |
|
181 | } |
||
182 | |||
183 | /** |
||
184 | * Processes multiple aggregation pipelines within a single stage on the |
||
185 | * same set of input documents. |
||
186 | * |
||
187 | * Each sub-pipeline has its own field in the output document where its |
||
188 | * results are stored as an array of documents. |
||
189 | */ |
||
190 | 1 | public function facet() : Stage\Facet |
|
191 | { |
||
192 | 1 | $stage = new Stage\Facet($this); |
|
193 | 1 | $this->addStage($stage); |
|
194 | |||
195 | 1 | return $stage; |
|
196 | } |
||
197 | |||
198 | /** |
||
199 | * Outputs documents in order of nearest to farthest from a specified point. |
||
200 | * |
||
201 | * A GeoJSON point may be provided as the first and only argument for |
||
202 | * 2dsphere queries. This single parameter may be a GeoJSON point object or |
||
203 | * an array corresponding to the point's JSON representation. If GeoJSON is |
||
204 | * used, the "spherical" option will default to true. |
||
205 | * |
||
206 | * You can only use this as the first stage of a pipeline. |
||
207 | * |
||
208 | * @see http://docs.mongodb.org/manual/reference/operator/aggregation/geoNear/ |
||
209 | * |
||
210 | * @param float|array|Point $x |
||
211 | * @param float $y |
||
212 | */ |
||
213 | 4 | public function geoNear($x, $y = null) : Stage\GeoNear |
|
214 | { |
||
215 | 4 | $stage = new Stage\GeoNear($this, $x, $y); |
|
216 | 4 | $this->addStage($stage); |
|
217 | |||
218 | 4 | return $stage; |
|
219 | } |
||
220 | |||
221 | /** |
||
222 | * Returns the assembled aggregation pipeline |
||
223 | * |
||
224 | * For pipelines where the first stage is a $geoNear stage, it will apply |
||
225 | * the document filters and discriminator queries to the query portion of |
||
226 | * the geoNear operation. For all other pipelines, it prepends a $match stage |
||
227 | * containing the required query. |
||
228 | */ |
||
229 | 70 | public function getPipeline() : array |
|
230 | { |
||
231 | 70 | $pipeline = array_map( |
|
232 | static function (Stage $stage) { |
||
233 | 70 | return $stage->getExpression(); |
|
234 | 70 | }, |
|
235 | 70 | $this->stages |
|
236 | ); |
||
237 | |||
238 | 70 | if ($this->getStage(0) instanceof Stage\GeoNear) { |
|
239 | 4 | $pipeline[0]['$geoNear']['query'] = $this->applyFilters($pipeline[0]['$geoNear']['query']); |
|
240 | 66 | } elseif ($this->getStage(0) instanceof Stage\IndexStats) { |
|
241 | // Don't apply any filters when using an IndexStats stage: since it |
||
242 | // needs to be the first pipeline stage, prepending a match stage |
||
243 | // with discriminator information will not work |
||
244 | |||
245 | 2 | return $pipeline; |
|
246 | } else { |
||
247 | 64 | $matchExpression = $this->applyFilters([]); |
|
248 | 64 | if ($matchExpression !== []) { |
|
249 | 1 | array_unshift($pipeline, ['$match' => $matchExpression]); |
|
250 | } |
||
251 | } |
||
252 | |||
253 | 68 | return $pipeline; |
|
254 | } |
||
255 | |||
256 | /** |
||
257 | * Returns a certain stage from the pipeline |
||
258 | */ |
||
259 | 70 | public function getStage(int $index) : Stage |
|
260 | { |
||
261 | 70 | if (! isset($this->stages[$index])) { |
|
262 | throw new OutOfRangeException(sprintf('Could not find stage with index %d.', $index)); |
||
263 | } |
||
264 | |||
265 | 70 | return $this->stages[$index]; |
|
266 | } |
||
267 | |||
268 | /** |
||
269 | * Performs a recursive search on a collection, with options for restricting |
||
270 | * the search by recursion depth and query filter. |
||
271 | * |
||
272 | * @see https://docs.mongodb.org/manual/reference/operator/aggregation/graphLookup/ |
||
273 | * |
||
274 | * @param string $from Target collection for the $graphLookup operation to |
||
275 | * search, recursively matching the connectFromField to the connectToField. |
||
276 | */ |
||
277 | 10 | View Code Duplication | public function graphLookup(string $from) : Stage\GraphLookup |
278 | { |
||
279 | 10 | $stage = new Stage\GraphLookup($this, $from, $this->dm, $this->class); |
|
280 | 9 | $this->addStage($stage); |
|
281 | |||
282 | 9 | return $stage; |
|
283 | } |
||
284 | |||
285 | /** |
||
286 | * Groups documents by some specified expression and outputs to the next |
||
287 | * stage a document for each distinct grouping. |
||
288 | * |
||
289 | * @see http://docs.mongodb.org/manual/reference/operator/aggregation/group/ |
||
290 | */ |
||
291 | 4 | public function group() : Stage\Group |
|
292 | { |
||
293 | 4 | $stage = new Stage\Group($this); |
|
294 | 4 | $this->addStage($stage); |
|
295 | |||
296 | 4 | return $stage; |
|
297 | } |
||
298 | |||
299 | /** |
||
300 | * Set which class to use when hydrating results as document class instances. |
||
301 | */ |
||
302 | 4 | public function hydrate(string $className) : self |
|
303 | { |
||
304 | 4 | $this->hydrationClass = $className; |
|
305 | |||
306 | 4 | return $this; |
|
307 | } |
||
308 | |||
309 | /** |
||
310 | * Returns statistics regarding the use of each index for the collection. |
||
311 | * |
||
312 | * @see https://docs.mongodb.org/manual/reference/operator/aggregation/indexStats/ |
||
313 | */ |
||
314 | 2 | public function indexStats() : Stage\IndexStats |
|
315 | { |
||
316 | 2 | $stage = new Stage\IndexStats($this); |
|
317 | 2 | $this->addStage($stage); |
|
318 | |||
319 | 2 | return $stage; |
|
320 | } |
||
321 | |||
322 | /** |
||
323 | * Limits the number of documents passed to the next stage in the pipeline. |
||
324 | * |
||
325 | * @see http://docs.mongodb.org/manual/reference/operator/aggregation/limit/ |
||
326 | */ |
||
327 | 2 | public function limit(int $limit) : Stage\Limit |
|
328 | { |
||
329 | 2 | $stage = new Stage\Limit($this, $limit); |
|
330 | 2 | $this->addStage($stage); |
|
331 | |||
332 | 2 | return $stage; |
|
333 | } |
||
334 | |||
335 | /** |
||
336 | * Performs a left outer join to an unsharded collection in the same |
||
337 | * database to filter in documents from the “joined” collection for |
||
338 | * processing. |
||
339 | * |
||
340 | * @see https://docs.mongodb.org/manual/reference/operator/aggregation/lookup/ |
||
341 | */ |
||
342 | 16 | View Code Duplication | public function lookup(string $from) : Stage\Lookup |
343 | { |
||
344 | 16 | $stage = new Stage\Lookup($this, $from, $this->dm, $this->class); |
|
345 | 14 | $this->addStage($stage); |
|
346 | |||
347 | 14 | return $stage; |
|
348 | } |
||
349 | |||
350 | /** |
||
351 | * Filters the documents to pass only the documents that match the specified |
||
352 | * condition(s) to the next pipeline stage. |
||
353 | * |
||
354 | * @see http://docs.mongodb.org/manual/reference/operator/aggregation/match/ |
||
355 | */ |
||
356 | 9 | public function match() : Stage\Match |
|
357 | { |
||
358 | 9 | $stage = new Stage\Match($this); |
|
359 | 9 | $this->addStage($stage); |
|
360 | |||
361 | 9 | return $stage; |
|
362 | } |
||
363 | |||
364 | /** |
||
365 | * Returns a query expression to be used in match stages |
||
366 | */ |
||
367 | 61 | public function matchExpr() : QueryExpr |
|
368 | { |
||
369 | 61 | $expr = new QueryExpr($this->dm); |
|
370 | 61 | $expr->setClassMetadata($this->class); |
|
371 | |||
372 | 61 | return $expr; |
|
373 | } |
||
374 | |||
375 | /** |
||
376 | * Takes the documents returned by the aggregation pipeline and writes them |
||
377 | * to a specified collection. This must be the last stage in the pipeline. |
||
378 | * |
||
379 | * @see http://docs.mongodb.org/manual/reference/operator/aggregation/out/ |
||
380 | */ |
||
381 | 6 | public function out(string $from) : Stage\Out |
|
382 | { |
||
383 | 6 | $stage = new Stage\Out($this, $from, $this->dm); |
|
384 | 5 | $this->addStage($stage); |
|
385 | |||
386 | 5 | return $stage; |
|
387 | } |
||
388 | |||
389 | /** |
||
390 | * Passes along the documents with only the specified fields to the next |
||
391 | * stage in the pipeline. The specified fields can be existing fields from |
||
392 | * the input documents or newly computed fields. |
||
393 | * |
||
394 | * @see http://docs.mongodb.org/manual/reference/operator/aggregation/project/ |
||
395 | */ |
||
396 | 15 | public function project() : Stage\Project |
|
397 | { |
||
398 | 15 | $stage = new Stage\Project($this); |
|
399 | 15 | $this->addStage($stage); |
|
400 | |||
401 | 15 | return $stage; |
|
402 | } |
||
403 | |||
404 | /** |
||
405 | * Restricts the contents of the documents based on information stored in |
||
406 | * the documents themselves. |
||
407 | * |
||
408 | * @see http://docs.mongodb.org/manual/reference/operator/aggregation/redact/ |
||
409 | */ |
||
410 | 2 | public function redact() : Stage\Redact |
|
411 | { |
||
412 | 2 | $stage = new Stage\Redact($this); |
|
413 | 2 | $this->addStage($stage); |
|
414 | |||
415 | 2 | return $stage; |
|
416 | } |
||
417 | |||
418 | /** |
||
419 | * Promotes a specified document to the top level and replaces all other |
||
420 | * fields. |
||
421 | * |
||
422 | * The operation replaces all existing fields in the input document, |
||
423 | * including the _id field. You can promote an existing embedded document to |
||
424 | * the top level, or create a new document for promotion. |
||
425 | * |
||
426 | * @param string|array|null $expression Optional. A replacement expression that |
||
427 | * resolves to a document. |
||
428 | */ |
||
429 | 6 | View Code Duplication | public function replaceRoot($expression = null) : Stage\ReplaceRoot |
430 | { |
||
431 | 6 | $stage = new Stage\ReplaceRoot($this, $this->dm, $this->class, $expression); |
|
432 | 6 | $this->addStage($stage); |
|
433 | |||
434 | 6 | return $stage; |
|
435 | } |
||
436 | |||
437 | /** |
||
438 | * Controls if resulting iterator should be wrapped with CachingIterator. |
||
439 | */ |
||
440 | 1 | public function rewindable(bool $rewindable = true) : self |
|
441 | { |
||
442 | 1 | $this->rewindable = $rewindable; |
|
443 | |||
444 | 1 | return $this; |
|
445 | } |
||
446 | |||
447 | /** |
||
448 | * Randomly selects the specified number of documents from its input. |
||
449 | * |
||
450 | * @see https://docs.mongodb.org/manual/reference/operator/aggregation/sample/ |
||
451 | */ |
||
452 | 2 | public function sample(int $size) : Stage\Sample |
|
453 | { |
||
454 | 2 | $stage = new Stage\Sample($this, $size); |
|
455 | 2 | $this->addStage($stage); |
|
456 | |||
457 | 2 | return $stage; |
|
458 | } |
||
459 | |||
460 | /** |
||
461 | * Skips over the specified number of documents that pass into the stage and |
||
462 | * passes the remaining documents to the next stage in the pipeline. |
||
463 | * |
||
464 | * @see http://docs.mongodb.org/manual/reference/operator/aggregation/skip/ |
||
465 | */ |
||
466 | 2 | public function skip(int $skip) : Stage\Skip |
|
467 | { |
||
468 | 2 | $stage = new Stage\Skip($this, $skip); |
|
469 | 2 | $this->addStage($stage); |
|
470 | |||
471 | 2 | return $stage; |
|
472 | } |
||
473 | |||
474 | /** |
||
475 | * Sorts all input documents and returns them to the pipeline in sorted |
||
476 | * order. |
||
477 | * |
||
478 | * If sorting by multiple fields, the first argument should be an array of |
||
479 | * field name (key) and order (value) pairs. |
||
480 | * |
||
481 | * @see http://docs.mongodb.org/manual/reference/operator/aggregation/sort/ |
||
482 | * |
||
483 | * @param array|string $fieldName Field name or array of field/order pairs |
||
484 | * @param int|string $order Field order (if one field is specified) |
||
485 | */ |
||
486 | 7 | public function sort($fieldName, $order = null) : Stage\Sort |
|
487 | { |
||
488 | 7 | $fields = is_array($fieldName) ? $fieldName : [$fieldName => $order]; |
|
489 | // fixme: move to sort stage |
||
490 | 7 | $stage = new Stage\Sort($this, $this->getDocumentPersister()->prepareSort($fields)); |
|
491 | 7 | $this->addStage($stage); |
|
492 | |||
493 | 7 | return $stage; |
|
494 | } |
||
495 | |||
496 | /** |
||
497 | * Groups incoming documents based on the value of a specified expression, |
||
498 | * then computes the count of documents in each distinct group. |
||
499 | * |
||
500 | * @see http://docs.mongodb.org/manual/reference/operator/aggregation/sortByCount/ |
||
501 | */ |
||
502 | 3 | View Code Duplication | public function sortByCount(string $expression) : Stage\SortByCount |
503 | { |
||
504 | 3 | $stage = new Stage\SortByCount($this, $expression, $this->dm, $this->class); |
|
505 | 3 | $this->addStage($stage); |
|
506 | |||
507 | 3 | return $stage; |
|
508 | } |
||
509 | |||
510 | /** |
||
511 | * Deconstructs an array field from the input documents to output a document |
||
512 | * for each element. Each output document is the input document with the |
||
513 | * value of the array field replaced by the element. |
||
514 | * |
||
515 | * @see http://docs.mongodb.org/manual/reference/operator/aggregation/unwind/ |
||
516 | */ |
||
517 | 7 | public function unwind(string $fieldName) : Stage\Unwind |
|
518 | { |
||
519 | // Fixme: move field name translation to stage |
||
520 | 7 | $stage = new Stage\Unwind($this, $this->getDocumentPersister()->prepareFieldName($fieldName)); |
|
521 | 7 | $this->addStage($stage); |
|
522 | |||
523 | 7 | return $stage; |
|
524 | } |
||
525 | |||
526 | /** |
||
527 | * Allows adding an arbitrary stage to the pipeline |
||
528 | * |
||
529 | * @return Stage The method returns the stage given as an argument |
||
530 | */ |
||
531 | 76 | public function addStage(Stage $stage) : Stage |
|
532 | { |
||
533 | 76 | $this->stages[] = $stage; |
|
534 | |||
535 | 76 | return $stage; |
|
536 | } |
||
537 | |||
538 | /** |
||
539 | * Applies filters and discriminator queries to the pipeline |
||
540 | */ |
||
541 | 68 | private function applyFilters(array $query) : array |
|
542 | { |
||
543 | 68 | $documentPersister = $this->dm->getUnitOfWork()->getDocumentPersister($this->class->name); |
|
544 | |||
545 | 68 | $query = $documentPersister->addDiscriminatorToPreparedQuery($query); |
|
546 | 68 | $query = $documentPersister->addFilterToPreparedQuery($query); |
|
547 | |||
548 | 68 | return $query; |
|
549 | } |
||
550 | |||
551 | 11 | private function getDocumentPersister() : DocumentPersister |
|
552 | { |
||
553 | 11 | return $this->dm->getUnitOfWork()->getDocumentPersister($this->class->name); |
|
554 | } |
||
555 | |||
556 | 20 | private function prepareIterator(Cursor $cursor) : Iterator |
|
557 | { |
||
558 | 20 | $class = null; |
|
559 | 20 | if ($this->hydrationClass) { |
|
560 | 4 | $class = $this->dm->getClassMetadata($this->hydrationClass); |
|
561 | } |
||
562 | |||
563 | 20 | if ($class) { |
|
564 | 4 | $cursor = new HydratingIterator($cursor, $this->dm->getUnitOfWork(), $class); |
|
565 | } |
||
566 | |||
567 | 20 | $cursor = $this->rewindable ? new CachingIterator($cursor) : new UnrewindableIterator($cursor); |
|
568 | |||
569 | 20 | return $cursor; |
|
570 | } |
||
571 | } |
||
572 |
This error could be the result of:
1. Missing dependencies
PHP Analyzer uses your
composer.json
file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects thecomposer.json
to be in the root folder of your repository.Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the
require
orrequire-dev
section?2. Missing use statement
PHP does not complain about undefined classes in
ìnstanceof
checks. For example, the following PHP code will work perfectly fine:If you have not tested against this specific condition, such errors might go unnoticed.