1<?php
2
3namespace dokuwiki\plugin\xref;
4
5/**
6 * Figure out what to send to Grok to hopefully show the right, single hit
7 */
8class Heuristics
9{
10
11    /** @var string the definition to search */
12    protected $def = '';
13    /** @var string the path to use */
14    protected $path = '';
15    /** @var array deprecated classes and their replacements */
16    protected $deprecations;
17
18    /**
19     * Try to gues what the given reference means and how to best search for it
20     *
21     * @param string $reference
22     */
23    public function __construct($reference)
24    {
25        $this->loadDeprecations();
26
27        if ($reference !== '') $reference = $this->checkDeprecation($reference);
28        if ($reference !== '') $reference = $this->checkHash($reference);
29        if ($reference !== '') $reference = $this->checkFilename($reference);
30        if ($reference !== '') $reference = $this->checkNamespace($reference);
31        if ($reference !== '') $reference = $this->checkClassPrefix($reference);
32        if ($reference !== '') $reference = $this->checkVariable($reference);
33        if ($reference !== '') $reference = $this->checkFunction($reference);
34        if ($reference !== '') $reference = $this->checkPSRClass($reference);
35        if ($reference !== '') $this->def = $reference;
36    }
37
38    /**
39     * @return string
40     */
41    public function getDef()
42    {
43        return trim(preg_replace('/[^\w]+/', '', $this->def));
44    }
45
46    /**
47     * @return string
48     */
49    public function getPath()
50    {
51        return trim(preg_replace('/[^\w.]+/', ' ', $this->path));
52    }
53
54    /**
55     * @return array
56     */
57    public function getDeprecations()
58    {
59        return $this->deprecations;
60    }
61
62    /**
63     * Replace deprecated classes
64     *
65     * @param string $reference
66     * @return string
67     */
68    protected function checkDeprecation($reference)
69    {
70        if (isset($this->deprecations[$reference])) {
71            return $this->deprecations[$reference];
72        }
73        return $reference;
74    }
75
76    /**
77     * Handle things in the form path#symbol
78     *
79     * @param string $reference
80     * @return string
81     */
82    protected function checkHash($reference)
83    {
84        if (strpos($reference, '#') === false) return $reference;
85        list($this->path, $this->def) = explode('#', $reference, 2);
86        return '';
87    }
88
89    /**
90     * Known file extension?
91     *
92     * @param string $reference
93     * @return mixed|string
94     */
95    protected function checkFilename($reference)
96    {
97        if (preg_match('/\.(php|js|css|html)$/', $reference)) {
98            $this->def = '';
99            $this->path = $reference;
100            return '';
101        }
102        return $reference;
103    }
104
105    /**
106     * Namespaces are paths
107     *
108     * @param string $reference
109     * @return string
110     */
111    protected function checkNamespace($reference)
112    {
113        if (strpos($reference, '\\') === false) return $reference;
114
115        $parts = explode('\\', $reference);
116        $parts = array_values(array_filter($parts));
117        $reference = array_pop($parts); // last part may be more than a class
118
119        // our classes are in inc
120        if ($parts[0] == 'dokuwiki') $parts[0] = 'inc';
121
122        $this->path = join(' ', $parts);
123
124        return $reference;
125    }
126
127    /**
128     * Is there something called on a class?
129     */
130    protected function checkClassPrefix($reference)
131    {
132        if (
133            strpos($reference, '::') === false &&
134            strpos($reference, '->') === false
135        ) {
136            return $reference;
137        }
138        list($class, $reference) = preg_split('/(::|->)/', $reference, 2);
139
140        $this->path .= ' ' . $class;
141        $this->def = $reference;
142        return '';
143    }
144
145    /**
146     * Clearly a variable
147     *
148     * @param string $reference
149     * @return string
150     */
151    protected function checkVariable($reference)
152    {
153        if ($reference[0] == '$') {
154            $this->def = $reference;
155            return '';
156        }
157        return $reference;
158    }
159
160    /**
161     * It's a function
162     *
163     * @param string $reference
164     * @return string
165     */
166    protected function checkFunction($reference)
167    {
168        if (substr($reference, -2) == '()') {
169            $this->def = $reference;
170            return '';
171        }
172        if (preg_match('/\(.+?\)$/', $reference)) {
173            [$reference, /* $arguments */] = explode('(', $reference, 2);
174            $this->def = $reference;
175            return '';
176        }
177        return $reference;
178    }
179
180    /**
181     * Upercase followed by lowercase letter, must be a class
182     *
183     * Those are in their own files, so add it to the path
184     * @param $reference
185     * @return mixed|string
186     */
187    protected function checkPSRClass($reference)
188    {
189        if (preg_match('/^[A-Z][a-z]/', $reference)) {
190            $this->def = $reference;
191            $this->path .= ' ' . $reference;
192            return '';
193        }
194        return $reference;
195    }
196
197    /**
198     * Load deprecated classes info
199     */
200    protected function loadDeprecations()
201    {
202        $this->deprecations = [];
203
204        // class aliases
205        $legacy = file_get_contents(DOKU_INC . 'inc/legacy.php');
206        if (preg_match_all('/class_alias\(\'([^\']+)\', *\'([^\']+)\'\)/', $legacy, $matches, PREG_SET_ORDER)) {
207            foreach ($matches as $match) {
208                $this->deprecations[$match[2]] = $match[1];
209            }
210        }
211
212        // deprecated classes
213        $deprecations = file_get_contents(DOKU_INC . 'inc/deprecated.php');
214        if (preg_match_all('/class (.+?) extends (\\\\dokuwiki\\\\.+?)(\s|$|{)/', $deprecations, $matches, PREG_SET_ORDER)) {
215            foreach ($matches as $match) {
216                $this->deprecations[$match[1]] = $match[2];
217            }
218        }
219    }
220
221}
222