Elgg  Version 2.2
 All Classes Namespaces Files Functions Variables Pages
ElggBatch.php
Go to the documentation of this file.
1 <?php
57 class ElggBatch
58  implements \Iterator {
59 
65  private $results = array();
66 
72  private $getter = null;
73 
79  private $chunkSize = 25;
80 
86  private $callback = null;
87 
93  private $offset = 0;
94 
100  private $limit = 0;
101 
107  private $retrievedResults = 0;
108 
114  private $resultIndex = 0;
115 
121  private $chunkIndex = 0;
122 
128  private $processedResults = 0;
129 
135  private $validGetter = null;
136 
142  public $callbackResult = null;
143 
149  private $incrementOffset = true;
150 
156  private $incompleteEntities = array();
157 
163  private $totalIncompletes = 0;
164 
187  public function __construct($getter, $options, $callback = null, $chunk_size = 25,
188  $inc_offset = true) {
189 
190  $this->getter = $getter;
191  $this->options = $options;
192  $this->callback = $callback;
193  $this->chunkSize = $chunk_size;
194  $this->setIncrementOffset($inc_offset);
195 
196  if ($this->chunkSize <= 0) {
197  $this->chunkSize = 25;
198  }
199 
200  // store these so we can compare later
201  $this->offset = elgg_extract('offset', $options, 0);
202  $this->limit = elgg_extract('limit', $options, elgg_get_config('default_limit'));
203 
204  // if passed a callback, create a new \ElggBatch with the same options
205  // and pass each to the callback.
206  if ($callback && is_callable($callback)) {
207  $batch = new \ElggBatch($getter, $options, null, $chunk_size, $inc_offset);
208 
209  $all_results = null;
210 
211  foreach ($batch as $result) {
212  $result = call_user_func($callback, $result, $getter, $options);
213 
214  if (!isset($all_results)) {
215  if ($result === true || $result === false || $result === null) {
216  $all_results = $result;
217  } else {
218  $all_results = array();
219  }
220  }
221 
222  if (($result === true || $result === false || $result === null) && !is_array($all_results)) {
223  $all_results = $result && $all_results;
224  } else {
225  $all_results[] = $result;
226  }
227  }
228 
229  $this->callbackResult = $all_results;
230  }
231  }
232 
240  public function reportIncompleteEntity(\stdClass $row) {
241  $this->incompleteEntities[] = $row;
242  }
243 
249  private function getNextResultsChunk() {
250 
251  // always reset results.
252  $this->results = array();
253 
254  if (!isset($this->validGetter)) {
255  $this->validGetter = is_callable($this->getter);
256  }
257 
258  if (!$this->validGetter) {
259  return false;
260  }
261 
262  $limit = $this->chunkSize;
263 
264  // if someone passed limit = 0 they want everything.
265  if ($this->limit != 0) {
266  if ($this->retrievedResults >= $this->limit) {
267  return false;
268  }
269 
270  // if original limit < chunk size, set limit to original limit
271  // else if the number of results we'll fetch if greater than the original limit
272  if ($this->limit < $this->chunkSize) {
273  $limit = $this->limit;
274  } elseif ($this->retrievedResults + $this->chunkSize > $this->limit) {
275  // set the limit to the number of results remaining in the original limit
276  $limit = $this->limit - $this->retrievedResults;
277  }
278  }
279 
280  if ($this->incrementOffset) {
281  $offset = $this->offset + $this->retrievedResults;
282  } else {
283  $offset = $this->offset + $this->totalIncompletes;
284  }
285 
286  $current_options = array(
287  'limit' => $limit,
288  'offset' => $offset,
289  '__ElggBatch' => $this,
290  );
291 
292  $options = array_merge($this->options, $current_options);
293 
294  $this->incompleteEntities = array();
295  $this->results = call_user_func($this->getter, $options);
296 
297  // batch result sets tend to be large; we don't want to cache these.
298  _elgg_services()->db->disableQueryCache();
299 
300  $num_results = count($this->results);
301  $num_incomplete = count($this->incompleteEntities);
302 
303  $this->totalIncompletes += $num_incomplete;
304 
305  if ($this->incompleteEntities) {
306  // pad the front of the results with nulls representing the incompletes
307  array_splice($this->results, 0, 0, array_pad(array(), $num_incomplete, null));
308  // ...and skip past them
309  reset($this->results);
310  for ($i = 0; $i < $num_incomplete; $i++) {
311  next($this->results);
312  }
313  }
314 
315  if ($this->results) {
316  $this->chunkIndex++;
317 
318  // let the system know we've jumped past the nulls
319  $this->resultIndex = $num_incomplete;
320 
321  $this->retrievedResults += ($num_results + $num_incomplete);
322  if ($num_results == 0) {
323  // This fetch was *all* incompletes! We need to fetch until we can either
324  // offer at least one row to iterate over, or give up.
325  return $this->getNextResultsChunk();
326  }
327  _elgg_services()->db->enableQueryCache();
328  return true;
329  } else {
330  _elgg_services()->db->enableQueryCache();
331  return false;
332  }
333  }
334 
342  public function setIncrementOffset($increment = true) {
343  $this->incrementOffset = (bool) $increment;
344  }
345 
356  public function rewind() {
357  $this->resultIndex = 0;
358  $this->retrievedResults = 0;
359  $this->processedResults = 0;
360 
361  // only grab results if we haven't yet or we're crossing chunks
362  if ($this->chunkIndex == 0 || $this->limit > $this->chunkSize) {
363  $this->chunkIndex = 0;
364  $this->getNextResultsChunk();
365  }
366  }
367 
374  public function current() {
375  return current($this->results);
376  }
377 
384  public function key() {
385  return $this->processedResults;
386  }
387 
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 = array();
398  return false;
399  }
400 
401  // if we'll need new results.
402  if (($this->resultIndex + 1) >= $this->chunkSize) {
403  if (!$this->getNextResultsChunk()) {
404  $this->results = array();
405  return false;
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 
426  public function valid() {
427  if (!is_array($this->results)) {
428  return false;
429  }
430  $key = key($this->results);
431  return ($key !== null && $key !== false);
432  }
433 }
elgg_get_config($name, $site_guid=0)
Get an Elgg configuration value.
__construct($getter, $options, $callback=null, $chunk_size=25, $inc_offset=true)
Batches operations on any elgg_get_*() or compatible function that supports an options array...
Definition: ElggBatch.php:187
valid()
PHP Iterator Interface.
Definition: ElggBatch.php:426
current()
PHP Iterator Interface.
Definition: ElggBatch.php:374
$options
Elgg admin footer.
Definition: footer.php:6
rewind()
Implements Iterator.
Definition: ElggBatch.php:356
$key
Definition: summary.php:34
reportIncompleteEntity(\stdClass $row)
Tell the process that an entity was incomplete during a fetch.
Definition: ElggBatch.php:240
_elgg_services(\Elgg\Di\ServiceProvider $services=null)
Get the global service provider.
Definition: autoloader.php:17
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:1310
next()
PHP Iterator Interface.
Definition: ElggBatch.php:394
key()
PHP Iterator Interface.
Definition: ElggBatch.php:384
$row
setIncrementOffset($increment=true)
Increment the offset from the original options array? Setting to false is required for callbacks that...
Definition: ElggBatch.php:342