Elgg  Version master
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(): bool {
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();
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 
313  _elgg_services()->queryCache->enable();
314  return true;
315  }
316 
317  // no result
318  _elgg_services()->queryCache->enable();
319  return false;
320  }
321 
329  public function setIncrementOffset(bool $increment = true): void {
330  $this->incrementOffset = $increment;
331  }
332 
338  public function setChunkSize(int $size = 25): void {
339  $this->chunkSize = $size;
340  }
341 
350  public function reportFailure(int $num = 1): void {
351  $this->reportedFailures += $num;
352  }
353 
361  #[\ReturnTypeWillChange]
362  public function rewind() {
363  $this->resultIndex = 0;
364  $this->retrievedResults = 0;
365  $this->processedResults = 0;
366  $this->reportedFailures = 0;
367 
368  // only grab results if we haven't yet or we're crossing chunks
369  if ($this->chunkIndex == 0 || $this->limit > $this->chunkSize) {
370  $this->chunkIndex = 0;
371  $this->getNextResultsChunk();
372  }
373  }
374 
378  #[\ReturnTypeWillChange]
379  public function current() {
380  return current($this->results);
381  }
382 
386  #[\ReturnTypeWillChange]
387  public function key() {
388  return $this->processedResults;
389  }
390 
394  #[\ReturnTypeWillChange]
395  public function next() {
396  // if we'll be at the end.
397  if (($this->processedResults + 1) >= $this->limit && $this->limit > 0) {
398  $this->results = [];
399  return;
400  }
401 
402  // if we'll need new results.
403  if (($this->resultIndex + 1) >= $this->chunkSize) {
404  if (!$this->getNextResultsChunk()) {
405  $this->results = [];
406  return;
407  }
408 
409  $result = current($this->results);
410  } else {
411  // the function above resets the indexes, so only inc if not
412  // getting new set
413  $this->resultIndex++;
414  $result = next($this->results);
415  }
416 
417  $this->processedResults++;
418  return $result;
419  }
420 
424  #[\ReturnTypeWillChange]
425  public function valid() {
426  if (!is_array($this->results)) {
427  return false;
428  }
429 
430  $key = key($this->results);
431  return ($key !== null && $key !== false);
432  }
433 
444  #[\ReturnTypeWillChange]
445  public function count() {
446  if (!is_callable($this->getter)) {
447  $inspector = new \Elgg\Debug\Inspector();
448  throw new ElggRuntimeException('Getter is not callable: ' . $inspector->describeCallable($this->getter));
449  }
450 
451  $options = array_merge($this->options, ['count' => true]);
452 
453  return call_user_func($this->getter, $options);
454  }
455 }
__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:425
current()
{}
Definition: ElggBatch.php:379
c Accompany it with the information you received as to the offer to distribute corresponding source complete source code means all the source code for all modules it plus any associated interface definition plus the scripts used to control compilation and installation of the executable as a special the source code distributed need not include anything that is normally and so on of the operating system on which the executable unless that component itself accompanies the executable If distribution of executable or object code is made by offering access to copy from a designated then offering equivalent access to copy the source code from the same place counts as distribution of the source even though third parties are not compelled to copy the source along with the object code You may not or distribute the Program except as expressly provided under this License Any attempt otherwise to sublicense or distribute the Program is void
Definition: LICENSE.txt:215
if($item instanceof\ElggEntity) elseif($item instanceof\ElggRiverItem) elseif($item instanceof\ElggRelationship) elseif(is_callable([$item, 'getType']))
Definition: item.php:48
elgg_extract($key, $array, $default=null, bool $strict=true)
Checks for $array[$key] and returns its value if it exists, else returns $default.
Definition: elgglib.php:254
rewind()
Implements Iterator.
Definition: ElggBatch.php:362
setIncrementOffset(bool $increment=true)
Increment the offset from the original options array? Setting to false is required for callbacks that...
Definition: ElggBatch.php:329
reportFailure(int $num=1)
Report a number of failures during the batch execution, this will increase the internal offset by $nu...
Definition: ElggBatch.php:350
setChunkSize(int $size=25)
Set chunk size.
Definition: ElggBatch.php:338
count()
Count the total results available at this moment.
Definition: ElggBatch.php:445
next()
{}
Definition: ElggBatch.php:395
$size
Definition: thumb.php:23
if($container instanceof ElggGroup &&$container->guid!=elgg_get_page_owner_guid()) $key
Definition: summary.php:44
key()
{}
Definition: ElggBatch.php:387
_elgg_services()
Get the global service provider.
Definition: elgglib.php:351