Source for file timeslotrule.php

Documentation is available at timeslotrule.php

  1. <?php
  2. /**
  3. * Timeslot validation based on rules passed to us (presumably from an SQL entry)
  4. *
  5. @author    Stuart Prescott
  6. @copyright  Copyright Stuart Prescott
  7. @license    http://opensource.org/licenses/gpl-license.php GNU Public License
  8. @version    $Id$
  9. @package    Bumblebee
  10. @subpackage Bookings
  11. */
  12.  
  13. /** Load ancillary functions */
  14. require_once 'inc/typeinfo.php';
  15.  
  16. /** date manipulation routines */
  17. require_once 'inc/date.php';
  18. /** timeslot object for bookings/vacancies */
  19. require_once 'timeslot.php';
  20.  
  21. /** date-time operation control: look for match with start of slot */
  22. define('TSSTART',  'tstart');
  23. /** date-time operation control: look for match with end of slot */
  24. define('TSSTOP',   'tstop');
  25. /** date-time operation control: look for slot where this time is within the slot */
  26. define('TSWITHIN'2);
  27. /** date-time operation control: look for slot following this time */
  28. define('TSNEXT',   3);
  29. // define('TSSLOT',   4);
  30. /** number of extra elements that will be in the array created from the slot rules */
  31. define('TSARRAYMIN'1);
  32. /** flag that slot was not found by the slot finding routines */
  33. define('TS_SLOT_NOT_FOUND'-10000);
  34.  
  35.  
  36. /**
  37. * Timeslot validation based on rules passed to us (presumably from an SQL entry)
  38. *
  39. @package    Bumblebee
  40. @subpackage Bookings
  41. @todo //TODO:       more documentation
  42. */
  43. class TimeSlotRule {
  44.   var $picture = '';
  45.   var $slots;
  46.  
  47.   var $DEBUG = 0;
  48.  
  49.   function TimeSlotRule($pic{
  50.     $this->picture = $pic;
  51.     $this->_interpret();
  52.   }
  53.  
  54.   /**
  55.    * timeslot picture syntax (concatinate onto one line, no spaces)
  56.    * [x] or [x-y]
  57.    *         specify the day of week (or range of days). 0=Sunday, 6=Saturday
  58.    * <slot;slot;slot>
  59.    *         around the comma-separated slot specifications for that day. All times from 00:00 to 24:00
  60.    *         must be included in the list
  61.    * starttime-stoptime/num_slots-discount%,comment
  62.    *         HH:MM time slots with the length of each slot.
  63.    *         e.g. 09:00-17:00/1 specifies a slot starting at 9am and finishing 5pm.
  64.    *              both 00:00 and 24:00 are valid, also 32:00 for 8am the next day (o'night)
  65.    *         num_slots is integer, or wildcard of * for freely adjustable time
  66.    *         e.g. 09:00-12:00/3 specifies 3 slots 9-10, 10-11 and 11-12.
  67.    *         discount (optional) is the percentage discount applied to bookings in that slot
  68.    *         comment  (optional) is displayed in vacant or unavailable slots
  69.    *
  70.    * EXAMPLES:
  71.    *
  72.    * [0-6]<00:00-08:00/*;08:00-13:00/1;13:00-18:00/1;18:00-24:00/*>
  73.    *    Available all days, free booking until 8am and after 6pm, other slots as defined
  74.    * [0]<00:00-24:00/0,Unavailable>[1-5]<09:00-17:00/01:00>[6]<00:00-24:00/0,Unavailable>
  75.    *    Not available Sunday and Saturday. Only available weekdays 9am till 5pm with 1hr booking
  76.    *    granularity. Calendar views of Sat & Sun will see message "Unavailable"
  77.    * [0]<>[1-5]<00:00-09:00/*;09:00-17:00/8;17:00-24:00/*-50%>[6]<>
  78.    *    Not available Sunday and Saturday. Available weekdays 9am till 5pm with 1hr booking
  79.    *    granularity, before 9am and after 5pm with no granularity and at a 50% discount.
  80.    * [0]<>[1-5]<09:00-13:00/4;13:00-17:00/2;17:00-33:00/1>[6]<>
  81.    *    Not available Sunday and Saturday. Available weekdays 9am till 5pm with 1hr booking
  82.    *    granularity, before 9am and after 5pm with no granularity
  83.    *
  84.    */
  85.   function _interpret({
  86.     $this->slots = array();
  87.     for ($dow=0$dow<=6$dow++{
  88.       $this->slots[$dowarray();
  89.     }
  90.     $this->_findDayLines();
  91.     for ($dow=0$dow<=6$dow++{
  92.       $this->_fillDaySlots($dow);
  93.     }
  94.   }
  95.  
  96.   function _findDayLines({
  97.     $daylines array();
  98.     preg_match_all('/\[.+?\>/'$this->picture$daylines);
  99.     foreach ($daylines[0as $d{
  100.       $day array();
  101.       #echo "considering $d\n";
  102.       if (preg_match('/\[(\d)\]/'$d$day)) {
  103.         #echo "found match: ".$day[1]."\n";
  104.         $this->slots[$day[1]]['picture']=$d;
  105.       }
  106.       if (preg_match('/\[(\d)-(\d)\]/'$d$day)) {
  107.         #echo "found multimatch: ".$day[1].'.'.$day[2]."\n";
  108.         for ($i=$day[1]$i<=$day[2]$i++{
  109.           $this->slots[$i]['picture']=$d;
  110.         }
  111.       }
  112.     }
  113.   }
  114.  
  115.   function _fillDaySlots($dow{
  116.     $times array();
  117.     #preDump($this->slots[$dow]['picture']);
  118.     preg_match('/\<(.*)\>/'$this->slots[$dow]['picture']$times);
  119.     #preDump($times);
  120.     if ($times[0== '<>'{
  121.       $times[1]='00:00-24:00/0';
  122.     }
  123.  
  124.     $i=0;
  125.     foreach(preg_split('/;/'$times[1]as $slot{
  126.       #echo "found slot=$slot\n";
  127.       #$this->slots[$dow][$i] = array();
  128.       $tmp array();
  129.       if (preg_match('/(\d\d:\d\d)-(\d\d:\d\d)\/(\d+|\*)(-\d+%)?(?:,(.+))?/'$slot$tmp)) {
  130.         /* regexp:       HH:MM    -  HH:MM     /n         -x%       ,comment
  131.            group:         1            2        3          4          5
  132.            notes:
  133.               n can be digit or *
  134.               x is optional and can be one or more digits
  135.               ,comment is optional and the comma is not included in the captured pattern
  136.               ?: means don't capture the contents of that subpattern () to $tmp
  137.         */
  138.         $start    $tmp[1];
  139.         $stop     $tmp[2];
  140.         $tstart   new SimpleTime($start);
  141.         $tstop    new SimpleTime($stop);
  142.         $numslots $tmp[3];
  143.         $optional=4;
  144.         $discount 0$comment '';
  145.         while (isset($tmp[$optional])) {
  146.           if (substr($tmp[$optional]01== '-' && substr($tmp[$optional]-11== '%'{
  147.             // then it's a discouint
  148.             $discount substr($tmp[$optional],1,-1);
  149.           else {
  150.             // then it's the comment at the end
  151.             $comment  $tmp[$optional];
  152.           }
  153.           $optional++;
  154.         }
  155.         //echo "(start,stop,slots) = ($start,$stop,$numslots)\n";
  156.         //echo "(discount,comment) = ($discount,$comment)<br />\n";
  157.         if ($numslots != '*' && $numslots != '0'{
  158.           #echo "Trying to interpolate timeslots\n";
  159.           $tgran new SimpleTime($tstop->subtract($tstart)/$numslots);
  160.           $siblingSlot=1;
  161.           for ($time clone($tstart)$time->ticks $tstop->ticks$time->addTime($tgran)) {
  162.             //echo $time->timeString().' '. $tstop->timeString().' '.$tgran->timeString()."<br />\n";
  163.             $sstop clone($time);
  164.             $sstop->addTime($tgran);
  165.             $this->slots[$dow][$inew RuleSlot($slot$start$stopclone($time)$sstop$tgran);
  166.             $this->slots[$dow][$i]->numslotsInGroup 1;
  167.             $this->slots[$dow][$i]->numslotsFollowing $numslots $siblingSlot;
  168.             $this->slots[$dow][$i]->comment $comment;
  169.             $this->slots[$dow][$i]->discount $discount;
  170.             if ($time->ticks != $tstart->ticks{
  171.               $this->slots[$dow][$i-1]->nextSlot &$this->slots[$dow][$i];
  172.             }
  173.             $i++;
  174.             $siblingSlot++;
  175.           }
  176.         else {
  177.           #echo "No need to interpolate\n";
  178.           $this->slots[$dow][$inew RuleSlot($slot$start$stop$tstart$tstop);
  179.           $this->slots[$dow][$i]->numslotsInGroup 1;
  180.           $this->slots[$dow][$i]->numslotsFollowing 0;
  181.           $this->slots[$dow][$i]->isFreeForm $numslots == '*';
  182.           $this->slots[$dow][$i]->isAvailable $numslots != '0';
  183.           $this->slots[$dow][$i]->comment $comment;
  184.           $this->slots[$dow][$i]->discount $discount;
  185.           $i++;
  186.         }
  187.       }
  188.     }
  189.     #var_dump($this->slots);
  190.   }
  191.  
  192. /*  function allSlotStart() {
  193.     echo "STUB: ". __FILE__ .' '. __LINE__;
  194.     return array('09:00','10:00','15:00');
  195.   }*/
  196.  
  197.   /**
  198.    * return true if the specified date & time correspond to a valid starting time according
  199.    * to this object's slot rules.
  200.    */
  201.   function isValidStart($date{
  202.     return $this->_isValidStartStop($dateTSSTART);
  203.   }
  204.  
  205.   /**
  206.    * return true if the specified date & time correspond to a valid stopping time according
  207.    * to this object's slot rules.
  208.    */
  209.   function isValidStop($date{
  210.     $startday clone($date);
  211.     $startday->dayRound();
  212.     //echo "$startday->dateTimeString() =? $date->dateTimeString()\n";
  213.     if ($startday->ticks == $date->ticks{
  214.       $time new SimpleTime('24:00');
  215.       $startday->addDays(-1);
  216.       //echo "$startday->dateTimeString() + $time->timeString()\n";
  217.       return $this->_isValidStartStop($timeTSSTOP$startday);
  218.     }
  219.     return $this->_isValidStartStop($dateTSSTOP);
  220.   }
  221.  
  222.   /**
  223.    * perform the above operations with no code duplication
  224.    */
  225.   function _isValidStartStop($date$type$daysdate=0{
  226.     return is_object($this->_findSlot($date$type$daysdate));
  227.   }
  228.  
  229.   /**
  230.    * return true if the specified dates & times are valid start/stop times
  231.    * to this object's slot rules.
  232.    */
  233.   function isValidSlot($startdate$stopdate{
  234.     return $this->isValidStart($startdate&& $this->isValidStop($stopdate);
  235.   }
  236.  
  237.   /**
  238.    * return true if the specified dates & times are valid as above, but only occupy one slot
  239.    */
  240.   function isValidSingleSlot($startdate$stopdate{
  241.     $slot $this->_findSlot($startdateTSSTART);
  242.     return $slot->stop->ticks == $stopdate->ticks;
  243.   }
  244.  
  245.   /**
  246.    * return the corresponding the slot specified by the given start date.
  247.    * ASSUMES that the specified date is a valid start, else behaviour is undefined.
  248.    *
  249.    * @param mixed SimpleDate $date the date to match or SimpleTime to match
  250.    * @param mixed optional SimpleDate iff $date was a SimpleTime. Provide the date component
  251.    */
  252.   function findSlotByStart($date$datetime=0{
  253.     return $this->_findSlot($dateTSSTART$datetime);
  254.   }
  255.  
  256.   /**
  257.    * return the
  258.    * as per findSlotByStart
  259.    */
  260.   function findSlotByStop($date$datetime=0{
  261.     return $this->_findSlot($dateTSSTOP$datetime);
  262.   }
  263.  
  264.   /**
  265.    * return the corresponding startdate/time to a time that is possibly within a slot
  266.    */
  267.   function findSlotFromWithin($date$datetime=0{
  268.     return $this->_findSlot($dateTSWITHIN$datetime);
  269.   }
  270.  
  271.   /**
  272.    * returns the slot that starts >= the date specified.
  273.    */
  274.   function findNextSlot($date$datetime=0{
  275.     return $this->_findSlot($dateTSNEXT$datetime);
  276.   }
  277.  
  278.  
  279.   /**
  280.    * return the corresponding slot number of the starting or stopping date-time $date
  281.    * returns -1 if no matching slot found.
  282.    * $match is TSSTART, TSSTOP, TSWITHIN, TSNEXT depending on what matching is queried.
  283.    * $return is TSSTART or TSSTOP depending on the required return value
  284.    *
  285.    * this function will return a slot from the previous day if the slot spans days and
  286.    * that is the appropriate slot.
  287.    *
  288.    * WARNING: There be dragons....
  289.    */
  290.   function _findSlot($date$match$datetime=0{
  291.     #$this->DEBUG=10;
  292.     //$this->log("TimeSlotRule::_findSlot:($date->dateTimeString(), $match, $datetime)", 10);
  293.     //preDump(debug_backtrace());
  294.     if ($datetime == 0{
  295.       $time $date->timePart();
  296.       $dow $date->dow();
  297.       $day clone($date);
  298.     else {
  299.       $time clone($date);
  300.       $dow $datetime->dow();
  301.       $day clone($datetime);
  302.       //echo "Found $time->timeString(), $dow, $day->dateTimeString()\n";
  303.     }
  304.     $slot=0;
  305.     $timecmp $match;
  306.     if ($match == TSWITHIN$timecmp TSSTOP;
  307.     if ($match == TSNEXT)   $timecmp TSSTART;
  308.     $startvar TSSTART;
  309.     $stopvar  TSSTOP;
  310.     #preDump($this->slots[$dow]);
  311.     $this->log("TimeSlotRule::_findSlot:(".$time->timeString().", ".$this->slots[$dow][0]->$startvar->ticks."$time->ticks$dow)"10);
  312.     if ($time->ticks $this->slots[$dow][0]->$startvar->ticks
  313.         || $match ==  TSSTOP && $time->ticks <= $this->slots[$dow][0]->$startvar->ticks{
  314.       $dow ($dow+6)%7;
  315.       $day->addDays(-1);
  316.       $time->addSecs(24*60*60);
  317.       $this->log("Stepped back a day to do the comparison");
  318.     }
  319. /*    $this->log("TimeSlotRule::_findSlot:($time->timeString(), ".$this->slots[$dow][0]->$stopvar->ticks.", $time->ticks, $dow)", 10);
  320.     if ($time->ticks > $this->slots[$dow][count($this->slots[$dow])-1-TSARRAYMIN]->$stopvar->ticks) {
  321.       $dow = ($dow+1)%7;
  322.       $day->addDays(1);
  323.       $time->addSecs(-24*60*60);
  324.       $this->log("Stepped forward a day to do the comparison");
  325.     }*/
  326.     $this->log("Asking for ($dow$slot$timecmp$match)"10);
  327.     //preDump($this->slots[$dow]);
  328.     while(
  329.             //print_r("Asking for ($dow, $slot, $match)<br />") &&
  330.             $slot count($this->slots[$dow])-TSARRAYMIN
  331.             && $time->ticks >= $this->slots[$dow][$slot]->$timecmp->ticks{
  332.       //echo $time->ticks .'#'. $this->slots[$dow][$slot]->$timecmp->ticks."\n";
  333.       //echo $slot .'#'.(count($this->slots[$dow])-TSARRAYMIN)."\n";
  334.       $slot++;
  335.     }
  336.     #echo count($this->slots[$dow])-TSARRAYMIN."/";
  337.     #echo $time->ticks .', '. $this->slots[$dow][$slot-1]->$timecmp->ticks."\n";
  338.     $this->log("Final ($dow$slot$match)",10);
  339.     if ($match == TSSTART || $match == TSSTOP{
  340.       $slot--;
  341.       $finalslot ($slot count($this->slots[$dow])-TSARRAYMIN
  342.                     && $slot >= 0
  343.                     && $time->ticks == $this->slots[$dow][$slot]->$timecmp->ticks)
  344.                     ? $slot TS_SLOT_NOT_FOUND ;
  345.     elseif ($match == TSWITHIN{
  346.       $a=TSSTART;
  347.       $b=TSSTOP;
  348.       $finalslot =  ($slot count($this->slots[$dow])-TSARRAYMIN
  349.                       && $time->ticks >= $this->slots[$dow][$slot]->$a->ticks
  350.                       && $time->ticks <  $this->slots[$dow][$slot]->$b->ticks)
  351.                       ? $slot TS_SLOT_NOT_FOUND ;
  352.       //echo "Found containing slot: $finalslot\n";
  353.     else //TSNEXT
  354.       if ($slot >= count($this->slots[$dow])-TSARRAYMIN{
  355.         //echo "Looking for next slot in overflow:\n";
  356.         do {
  357.           //echo "($dow, $day->dateTimeString(),".count($this->slots[$dow]).")\n";
  358.           $dow ($dow+17;
  359.           $day->addDays(1);
  360.           $finalslot=0;
  361.         while (count($this->slots[$dow]<= TSARRAYMIN);
  362.         //echo "($dow, $day->dateTimeString(),".TSARRAYMIN.")\n".count($this->slots[$dow]);
  363.       else {
  364.         $finalslot $slot;
  365.       }
  366.     }
  367.     $this->log("ReallyFinal ($dow$finalslot$match)",10);
  368.     if ($finalslot == TS_SLOT_NOT_FOUND{
  369.       //trigger_error('Could not find a match to this time slot.', E_USER_NOTICE);
  370.       return 0;
  371.       $finalslot==0;
  372.     }
  373.     $returnSlot clone($this->slots[$dow][$finalslot]);
  374.     //preDump($returnSlot);
  375.     #echo $day->datetimestring();
  376.     $returnSlot->setDate($day);
  377.     #preDump($returnSlot->dump());
  378.     return $returnSlot;
  379.   }
  380.  
  381.   function dump($html=1{
  382.     #preDump($this->slots);
  383.     #return;
  384.     $eol $html "<br />\n" "\n";
  385.     $s '';
  386.     $s .= "Initial Pattern = '"$this->picture ."'".$eol;
  387.     for ($day=0$day<=6$day++{
  388.       $s .= "Day Pattern[$day] = '"$this->slots[$day]['picture'."'".$eol;
  389.       //for ($j=0; $j<count($this->slots[$day]) && isset($this->slots[$day][$j]); $j++) {
  390.       foreach ($this->slots[$dayas $k => $v{
  391.         if (is_numeric($k)) {
  392.           $s .= $v->dump($html);
  393. //           $s .= "\t" . $v->tstart->timeString()
  394. //                 ." - ". $v->tstop->timeString() .$eol;
  395.         }
  396.       }
  397.     }
  398.     return $s;
  399.   }
  400.  
  401.   function log($logstring$prio=10{
  402.     if ($prio <= $this->DEBUG{
  403.       echo $logstring."<br />\n";
  404.     }
  405.   }
  406.  
  407.  
  408. //class TimeSlotRule
  409.  
  410.  
  411.  
  412. /**
  413. * Data object for an individual time slot defined by the time slot picture
  414. *
  415. @author    Stuart Prescott
  416. @copyright  Copyright Stuart Prescott
  417. @license    http://opensource.org/licenses/gpl-license.php GNU Public License
  418. @version    $Id$
  419. @package    Bumblebee
  420. @subpackage Bookings
  421. @todo //TODO:       more documentation
  422. */
  423. class RuleSlot {
  424.   var $tstart;
  425.   var $tstop;
  426.   var $tgran;
  427.   var $start;
  428.   var $stop;
  429.   var $startStr;
  430.   var $stopStr;
  431.   var $numslotsFollowing = 0;
  432.   var $numslotsInGroup = 1;
  433.   var $isFreeForm = 0;
  434.   var $isAvailable = 1;
  435.   var $picture = '';
  436.   var $nextSlot;
  437.   var $comment = '';
  438.   var $discount = 0;
  439.  
  440.   function RuleSlot($picture$startStr$stopStr$tstart$tstop$tgran=NULL{
  441.     $this->picture = $picture;
  442.     $this->startStr = $startStr;
  443.     $this->stopStr = $stopStr;
  444.     $this->tstart = $tstart;
  445.     $this->tstop = $tstop;
  446.     $this->tgran = type_is_a($tgran'SimpleTime'$tgran new SimpleTime(0);
  447.   }
  448.  
  449.   function setDate($date{
  450.     $this->start = clone($date);
  451.     $this->start->setTime($this->tstart);
  452.     $this->stop = clone($date);
  453.     $this->stop->setTime($this->tstop);
  454.   }
  455.  
  456.   function dump($html=1{
  457.     $eol $html "<br />\n" "\n";
  458.     return 'Slot:'."\t"
  459.           .$this->tstart->timeString().' - '
  460.           .$this->tstop->timeString().' : '
  461.           .($this->isAvailable'Available' 'Not available')
  462.           .($this->isFreeForm ? ' Freeform' '')
  463.           .($this->comment !== '' && $this->comment !== NULL ' Comment = '.$this->comment : '')
  464.           .$eol
  465.     ;
  466.   }
  467.  
  468.   function allSlotDurations($offset=0{
  469.     $duration array();
  470.     $cslot $this;
  471.     $cdur $this->tgran;
  472.     for ($i=0$i<=$this->numslotsFollowing$i++{
  473.       #echo $i.': length='.$cslot->tgran->timeString().', sum='.$cdur->timeString()."<br />\n";
  474.       $dur clone($cdur);
  475.       $dur->addSecs($offset);
  476.       $duration[clone($cdur);
  477.       $cdur->addTime($cslot->tgran);
  478.       $cslot $cslot->nextSlot;
  479.     }
  480.     return $duration;
  481.   }
  482.  
  483.   function allSlotEnds({
  484.     $ends array();
  485.     $cslot $this;
  486.     for ($i=0$i<=$this->numslotsFollowing$i++{
  487.       $cslot->setDate($this->start);
  488.       $ends[clone($cslot->stop);
  489.       $cslot $cslot->nextSlot;
  490.     }
  491.     return $ends;
  492.   }
  493.  

Documentation generated on Tue, 06 Mar 2007 10:02:04 +0000 by phpDocumentor 1.3.0