1*2cd6cc0aSAndreas Gohr<?php 2*2cd6cc0aSAndreas Gohr 3*2cd6cc0aSAndreas Gohrnamespace dokuwiki\File; 4*2cd6cc0aSAndreas Gohr 5*2cd6cc0aSAndreas Gohr/** 6*2cd6cc0aSAndreas Gohr * Resolving relative IDs to absolute ones 7*2cd6cc0aSAndreas Gohr */ 8*2cd6cc0aSAndreas Gohrabstract class Resolver 9*2cd6cc0aSAndreas Gohr{ 10*2cd6cc0aSAndreas Gohr 11*2cd6cc0aSAndreas Gohr /** @var string context page ID */ 12*2cd6cc0aSAndreas Gohr protected $contextID; 13*2cd6cc0aSAndreas Gohr /** @var string namespace of context page ID */ 14*2cd6cc0aSAndreas Gohr protected $contextNS; 15*2cd6cc0aSAndreas Gohr 16*2cd6cc0aSAndreas Gohr /** 17*2cd6cc0aSAndreas Gohr * @param string $contextID the current pageID that's the context to resolve relative IDs to 18*2cd6cc0aSAndreas Gohr */ 19*2cd6cc0aSAndreas Gohr public function __construct($contextID) 20*2cd6cc0aSAndreas Gohr { 21*2cd6cc0aSAndreas Gohr $this->contextID = $contextID; 22*2cd6cc0aSAndreas Gohr $this->contextNS = (string)getNS($contextID); 23*2cd6cc0aSAndreas Gohr } 24*2cd6cc0aSAndreas Gohr 25*2cd6cc0aSAndreas Gohr /** 26*2cd6cc0aSAndreas Gohr * Resolves a given ID to be absolute 27*2cd6cc0aSAndreas Gohr * 28*2cd6cc0aSAndreas Gohr * @param string $id The ID to resolve 29*2cd6cc0aSAndreas Gohr * @param string|int|false $rev The revision time to use when resolving 30*2cd6cc0aSAndreas Gohr * @param bool $isDateAt Is the given revision only a datetime hint not an exact revision? 31*2cd6cc0aSAndreas Gohr * @return string 32*2cd6cc0aSAndreas Gohr */ 33*2cd6cc0aSAndreas Gohr public function resolveId($id, $rev = '', $isDateAt = false) 34*2cd6cc0aSAndreas Gohr { 35*2cd6cc0aSAndreas Gohr global $conf; 36*2cd6cc0aSAndreas Gohr 37*2cd6cc0aSAndreas Gohr // some pre cleaning for useslash: 38*2cd6cc0aSAndreas Gohr if ($conf['useslash']) $id = str_replace('/', ':', $id); 39*2cd6cc0aSAndreas Gohr // on some systems, semicolons might be used instead of colons: 40*2cd6cc0aSAndreas Gohr $id = str_replace(';', ':', $id); 41*2cd6cc0aSAndreas Gohr 42*2cd6cc0aSAndreas Gohr $id = $this->resolvePrefix($id); 43*2cd6cc0aSAndreas Gohr $id = $this->resolveRelatives($id); 44*2cd6cc0aSAndreas Gohr 45*2cd6cc0aSAndreas Gohr return $id; 46*2cd6cc0aSAndreas Gohr } 47*2cd6cc0aSAndreas Gohr 48*2cd6cc0aSAndreas Gohr /** 49*2cd6cc0aSAndreas Gohr * Handle IDs starting with . or ~ and prepend the proper prefix 50*2cd6cc0aSAndreas Gohr * 51*2cd6cc0aSAndreas Gohr * @param string $id 52*2cd6cc0aSAndreas Gohr * @return string 53*2cd6cc0aSAndreas Gohr */ 54*2cd6cc0aSAndreas Gohr protected function resolvePrefix($id) 55*2cd6cc0aSAndreas Gohr { 56*2cd6cc0aSAndreas Gohr // relative to current page (makes the current page a start page) 57*2cd6cc0aSAndreas Gohr if ($id[0] === '~') { 58*2cd6cc0aSAndreas Gohr $id = $this->contextID . ':' . substr($id, 1); 59*2cd6cc0aSAndreas Gohr } 60*2cd6cc0aSAndreas Gohr 61*2cd6cc0aSAndreas Gohr // relative to current namespace 62*2cd6cc0aSAndreas Gohr if ($id[0] === '.') { 63*2cd6cc0aSAndreas Gohr // normalize initial dots without a colon 64*2cd6cc0aSAndreas Gohr $id = preg_replace('/^((\.+:)*)(\.+)(?=[^:\.])/', '\1\3:', $id); 65*2cd6cc0aSAndreas Gohr $id = $this->contextNS . ':' . $id; 66*2cd6cc0aSAndreas Gohr } 67*2cd6cc0aSAndreas Gohr 68*2cd6cc0aSAndreas Gohr // auto-relative, because there is a context namespace but no namespace in the ID 69*2cd6cc0aSAndreas Gohr if ($this->contextID !== '' && strpos($id, ':') === false) { 70*2cd6cc0aSAndreas Gohr $id = $this->contextNS . ':' . $id; 71*2cd6cc0aSAndreas Gohr } 72*2cd6cc0aSAndreas Gohr 73*2cd6cc0aSAndreas Gohr return $id; 74*2cd6cc0aSAndreas Gohr } 75*2cd6cc0aSAndreas Gohr 76*2cd6cc0aSAndreas Gohr /** 77*2cd6cc0aSAndreas Gohr * Handle . and .. within IDs 78*2cd6cc0aSAndreas Gohr * 79*2cd6cc0aSAndreas Gohr * @param string $id 80*2cd6cc0aSAndreas Gohr * @return string 81*2cd6cc0aSAndreas Gohr */ 82*2cd6cc0aSAndreas Gohr protected function resolveRelatives($id) 83*2cd6cc0aSAndreas Gohr { 84*2cd6cc0aSAndreas Gohr if ($id === '') return ''; 85*2cd6cc0aSAndreas Gohr $trail = ($id[-1] === ':') ? ':' : ''; // keep trailing colon 86*2cd6cc0aSAndreas Gohr 87*2cd6cc0aSAndreas Gohr $result = []; 88*2cd6cc0aSAndreas Gohr $parts = explode(':', $id); 89*2cd6cc0aSAndreas Gohr 90*2cd6cc0aSAndreas Gohr foreach ($parts as $dir) { 91*2cd6cc0aSAndreas Gohr if ($dir === '.') continue; 92*2cd6cc0aSAndreas Gohr if ($dir === '') continue; 93*2cd6cc0aSAndreas Gohr if ($dir === '..') { 94*2cd6cc0aSAndreas Gohr array_pop($result); 95*2cd6cc0aSAndreas Gohr continue; 96*2cd6cc0aSAndreas Gohr } 97*2cd6cc0aSAndreas Gohr array_push($result, $dir); 98*2cd6cc0aSAndreas Gohr } 99*2cd6cc0aSAndreas Gohr 100*2cd6cc0aSAndreas Gohr $id = implode(':', $result); 101*2cd6cc0aSAndreas Gohr $id .= $trail; 102*2cd6cc0aSAndreas Gohr 103*2cd6cc0aSAndreas Gohr return $id; 104*2cd6cc0aSAndreas Gohr } 105*2cd6cc0aSAndreas Gohr 106*2cd6cc0aSAndreas Gohr} 107