Removed modules/contrib/media module to allow update to the core media module
[yaffs-website] / vendor / psy / psysh / bin / build-manual
1 #!/usr/bin/env php
2 <?php
3
4 /*
5  * This file is part of Psy Shell.
6  *
7  * (c) 2012-2017 Justin Hileman
8  *
9  * For the full copyright and license information, please view the LICENSE
10  * file that was distributed with this source code.
11  */
12
13 define('WRAP_WIDTH', 100);
14
15 $count = 0;
16
17 if (count($argv) !== 3 || !is_dir($argv[1])) {
18     echo "usage: build_manual path/to/manual output_filename.db\n";
19     exit(1);
20 }
21
22 function htmlwrap($text, $width = null)
23 {
24     if ($width === null) {
25         $width = WRAP_WIDTH;
26     }
27
28     $len = strlen($text);
29
30     $return = [];
31     $lastSpace = null;
32     $inTag = false;
33     $i = $tagWidth = 0;
34     do {
35         switch (substr($text, $i, 1)) {
36             case "\n":
37                 $return[] = trim(substr($text, 0, $i));
38                 $text     = substr($text, $i);
39                 $len      = strlen($text);
40
41                 $i = $lastSpace = 0;
42                 continue;
43
44             case ' ':
45                 if (!$inTag) {
46                     $lastSpace = $i;
47                 }
48                 break;
49
50             case '<':
51                 $inTag = true;
52                 break;
53
54             case '>':
55                 $inTag = false;
56                 break;
57         }
58
59         if ($inTag) {
60             $tagWidth++;
61         }
62
63         $i++;
64
65         if (!$inTag && ($i - $tagWidth > $width)) {
66             $lastSpace = $lastSpace ?: $width;
67
68             $return[] = trim(substr($text, 0, $lastSpace));
69             $text     = substr($text, $lastSpace);
70             $len      = strlen($text);
71
72             $i = $tagWidth = 0;
73         }
74     } while ($i < $len);
75
76     $return[] = trim($text);
77
78     return implode("\n", $return);
79 }
80
81 function extract_paragraphs($element)
82 {
83     $paragraphs = [];
84     foreach ($element->getElementsByTagName('para') as $p) {
85         $text = '';
86         foreach ($p->childNodes as $child) {
87             // @todo figure out if there's something we can do with tables.
88             if ($child instanceof DOMElement && $child->tagName === 'table') {
89                 continue;
90             }
91
92             // skip references, because ugh.
93             if (preg_match('{^\s*&[a-z][a-z\.]+;\s*$}', $child->textContent)) {
94                 continue;
95             }
96
97             $text .= $child->ownerDocument->saveXML($child);
98         }
99
100         if ($text = trim(preg_replace('{\n[ \t]+}', ' ', $text))) {
101             $paragraphs[] = $text;
102         }
103     }
104
105     return implode("\n\n", $paragraphs);
106 }
107
108 function format_doc($doc)
109 {
110     $chunks   = [];
111
112     if (!empty($doc['description'])) {
113         $chunks[] = '<comment>Description:</comment>';
114         $chunks[] = indent_text(htmlwrap(thunk_tags($doc['description']), WRAP_WIDTH - 2));
115         $chunks[] = '';
116     }
117
118     if (!empty($doc['params'])) {
119         $chunks[] = '<comment>Param:</comment>';
120
121         $typeMax = max(array_map(function ($param) {
122             return strlen($param['type']);
123         }, $doc['params']));
124
125         $max = max(array_map(function ($param) {
126             return strlen($param['name']);
127         }, $doc['params']));
128
129         $template  = '  <info>%-' . $typeMax . 's</info>  <strong>%-' . $max . 's</strong>  %s';
130         $indent    = str_repeat(' ', $typeMax + $max + 6);
131         $wrapWidth = WRAP_WIDTH - strlen($indent);
132
133         foreach ($doc['params'] as $param) {
134             $desc = indent_text(htmlwrap(thunk_tags($param['description']), $wrapWidth), $indent, false);
135             $chunks[] = sprintf($template, $param['type'], $param['name'], $desc);
136         }
137         $chunks[] = '';
138     }
139
140     if (isset($doc['return']) || isset($doc['return_type'])) {
141         $chunks[] = '<comment>Return:</comment>';
142
143         $type   = isset($doc['return_type']) ? $doc['return_type'] : 'unknown';
144         $desc   = isset($doc['return']) ? $doc['return'] : '';
145
146         $indent    = str_repeat(' ', strlen($type) + 4);
147         $wrapWidth = WRAP_WIDTH - strlen($indent);
148
149         if (!empty($desc)) {
150             $desc = indent_text(htmlwrap(thunk_tags($doc['return']), $wrapWidth), $indent, false);
151         }
152
153         $chunks[] = sprintf('  <info>%s</info>  %s', $type, $desc);
154         $chunks[] = '';
155     }
156
157     array_pop($chunks); // get rid of the trailing newline
158
159     return implode("\n", $chunks);
160 }
161
162 function thunk_tags($text)
163 {
164     $tagMap = [
165         'parameter>' => 'strong>',
166         'function>'  => 'strong>',
167         'literal>'   => 'return>',
168         'type>'      => 'info>',
169         'constant>'  => 'info>',
170     ];
171
172     $andBack = [
173         '&amp;'       => '&',
174         '&amp;true;'  => '<return>true</return>',
175         '&amp;false;' => '<return>false</return>',
176         '&amp;null;'  => '<return>null</return>',
177     ];
178
179     return strtr(strip_tags(strtr($text, $tagMap), '<strong><return><info>'), $andBack);
180 }
181
182 function indent_text($text, $indent = '  ', $leading = true)
183 {
184     return ($leading ? $indent : '') . str_replace("\n", "\n" . $indent, $text);
185 }
186
187 function find_type($xml, $paramName)
188 {
189     foreach ($xml->getElementsByTagName('methodparam') as $param) {
190         if ($type = $param->getElementsByTagName('type')->item(0)) {
191             if ($parameter = $param->getElementsByTagName('parameter')->item(0)) {
192                 if ($paramName === $parameter->textContent) {
193                     return $type->textContent;
194                 }
195             }
196         }
197     }
198 }
199
200 function format_function_doc($xml)
201 {
202     $doc = [];
203     $refsect1s = $xml->getElementsByTagName('refsect1');
204     foreach ($refsect1s as $refsect1) {
205         $role = $refsect1->getAttribute('role');
206         switch ($role) {
207             case 'description':
208                 $doc['description'] = extract_paragraphs($refsect1);
209
210                 if ($synopsis = $refsect1->getElementsByTagName('methodsynopsis')->item(0)) {
211                     foreach ($synopsis->childNodes as $node) {
212                         if ($node instanceof DOMElement && $node->tagName === 'type') {
213                             $doc['return_type'] = $node->textContent;
214                             break;
215                         }
216                     }
217                 }
218                 break;
219
220             case 'returnvalues':
221                 // do nothing.
222                 $doc['return'] = extract_paragraphs($refsect1);
223                 break;
224
225             case 'parameters':
226                 $params = [];
227                 $vars = $refsect1->getElementsByTagName('varlistentry');
228                 foreach ($vars as $var) {
229                     if ($name = $var->getElementsByTagName('parameter')->item(0)) {
230                         $params[] = [
231                             'name'        => '$' . $name->textContent,
232                             'type'        => find_type($xml, $name->textContent),
233                             'description' => extract_paragraphs($var),
234                         ];
235                     }
236                 }
237
238                 $doc['params'] = $params;
239                 break;
240         }
241     }
242
243     // and the purpose
244     if ($purpose = $xml->getElementsByTagName('refpurpose')->item(0)) {
245         $desc = htmlwrap($purpose->textContent);
246         if (isset($doc['description'])) {
247             $desc .= "\n\n" . $doc['description'];
248         }
249
250         $doc['description'] = trim($desc);
251     }
252
253     $ids = [];
254     foreach ($xml->getElementsByTagName('refname') as $ref) {
255         $ids[] = $ref->textContent;
256     }
257
258     return [$ids, format_doc($doc)];
259 }
260
261 function format_class_doc($xml)
262 {
263     // @todo implement this
264     return [[], null];
265 }
266
267 $dir = new RecursiveDirectoryIterator($argv[1]);
268 $filter = new RecursiveCallbackFilterIterator($dir, function ($current, $key, $iterator) {
269     return $current->getFilename()[0] !== '.' &&
270         ($current->isDir() || $current->getExtension() === 'xml') &&
271         strpos($current->getFilename(), 'entities.') !== 0 &&
272         $current->getFilename() !== 'pdo_4d'; // Temporarily blacklist this one, the docs are weird.
273 });
274 $iterator = new RecursiveIteratorIterator($filter);
275
276 $docs = [];
277 foreach ($iterator as $file) {
278     $xmlstr = str_replace('&', '&amp;', file_get_contents($file));
279
280     $xml = new DOMDocument();
281     $xml->preserveWhiteSpace = false;
282
283     if (!@$xml->loadXml($xmlstr)) {
284         echo "XML Parse Error: $file\n";
285         continue;
286     }
287
288     if ($xml->getElementsByTagName('refentry')->length !== 0) {
289         list($ids, $doc) = format_function_doc($xml);
290     } elseif ($xml->getElementsByTagName('classref')->length !== 0) {
291         list($ids, $doc) = format_class_doc($xml);
292     } else {
293         $ids = [];
294         $doc = null;
295     }
296
297     foreach ($ids as $id) {
298         $docs[$id] = $doc;
299     }
300 }
301
302 if (is_file($argv[2])) {
303     unlink($argv[2]);
304 }
305
306 $db = new PDO('sqlite:' . $argv[2]);
307
308 $db->query('CREATE TABLE php_manual (id char(256) PRIMARY KEY, doc TEXT)');
309 $cmd = $db->prepare('INSERT INTO php_manual (id, doc) VALUES (?, ?)');
310 foreach ($docs as $id => $doc) {
311     $cmd->execute([$id, $doc]);
312 }