Elgg  Version 4.3
ElggBatch.php
Go to the documentation of this file.
1 <?php
2 
4 
71 class ElggBatch implements \Countable, \Iterator {
72 
78  private $results = [];
79 
85  private $getter = null;
86 
92  private $options = [];
93 
99  private $chunkSize = 25;
100 
106  private $callback = null;
107 
113  private $offset = 0;
114 
120  private $limit = 0;
121 
127  private $retrievedResults = 0;
128 
134  private $resultIndex = 0;
135 
141  private $chunkIndex = 0;
142 
148  private $processedResults = 0;
149 
155  private $validGetter = null;
156 
162  public $callbackResult = null;
163 
169  private $incrementOffset = true;
170 
176  private $reportedFailures = 0;
177 
200  public function __construct(callable $getter, array $options, $callback = null, int $chunk_size = 25, bool $inc_offset = true) {
201 
202  $this->getter = $getter;
203  $this->options = $options;
204  $this->callback = $callback;
205  $this->chunkSize = $chunk_size;
206  $this->setIncrementOffset($inc_offset);
207 
208  if ($this->chunkSize <= 0) {
209  $this->chunkSize = 25;
210  }
211 
212  // store these so we can compare later
213  $this->offset = elgg_extract('offset', $options, 0);
214  $this->limit = elgg_extract('limit', $options, _elgg_services()->config->default_limit);
215 
216  // if passed a callback, create a new \ElggBatch with the same options
217  // and pass each to the callback.
218  if ($callback && is_callable($callback)) {
219  $batch = new \ElggBatch($getter, $options, null, $chunk_size, $inc_offset);
220 
221  $all_results = null;
222 
223  foreach ($batch as $result) {
224  $result = call_user_func($callback, $result, $getter, $options);
225 
226  if (!isset($all_results)) {
227  if ($result === true || $result === false || $result === null) {
228  $all_results = $result;
229  } else {
230  $all_results = [];
231  }
232  }
233 
234  if (($result === true || $result === false || $result === null) && !is_array($all_results)) {
235  $all_results = $result && $all_results;
236  } else {
237  $all_results[] = $result;
238  }
239  }
240 
241  $this->callbackResult = $all_results;
242  }
243  }
244 
250  private function getNextResultsChunk() {
251 
252  // always reset results.
253  $this->results = [];
254 
255  if (!isset($this->validGetter)) {
256  $this->validGetter = is_callable($this->getter);
257  }
258 
259  if (!$this->validGetter) {
260  return false;
261  }
262 
263  $limit = $this->chunkSize;
264 
265  // if someone passed limit = 0 they want everything.
266  if ($this->limit != 0) {
267  if ($this->retrievedResults >= $this->limit) {
268  return false;
269  }
270 
271  // if original limit < chunk size, set limit to original limit
272  // else if the number of results we'll fetch if greater than the original limit
273  if ($this->limit < $this->chunkSize) {
274  $limit = $this->limit;
275  } elseif ($this->retrievedResults + $this->chunkSize > $this->limit) {
276  // set the limit to the number of results remaining in the original limit
277  $limit = $this->limit - $this->retrievedResults;
278  }
279  }
280 
281  if ($this->incrementOffset) {
282  $offset = $this->offset + $this->retrievedResults;
283  } else {
284  $offset = $this->offset + $this->reportedFailures;
285  }
286 
287  $current_options = [
288  'limit' => $limit,
289  'offset' => $offset,
290  '__ElggBatch' => $this,
291  ];
292 
293  $options = array_merge($this->options, $current_options);
294 
295  // batch result sets tend to be large; we don't want to cache these.
296  _elgg_services()->queryCache->disable(false);
297 
298  $this->results = call_user_func($this->getter, $options);
299 
300  $num_results = count($this->results);
301 
302  if ($this->results) {
303  $this->chunkIndex++;
304  $this->resultIndex = 0;
305 
306  $this->retrievedResults += $num_results;
307  if ($num_results === 0) {
308  // This fetch was *all* incompletes! We need to fetch until we can either
309  // offer at least one row to iterate over, or give up.
310  return $this->getNextResultsChunk();
311  }
312  _elgg_services()->queryCache->enable();
313  return true;
314  }
315 
316  // no result
317  _elgg_services()->queryCache->enable();
318  return false;
319  }
320 
328  public function setIncrementOffset(bool $increment = true) {
329  $this->incrementOffset = $increment;
330  }
331 
337  public function setChunkSize(int $size = 25) {
338  $this->chunkSize = $size;
339  }
340 
349  public function reportFailure(int $num = 1) {
350  $this->reportedFailures += $num;
351  }
352 
360  #[\ReturnTypeWillChange]
361  public function rewind() {
362  $this->resultIndex = 0;
363  $this->retrievedResults = 0;
364  $this->processedResults = 0;
365  $this->reportedFailures = 0;
366 
367  // only grab results if we haven't yet or we're crossing chunks
368  if ($this->chunkIndex == 0 || $this->limit > $this->chunkSize) {
369  $this->chunkIndex = 0;
370  $this->getNextResultsChunk();
371  }
372  }
373 
377  #[\ReturnTypeWillChange]
378  public function current() {
379  return current($this->results);
380  }
381 
385  #[\ReturnTypeWillChange]
386  public function key() {
387  return $this->processedResults;
388  }
389 
393  #[\ReturnTypeWillChange]
394  public function next() {
395  // if we'll be at the end.
396  if (($this->processedResults + 1) >= $this->limit && $this->limit > 0) {
397  $this->results = [];
398  return;
399  }
400 
401  // if we'll need new results.
402  if (($this->resultIndex + 1) >= $this->chunkSize) {
403  if (!$this->getNextResultsChunk()) {
404  $this->results = [];
405  return;
406  }
407 
408  $result = current($this->results);
409  } else {
410  // the function above resets the indexes, so only inc if not
411  // getting new set
412  $this->resultIndex++;
413  $result = next($this->results);
414  }
415 
416  $this->processedResults++;
417  return $result;
418  }
419 
423  #[\ReturnTypeWillChange]
424  public function valid() {
425  if (!is_array($this->results)) {
426  return false;
427  }
428  $key = key($this->results);
429  return ($key !== null && $key !== false);
430  }
431 
442  #[\ReturnTypeWillChange]
443  public function count() {
444  if (!is_callable($this->getter)) {
445  $inspector = new \Elgg\Debug\Inspector();
446  throw new ElggRuntimeException("Getter is not callable: " . $inspector->describeCallable($this->getter));
447  }
448 
449  $options = array_merge($this->options, ['count' => true]);
450 
451  return call_user_func($this->getter, $options);
452  }
453 }
__construct(callable $getter, array $options, $callback=null, int $chunk_size=25, bool $inc_offset=true)
Batches operations on any elgg_get_*() or compatible function that supports an options array...
Definition: ElggBatch.php:200
valid()
{}
Definition: ElggBatch.php:424
current()
{}
Definition: ElggBatch.php:378
rewind()
Implements Iterator.
Definition: ElggBatch.php:361
setIncrementOffset(bool $increment=true)
Increment the offset from the original options array? Setting to false is required for callbacks that...
Definition: ElggBatch.php:328
reportFailure(int $num=1)
Report a number of failures during the batch execution, this will increase the internal offset by $nu...
Definition: ElggBatch.php:349
setChunkSize(int $size=25)
Set chunk size.
Definition: ElggBatch.php:337
count()
Count the total results available at this moment.
Definition: ElggBatch.php:443
elgg_extract($key, $array, $default=null, $strict=true)
Checks for $array[$key] and returns its value if it exists, else returns $default.
Definition: elgglib.php:547
next()
{}
Definition: ElggBatch.php:394
$size
Definition: thumb.php:23
if($container instanceof ElggGroup &&$container->guid!=elgg_get_page_owner_guid()) $key
Definition: summary.php:44
if($item instanceof\ElggEntity) elseif($item instanceof\ElggRiverItem) elseif($item instanceof ElggRelationship) elseif(is_callable([$item, 'getType']))
Definition: item.php:48
key()
{}
Definition: ElggBatch.php:386
_elgg_services()
Get the global service provider.
Definition: elgglib.php:638