1  <?php
  2  /* Copyright (c) 2012, Geert Bergman (geert@scrivo.nl)
  3   * All rights reserved.
  4   *
  5   * Redistribution and use in source and binary forms, with or without
  6   * modification, are permitted provided that the following conditions are met:
  7   *
  8   * 1. Redistributions of source code must retain the above copyright notice,
  9   *    this list of conditions and the following disclaimer.
 10   * 2. Redistributions in binary form must reproduce the above copyright notice,
 11   *    this list of conditions and the following disclaimer in the documentation
 12   *    and/or other materials provided with the distribution.
 13   * 3. Neither the name of "Scrivo" nor the names of its contributors may be
 14   *    used to endorse or promote products derived from this software without
 15   *    specific prior written permission.
 16   *
 17   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 18   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 19   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 20   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 21   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 22   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 23   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 24   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 25   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 26   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 27   * POSSIBILITY OF SUCH DAMAGE.
 28   *
 29   * $Id: ByteArray.php 708 2013-07-02 11:59:37Z geert $
 30   */
 31  
 32  /**
 33   * Implementation of the \Scrivo\ByteArray class.
 34   */
 35  
 36  namespace Scrivo;
 37  
 38  /**
 39   * Wrapper class for 8 byte character strings.
 40   *
 41   * ByteArray is a primitive wrapper class for 8 byte character strings. So
 42   * this is a wrapper class for PHP native strings. It's purpose is to create
 43   * a clear distinction in the code between byte arrays and UTF-8 Strings.
 44   *
 45   * Using only these two classes to handle strings will force you to make a
 46   * consious decicion each time you work with string data, and thus hopefully
 47   * prevent error by preventing mixups. A secondary objective is to create a
 48   * more consistent interface for string handling as PHP itself provides.
 49   */
 50  class ByteArray implements \Iterator, \ArrayAccess, \Countable {
 51  
 52      /**
 53       * The primitive string/byte array.
 54       * @var string
 55       */
 56      private $str;
 57  
 58      /**
 59       * The current position when iterating.
 60       * @var string
 61       */
 62      private $pos;
 63  
 64      /**
 65       * The length of the string (characters not bytes).
 66       * @var int
 67       */
 68      private $len = -1;
 69  
 70      /**
 71       * Get a substring from a string without first checking the boundaries.
 72       *
 73       * @param int $start Start offset for the substring, use a negative number
 74       *   to use an offset from the end of the string.
 75       * @param int $length The length of the substring.
 76       *
 77       * @return ByteArray The requested portion of this string.
 78       */
 79      private function unsafeSubstr($start$length) {
 80          return new ByteArray(substr($this->str$start$length));
 81      }
 82  
 83      /**
 84       * Construct an ByteArray.
 85       *
 86       * @param string $str The source string.
 87       */
 88      public function __construct($str="") {
 89          \Scrivo\ArgumentCheck::assert($str, \Scrivo\ArgumentCheck::TYPE_STRING);
 90          $this->str $str;
 91          $this->pos 0;
 92      }
 93  
 94      /**
 95       * Factory method to construct an ByteArray.
 96       *
 97       * @see ByteArray::__construct()
 98       *
 99       * @param string|array $str The source strings.
100       *
101       * @return ByteArray|ByteArray[] An ByteArray wrapper object.
102       */
103      public static function create($str="") {
104          if (is_array($str)) {
105              foreach($str as $k=>$v) {
106                  $str[$k] = self::create($v);
107              }
108              return $str;
109          }
110          return new ByteArray($str);
111      }
112  
113      /**
114       * Return the primitive string for this instance.
115       *
116       * @return string The primitive string for this instance.
117       */
118      public function __toString() {
119          return $this->str;
120      }
121  
122      /**
123       * Implementation of the readable properties using the PHP magic
124       * method __get().
125       *
126       * @param string $name The name of the property to get.
127       *
128       * @return mixed The value of the requested property.
129       */
130      public function __get($name) {
131          switch($name) {
132              case "length": return $this->getLength();
133          }
134          throw new \Scrivo\SystemException("No such property '$name'.");
135      }
136  
137      /**
138       * Test if this string equals another ByteArray object.
139       *
140       * When you want test ByteArray object for equality, use this method
141       * and never the equality operator (==) because then you'll compare
142       * objects and therefore all data members of ByteArray and this can
143       * give you other results (or cast the ByteArray strings to PHP strings
144       * before comparing).
145       *
146       * @param ByteArray $str The string to compare this string to.
147       *
148       * @return boolean True if the given string equals this string.
149       */
150      public function equals(ByteArray $str) {
151          return (string)$this->str == (string)$str;
152      }
153  
154      /**
155       * Get the length of the string.
156       *
157       * @return int The length of the string in characters (not bytes).
158       */
159      public function getLength() {
160          if ($this->len == -1) {
161              $this->len strlen($this->str);
162          }
163          return $this->len;
164      }
165  
166      /**
167       * Return the character count of the string.
168       *
169       * This is an alias for getLength() and part of the implementation of
170       * Countable.
171       *
172       * @return int The length of the string in characters.
173       */
174      public function count() {
175          return $this->getLength();
176      }
177  
178      /**
179       * Return the current character when iterating.
180       *
181       * Note that this method is part of the implementation of Iterator and
182       * should not be called from an other context.
183       *
184       * @return string The current character in this string when
185       *   iterating.
186       */
187      public function current() {
188          // note: iterator will call valid() before current().
189          return $this->unsafeSubstr($this->pos1);
190      }
191  
192      /**
193       * Return the index of the current character when iterating.
194       *
195       * Note that this method is part of the implementation of Iterator and
196       * should not be called from an other context.
197       *
198       * @return int The index of the current character in this string
199       *   when iterating.
200       */
201      public function key() {
202          return $this->pos;
203      }
204  
205      /**
206       * Move forward in this string to the next character when iterating.
207       *
208       * Note that this method is part of the implementation of Iterator and
209       * should not be called from an other context.
210       */
211      public function next() {
212          $this->pos++;
213      }
214  
215      /**
216       * Reset the current character index so iterating will (re)start at the
217       * beginning of this string.
218       *
219       * Note that this method is part of the implementation of Iterator and
220       * should not be called from an other context.
221       */
222      public function rewind() {
223          $this->pos 0;
224      }
225  
226      /**
227       * Check if the current character index for iterating is valid.
228       *
229       * Note that this method is part of the implementation of Iterator and
230       * should not be called from an other context.
231       *
232       * @return boolean True if the current character index is valid else false.
233       */
234      public function valid() {
235          return ($this->pos >= && $this->pos $this->getLength());
236      }
237  
238      /**
239       * Illegal method: set a character at a specified index location.
240       *
241       * Note that this method is part of the implementation of ArrayAccess.
242       * ByteArrays are immutable and therefore it is prohibited to set
243       * elements (characters) in a string, so this method implementation is
244       * not relevant and throws an exception if called.
245       *
246       * @param int $offset
247       * @param string $value
248       *
249       * @throws \Scrivo\SystemException If this method is called.
250       */
251      public function offsetSet($offset$value) {
252          throw new \Scrivo\SystemException(
253              "offsetSet can't be called on ByteArray objects");
254      }
255  
256      /**
257       * Get an UTF-8 character from a string using array brackets.
258       *
259       * Note that this method is part of the implementation of ArrayAccess and
260       * should not be called from an other context.
261       *
262       * @param int $offset A character offet in the string.
263       *
264       * @throws \Scrivo\SystemException If the requested offset was out of range.
265       */
266      public function offsetGet($offset) {
267          \Scrivo\ArgumentCheck::assert(
268              $offset, \Scrivo\ArgumentCheck::TYPE_INTEGER);
269          if (!$this->offsetExists($offset)) {
270              throw new \Scrivo\SystemException(
271                  "String index [$offset] out of bounds");
272          }
273          return $this->unsafeSubstr($offset1);
274      }
275  
276      /**
277       * Check if the specified index location in this string is valid.
278       *
279       * Note that this method is part of the implementation of ArrayAccess and
280       * should not be called from an other context.
281       *
282       * @param int $offset A character offet in the string.
283       *
284       * @return boolean True if the specified in index is within the valid range.
285       */
286      public function offsetExists($offset) {
287          \Scrivo\ArgumentCheck::assert(
288              $offset, \Scrivo\ArgumentCheck::TYPE_INTEGER);
289          return ($offset >= && $offset $this->getLength());
290      }
291  
292      /**
293       * Illegal method: unset a character at a specified index location.
294       *
295       * Note that this method is part of the implementation of ArrayAccess.
296       * ByteArrays are immutable and therefore it is prohibited to unset
297       * elements (characters) in a string, so this method implementation is
298       * not relevant and throws an exception if called.
299       *
300       * @param int $offset
301       *
302       * @throws \Scrivo\SystemException If this method is called.
303       */
304      public function offsetUnset($offset) {
305          \Scrivo\ArgumentCheck::assert(
306              $offset, \Scrivo\ArgumentCheck::TYPE_INTEGER);
307          throw new \Scrivo\SystemException(
308              "offsetUnset can't be called on ByteArray objects");
309      }
310  
311      /**
312       * Get a substring from a string using an offset and a length.
313       *
314       * Just like PHP's native substr function this method returns a substring
315       * from this string using an offset and a length. But note that this
316       * method will throw an exception if the offset is invalid.
317       *
318       * @param int $start Start offset for the substring, use a negative number
319       *   to use an offset from the end of the string.
320       * @param int $length The length of the substring.
321       *
322       * @return ByteArray The portion of this string specified by the $start
323       *   and $length parameter.
324       *
325       * @throws \Scrivo\SystemException if the requested offset was out of range.
326       */
327      public function substr($start$length=0xFFFF) {
328          \Scrivo\ArgumentCheck::assert(
329              $start, \Scrivo\ArgumentCheck::TYPE_INTEGER);
330          \Scrivo\ArgumentCheck::assert(
331              $length, \Scrivo\ArgumentCheck::TYPE_INTEGER);
332          $tmp $start ? -$start $start;
333          if (!$this->offsetExists($tmp)) {
334              throw new \Scrivo\SystemException(
335                  "String index [$start] out of bounds");
336          }
337          return $this->unsafeSubstr($start$length);
338      }
339  
340      /**
341       * Get a substring from a string using a start and end index.
342       *
343       * This method is inspired by it's JAVA counterpart and returns a
344       * substring of this string using an start and end index.
345       *
346       * @param int $start Start offset for the substring.
347       * @param int $end The end offset for the substring.
348       *
349       * @return ByteArray The portion of this string specified by the $start
350       *   and $end parameter.
351       *
352       * @throws \Scrivo\SystemException if the requested offset was out of range.
353       */
354      public function substring($start$end) {
355          \Scrivo\ArgumentCheck::assert(
356              $start, \Scrivo\ArgumentCheck::TYPE_INTEGER);
357          \Scrivo\ArgumentCheck::assert(
358              $end, \Scrivo\ArgumentCheck::TYPE_INTEGER);
359          if (!$this->offsetExists($start) || !$this->offsetExists($end)
360                  || $start $end) {
361              throw new \Scrivo\SystemException(
362                  "String index [$start$end] out of bounds");
363          }
364          return $this->unsafeSubstr($start$end-$start);
365      }
366  
367      /**
368       * Get a trimmed copy of this string.
369       *
370       * Returns a copy of the string, with leading and trailing whitespace
371       * removed. Whitespace characters are: ' ', \t, \r, \n.
372       *
373       * @return ByteArray A copy of this string with leading and trailing
374       *   white space removed.
375       */
376      public function trim() {
377          return new ByteArray(
378              preg_replace("/(^[\s]+)|([\s]+$)/s"""$this->str));
379      }
380  
381      /**
382       * Check if the string contains the given substring.
383       *
384       * This is the test you normally use strpos(...) !== false for.
385       *
386       * @param ByteArray $str The string to search for.
387       * @param int $offset An offset from where to start the search.
388       * @param boolean $ignoreCase Set to perform an case insensitive lookup.
389       *
390       * @return boolean True if the given string is contained by this string.
391       *
392       * @throws \Scrivo\SystemException If the $offset is out of range.
393       */
394      public function contains(ByteArray $str$offset=0$ignoreCase=false) {
395          \Scrivo\ArgumentCheck::assert(
396              $offset, \Scrivo\ArgumentCheck::TYPE_INTEGER);
397          \Scrivo\ArgumentCheck::assert(
398              $ignoreCase, \Scrivo\ArgumentCheck::TYPE_BOOLEAN);
399          if ($offset && !$this->offsetExists($offset)) {
400              throw new \Scrivo\SystemException(
401                  "String index [$offset] out of bounds");
402          }
403          if ($ignoreCase) {
404              return stripos(
405                  $this->str, (string)$str$offset) !== false;
406          } else {
407              return strpos($this->str, (string)$str$offset) !== false;
408          }
409      }
410  
411      /**
412       * Returns the index of the given substring in this string.
413       *
414       * Just like the PHP's native strpos and stripos functions this method
415       * returns the index of a substring in this string. But there are two
416       * important differences: this method returns -1 if the substring was
417       * not found, and this method will raise an exception if the given
418       * offset was out of range.
419       *
420       * @param ByteArray $str The string to search for.
421       * @param int $offset An offset from where to start the search.
422       * @param boolean $ignoreCase Set to perform an case insensitive lookup.
423       *
424       * @return int The index of the first occurance of the substring after
425       *   $offset and -1 if the substring was not found.
426       *
427       * @throws \Scrivo\SystemException If the $offset is out of range.
428       */
429      public function indexOf(ByteArray $str$offset=0$ignoreCase=false) {
430          \Scrivo\ArgumentCheck::assert(
431              $offset, \Scrivo\ArgumentCheck::TYPE_INTEGER);
432          \Scrivo\ArgumentCheck::assert(
433              $ignoreCase, \Scrivo\ArgumentCheck::TYPE_BOOLEAN);
434          if ($offset && !$this->offsetExists($offset)) {
435              throw new \Scrivo\SystemException(
436                  "String index [$offset] out of bounds");
437          }
438          $res = -1;
439          if ($ignoreCase) {
440              $res stripos($this->str, (string)$str$offset);
441          } else {
442              $res strpos($this->str, (string)$str$offset);
443          }
444          return $res !== false $res : -1;
445      }
446  
447      /**
448       * Returns the index of the last occurance of the given substring in this
449       * string.
450       *
451       * Just like the PHP's native strrpos and strripos functions this method
452       * returns the substring of this string that start with the first occurance
453       * of the given a substring in this string.  But note that this
454       * method will throw an exception if the offset is invalid.
455       * Also an negative offset to indicate an offset measured from the end
456       * of the string is allowed. But there are two important differences:
457       * this method returns -1 if the substring was not found, and this method
458       * will raise an exception if the given offset was out of range.
459       *
460       * @param ByteArray $str The string to search for.
461       * @param int $offset An offset from where to start the search. A positive
462       *   value indicates an offset measured from the start of the string, a
463       *   negative value from the end of the string.
464       * @param boolean $ignoreCase Perform an case insensitive lookup.
465       *
466       * @return int The index of the last occurance of the substring after
467       *   $offset.
468       * @throws \Scrivo\SystemException If the $offset is out of range.
469       */
470      public function lastIndexOf(ByteArray $str$offset=0$ignoreCase=false) {
471          \Scrivo\ArgumentCheck::assert(
472              $offset, \Scrivo\ArgumentCheck::TYPE_INTEGER);
473          \Scrivo\ArgumentCheck::assert(
474              $ignoreCase, \Scrivo\ArgumentCheck::TYPE_BOOLEAN);
475          if ($offset) {
476              $tmp $offset ? -$offset $offset;
477              if (!$this->offsetExists($tmp)) {
478                  throw new \Scrivo\SystemException(
479                      "String index [$offset] out of bounds");
480              }
481          }
482          $res = -1;
483          if ($ignoreCase) {
484              $res strripos($this->str, (string)$str$offset);
485          } else {
486              $res strrpos($this->str, (string)$str$offset);
487          }
488          return $res !== false $res : -1;
489      }
490  
491      /**
492       * Returns the first occurance of a given substring in this string.
493       *
494       * Just like the PHP's native strstr and stristr functions this method
495       * returns the substring of this string that start with the first occurance
496       * of the given a substring in this string. Note that this method throws
497       * an exception if an empty string was given as search string and not
498       * a warning as strstr does.
499       *
500       * @param ByteArray $str The string to search for.
501       * @param boolean $part Flag to indicate to return the part of the string
502       *   before the first occurance of the given substring i.o. the part
503       *   after the substring.
504       * @param boolean $ignoreCase Perform an case insensitive lookup.
505       *
506       * @return ByteArray The substring plus the part of the string after the
507       *   the first occurance of the substring, or the part of the string before
508       *   the first occurance of the substring (excluding the substring) or NULL
509       *   if not found.
510       *
511       * @throws \Scrivo\SystemException If an empty search string was given.
512       */
513      public function firstOccurranceOf(ByteArray $str$part=false,
514              $ignoreCase=false) {
515          \Scrivo\ArgumentCheck::assert(
516              $part, \Scrivo\ArgumentCheck::TYPE_BOOLEAN);
517          \Scrivo\ArgumentCheck::assert(
518              $ignoreCase, \Scrivo\ArgumentCheck::TYPE_BOOLEAN);
519          if (!$str->getLength()) {
520              throw new \Scrivo\SystemException(
521                  "firstOccurranceOf requires a search string");
522          }
523          $res NULL;
524          if ($ignoreCase) {
525              $res stristr($this->str, (string)$str$part);
526          } else {
527              $res strstr($this->str, (string)$str$part);
528          }
529          return $res !== false ? new ByteArray($res) : NULL;
530      }
531  
532      /**
533       * Returns the last occurance of a given character in this string.
534       *
535       * Just like the PHP's native strrchr and strrichr functions this method
536       * returns the substring of this string that start with the first occurance
537       * of the given a substring in this string. Note that this method throws
538       * an exception if an empty string was given as search string and not
539       * a warning as strstr does.
540       *
541       * @param ByteArray $str The character to search for.
542       * @param boolean $part Flag to indicate to return part of the string before
543       *   the last occurance of the given character i.o. the part after the
544       *   character.
545       * @param boolean $ignoreCase Perform an case insensitive lookup.
546       *
547       * @return ByteArray The substring plus the part of the string after the
548       *   the last occurance of the character, or the part of the string before
549       *   the last occurance of the character (excluding the character) or NULL
550       *   if not found.
551       *
552       * @throws \Scrivo\SystemException If a search string of not exactly one
553       *   character in length was given.
554       */
555      public function lastOccurranceOf(ByteArray $str$part=false,
556              $ignoreCase=false) {
557          \Scrivo\ArgumentCheck::assert(
558              $part, \Scrivo\ArgumentCheck::TYPE_BOOLEAN);
559          \Scrivo\ArgumentCheck::assert(
560              $ignoreCase, \Scrivo\ArgumentCheck::TYPE_BOOLEAN);
561          if ($str->getLength() != 1) {
562              throw new \Scrivo\SystemException(
563                  "lastOccurranceOf accepts single charaters only");
564          }
565          $pos $this->lastIndexOf($str0$ignoreCase);
566          if ($pos == -1) {
567              return null;
568          }
569          if ($part) {
570              return $this->unsafeSubstr(0$pos);
571          }
572          return $this->unsafeSubstr($pos$this->getLength()-$pos);
573      }
574  
575      /**
576       * Replace a substring or set of substrings in this string.
577       *
578       * You can use this method in favour of PHP's native str_replace and strtr
579       * functions. This method will do proper type checking for you. Note
580       * that you can safely use str_replace: if all input parameter are
581       * correct UTF-8 this method is UTF-8 safe too.
582       *
583       * @param ByteArray|ByteArray[] $from A (set of) string(s) to replace
584       *   in this string.
585       * @param ByteArray|ByteArray[] $to A (set of) replacement string(s) to
586       *   replace the found string(s).
587       *
588       * @return ByteArray A string with the replaced values.
589       *
590       * @throws \Scrivo\SystemException If the input data is not of type
591       *   ByteArray or ByteArray[], of if the $to parameter is an array
592       *     and $from isn't or hasn't the same number of elements.
593       */
594      public function replace($from$to) {
595          if ($from instanceof ByteArray && $to instanceof ByteArray) {
596              return new ByteArray(str_replace($from$to$this->str));
597          } else if (is_array($from) && $to instanceof ByteArray) {
598              foreach ($from as $k=>$v) {
599                  if (!($v instanceof ByteArray)) {
600                      throw new \Scrivo\SystemException("From element is"
601                          " not an ByteArray as array position [$k]");
602                  }
603              }
604              return new ByteArray(str_replace($from$to$this->str));
605          } else if (is_array($from) && is_array($to)) {
606              if (count($from) != count($to)) {
607                  throw new \Scrivo\SystemException(
608                      "Input arrays are not the same size");
609              }
610              foreach ($from as $k=>$v) {
611                  if (!($v instanceof ByteArray)
612                          || !($to[$k] instanceof ByteArray)) {
613                      throw new \Scrivo\SystemException("To or from element is"
614                          " not an ByteArray as array position [$k]");
615                  }
616              }
617              return new ByteArray(str_replace($from$to$this->str));
618          }
619          throw new \Scrivo\SystemException("Invalid argument types");
620      }
621  
622      /**
623       * Split this string using a delimiter.
624       *
625       * Just like PHP's native explode this method splits a string on
626       * boundaries formed by the string delimiter. Note that the behavoir
627       * of the limit parameter is a little bit different and that this method
628       * will throw an exception if an empty string is passed as a delimiter.
629       *
630       * @param ByteArray $delimiter The boundary string.
631       * @param int $limit If limit is set and positive, the returned array
632       *     will contain a maximum of limit elements with the last element
633       *     containing the rest of string. If the limit parameter is negative,
634       *     all components except the last -limit are returned. If the limit is
635       *     not set or 0 no limit wil be used.
636       *
637       * @return ByteArray[] An array of strings created by splitting the
638       *     string parameter on boundaries formed by the delimiter. If the
639       *     delimiter was not found and array containing a copy of this string
640       *     will be returned except if limit was negative, in that case an
641       *     empty array will be returned.
642       *
643       * @throws \Scrivo\SystemException If an empty search string was given.
644       */
645      public function split(ByteArray $delimiter$limit=0) {
646          \Scrivo\ArgumentCheck::assert(
647              $limit, \Scrivo\ArgumentCheck::TYPE_INTEGER);
648          if ($delimiter == "") {
649              throw new \Scrivo\SystemException(
650                      "split cannot use an empty \"\" delimiter.");
651          }
652          $r $limit explode($delimiter$this->str$limit)
653              : explode($delimiter$this->str);
654          foreach ($r as $k=>$v) {
655              $r[$k] = new ByteArray($v);
656          }
657          return $r;
658      }
659  
660      /**
661       * Get a copy of this string with all of its characters converted to lower
662       * case.
663       *
664       * @return ByteArray A string containing only lower case characters.
665       */
666      public function toLowerCase() {
667          return new ByteArray(strtolower($this->str));
668      }
669  
670      /**
671       * Get a copy of this string with all of its characters converted to upper
672       * case.
673       *
674       * @return ByteArray A string containing only upper case characters.
675       */
676      public function toUpperCase() {
677          return new ByteArray(strtoupper($this->str));
678      }
679  
680      /**
681       * Compare this string to another ByteArray object.
682       *
683       * @param ByteArray $str The string to compare this string to.
684       *
685       * @return int Less than 0 if this string is less than the given
686       *   string $str; more than 0 if this string is greater than $str, and
687       *   0 if they are equal.
688       */
689      public function compareTo(ByteArray $str) {
690          return strcmp($this->str$str);
691      }
692  
693  }
694  
695  ?>

Documentation generated by phpDocumentor 2.0.0a12 and ScrivoDocumentor on August 29, 2013