12cd6cc0aSAndreas Gohr<?php 22cd6cc0aSAndreas Gohr 32cd6cc0aSAndreas Gohrnamespace dokuwiki\File; 42cd6cc0aSAndreas Gohr 52cd6cc0aSAndreas Gohr/** 62cd6cc0aSAndreas Gohr * Resolving relative IDs to absolute ones 72cd6cc0aSAndreas Gohr */ 82cd6cc0aSAndreas Gohrabstract class Resolver 92cd6cc0aSAndreas Gohr{ 102cd6cc0aSAndreas Gohr /** @var string context page ID */ 112cd6cc0aSAndreas Gohr protected $contextID; 122cd6cc0aSAndreas Gohr /** @var string namespace of context page ID */ 132cd6cc0aSAndreas Gohr protected $contextNS; 142cd6cc0aSAndreas Gohr 152cd6cc0aSAndreas Gohr /** 162cd6cc0aSAndreas Gohr * @param string $contextID the current pageID that's the context to resolve relative IDs to 172cd6cc0aSAndreas Gohr */ 182cd6cc0aSAndreas Gohr public function __construct($contextID) 192cd6cc0aSAndreas Gohr { 202cd6cc0aSAndreas Gohr $this->contextID = $contextID; 212cd6cc0aSAndreas Gohr $this->contextNS = (string)getNS($contextID); 222cd6cc0aSAndreas Gohr } 232cd6cc0aSAndreas Gohr 242cd6cc0aSAndreas Gohr /** 252cd6cc0aSAndreas Gohr * Resolves a given ID to be absolute 262cd6cc0aSAndreas Gohr * 272cd6cc0aSAndreas Gohr * @param string $id The ID to resolve 282cd6cc0aSAndreas Gohr * @param string|int|false $rev The revision time to use when resolving 292cd6cc0aSAndreas Gohr * @param bool $isDateAt Is the given revision only a datetime hint not an exact revision? 302cd6cc0aSAndreas Gohr * @return string 312cd6cc0aSAndreas Gohr */ 322cd6cc0aSAndreas Gohr public function resolveId($id, $rev = '', $isDateAt = false) 332cd6cc0aSAndreas Gohr { 342cd6cc0aSAndreas Gohr global $conf; 352cd6cc0aSAndreas Gohr 362cd6cc0aSAndreas Gohr // some pre cleaning for useslash: 372cd6cc0aSAndreas Gohr if ($conf['useslash']) $id = str_replace('/', ':', $id); 382cd6cc0aSAndreas Gohr // on some systems, semicolons might be used instead of colons: 392cd6cc0aSAndreas Gohr $id = str_replace(';', ':', $id); 402cd6cc0aSAndreas Gohr 412cd6cc0aSAndreas Gohr $id = $this->resolvePrefix($id); 4279a2d784SGerrit Uitslag return $this->resolveRelatives($id); 432cd6cc0aSAndreas Gohr } 442cd6cc0aSAndreas Gohr 452cd6cc0aSAndreas Gohr /** 462cd6cc0aSAndreas Gohr * Handle IDs starting with . or ~ and prepend the proper prefix 472cd6cc0aSAndreas Gohr * 482cd6cc0aSAndreas Gohr * @param string $id 492cd6cc0aSAndreas Gohr * @return string 502cd6cc0aSAndreas Gohr */ 512cd6cc0aSAndreas Gohr protected function resolvePrefix($id) 522cd6cc0aSAndreas Gohr { 53c4055b85SAndreas Gohr if ($id === '') return $id; 54c4055b85SAndreas Gohr 552cd6cc0aSAndreas Gohr // relative to current page (makes the current page a start page) 562cd6cc0aSAndreas Gohr if ($id[0] === '~') { 572cd6cc0aSAndreas Gohr $id = $this->contextID . ':' . substr($id, 1); 582cd6cc0aSAndreas Gohr } 592cd6cc0aSAndreas Gohr 602cd6cc0aSAndreas Gohr // relative to current namespace 612cd6cc0aSAndreas Gohr if ($id[0] === '.') { 622cd6cc0aSAndreas Gohr // normalize initial dots without a colon 632cd6cc0aSAndreas Gohr $id = preg_replace('/^((\.+:)*)(\.+)(?=[^:\.])/', '\1\3:', $id); 642cd6cc0aSAndreas Gohr $id = $this->contextNS . ':' . $id; 652cd6cc0aSAndreas Gohr } 662cd6cc0aSAndreas Gohr 672cd6cc0aSAndreas Gohr // auto-relative, because there is a context namespace but no namespace in the ID 682cd6cc0aSAndreas Gohr if ($this->contextID !== '' && strpos($id, ':') === false) { 692cd6cc0aSAndreas Gohr $id = $this->contextNS . ':' . $id; 702cd6cc0aSAndreas Gohr } 712cd6cc0aSAndreas Gohr 722cd6cc0aSAndreas Gohr return $id; 732cd6cc0aSAndreas Gohr } 742cd6cc0aSAndreas Gohr 752cd6cc0aSAndreas Gohr /** 762cd6cc0aSAndreas Gohr * Handle . and .. within IDs 772cd6cc0aSAndreas Gohr * 782cd6cc0aSAndreas Gohr * @param string $id 792cd6cc0aSAndreas Gohr * @return string 802cd6cc0aSAndreas Gohr */ 812cd6cc0aSAndreas Gohr protected function resolveRelatives($id) 822cd6cc0aSAndreas Gohr { 83*e66f8dfeSAndreas Gohr $id = rtrim($id, '.'); // trailing dots are invalid 842cd6cc0aSAndreas Gohr if ($id === '') return ''; 852cd6cc0aSAndreas Gohr $trail = ($id[-1] === ':') ? ':' : ''; // keep trailing colon 862cd6cc0aSAndreas Gohr 872cd6cc0aSAndreas Gohr $result = []; 882cd6cc0aSAndreas Gohr $parts = explode(':', $id); 892cd6cc0aSAndreas Gohr 902cd6cc0aSAndreas Gohr foreach ($parts as $dir) { 912cd6cc0aSAndreas Gohr if ($dir === '.') continue; 922cd6cc0aSAndreas Gohr if ($dir === '') continue; 932cd6cc0aSAndreas Gohr if ($dir === '..') { 942cd6cc0aSAndreas Gohr array_pop($result); 952cd6cc0aSAndreas Gohr continue; 962cd6cc0aSAndreas Gohr } 97445164b2SAndreas Gohr $result[] = $dir; 982cd6cc0aSAndreas Gohr } 992cd6cc0aSAndreas Gohr 1002cd6cc0aSAndreas Gohr $id = implode(':', $result); 1012cd6cc0aSAndreas Gohr $id .= $trail; 1022cd6cc0aSAndreas Gohr 1032cd6cc0aSAndreas Gohr return $id; 1042cd6cc0aSAndreas Gohr } 1052cd6cc0aSAndreas Gohr} 106