Overview
  • Namespace
  • Class
  • Tree

Namespaces

  • CupOfTea
    • Counter
      • Facades

Classes

  • CupOfTea\Counter\Counter
  • CupOfTea\Counter\CounterServiceProvider
  • CupOfTea\Counter\Facades\Counter
  1 <?php namespace CupOfTea\Counter;
  2 
  3 use Iterator;
  4 use Countable;
  5 use Traversable;
  6 use ArrayIterator;
  7 use SeekableIterator;
  8 use IteratorAggregate;
  9 use OutOfBoundsException;
 10 use InvalidArgumentException;
 11 use CupOfTea\Package\Package;
 12 
 13 class Counter implements SeekableIterator
 14 {
 15     use Package;
 16     
 17     /**
 18      * Package Name.
 19      *
 20      * @const string
 21      */
 22     const PACKAGE = 'CupOfTea/Counter';
 23     
 24     /**
 25      * Package Version.
 26      *
 27      * @const string
 28      */
 29     const VERSION = '1.0.3';
 30     
 31     /**
 32      * The current position.
 33      *
 34      * @var int
 35      */
 36     private $i = 0;
 37     
 38     /**
 39      * The length of the traversable or counter.
 40      *
 41      * @var int
 42      */
 43     private $length;
 44     
 45     /**
 46      * The Traversable.
 47      *
 48      * @var \Iterator
 49      */
 50     private $traversable;
 51     
 52     //
 53     // Traversing counter
 54     //
 55     
 56     /**
 57      * Loop over a variable.
 58      *
 59      * @param  mixed  $traversable
 60      * @return \CupOfTea\Support\Counter
 61      */
 62     public function loop($traversable)
 63     {
 64         $this->clear();
 65         $this->setTraversable($traversable);
 66         $this->setLength($this->traversable);
 67         
 68         return $this;
 69     }
 70     
 71     /**
 72      * Resolve the variable we are looping over to something traversable.
 73      *
 74      * @param  mixed  $traversable
 75      * @return void
 76      */
 77     private function setTraversable($traversable)
 78     {
 79         $traversable = value($traversable);
 80         
 81         if ($traversable instanceof Iterator) {
 82             $this->traversable = $traversable;
 83         } elseif ($traversable instanceof IteratorAggregate) {
 84             $this->traversable = $traversable->getIterator();
 85         } elseif (class_exists('Illuminate\Contracts\Support\Arrayable') && $traversable instanceof \Illuminate\Contracts\Support\Arrayable) {
 86             $this->traversable = new ArrayIterator($traversable->toArray());
 87         } else {
 88             $this->traversable = new ArrayIterator((array) $traversable);
 89         }
 90     }
 91     
 92     /**
 93      * Get the item that's being traversed.
 94      *
 95      * @return \Traversable|array
 96      */
 97     public function getTraversable()
 98     {
 99         if ($this->traversable instanceof ArrayIterator) {
100             $array = $this->traversable->getArrayCopy();
101             
102             for ($i = 0; $i < $this->i; $i++) {
103                 next($array);
104             }
105             
106             return $array;
107         }
108         
109         return $this->traversable;
110     }
111     
112     /**
113      * Determine if a traversable is set.
114      *
115      * @return bool
116      */
117     private function traversable()
118     {
119         return isset($this->traversable);
120     }
121     
122     /**
123      * Determine the length of a countable.
124      *
125      * @param  mixed  $countable
126      * @return void
127      */
128     private function setLength($countable)
129     {
130         $countable = value($countable);
131         
132         if ($this->isInt($countable)) {
133             $this->length = (int) $countable;
134         } elseif (is_array($countable) || $countable instanceof Countable) {
135             $this->length = count($countable);
136         } elseif (class_exists('Illuminate\Contracts\Support\Arrayable') && $countable instanceof \Illuminate\Contracts\Support\Arrayable) {
137             $this->length = count($countable->toArray());
138         } elseif ($countable instanceof Traversable) {
139             $this->length = 0;
140             
141             foreach ($countable as $v) {
142                 $this->length++;
143             }
144         } else {
145             $this->length = INF;
146         }
147     }
148     
149     //
150     // SeekableIterator implementation
151     //
152     
153     /**
154      * Seeks to a position.
155      *
156      * @param  int  $position
157      * @return void
158      * @throws \InvalidArgumentException when $position is not an integer.
159      * @throws \OutOfBoundsException when the seek $position exeeds the traversable's length.
160      */
161     public function seek($position)
162     {
163         if (! $this->isInt($position)) {
164             throw new InvalidArgumentException('Seek position must be an integer.');
165         }
166         
167         $position = max(0, (int) $position);
168         
169         if ($this->length !== null && $this->length < $position) {
170             throw new OutOfBoundsException('Invalid seek position (' . $position . ').');
171         }
172         
173         if ($this->traversable()) {
174             if ($this->traversable instanceof SeekableIterator) {
175                 $this->traversable->seek($position);
176             } else {
177                 $this->traversable->rewind();
178                 
179                 for ($i = 0; $i < $position; $i++) {
180                     $this->traversable->next();
181                 }
182             }
183         }
184         
185         $this->i = $position;
186     }
187     
188     /**
189      * Set the internal pointer of the traversable to its first element.
190      *
191      * @return mixed
192      */
193     public function rewind()
194     {
195         $this->i = 0;
196         
197         if ($this->traversable()) {
198             $this->traversable->rewind();
199             
200             return $this->length !== 0 ? $this->traversable->current() : false;
201         }
202         
203         return $this->i;
204     }
205     
206     /**
207      * Return the current element in the traversable.
208      *
209      * @return mixed
210      */
211     public function current()
212     {
213         if ($this->traversable()) {
214             return $this->traversable->current();
215         }
216         
217         return $this->i;
218     }
219     
220     /**
221      * Return the index element of the current traversable position.
222      *
223      * @return mixed
224      */
225     public function key()
226     {
227         if ($this->traversable()) {
228             return $this->traversable->key();
229         }
230         
231         return $this->i;
232     }
233     
234     /**
235      * Advance the internal array pointer of the traversable.
236      *
237      * @return mixed
238      */
239     public function next()
240     {
241         $this->i++;
242         
243         if ($this->traversable()) {
244             $this->traversable->next();
245             
246             return $this->traversable->current();
247         }
248         
249         return $this->i;
250     }
251     
252     /**
253      * Rewind the internal array pointer of the traversable.
254      *
255      * @return mixed
256      */
257     public function prev()
258     {
259         $this->i = max(0, $this->i - 1);
260         
261         if ($this->traversable()) {
262             $this->seek($this->i);
263             
264             return $this->traversable->current();
265         }
266         
267         return $this->i;
268     }
269     
270     /**
271      * Checks if current position of the traversable is valid.
272      *
273      * @return bool
274      */
275     public function valid()
276     {
277         if ($this->traversable()) {
278             $key = $this->traversable->key();
279             
280             // The is_null check is a safetyguard to make sure we don't end up
281             // in an infinite loop if some idiot decided to use null as a key.
282             return ! is_null($key) && $this->traversable->valid();
283         }
284         
285         return $this->i < $this->length;
286     }
287     
288     /**
289      * Set the internal pointer of the traversable to its last element.
290      *
291      * @return mixed
292      */
293     public function end()
294     {
295         if ($this->traversable()) {
296             if ($this->length !== 0) {
297                 $this->seek($this->length);
298                 
299                 return $this->traversable->current();
300             }
301             
302             return false;
303         }
304         
305         return ! is_null($this->length) ? $this->length : false;
306     }
307     
308     //
309     // Simple counter
310     //
311     
312     /**
313      * Start a simple counter.
314      *
315      * @param  bool|int  $length
316      * @return void
317      */
318     public function start($length = false)
319     {
320         $this->clear();
321         $this->setLength($length);
322         
323         return $this;
324     }
325     
326     //
327     // Counter methods
328     //
329     
330     /**
331      * Increment the counter by a specified amount.
332      *
333      * @param  int  $by
334      * @return void
335      */
336     public function increment($by = 1)
337     {
338         switch ($by) {
339             case 0:
340                 break;
341             case 1:
342                 $this->next();
343                 break;
344             default:
345                 $seek = $this->traversable() ? min($this->length - 1, $this->i + $by) : $this->i + $by;
346                 
347                 $this->seek($seek);
348                 break;
349         }
350     }
351     
352     /**
353      * Decrement the counter by a specified amount.
354      *
355      * @param  int  $by
356      * @return void
357      */
358     public function decrement($by = 1)
359     {
360         switch ($by) {
361             case 0:
362                 break;
363             case 1:
364                 $this->prev();
365                 break;
366             default:
367                 $this->seek(max(0, $this->i - $by));
368                 break;
369         }
370     }
371     
372     /**
373      * Increment the counter by 1.
374      *
375      * @return void
376      */
377     public function tick()
378     {
379         $this->increment();
380     }
381     
382     /**
383      * Check if the current position is the initial position.
384      *
385      * @return bool
386      */
387     public function first()
388     {
389         return $this->i == 0;
390     }
391     
392     /**
393      * Check if the current position is the last position.
394      * Will always return false if no length was
395      * specified for a simple counter.
396      *
397      * @return bool
398      */
399     public function last()
400     {
401         return $this->length !== null && $this->i + 1 >= $this->length;
402     }
403     
404     /**
405      * Check if the current iteration is the nth iteration (1 based).
406      *
407      * @return bool
408      */
409     public function nth($n)
410     {
411         return $this->iteration() % $n == 0;
412     }
413     
414     /**
415      * Check if the current iteration is an even iteration (1 based).
416      *
417      * @return bool
418      */
419     public function even()
420     {
421         return $this->nth(2);
422     }
423     
424     /**
425      * Check if the current iteration is an odd iteration (1 based).
426      *
427      * @return bool
428      */
429     public function odd()
430     {
431         return ! $this->even();
432     }
433     
434     /**
435      * Return the current element.
436      *
437      * @return mixed
438      */
439     public function item()
440     {
441         if ($this->traversable()) {
442             return $this->current();
443         }
444     }
445     
446     /**
447      * Get the current position.
448      *
449      * @return int
450      */
451     public function index()
452     {
453         return $this->i;
454     }
455     
456     /**
457      * Get the current iteration.
458      *
459      * @return int
460      */
461     public function iteration()
462     {
463         return $this->i + 1;
464     }
465     
466     /**
467      * Get the length of the traversable or the counter.
468      *
469      * @return float
470      */
471     public function length()
472     {
473         return $this->length;
474     }
475     
476     /**
477      * Reset the counter to its default state.
478      *
479      * @return void
480      */
481     private function clear()
482     {
483         $this->i = 0;
484         $this->length = $this->traversable = null;
485     }
486     
487     //
488     // helper methods
489     //
490     
491     /**
492      * Check if a variable is castable to an integer.
493      *
494      * @param  mixed  $int
495      * @return bool
496      */
497     private function isInt($int)
498     {
499         return (is_numeric($int) && (int) $int == (float) $int) || is_null($int);
500     }
501     
502     //
503     // method aliases
504     //
505     
506     /**
507      * @see \CupOfTea\Support\Counter::loop
508      */
509     public function traverse($traversable)
510     {
511         return $this->loop($traversable);
512     }
513     
514     /**
515      * @see \CupOfTea\Support\Counter::index
516      */
517     public function i()
518     {
519         return $this->index();
520     }
521     
522     /**
523      * @see \CupOfTea\Support\Counter::index
524      */
525     public function position()
526     {
527         return $this->index();
528     }
529 }
530 
API documentation generated by ApiGen