format('U'); unset($time); return $stamp; } public static function stamp2iso($stamp) { return date('Y-m-d\TH:i:s',$stamp); } } class SortPartCacheObject { public static $TYPE_UNKNOWN = 0; public static $TYPE_START = 1; public static $TYPE_STOP = 2; public static $TYPE_DURATION_SEC = 3; public static $TYPE_DURATION_MIN = 4; public static $TYPE_DURATION_HOUR = 5; public static $DIRECTION_UNKNOWN = 0; public static $DIRECTION_ASC = 1; public static $DIRECTION_DES = 2; protected $type; protected $dir; function __construct() { $this->type = self::$TYPE_UNKNOWN; $this->dir = self::$DIRECTION_UNKNOWN; } public function getType() { return $this->type; } public function getDir() { return $this->dir; } public function isSame($part) { return (($this->type == $part->getType()) && ($this->dir == $part->getDir())); } public function compare($interval_a, $interval_b) { switch ($this->type) { case self::$TYPE_START : { switch ($this->dir) { case self::$DIRECTION_ASC : return ($interval_b->getStartToStamp() - $interval_a->getStartToStamp()); default : return ($interval_a->getStartToStamp() - $interval_b->getStartToStamp()); } } break; case self::$TYPE_STOP : { switch ($this->dir) { case self::$DIRECTION_ASC : return ($interval_b->getStopToStamp() - $interval_a->getStopToStamp()); default : return ($interval_a->getStopToStamp() - $interval_b->getStopToStamp()); } } break; case self::$TYPE_DURATION_SEC : case self::$TYPE_DURATION_MIN : case self::$DIRECTION_DES : { switch ($this->dir) { case self::$DIRECTION_ASC : return ($interval_b->getDuration() - $interval_a->getDuration()); default : return ($interval_a->getDuration() - $interval_b->getDuration()); } } break; default : return 0; } return 0; } public function loadFromObject($part_obj) { switch ($part_obj->property) { case 'start' : $this->type = self::$TYPE_START; break; case 'stop' : $this->type = self::$TYPE_STOP; break; case 'durationMin' : $this->type = self::$TYPE_DURATION_MIN; break; case 'durationHour' : $this->type = self::$TYPE_DURATION_HOUR; break; case 'durationSec' : $this->type = self::$TYPE_DURATION_SEC; break; default: $this->type = self::$TYPE_UNKNOWN; } switch ($part_obj->direction) { case 'ASC' : $this->dir = self::$DIRECTION_ASC; break; case 'DESC' : $this->dir = self::$DIRECTION_DES; break; default: $this->dir = self::$DIRECTION_UNKNOWN; } } public function writeBin($handle) { fwrite($handle,pack('L2',$this->type,$this->dir)); } public function loadBin($handle) { $res = unpack('L2data',fread($handle,4*2)); $this->type = $res['data1']; $this->dir = $res['data2']; } public function dump() { echo " => SortPartCacheObject : type = "; switch ($this->type) { case self::$TYPE_START : echo "start"; break; case self::$TYPE_STOP : echo "stop"; break; case self::$TYPE_DURATION_SEC : echo "duration seconde"; break; case self::$TYPE_DURATION_MIN : echo "duration minute"; break; case self::$TYPE_DURATION_HOUR : echo "duration hour"; break; default: echo "unknown"; } echo ", direction = "; switch ($this->dir) { case self::$DIRECTION_ASC : echo "ASC"; break; case self::$DIRECTION_DES : echo "DESC"; break; default: echo "unknown"; } echo PHP_EOL; } } class SortCacheObject { protected $parts = array(); function __construct() { } public function getParts() { return $this->parts; } public function reset() { $this->parts = array(); } public function isEmpty() { return (count($this->parts) == 0); } public function loadFromObject($sort_obj) { $this->reset(); foreach ($sort_obj as $sort_part) { $part = new SortPartCacheObject(); $part->loadFromObject($sort_part); array_push($this->parts, $part); } } public function isSameFromObject($sort_obj) { $sort = new SortCacheObject(); $sort->loadFromObject($sort_obj); return $this->isSame($sort); } public function isSame($sort) { if (count($this->parts) != count($sort->getParts())) return false; $identique = true; for ($i = 0; $i < count($this->parts); ++$i) { if (!$this->parts[$i]->isSame($sort->getParts()[$i])) { return false; } } return true; } public function apply($intervals) { $sorted_indexes = array(); global $global_parts, $global_intervals; $global_parts = $this->parts; $global_intervals = $intervals; foreach ($intervals as $interval) array_push($sorted_indexes, $interval->getIndex()); if (count($global_parts) == 0) return $sorted_indexes; usort($sorted_indexes, function ($index_a, $index_b) { global $global_parts, $global_intervals; foreach ($global_parts as $part) { $res = $part->compare($global_intervals[$index_a], $global_intervals[$index_b]); if ($res != 0) return $res; } return $index_a-$index_b; }); return $sorted_indexes; } public function writeBin($handle) { fwrite($handle,pack('L',count($this->parts))); foreach ($this->parts as $part) $part->writeBin($handle); } public function loadBin($handle) { $this->reset(); $res = unpack('Lcount',fread($handle,4)); for ($i = 0; $i < $res['count']; ++$i) { $part = new SortPartCacheObject(); $part->loadBin($handle); array_push($this->parts, $part); } } public function dump() { echo " => SortCacheObject : number of parts = ".count($this->parts).PHP_EOL; foreach ($this->parts as $part) $part->dump(); } } class FilterPartCacheObject { public static $TYPE_UNKNOWN = 0; public static $TYPE_START = 1; public static $TYPE_STOP = 2; public static $TYPE_DURATION_SEC = 3; public static $TYPE_DURATION_MIN = 4; public static $TYPE_DURATION_HOUR = 5; public static $OPERATION_UNKNOWN = 0; public static $OPERATION_LT = 1; public static $OPERATION_GT = 2; public static $OPERATION_EQ = 3; protected $type; protected $op; protected $value; function __construct() { $this->type = self::$TYPE_UNKNOWN; $this->op = self::$OPERATION_UNKNOWN; $this->value = 0.; } public function getType() { return $this->type; } public function getOp() { return $this->op; } public function getValue() { return $this->value; } public function isSame($part) { return (($this->type == $part->getType()) && ($this->op == $part->getOp()) && ($this->value == $part->getValue())); } public function toFiltered($interval) { switch ($this->type) { case self::$TYPE_START : { switch ($this->op) { case self::$OPERATION_LT : return ($interval->getStartToStamp() < $this->value); case self::$OPERATION_GT : return ($interval->getStartToStamp() > $this->value); case self::$OPERATION_EQ : return (!(($interval->getStartToStamp() >= $this->value) && ($interval->getStartToStamp() <= $this->value+86400))); default : return false; } } break; case self::$TYPE_STOP : { switch ($this->op) { case self::$OPERATION_LT : return ($interval->getStopToStamp() < $this->value); case self::$OPERATION_GT : return ($interval->getStopToStamp() > $this->value); case self::$OPERATION_EQ : return (!(($interval->getStopToStamp() >= $this->value) && ($interval->getStopToStamp() <= $this->value+86400))); default : return false; } } break; case self::$TYPE_DURATION_SEC : case self::$TYPE_DURATION_MIN : case self::$TYPE_DURATION_HOUR : { $value = $this->value; if ($this->type == self::$TYPE_DURATION_MIN) $value *= 60; else if ($this->type == self::$TYPE_DURATION_HOUR) $value *= 3600; switch ($this->op) { case self::$OPERATION_LT : return ($interval->getDuration() < $value); case self::$OPERATION_GT : return ($interval->getDuration() > $value); case self::$OPERATION_EQ : return ($interval->getDuration() != $value); default : return false; } } break; default: return false; } } public function loadFromObject($part_obj) { $this->value = 0.; switch ($part_obj->field) { case 'start' : $this->value = CacheTools::iso2stamp($part_obj->value); $this->type = self::$TYPE_START; break; case 'stop' : $this->value = CacheTools::iso2stamp($part_obj->value); $this->type = self::$TYPE_STOP; break; case 'durationMin' : $this->value = $part_obj->value; $this->type = self::$TYPE_DURATION_MIN; break; case 'durationHour' : $this->value = $part_obj->value; $this->type = self::$TYPE_DURATION_HOUR; break; case 'durationSec' : $this->value = $part_obj->value; $this->type = self::$TYPE_DURATION_SEC; break; default: $this->value = 0.; $this->type = self::$TYPE_UNKNOWN; } switch ($part_obj->comparison) { case 'lt' : $this->op = self::$OPERATION_LT; break; case 'gt' : $this->op = self::$OPERATION_GT; break; case 'eq' : $this->op = self::$OPERATION_EQ; break; default: $this->op = self::$OPERATION_UNKNOWN; } } public function writeBin($handle) { fwrite($handle,pack('L2',$this->type,$this->op)); fwrite($handle,pack('f',$this->value)); } public function loadBin($handle) { $res = unpack('L2data',fread($handle,4*2)); $this->type = $res['data1']; $this->op = $res['data2']; $res = unpack('fvalue',fread($handle,4)); $this->value = $res['value']; } public function dump() { echo " => FilterPartCacheObject : type = "; switch ($this->type) { case self::$TYPE_START : echo "start"; break; case self::$TYPE_STOP : echo "stop"; break; case self::$TYPE_DURATION_SEC : echo "duration seconde"; break; case self::$TYPE_DURATION_MIN : echo "duration minute"; break; case self::$TYPE_DURATION_HOUR : echo "duration hour"; break; default: echo "unknown"; } echo ", operation = "; switch ($this->op) { case self::$OPERATION_LT : echo "lt"; break; case self::$OPERATION_GT : echo "gt"; break; case self::$OPERATION_EQ : echo "eq"; break; default: echo "unknown"; } echo ", value = ".$this->value.PHP_EOL; } } class FilterCacheObject { protected $parts = array(); function __construct() { } public function getParts() { return $this->parts; } public function reset() { $this->parts = array(); } public function isEmpty() { return (count($this->parts) == 0); } public function loadFromJSON($filter_json) { $this->reset(); $filter_obj = json_decode($filter_json); foreach ($filter_obj as $filter_part) { $part = new FilterPartCacheObject(); $part->loadFromObject($filter_part); array_push($this->parts, $part); } } public function isSame($filter) { if (count($this->parts) != count($filter->getParts())) return false; $identique = true; for ($i = 0; $i < count($this->parts); ++$i) { if (!$this->parts[$i]->isSame($filter->getParts()[$i])) { return false; } } return true; } public function isSameFromJSON($filter_json) { $filter = new FilterCacheObject(); $filter->loadFromJSON($filter_json); return $this->isSame($filter); } public function toFiltered($interval) { foreach ($this->parts as $part) { if ($part->toFiltered($interval)) return true; } return false; } public function writeBin($handle) { fwrite($handle,pack('L',count($this->parts))); foreach ($this->parts as $part) $part->writeBin($handle); } public function loadBin($handle) { $this->reset(); $res = unpack('Lcount',fread($handle,4)); for ($i = 0; $i < $res['count']; ++$i) { $part = new FilterPartCacheObject(); $part->loadBin($handle); array_push($this->parts, $part); } } public function dump() { echo " => FilterCacheObject : number of parts = ".count($this->parts).PHP_EOL; foreach ($this->parts as $part) $part->dump(); } } class TimeTableCacheMgr { protected static $cache_file = "cacheTT"; protected $ttMgr = null; protected $cache = null; function __construct() { $this->ttMgr = new TimeTableMgr(); } public function initTTCache() { //Create new cache $this->cache = new TimeTableCacheObject(); //Save cache file return array('success' => $this->saveToFile(), 'token' => $this->cache->getToken(), 'status' => $this->cache->getStatus()); } public function initFromTT($id, $typeTT) { //Create new cache $this->cache = new TimeTableCacheObject(); //Load intervals from TT file and add to cache $intervals_res = $this->ttMgr->loadIntervalsFromTT($id,$typeTT); if (!$intervals_res['success']) return $intervals_res; foreach ($intervals_res['intervals'] as $interval) { //Add interval $this->cache->addInterval($interval['start'], $interval['stop']); } unset($intervals_res); //Update cache $this->cache->updateIndexes(); //Save cache file return array('success' => $this->saveToFile(), 'token' => $this->cache->getToken(), 'status' => $this->cache->getStatus()); } public function initFromTmpObject($folderId, $name) { //Create new cache $this->cache = new TimeTableCacheObject(); //Load intervals from TmpObject file (DD_Search output) $intervals_res = $this->ttMgr->getTmpObject($folderId, $name); if (!isset($intervals_res)) return array('success' => false, 'message' => 'Cannot get Tmp Object'); if (array_key_exists('intervals', $intervals_res)) { foreach ($intervals_res['intervals'] as $interval) { //Add interval $this->cache->addInterval($interval['start'], $interval['stop']); } } $this->cache->setIsModified(true); unset($intervals_res); //Update cache $this->cache->updateIndexes(); //Save cache file return array('success' => $this->saveToFile(), 'token' => $this->cache->getToken(), 'status' => $this->cache->getStatus()); } public function initFromUploadedFile($name, $format) { //Create new cache $this->cache = new TimeTableCacheObject(); //Load intervals from uploaded file $intervals_res = $this->ttMgr->getUploadedObject($name, $format); if (!isset($intervals_res)) return array('success' => false, 'message' => 'Cannot get Tmp Object'); if (array_key_exists('intervals', $intervals_res)) { foreach ($intervals_res['intervals'] as $interval) { //Add interval $this->cache->addInterval($interval['start'], $interval['stop']); } } $this->cache->setIsModified(true); unset($intervals_res); //Update cache $this->cache->updateIndexes(); //Save cache file return array('success' => $this->saveToFile(), 'token' => $this->cache->getToken(), 'status' => $this->cache->getStatus()); } public function saveInTT($id, $action, $token) { if (!$this->loadFromFile()) return array('success' => false, 'message' => 'Cannot load cache file'); if ($token != $this->cache->getToken()) return array('success' => false, 'message' => 'Cache token check error'); $this->cache->updateIndexes(); $this->saveToFile(); $intervals = $this->cache->getIntervalsArray(NULL,NULL,true); $this->cache->reset(); $res_intervals = array(); foreach ($intervals as $interval) { array_push( $res_intervals, (object)array( 'start' => $interval['start'], 'stop' => $interval['stop'] ) ); } unset($intervals); return $this->ttMgr->saveIntervals($id, $res_intervals, $action); } public function getIntervals($start,$limit,$sort_obj,$filter_json) { if (!$this->loadFromFile()) return array('success' => false, 'message' => 'Cannot load cache file'); $needToUpdate = false; if (isset($filter_json)) { if (!$this->cache->getFilter()->isSameFromJSON($filter_json)) { $needToUpdate = true; $this->cache->getFilter()->loadFromJSON($filter_json); } } else { if (!$this->cache->getFilter()->isEmpty()) { $needToUpdate = true; $this->cache->getFilter()->reset(); } } if (isset($sort_obj)) { if (!$this->cache->getSort()->isSameFromObject($sort_obj)) { $needToUpdate = true; $this->cache->getSort()->loadFromObject($sort_obj); } } else { if (!$this->cache->getSort()->isEmpty()) { $needToUpdate = true; $this->cache->getSort()->reset(); } } if ($needToUpdate) { $this->cache->updateIndexes(); $this->saveToFile(); } return array( 'token' => $this->cache->getToken(), 'totalCount' => $this->cache->getLength(), 'intervals' => $this->cache->getIntervalsArray($start, $limit), 'start' => isset($start) ? $start : 0, 'limit' => isset($limit) ? $limit : 0, 'status' => $this->cache->getStatus(), 'success' => true ); } public function addInterval($index, $start, $stop) { if (!$this->loadFromFile()) return array('success' => false, 'message' => 'Cannot load cache file'); if (!isset($index)) $index = 0; if (!isset($start)) $start = date('Y-m-d\TH:i:s'); if (!isset($stop)) $stop = date('Y-m-d\TH:i:s'); $this->cache->addInterval($start, $stop, true, $index); //$this->cache->updateIndexes(); $this->saveToFile(); return array('success' => true, 'index' => $index, 'status' => $this->cache->getStatus()); } public function removeIntervalFromId($id) { if (!$this->loadFromFile()) return array('success' => false, 'message' => 'Cannot load cache file'); $this->cache->removeIntervalFromId($id); $this->cache->updateIndexes(); $this->saveToFile(); return array('success' => true, 'status' => $this->cache->getStatus()); } public function modifyIntervalFromId($id, $start, $stop) { if (!$this->loadFromFile()) return array('success' => false, 'message' => 'Cannot load cache file'); $this->cache->modifyIntervalFromId($id, $start, $stop); $this->saveToFile(); return array('success' => true, 'status' => $this->cache->getStatus()); } public function operationIntervals($extendTime, $shiftTime) { if (!$this->loadFromFile()) return array('success' => false, 'message' => 'Cannot load cache file'); $this->cache->operationIntervals($extendTime, $shiftTime); $this->saveToFile(); return array('success' => true, 'status' => $this->cache->getStatus()); } public function mergeIntervals() { if (!$this->loadFromFile()) return array('success' => false, 'message' => 'Cannot load cache file'); $this->cache->mergeIntervals(); $this->saveToFile(); return array('success' => true, 'status' => $this->cache->getStatus()); } public function getStatistics() { if (!$this->loadFromFile()) return array('success' => false, 'message' => 'Cannot load cache file'); return array('success' => true, "result" => $this->cache->getStatistics(), 'status' => $this->cache->getStatus()); } public function dump() { if (!$this->loadFromFile()) { echo "ERROR to load cache file : ".$this->getCacheFilePath().PHP_EOL; return; } $this->cache->dump(); } protected function getCacheFilePath() { return USERTTDIR.(self::$cache_file); } protected function saveToFile() { if (!isset($this->cache)) return false; $handle = fopen($this->getCacheFilePath(), 'wb'); $result = false; if (flock($handle, LOCK_EX)) { $this->cache->writeBin($handle); flock( $handle, LOCK_UN ); $result = true; } fclose($handle); return $result; } protected function loadFromFile() { if (!file_exists($this->getCacheFilePath())) return false; $this->cache = new TimeTableCacheObject(); $handle = fopen($this->getCacheFilePath(), 'rb'); $result = false; if (flock($handle, LOCK_SH)) { $this->cache->loadBin($handle); flock( $handle, LOCK_UN ); $result = true; } fclose($handle); return $result; } } ?>