annotate vendor/psy/psysh/bin/build-manual @ 13:5fb285c0d0e3

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