| Server IP : 188.114.96.3 / Your IP : 216.73.216.143 Web Server : Apache System : Linux sv058.home.net.pl 6.8.2 husak_dirty_pages_fix #1 SMP PREEMPT_DYNAMIC Tue Apr 2 22:06:51 CEST 2024 x86_64 User : ( 3194769) PHP Version : 8.5.12 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /polana/includes/ |
Upload File : |
<?php
declare(strict_types=1);
/**
* Polana V20: parse the canonical, human-readable `opening_hours` setting.
* Strict/fail-closed: an ambiguous line never makes a day bookable.
*/
function pv20_source_present(): bool {
return trim(setting('opening_hours', '')) !== '';
}
function pv20_day_number(string $token): ?int {
$k = strtolower(strtr(trim($token), ['ą'=>'a','ć'=>'c','ę'=>'e','ł'=>'l','ń'=>'n','ó'=>'o','ś'=>'s','ż'=>'z','ź'=>'z','Ą'=>'a','Ć'=>'c','Ę'=>'e','Ł'=>'l','Ń'=>'n','Ó'=>'o','Ś'=>'s','Ż'=>'z','Ź'=>'z']));
$map = [
'pon'=>1,'pn'=>1,'poniedzialek'=>1,'monday'=>1,'mon'=>1,
'wt'=>2,'wto'=>2,'wtorek'=>2,'tuesday'=>2,'tue'=>2,'tues'=>2,
'sr'=>3,'sro'=>3,'sroda'=>3,'wednesday'=>3,'wed'=>3,
'czw'=>4,'cz'=>4,'czwartek'=>4,'thursday'=>4,'thu'=>4,'thur'=>4,
'pt'=>5,'piatek'=>5,'friday'=>5,'fri'=>5,
'sob'=>6,'sb'=>6,'sobota'=>6,'saturday'=>6,'sat'=>6,
'nd'=>7,'niedz'=>7,'niedziela'=>7,'sunday'=>7,'sun'=>7,
];
return $map[$k] ?? null;
}
function pv20_day_words_regex(): string {
return '(?:poniedzia[łl]ek|pon|pn|wtorek|wto|wt|[śs]roda|[śs]ro|[śs]r|czwartek|czw|cz|pi[aą]tek|pt|sobota|sob|sb|niedziela|niedz|nd|monday|mon|tuesday|tues|tue|wednesday|wed|thursday|thur|thu|friday|fri|saturday|sat|sunday|sun)';
}
function pv20_parse_opening_hours(string $source): array {
$days = array_fill_keys(array_map('strval', range(1, 7)), null);
$errors=[];
$matched=0;
$decoded=html_entity_decode($source, ENT_QUOTES | ENT_HTML5, 'UTF-8');
$decoded=(string)preg_replace('~<\s*br\s*/?\s*>~iu', "\n", $decoded);
$decoded=(string)preg_replace('~</(?:p|div|li)>~iu', "\n", $decoded);
$decoded=strip_tags($decoded);
$decoded=str_replace(["\r\n","\r","\xC2\xA0"], ["\n","\n"," "], $decoded);
$word=pv20_day_words_regex();
// Support comma-separated entries like "Pt 13-22, Sob 13-22".
$decoded=(string)preg_replace('/(?<=\d)\s*,\s*(?='.$word.'\b)/iu', "\n", $decoded);
$decoded=(string)preg_replace('/\b(nieczynne|zamkni[eę]te|closed)\s*,\s*(?='.$word.'\b)/iu', "$1\n", $decoded);
$lines=preg_split('/[\n;]+/u',$decoded) ?: [];
foreach ($lines as $lineNo=>$rawLine) {
$line=trim($rawLine, " \t\x0B\xC2\xA0•*- ");
if ($line==='') continue;
$clean=strtolower($line);
$isHeading=(bool)preg_match('/^(?:godziny\s+(?:otwarcia|pracy)|opening\s+hours|restauracja)\s*:?$/iu',$clean);
if ($isHeading) continue;
preg_match_all('/(?<!\p{L})'.$word.'(?!\p{L})/iu',$line,$hits,PREG_OFFSET_CAPTURE);
if (empty($hits[0])) {
$errors[]='Linia '.($lineNo+1).': brak rozpoznanego dnia tygodnia.';
continue;
}
$numbers=[];
foreach ($hits[0] as $match) {
$n=pv20_day_number($match[0]);
if ($n!==null) $numbers[]=$n;
}
if (!$numbers) { $errors[]='Linia '.($lineNo+1).': nieznany dzień tygodnia.';continue; }
$labelEnd=$hits[0][count($hits[0])-1][1]+strlen($hits[0][count($hits[0])-1][0]);
$dayPart=substr($line,0,$labelEnd);
$rest=trim(substr($line,$labelEnd)," \t:—–-|");
// e.g. "Pon–Pt 12:00–21:00": both days define an inclusive range.
if (count($numbers)===2 && preg_match('/[-–—]|\bdo\b|\bto\b/iu',$dayPart)) {
$first=$numbers[0];$last=$numbers[1];
if ($first<=$last) $numbers=range($first,$last);
else { $errors[]='Linia '.($lineNo+1).': odwrócony zakres dni.';continue; }
}
$numbers=array_values(array_unique($numbers));
if (preg_match('/\b(?:nieczynne|nieczynny|zamkni[eę]te|zamkni[eę]ty|closed|off)\b/iu',$rest)) {
$window=null;
} else {
// Accept 15-21, 15:00–21:00, 15.00 do 21.00, 15:00 - 22:00.
preg_match_all('/(?<![\d:])(?:[01]?\d|2[0-4])(?:[:.]\d{2})?(?![\d:])/u',$rest,$matches);
if (count($matches[0])!==2) { $errors[]='Linia '.($lineNo+1).': oczekiwane godziny od–do (np. 15:00–21:00).';continue; }
$values=[];
foreach ($matches[0] as $idx=>$raw) {
$v=str_replace('.',':',$raw);
if (!str_contains($v,':')) $v.=':00';
[$h,$m]=array_map('intval',explode(':',$v));
if ($h>24||$m>59||($h===24 && ($m!==0 || $idx===0))) {
$values=[];break;
}
$values[]=sprintf('%02d:%02d',$h,$m);
}
if (count($values)!==2 || ((int)substr($values[0],0,2)*60+(int)substr($values[0],3)) >= ((int)substr($values[1],0,2)*60+(int)substr($values[1],3))) {
$errors[]='Linia '.($lineNo+1).': nieprawidłowy zakres godzin.';continue;
}
$window=['open'=>$values[0],'close'=>$values[1]];
}
foreach ($numbers as $dayNo) {
$key=(string)$dayNo;
if (isset($seen[$key]) && $days[$key]!==$window) {
$errors[]='Linia '.($lineNo+1).': sprzeczne godziny dla dnia '.$dayNo.'.';
}
$seen[$key]=true;
$days[$key]=$window;
$matched++;
}
}
if (!$matched) $errors[]='Nie rozpoznano żadnego dnia ani godzin otwarcia.';
// A parsing error makes the entire schedule unavailable until corrected.
if ($errors) $days=array_fill_keys(array_map('strval',range(1,7)),null);
return ['days'=>$days,'errors'=>$errors,'matched'=>$matched];
}
function pv20_opening_status():array {
$source=setting('opening_hours','');
if(trim($source)==='') return ['active'=>false,'days'=>[],'errors'=>[],'matched'=>0];
return ['active'=>true]+pv20_parse_opening_hours($source);
}
function pv20_apply_opening_hours(array $cfg):array {
$status=pv20_opening_status();
if (!$status['active']) return $cfg; // old manual schedule still works
$cfg['days']=$status['days'];
if ($status['errors']) $cfg['_opening_hours_error']=true;
return $cfg;
}