xref: /dokuwiki/inc/File/Resolver.php (revision c4055b85c837b6e07522adc69109b9fad9eb7c89)
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
112cd6cc0aSAndreas Gohr    /** @var string context page ID */
122cd6cc0aSAndreas Gohr    protected $contextID;
132cd6cc0aSAndreas Gohr    /** @var string namespace of context page ID */
142cd6cc0aSAndreas Gohr    protected $contextNS;
152cd6cc0aSAndreas Gohr
162cd6cc0aSAndreas Gohr    /**
172cd6cc0aSAndreas Gohr     * @param string $contextID the current pageID that's the context to resolve relative IDs to
182cd6cc0aSAndreas Gohr     */
192cd6cc0aSAndreas Gohr    public function __construct($contextID)
202cd6cc0aSAndreas Gohr    {
212cd6cc0aSAndreas Gohr        $this->contextID = $contextID;
222cd6cc0aSAndreas Gohr        $this->contextNS = (string)getNS($contextID);
232cd6cc0aSAndreas Gohr    }
242cd6cc0aSAndreas Gohr
252cd6cc0aSAndreas Gohr    /**
262cd6cc0aSAndreas Gohr     * Resolves a given ID to be absolute
272cd6cc0aSAndreas Gohr     *
282cd6cc0aSAndreas Gohr     * @param string $id The ID to resolve
292cd6cc0aSAndreas Gohr     * @param string|int|false $rev The revision time to use when resolving
302cd6cc0aSAndreas Gohr     * @param bool $isDateAt Is the given revision only a datetime hint not an exact revision?
312cd6cc0aSAndreas Gohr     * @return string
322cd6cc0aSAndreas Gohr     */
332cd6cc0aSAndreas Gohr    public function resolveId($id, $rev = '', $isDateAt = false)
342cd6cc0aSAndreas Gohr    {
352cd6cc0aSAndreas Gohr        global $conf;
362cd6cc0aSAndreas Gohr
372cd6cc0aSAndreas Gohr        // some pre cleaning for useslash:
382cd6cc0aSAndreas Gohr        if ($conf['useslash']) $id = str_replace('/', ':', $id);
392cd6cc0aSAndreas Gohr        // on some systems, semicolons might be used instead of colons:
402cd6cc0aSAndreas Gohr        $id = str_replace(';', ':', $id);
412cd6cc0aSAndreas Gohr
422cd6cc0aSAndreas Gohr        $id = $this->resolvePrefix($id);
4379a2d784SGerrit Uitslag        return $this->resolveRelatives($id);
442cd6cc0aSAndreas Gohr    }
452cd6cc0aSAndreas Gohr
462cd6cc0aSAndreas Gohr    /**
472cd6cc0aSAndreas Gohr     * Handle IDs starting with . or ~ and prepend the proper prefix
482cd6cc0aSAndreas Gohr     *
492cd6cc0aSAndreas Gohr     * @param string $id
502cd6cc0aSAndreas Gohr     * @return string
512cd6cc0aSAndreas Gohr     */
522cd6cc0aSAndreas Gohr    protected function resolvePrefix($id)
532cd6cc0aSAndreas Gohr    {
54*c4055b85SAndreas Gohr        if($id === '') return $id;
55*c4055b85SAndreas Gohr
562cd6cc0aSAndreas Gohr        // relative to current page (makes the current page a start page)
572cd6cc0aSAndreas Gohr        if ($id[0] === '~') {
582cd6cc0aSAndreas Gohr            $id = $this->contextID . ':' . substr($id, 1);
592cd6cc0aSAndreas Gohr        }
602cd6cc0aSAndreas Gohr
612cd6cc0aSAndreas Gohr        // relative to current namespace
622cd6cc0aSAndreas Gohr        if ($id[0] === '.') {
632cd6cc0aSAndreas Gohr            // normalize initial dots without a colon
642cd6cc0aSAndreas Gohr            $id = preg_replace('/^((\.+:)*)(\.+)(?=[^:\.])/', '\1\3:', $id);
652cd6cc0aSAndreas Gohr            $id = $this->contextNS . ':' . $id;
662cd6cc0aSAndreas Gohr        }
672cd6cc0aSAndreas Gohr
682cd6cc0aSAndreas Gohr        // auto-relative, because there is a context namespace but no namespace in the ID
692cd6cc0aSAndreas Gohr        if ($this->contextID !== '' && strpos($id, ':') === false) {
702cd6cc0aSAndreas Gohr            $id = $this->contextNS . ':' . $id;
712cd6cc0aSAndreas Gohr        }
722cd6cc0aSAndreas Gohr
732cd6cc0aSAndreas Gohr        return $id;
742cd6cc0aSAndreas Gohr    }
752cd6cc0aSAndreas Gohr
762cd6cc0aSAndreas Gohr    /**
772cd6cc0aSAndreas Gohr     * Handle . and .. within IDs
782cd6cc0aSAndreas Gohr     *
792cd6cc0aSAndreas Gohr     * @param string $id
802cd6cc0aSAndreas Gohr     * @return string
812cd6cc0aSAndreas Gohr     */
822cd6cc0aSAndreas Gohr    protected function resolveRelatives($id)
832cd6cc0aSAndreas Gohr    {
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            }
972cd6cc0aSAndreas Gohr            array_push($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
1062cd6cc0aSAndreas Gohr}
107