Completed
Pull Request — master (#3272)
by Andreas
03:04
created

Resolver::resolvePrefix()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

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